Browse Source

allow to disable SFTP service

Fixes #228
Nicola Murino 4 years ago
parent
commit
0609188d3f

+ 3 - 2
cmd/portable.go

@@ -206,7 +206,8 @@ func init() {
 This can be an absolute path or a path
 relative to the current directory
 `)
-	portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, "0 means a random unprivileged port")
+	portableCmd.Flags().IntVarP(&portableSFTPDPort, "sftpd-port", "s", 0, `0 means a random unprivileged port,
+< 0 disabled`)
 	portableCmd.Flags().IntVar(&portableFTPDPort, "ftpd-port", -1, `0 means a random unprivileged port,
 < 0 disabled`)
 	portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
@@ -237,7 +238,7 @@ The format is:
 /dir::pattern1,pattern2.
 For example: "/somedir::*.jpg,a*b?.png"`)
 	portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", false,
-		`Advertise SFTP/FTP service using
+		`Advertise configured services using
 multicast DNS`)
 	portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false,
 		`If the SFTP/FTP service is

+ 1 - 1
common/common.go

@@ -582,7 +582,7 @@ type ConnectionStatus struct {
 	Protocol string `json:"protocol"`
 	// active uploads/downloads
 	Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
-	// SSH command or WevDAV method
+	// SSH command or WebDAV method
 	Command string `json:"command,omitempty"`
 }
 

+ 15 - 0
config/config.go

@@ -240,6 +240,21 @@ func GetHTTPConfig() httpclient.Config {
 	return globalConf.HTTPConfig
 }
 
