mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-22 15:40:23 +00:00
parent
99cd1ccfe5
commit
0609188d3f
9 changed files with 112 additions and 48 deletions
|
@ -206,7 +206,8 @@ func init() {
|
||||||
This can be an absolute path or a path
|
This can be an absolute path or a path
|
||||||
relative to the current directory
|
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,
|
portableCmd.Flags().IntVar(&portableFTPDPort, "ftpd-port", -1, `0 means a random unprivileged port,
|
||||||
< 0 disabled`)
|
< 0 disabled`)
|
||||||
portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
|
portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
|
||||||
|
@ -237,7 +238,7 @@ The format is:
|
||||||
/dir::pattern1,pattern2.
|
/dir::pattern1,pattern2.
|
||||||
For example: "/somedir::*.jpg,a*b?.png"`)
|
For example: "/somedir::*.jpg,a*b?.png"`)
|
||||||
portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", false,
|
portableCmd.Flags().BoolVarP(&portableAdvertiseService, "advertise-service", "S", false,
|
||||||
`Advertise SFTP/FTP service using
|
`Advertise configured services using
|
||||||
multicast DNS`)
|
multicast DNS`)
|
||||||
portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false,
|
portableCmd.Flags().BoolVarP(&portableAdvertiseCredentials, "advertise-credentials", "C", false,
|
||||||
`If the SFTP/FTP service is
|
`If the SFTP/FTP service is
|
||||||
|
|
|
@ -582,7 +582,7 @@ type ConnectionStatus struct {
|
||||||
Protocol string `json:"protocol"`
|
Protocol string `json:"protocol"`
|
||||||
// active uploads/downloads
|
// active uploads/downloads
|
||||||
Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
|
Transfers []ConnectionTransfer `json:"active_transfers,omitempty"`
|
||||||
// SSH command or WevDAV method
|
// SSH command or WebDAV method
|
||||||
Command string `json:"command,omitempty"`
|
Command string `json:"command,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -240,6 +240,21 @@ func GetHTTPConfig() httpclient.Config {
|
||||||
return globalConf.HTTPConfig
|
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 {
|
func getRedactedGlobalConf() globalConfig {
|
||||||
conf := globalConf
|
conf := globalConf
|
||||||
conf.ProviderConf.Password = "[redacted]"
|
conf.ProviderConf.Password = "[redacted]"
|
||||||
|
|
|
@ -281,6 +281,33 @@ func TestSetGetConfig(t *testing.T) {
|
||||||
assert.Equal(t, webDavConf.CertificateKeyFile, config.GetWebDAVDConfig().CertificateKeyFile)
|
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) {
|
func TestConfigFromEnv(t *testing.T) {
|
||||||
os.Setenv("SFTPGO_SFTPD__BIND_ADDRESS", "127.0.0.1")
|
os.Setenv("SFTPGO_SFTPD__BIND_ADDRESS", "127.0.0.1")
|
||||||
os.Setenv("SFTPGO_DATA_PROVIDER__PASSWORD_HASHING__ARGON2_OPTIONS__ITERATIONS", "41")
|
os.Setenv("SFTPGO_DATA_PROVIDER__PASSWORD_HASHING__ARGON2_OPTIONS__ITERATIONS", "41")
|
||||||
|
|
|
@ -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
|
- 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
|
- `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
|
- **"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: ""
|
- `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.
|
- `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.
|
- `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.
|
||||||
|
|
|
@ -19,7 +19,7 @@ Flags:
|
||||||
advertised via multicast DNS, this
|
advertised via multicast DNS, this
|
||||||
flag allows to put username/password
|
flag allows to put username/password
|
||||||
inside the advertised TXT record
|
inside the advertised TXT record
|
||||||
-S, --advertise-service Advertise SFTP/FTP service using
|
-S, --advertise-service Advertise configured services using
|
||||||
multicast DNS
|
multicast DNS
|
||||||
--allowed-patterns stringArray Allowed file patterns case insensitive.
|
--allowed-patterns stringArray Allowed file patterns case insensitive.
|
||||||
The format is:
|
The format is:
|
||||||
|
@ -88,7 +88,8 @@ Flags:
|
||||||
parallel (default 2)
|
parallel (default 2)
|
||||||
--s3-upload-part-size int The buffer size for multipart uploads
|
--s3-upload-part-size int The buffer size for multipart uploads
|
||||||
(MB) (default 5)
|
(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.
|
-c, --ssh-commands strings SSH commands to enable.
|
||||||
"*" means any supported SSH command
|
"*" means any supported SSH command
|
||||||
including scp
|
including scp
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
package service
|
package service
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
@ -73,6 +74,12 @@ func (s *Service) Start() error {
|
||||||
logger.Error(logSender, "", "error loading configuration: %v", err)
|
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())
|
common.Initialize(config.GetCommonConfig())
|
||||||
|
|
||||||
|
@ -115,15 +122,19 @@ func (s *Service) startServices() {
|
||||||
httpdConf := config.GetHTTPDConfig()
|
httpdConf := config.GetHTTPDConfig()
|
||||||
webDavDConf := config.GetWebDAVDConfig()
|
webDavDConf := config.GetWebDAVDConfig()
|
||||||
|
|
||||||
go func() {
|
if sftpdConf.BindPort > 0 {
|
||||||
logger.Debug(logSender, "", "initializing SFTP server with config %+v", sftpdConf)
|
go func() {
|
||||||
if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
|
logger.Debug(logSender, "", "initializing SFTP server with config %+v", sftpdConf)
|
||||||
logger.Error(logSender, "", "could not start SFTP server: %v", err)
|
if err := sftpdConf.Initialize(s.ConfigDir); err != nil {
|
||||||
logger.ErrorToConsole("could not start SFTP server: %v", err)
|
logger.Error(logSender, "", "could not start SFTP server: %v", err)
|
||||||
s.Error = err
|
logger.ErrorToConsole("could not start SFTP server: %v", err)
|
||||||
}
|
s.Error = err
|
||||||
s.Shutdown <- true
|
}
|
||||||
}()
|
s.Shutdown <- true
|
||||||
|
}()
|
||||||
|
} else {
|
||||||
|
logger.Debug(logSender, "", "SFTP server not started, disabled in config file")
|
||||||
|
}
|
||||||
|
|
||||||
if httpdConf.BindPort > 0 {
|
if httpdConf.BindPort > 0 {
|
||||||
go func() {
|
go func() {
|
||||||
|
@ -162,7 +173,7 @@ func (s *Service) startServices() {
|
||||||
s.Shutdown <- true
|
s.Shutdown <- true
|
||||||
}()
|
}()
|
||||||
} else {
|
} else {
|
||||||
logger.Debug(logSender, "", "WevDAV server not started, disabled in config file")
|
logger.Debug(logSender, "", "WebDAV server not started, disabled in config file")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -32,21 +32,7 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("error loading configuration file: %v using defaults\n", err)
|
fmt.Printf("error loading configuration file: %v using defaults\n", err)
|
||||||
}
|
}
|
||||||
if len(s.PortableUser.Username) == 0 {
|
printablePassword := s.configurePortableUser()
|
||||||
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
|
|
||||||
}
|
|
||||||
dataProviderConf := config.GetProviderConf()
|
dataProviderConf := config.GetProviderConf()
|
||||||
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
|
dataProviderConf.Driver = dataprovider.MemoryDataProviderName
|
||||||
dataProviderConf.Name = ""
|
dataProviderConf.Name = ""
|
||||||
|
@ -57,16 +43,19 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
|
||||||
config.SetHTTPDConfig(httpdConf)
|
config.SetHTTPDConfig(httpdConf)
|
||||||
sftpdConf := config.GetSFTPDConfig()
|
sftpdConf := config.GetSFTPDConfig()
|
||||||
sftpdConf.MaxAuthTries = 12
|
sftpdConf.MaxAuthTries = 12
|
||||||
if sftpdPort > 0 {
|
sftpdConf.BindPort = sftpdPort
|
||||||
sftpdConf.BindPort = sftpdPort
|
if sftpdPort >= 0 {
|
||||||
} else {
|
if sftpdPort > 0 {
|
||||||
// dynamic ports starts from 49152
|
sftpdConf.BindPort = sftpdPort
|
||||||
sftpdConf.BindPort = 49152 + rand.Intn(15000)
|
} else {
|
||||||
}
|
// dynamic ports starts from 49152
|
||||||
if utils.IsStringInSlice("*", enabledSSHCommands) {
|
sftpdConf.BindPort = 49152 + rand.Intn(15000)
|
||||||
sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
|
}
|
||||||
} else {
|
if utils.IsStringInSlice("*", enabledSSHCommands) {
|
||||||
sftpdConf.EnabledSSHCommands = enabledSSHCommands
|
sftpdConf.EnabledSSHCommands = sftpd.GetSupportedSSHCommands()
|
||||||
|
} else {
|
||||||
|
sftpdConf.EnabledSSHCommands = enabledSSHCommands
|
||||||
|
}
|
||||||
}
|
}
|
||||||
config.SetSFTPDConfig(sftpdConf)
|
config.SetSFTPDConfig(sftpdConf)
|
||||||
|
|
||||||
|
@ -102,8 +91,8 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
|
||||||
|
|
||||||
s.advertiseServices(advertiseService, advertiseCredentials)
|
s.advertiseServices(advertiseService, advertiseCredentials)
|
||||||
|
|
||||||
logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
|
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", sftpdConf.BindPort, s.PortableUser.Username,
|
"permissions: %+v, enabled ssh commands: %v file patterns filters: %+v %v", s.PortableUser.Username,
|
||||||
printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
|
printablePassword, s.PortableUser.PublicKeys, s.getPortableDirToServe(), s.PortableUser.Permissions,
|
||||||
sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FilePatterns, s.getServiceOptionalInfoString())
|
sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FilePatterns, s.getServiceOptionalInfoString())
|
||||||
return nil
|
return nil
|
||||||
|
@ -111,15 +100,15 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledS
|
||||||
|
|
||||||
func (s *Service) getServiceOptionalInfoString() string {
|
func (s *Service) getServiceOptionalInfoString() string {
|
||||||
var info strings.Builder
|
var info strings.Builder
|
||||||
|
if config.GetSFTPDConfig().BindPort > 0 {
|
||||||
|
info.WriteString(fmt.Sprintf("SFTP port: %v ", config.GetSFTPDConfig().BindPort))
|
||||||
|
}
|
||||||
if config.GetFTPDConfig().BindPort > 0 {
|
if config.GetFTPDConfig().BindPort > 0 {
|
||||||
info.WriteString(fmt.Sprintf("FTP port: %v ", config.GetFTPDConfig().BindPort))
|
info.WriteString(fmt.Sprintf("FTP port: %v ", config.GetFTPDConfig().BindPort))
|
||||||
}
|
}
|
||||||
if config.GetWebDAVDConfig().BindPort > 0 {
|
if config.GetWebDAVDConfig().BindPort > 0 {
|
||||||
if info.Len() == 0 {
|
|
||||||
info.WriteString(" ")
|
|
||||||
}
|
|
||||||
scheme := "http"
|
scheme := "http"
|
||||||
if len(config.GetWebDAVDConfig().CertificateFile) > 0 && len(config.GetWebDAVDConfig().CertificateKeyFile) > 0 {
|
if config.GetWebDAVDConfig().CertificateFile != "" && config.GetWebDAVDConfig().CertificateKeyFile != "" {
|
||||||
scheme = "https"
|
scheme = "https"
|
||||||
}
|
}
|
||||||
info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
|
info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
|
||||||
|
@ -230,3 +219,23 @@ func (s *Service) getPortableDirToServe() string {
|
||||||
}
|
}
|
||||||
return dirToServe
|
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
|
||||||
|
}
|
||||||
|
|
|
@ -73,7 +73,7 @@ type Configuration struct {
|
||||||
// Initialize configures and starts the WebDav server
|
// Initialize configures and starts the WebDav server
|
||||||
func (c *Configuration) Initialize(configDir string) error {
|
func (c *Configuration) Initialize(configDir string) error {
|
||||||
var err 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{
|
mimeTypeCache = mimeCache{
|
||||||
maxSize: c.Cache.MimeTypes.MaxSize,
|
maxSize: c.Cache.MimeTypes.MaxSize,
|
||||||
mimeTypes: make(map[string]string),
|
mimeTypes: make(map[string]string),
|
||||||
|
|
Loading…
Reference in a new issue