From bbc8c091e61817584a97c61e122b4f237d0706d3 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Mon, 17 Aug 2020 14:08:08 +0200 Subject: [PATCH] portable mode: add WebDAV support --- cmd/portable.go | 21 +++++++++++++++++-- docs/portable-mode.md | 6 ++++++ httpd/httpd_test.go | 2 ++ service/service_portable.go | 41 ++++++++++++++++++++++++++++++------- webdavd/webdavd_test.go | 1 + 5 files changed, 62 insertions(+), 9 deletions(-) diff --git a/cmd/portable.go b/cmd/portable.go index 6edb0a56..c4b4655b 100644 --- a/cmd/portable.go +++ b/cmd/portable.go @@ -53,6 +53,9 @@ var ( portableFTPDPort int portableFTPSCert string portableFTPSKey string + portableWebDAVPort int + portableWebDAVCert string + portableWebDAVKey string portableCmd = &cobra.Command{ Use: "portable", 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) } } + 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{ ConfigDir: filepath.Clean(defaultConfigDir), 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, - portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey); err == nil { + if err := service.StartPortableMode(portableSFTPDPort, portableFTPDPort, portableWebDAVPort, portableSSHCommands, portableAdvertiseService, + portableAdvertiseCredentials, portableFTPSCert, portableFTPSKey, portableWebDAVCert, portableWebDAVKey); err == nil { service.Wait() if service.Error == nil { 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().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`) portableCmd.Flags().StringSliceVarP(&portableSSHCommands, "ssh-commands", "c", sftpd.GetDefaultSSHCommands(), `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(&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) } diff --git a/docs/portable-mode.md b/docs/portable-mode.md index 2100d474..ce8cf138 100644 --- a/docs/portable-mode.md +++ b/docs/portable-mode.md @@ -78,6 +78,12 @@ Flags: (default [md5sum,sha1sum,cd,pwd,scp]) -u, --username string Leave empty to use an auto generated 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. diff --git a/httpd/httpd_test.go b/httpd/httpd_test.go index 8727c2e7..90d90131 100644 --- a/httpd/httpd_test.go +++ b/httpd/httpd_test.go @@ -355,8 +355,10 @@ func TestAddUserInvalidFilters(t *testing.T) { u.Filters.FileExtensions = nil u.Filters.DeniedProtocols = []string{"invalid"} _, _, err = httpd.AddUser(u, http.StatusBadRequest) + assert.NoError(t, err) u.Filters.DeniedProtocols = dataprovider.ValidProtocols _, _, err = httpd.AddUser(u, http.StatusBadRequest) + assert.NoError(t, err) } func TestAddUserInvalidFsConfig(t *testing.T) { diff --git a/service/service_portable.go b/service/service_portable.go index b7e07e59..48460cea 100644 --- a/service/service_portable.go +++ b/service/service_portable.go @@ -23,8 +23,8 @@ import ( ) // StartPortableMode starts the service in portable mode -func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool, - ftpsCert, ftpsKey string) error { +func (s *Service) StartPortableMode(sftpdPort, ftpPort, webdavPort int, enabledSSHCommands []string, advertiseService, advertiseCredentials bool, + ftpsCert, ftpsKey, webDavCert, webDavKey string) error { if s.PortableMode != 1 { 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) } + 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() if err != nil { return err @@ -88,17 +100,32 @@ func (s *Service) StartPortableMode(sftpdPort, ftpPort int, enabledSSHCommands [ 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, "+ "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, - sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, ftpInfo) + sftpdConf.EnabledSSHCommands, s.PortableUser.Filters.FileExtensions, s.getServiceOptionalInfoString()) 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://:%v/%v", + scheme, config.GetWebDAVDConfig().BindPort, s.PortableUser.Username)) + } + return info.String() +} + func (s *Service) advertiseServices(advertiseService, advertiseCredentials bool) { var mDNSServiceSFTP *zeroconf.Server var mDNSServiceFTP *zeroconf.Server diff --git a/webdavd/webdavd_test.go b/webdavd/webdavd_test.go index 40fcab45..bd1bd90a 100644 --- a/webdavd/webdavd_test.go +++ b/webdavd/webdavd_test.go @@ -576,6 +576,7 @@ func TestDeniedProtocols(t *testing.T) { user.Filters.DeniedProtocols = []string{common.ProtocolSSH, common.ProtocolFTP} user, _, err = httpd.UpdateUser(user, http.StatusOK) + assert.NoError(t, err) client = getWebDavClient(user) assert.NoError(t, checkBasicFunc(client))