+// HasServicesToStart returns true if the config defines at least a service to start.
+// Supported services are SFTP, FTP and WebDAV
+func HasServicesToStart() bool {
+	if globalConf.SFTPD.BindPort > 0 {
+		return true
+	}
+	if globalConf.FTPD.BindPort > 0 {
+		return true
+	}
+	if globalConf.WebDAVD.BindPort > 0 {
+		return true
+	}
+	return false
+}
+
 func getRedactedGlobalConf() globalConfig {
 	conf := globalConf
 	conf.ProviderConf.Password = "[redacted]"

+ 27 - 0
config/config_test.go

@@ -281,6 +281,33 @@ func TestSetGetConfig(t *testing.T) {
 	assert.Equal(t, webDavConf.CertificateKeyFile, config.GetWebDAVDConfig().CertificateKeyFile)
 }
 
+func TestServiceToStart(t *testing.T) {
+	configDir := ".."
+	err := config.LoadConfig(configDir, configName)
+	assert.NoError(t, err)
+	assert.True(t, config.HasServicesToStart())
+	sftpdConf := config.GetSFTPDConfig()
+	sftpdConf.BindPort = 0
+	config.SetSFTPDConfig(sftpdConf)
+	assert.False(t, config.HasServicesToStart())
+	ftpdConf := config.GetFTPDConfig()
+	ftpdConf.BindPort = 2121
+	config.SetFTPDConfig(ftpdConf)
+	assert.True(t, config.HasServicesToStart())
+	ftpdConf.BindPort = 0
+	config.SetFTPDConfig(ftpdConf)
+	webdavdConf := config.GetWebDAVDConfig()
+	webdavdConf.BindPort = 9000
+	config.SetWebDAVDConfig(webdavdConf)
+	assert.True(t, config.HasServicesToStart())
+	webdavdConf.BindPort = 0
+	config.SetWebDAVDConfig(webdavdConf)
+	assert.False(t, config.HasServicesToStart())
+	sftpdConf.BindPort = 2022
+	config.SetSFTPDConfig(sftpdConf)
+	assert.True(t, config.HasServicesToStart())
+}
+
 func TestConfigFromEnv(t *testing.T) {
 	os.Setenv("SFTPGO_SFTPD__BIND_ADDRESS", "127.0.0.1")
 	os.Setenv("SFTPGO_DATA_PROVIDER__PASSWORD_HASHING__ARGON2_OPTIONS__ITERATIONS", "41")

+ 1 - 1
docs/full-configuration.md

@@ -64,7 +64,7 @@ The configuration file contains the following sections:
     - If `proxy_protocol` is set to 2 and we receive a proxy header from an IP that is not in the list then the connection will be rejected
   - `post_connect_hook`, string. Absolute path to the command to execute or HTTP URL to notify. See [Post connect hook](./post-connect-hook.md) for more details. Leave empty to disable
 - **"sftpd"**, the configuration for the SFTP server
-  - `bind_port`, integer. The port used for serving SFTP requests. Default: 2022
+  - `bind_port`, integer. The port used for serving SFTP requests. 0 means disabled. Default: 2022
   - `bind_address`, string. Leave blank to listen on all available network interfaces. Default: ""
   - `idle_timeout`, integer. Deprecated, please use the same key in `common` section.
   - `max_auth_tries` integer. Maximum number of authentication attempts permitted per connection. If set to a negative number, the number of attempts is unlimited. If set to zero, the number of attempts is limited to 6.

+ 3 - 2
docs/portable-mode.md

@@ -19,7 +19,7 @@ Flags:
                                         advertised via multicast DNS, this
                                         flag allows to put username/password
                                         inside the advertised TXT record
-  -S, --advertise-service               Advertise SFTP/FTP service using
+  -S, --advertise-service               Advertise configured services using
                                         multicast DNS
       --allowed-patterns stringArray    Allowed file patterns case insensitive.
                                         The format is:
@@ -88,7 +88,8 @@ Flags:
                                         parallel (default 2)
       --s3-upload-part-size int         The buffer size for multipart uploads
                                         (MB) (default 5)
-  -s, --sftpd-port int                  0 means a random unprivileged port
+  -s, --sftpd-port int                  0 means a random unprivileged port,
+                                        < 0 disabled
   -c, --ssh-commands strings            SSH commands to enable.
                                         "*" means any supported SSH command
                                         including scp

+ 21 - 10
service/service.go

@@ -2,6 +2,7 @@
 package service
 
 import (
+	"errors"
 	"fmt"
 	"io/ioutil"
 	"os"
@@ -73,6 +74,12 @@ func (s *Service) Start() error {
 			logger.Error(logSender, "", "error loading configuration: %v", err)
 		}
 	}
+	if !config.HasServicesToStart() {
+		infoString := "No service configured, nothing to do"
+		logger.Info(logSender, "", infoString)
+		logger.InfoToConsole(infoString)
+		return errors.New(infoString)
+	}
 
 	common.Initialize(config.GetCommonConfig())
 
@@ -115,15 +122,19 @@ func (s *Service) startServices() {
 	httpdConf := config.GetHTTPDConfig()
 	webDavDConf := config.GetWebDAVDConfig()
 
-	go func() {
-		logger.Debug(logSender, "", "initializing SFTP server with config %+v", sftpdConf)
-		if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
-			logger.Error(logSender, "", "could not start SFTP server: %v", err)
-			logger.ErrorToConsole("could not start SFTP server: %v", err)
-			s.Error = err
-		}
-		s.Shutdown <- true
-	}()
+	if sftpdConf.BindPort > 0 {
+		go func() {
+			logger.Debug(logSender, "", "initializing SFTP server with config %+v", sftpdConf)
+			if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
+				logger.Error(logSender, "", "could not start SFTP server: %v", err)
+				logger.ErrorToConsole("could not start SFTP server: %v", err)
+				s.Error = err
+			}
+			s.Shutdown <- true
+		}()
+	} else {
+		logger.Debug(logSender, "", "SFTP server not started, disabled in config file")
+	}
 
 	if httpdConf.BindPort > 0 {
 		go func() {
@@ -162,7 +173,7 @@ func (s *Service) startServices() {
 			s.Shutdown <- true
 		}()
 	} else {
-		logger.Debug(logSender, "", "WevDAV server not started, disabled in config file")
+		logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
 	}
 }
 

+ 40 - 31
service/service_portable.go

@@ -32,21 +32,7 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
 	if err != nil {
 		fmt.Printf("error loading configuration file: %v using defaults\n", err)
 	}
-	if len(s.PortableUser.Username) == 0 {
-		s.PortableUser.Username = "user"
-	}
-	printablePassword := ""
-	if len(s.PortableUser.Password) > 0 {
-		printablePassword = "[redacted]"
-	}
-	if len(s.PortableUser.PublicKeys) == 0 && len(s.PortableUser.Password) == 0 {
-		var b strings.Builder
-		for i := 0; i < 8; i++ {
-			b.WriteRune(chars[rand.Intn(len(chars))])
-		}
-		s.PortableUser.Password = b.String()
-		printablePassword = s.PortableUser.Password
-	}
+	printablePassword := s.configurePortableUser()
 	dataProviderConf := config.GetProviderConf()
 	dataProviderConf.Driver = dataprovider.MemoryDataProviderName
 	dataProviderConf.Name = ""
@@ -57,16 +43,19 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
 	config.SetHTTPDConfig(httpdConf)
 	sftpdConf := config.GetSFTPDConfig()
 	sftpdConf.MaxAuthTries = 12
-	if sftpdPort > 0 {
-		sftpdConf.BindPort = sftpdPort
-	} else {
-		// dynamic ports starts from 49152
-		sftpdConf.BindPort = 49152 + rand.Intn(15000)
-	}
-	if utils.IsStringInSlice("*", enabledSSHCommands) {
-		sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
-	} else {
-		sftpdConf.EnabledSSHCommands = enabledSSHCommands
+	sftpdConf.BindPort = sftpdPort
+	if sftpdPort >= 0 {
+		if sftpdPort > 0 {
+			sftpdConf.BindPort = sftpdPort
+		} else {
+			// dynamic ports starts from 49152
+			sftpdConf.BindPort = 49152 + rand.Intn(15000)
+		}
+		if utils.IsStringInSlice("*", enabledSSHCommands) {
+			sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
+		} else {
+			sftpdConf.EnabledSSHCommands = enabledSSHCommands
+		}
 	}
 	config.SetSFTPDConfig(sftpdConf)
 
@@ -102,8 +91,8 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
 
 	s.advertiseServices(advertiseService, advertiseCredentials)
 
-	logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
-		"permissions: %+v, enabled ssh commands: %v file patterns filters: %+v %v", sftpdConf.BindPort, s.PortableUser.Username,
+	logger.InfoToConsole("Portable mode ready, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
+		"permissions: %+v, enabled ssh commands: %v file patterns filters: %+v %v", s.PortableUser.Username,
 		printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
 		sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FilePatterns, s.getServiceOptionalInfoString())
 	return nil
@@ -111,15 +100,15 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
 
 func (s *Service) getServiceOptionalInfoString() string {
 	var info strings.Builder
+	if config.GetSFTPDConfig().BindPort > 0 {
+		info.WriteString(fmt.Sprintf("SFTP port: %v ", config.GetSFTPDConfig().BindPort))
+	}
 	if config.GetFTPDConfig().BindPort > 0 {
 		info.WriteString(fmt.Sprintf("FTP port: %v ", config.GetFTPDConfig().BindPort))
 	}
 	if config.GetWebDAVDConfig().BindPort > 0 {
-		if info.Len() == 0 {
-			info.WriteString(" ")
-		}
 		scheme := "http"
-		if len(config.GetWebDAVDConfig().CertificateFile) > 0 && len(config.GetWebDAVDConfig().CertificateKeyFile) > 0 {
+		if config.GetWebDAVDConfig().CertificateFile != "" && config.GetWebDAVDConfig().CertificateKeyFile != "" {
 			scheme = "https"
 		}
 		info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
@@ -230,3 +219,23 @@ func (s *Service) getPortableDirToServe() string {
 	}
 	return dirToServe
 }
+
+// configures the portable user and return the printable password if any
+func (s *Service) configurePortableUser() string {
+	if s.PortableUser.Username == "" {
+		s.PortableUser.Username = "user"
+	}
+	printablePassword := ""
+	if len(s.PortableUser.Password) > 0 {
+		printablePassword = "[redacted]"
+	}
+	if len(s.PortableUser.PublicKeys) == 0 && len(s.PortableUser.Password) == 0 {
+		var b strings.Builder
+		for i := 0; i < 8; i++ {
+			b.WriteRune(chars[rand.Intn(len(chars))])
+		}
+		s.PortableUser.Password = b.String()
+		printablePassword = s.PortableUser.Password
+	}
+	return printablePassword
+}

+ 1 - 1
webdavd/webdavd.go

@@ -73,7 +73,7 @@ type Configuration struct {
 // Initialize configures and starts the WebDav server
 func (c *Configuration) Initialize(configDir string) error {
 	var err error
-	logger.Debug(logSender, "", "initializing WevDav server with config %+v", *c)
+	logger.Debug(logSender, "", "initializing WebDAV server with config %+v", *c)
 	mimeTypeCache = mimeCache{
 		maxSize:   c.Cache.MimeTypes.MaxSize,
 		mimeTypes: make(map[string]string),