portable mode: add WebDAV support

This commit is contained in:
Nicola Murino 2020-08-17 14:08:08 +02:00
parent f3228713bc
commit bbc8c091e6
5 changed files with 62 additions and 9 deletions

View file

@ -53,6 +53,9 @@ var (
portableFTPDPort int portableFTPDPort int
portableFTPSCert string portableFTPSCert string
portableFTPSKey string portableFTPSKey string
portableWebDAVPort int
portableWebDAVCert string
portableWebDAVKey string
portableCmd = &cobra.Command{ portableCmd = &cobra.Command{
Use: "portable", Use: "portable",
Short: "Serve a single directory", Short: "Serve a single directory",
@ -100,6 +103,14 @@ Please take a look at the usage below to customize the serving parameters`,
os.Exit(1) os.Exit(1)
} }
} }
if portableWebDAVPort > 0 && len(portableWebDAVCert) > 0 && len(portableWebDAVKey) > 0 {
_, err := common.NewCertManager(portableWebDAVCert, portableWebDAVKey, "WebDAV portable")
if err != nil {
fmt.Printf("Unable to load WebDAV key pair, cert file %#v key file %#v error: %v\n",
portableWebDAVCert, portableWebDAVKey, err)
os.Exit(1)
}
}
service := service.Service{ service := service.Service{
ConfigDir: filepath.Clean(defaultConfigDir), ConfigDir: filepath.Clean(defaultConfigDir),
ConfigFile: defaultConfigName, ConfigFile: defaultConfigName,
@ -145,8 +156,8 @@ Please take a look at the usage below to customize the serving parameters`,
}, },
}, },
} }
if err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableSSHCommands, portableAdvertiseService, if err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableWebDAVPort, portableSSHCommands, portableAdvertiseService,
portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey); err == nil { portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey, portableWebDAVCert, portableWebDAVKey); err == nil {
service.Wait() service.Wait()
if service.Error == nil { if service.Error == nil {
os.Exit(0) os.Exit(0)
@ -166,6 +177,8 @@ 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")
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`)
portableCmd.Flags().IntVar(&portableWebDAVPort, "webdav-port", -1, `0 means a random unprivileged port,
< 0 disabled`) < 0 disabled`)
portableCmd.Flags().StringSliceVarP(&portableSSHCommands, "ssh-commands", "c", sftpd.GetDefaultSSHCommands(), portableCmd.Flags().StringSliceVarP(&portableSSHCommands, "ssh-commands", "c", sftpd.GetDefaultSSHCommands(),
`SSH commands to enable. `SSH commands to enable.
@ -228,6 +241,10 @@ a JSON credentials file, 1 automatic
`) `)
portableCmd.Flags().StringVar(&portableFTPSCert, "ftpd-cert", "", "Path to the certificate file for FTPS") portableCmd.Flags().StringVar(&portableFTPSCert, "ftpd-cert", "", "Path to the certificate file for FTPS")
portableCmd.Flags().StringVar(&portableFTPSKey, "ftpd-key", "", "Path to the key file for FTPS") portableCmd.Flags().StringVar(&portableFTPSKey, "ftpd-key", "", "Path to the key file for FTPS")
portableCmd.Flags().StringVar(&portableWebDAVCert, "webdav-cert", "", `Path to the certificate file for WebDAV
over HTTPS`)
portableCmd.Flags().StringVar(&portableWebDAVKey, "webdav-key", "", `Path to the key file for WebDAV over
HTTPS`)
rootCmd.AddCommand(portableCmd) rootCmd.AddCommand(portableCmd)
} }

View file

@ -78,6 +78,12 @@ Flags:
(default [md5sum,sha1sum,cd,pwd,scp]) (default [md5sum,sha1sum,cd,pwd,scp])
-u, --username string Leave empty to use an auto generated -u, --username string Leave empty to use an auto generated
value value
--webdav-cert string Path to the certificate file for WebDAV
over HTTPS
--webdav-key string Path to the key file for WebDAV over
HTTPS
--webdav-port int 0 means a random unprivileged port,
< 0 disabled (default -1)
``` ```
In portable mode, SFTPGo can advertise the SFTP/FTP services and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it. In portable mode, SFTPGo can advertise the SFTP/FTP services and, optionally, the credentials via multicast DNS, so there is a standard way to discover the service and to automatically connect to it.

View file

@ -355,8 +355,10 @@ func TestAddUserInvalidFilters(t *testing.T) {
u.Filters.FileExtensions = nil u.Filters.FileExtensions = nil
u.Filters.DeniedProtocols = []string{"invalid"} u.Filters.DeniedProtocols = []string{"invalid"}
_, _, err = httpd.AddUser(u, http.StatusBadRequest) _, _, err = httpd.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
u.Filters.DeniedProtocols = dataprovider.ValidProtocols u.Filters.DeniedProtocols = dataprovider.ValidProtocols
_, _, err = httpd.AddUser(u, http.StatusBadRequest) _, _, err = httpd.AddUser(u, http.StatusBadRequest)
assert.NoError(t, err)
} }
func TestAddUserInvalidFsConfig(t *testing.T) { func TestAddUserInvalidFsConfig(t *testing.T) {

View file

@ -23,8 +23,8 @@ import (
) )
// StartPortableMode starts the service in portable mode // StartPortableMode starts the service in portable mode
func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool, func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool,
ftpsCert, ftpsKey string) error { ftpsCert, ftpsKey, webDavCert, webDavKey string) error {
if s.PortableMode != 1 { if s.PortableMode != 1 {
return fmt.Errorf("service is not configured for portable mode") return fmt.Errorf("service is not configured for portable mode")
} }
@ -81,6 +81,18 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands [
config.SetFTPDConfig(ftpConf) config.SetFTPDConfig(ftpConf)
} }
if webdavPort > 0 {
webDavConf := config.GetWebDAVDConfig()
if webdavPort > 0 {
webDavConf.BindPort = webdavPort
} else {
webDavConf.BindPort = 49152 + rand.Intn(15000)
}
webDavConf.CertificateFile = webDavCert
webDavConf.CertificateKeyFile = webDavKey
config.SetWebDAVDConfig(webDavConf)
}
err = s.Start() err = s.Start()
if err != nil { if err != nil {
return err return err
@ -88,17 +100,32 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands [
s.advertiseServices(advertiseService, advertiseCredentials) s.advertiseServices(advertiseService, advertiseCredentials)
var ftpInfo string
if config.GetFTPDConfig().BindPort > 0 {
ftpInfo = fmt.Sprintf("FTP port: %v", config.GetFTPDConfig().BindPort)
}
logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+ logger.InfoToConsole("Portable mode ready, SFTP port: %v, user: %#v, password: %#v, public keys: %v, directory: %#v, "+
"permissions: %+v, enabled ssh commands: %v file extensions filters: %+v %v", sftpdConf.BindPort, s.PortableUser.Username, "permissions: %+v, enabled ssh commands: %v file extensions filters: %+v %v", sftpdConf.BindPort, 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.FileExtensions, ftpInfo) sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, s.getServiceOptionalInfoString())
return nil return nil
} }
func (s *Service) getServiceOptionalInfoString() string {
var info strings.Builder
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 {
scheme = "https"
}
info.WriteString(fmt.Sprintf("WebDAV URL: %v://<your IP>:%v/%v",
scheme, config.GetWebDAVDConfig().BindPort, s.PortableUser.Username))
}
return info.String()
}
func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) { func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) {
var mDNSServiceSFTP *zeroconf.Server var mDNSServiceSFTP *zeroconf.Server
var mDNSServiceFTP *zeroconf.Server var mDNSServiceFTP *zeroconf.Server

View file

@ -576,6 +576,7 @@ func TestDeniedProtocols(t *testing.T) {
user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP} user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP}
user, _, err = httpd.UpdateUser(user, http.StatusOK) user, _, err = httpd.UpdateUser(user, http.StatusOK)
assert.NoError(t, err)
client = getWebDavClient(user) client = getWebDavClient(user)
assert.NoError(t, checkBasicFunc(client)) assert.NoError(t, checkBasicFunc(client))