From 7f674a7fb336779cb3b7b6589d617227046d3015 Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Sat, 26 Feb 2022 16:43:29 +0100 Subject: [PATCH] add more details to the server status page add all supported fields to the OpenAPI docs Signed-off-by: Nicola Murino --- ftpd/ftpd.go | 5 +++ ftpd/internal_test.go | 1 + httpd/httpd.go | 4 +- httpd/webadmin.go | 2 +- openapi/openapi.yaml | 77 ++++++++++++++++++++++++++++++++++ service/service.go | 5 +++ sftpd/server.go | 20 +++++++++ sftpd/sftpd.go | 16 ++++--- sftpd/sftpd_test.go | 2 + templates/webadmin/status.html | 8 +++- webdavd/webdavd.go | 1 - 11 files changed, 131 insertions(+), 10 deletions(-) diff --git a/ftpd/ftpd.go b/ftpd/ftpd.go index 108ed3ff..f61c92e1 100644 --- a/ftpd/ftpd.go +++ b/ftpd/ftpd.go @@ -32,6 +32,11 @@ type PassiveIPOverride struct { parsedNetworks []func(net.IP) bool } +// GetNetworksAsString returns the configured networks as string +func (p *PassiveIPOverride) GetNetworksAsString() string { + return strings.Join(p.Networks, ", ") +} + // Binding defines the configuration for a network listener type Binding struct { // The address to listen on. A blank value means listen on all available network interfaces. diff --git a/ftpd/internal_test.go b/ftpd/internal_test.go index 52923b2f..12b84bc1 100644 --- a/ftpd/internal_test.go +++ b/ftpd/internal_test.go @@ -975,6 +975,7 @@ func TestPassiveIPResolver(t *testing.T) { } err = b.checkPassiveIP() assert.NoError(t, err) + assert.NotEmpty(t, b.PassiveIPOverrides[0].GetNetworksAsString()) assert.Equal(t, "192.168.1.1", b.PassiveIPOverrides[0].IP) require.Len(t, b.PassiveIPOverrides[0].parsedNetworks, 1) ip := net.ParseIP("192.168.1.2") diff --git a/httpd/httpd.go b/httpd/httpd.go index bc85a67c..c889b548 100644 --- a/httpd/httpd.go +++ b/httpd/httpd.go @@ -634,8 +634,8 @@ func getConfigPath(name, configDir string) string { return name } -func getServicesStatus() ServicesStatus { - status := ServicesStatus{ +func getServicesStatus() *ServicesStatus { + status := &ServicesStatus{ SSH: sftpd.GetStatus(), FTP: ftpd.GetStatus(), WebDAV: webdavd.GetStatus(), diff --git a/httpd/webadmin.go b/httpd/webadmin.go index 69345f71..6d775afd 100644 --- a/httpd/webadmin.go +++ b/httpd/webadmin.go @@ -141,7 +141,7 @@ type connectionsPage struct { type statusPage struct { basePage - Status ServicesStatus + Status *ServicesStatus } type fsWrapper struct { diff --git a/openapi/openapi.yaml b/openapi/openapi.yaml index 43b875b1..e2dc1c9a 100644 --- a/openapi/openapi.yaml +++ b/openapi/openapi.yaml @@ -4467,6 +4467,23 @@ components: - admin - api_key - share + SSHAuthentications: + type: string + enum: + - publickey + - password + - keyboard-interactive + - publickey+password + - publickey+keyboard-interactive + TLSVersions: + type: integer + enum: + - 12 + - 13 + description: > + TLS version: + * `12` - TLS 1.2 + * `13` - TLS 1.3 TOTPConfig: type: object properties: @@ -5373,9 +5390,33 @@ components: description: the port used for serving requests enable_https: type: boolean + min_tls_version: + $ref: '#/components/schemas/TLSVersions' client_auth_type: type: integer description: 1 means that client certificate authentication is required in addition to HTTP basic authentication + tls_cipher_suites: + type: array + items: + type: string + description: 'List of supported cipher suites for TLS version 1.2. If empty a default list of secure cipher suites is used, with a preference order based on hardware performance' + prefix: + type: string + description: 'Prefix for WebDAV resources, if empty WebDAV resources will be available at the `/` URI' + proxy_allowed: + type: array + items: + type: string + description: 'List of IP addresses and IP ranges allowed to set proxy headers' + PassiveIPOverride: + type: object + properties: + networks: + type: array + items: + type: string + ip: + type: string FTPDBinding: type: object properties: @@ -5399,12 +5440,44 @@ components: * `0` - clear or explicit TLS * `1` - explicit TLS required * `2` - implicit TLS + min_tls_version: + $ref: '#/components/schemas/TLSVersions' force_passive_ip: type: string description: External IP address to expose for passive connections + passive_ip_overrides: + type: array + items: + $ref: '#/components/schemas/PassiveIPOverride' client_auth_type: type: integer description: 1 means that client certificate authentication is required in addition to FTP authentication + tls_cipher_suites: + type: array + items: + type: string + description: 'List of supported cipher suites for TLS version 1.2. If empty a default list of secure cipher suites is used, with a preference order based on hardware performance' + passive_connections_security: + type: integer + enum: + - 0 + - 1 + description: | + Active connections security: + * `0` - require matching peer IP addresses of control and data connection + * `1` - disable any checks + active_connections_security: + type: integer + enum: + - 0 + - 1 + description: | + Active connections security: + * `0` - require matching peer IP addresses of control and data connection + * `1` - disable any checks + debug: + type: boolean + description: 'If enabled any FTP command will be logged' SSHServiceStatus: type: object properties: @@ -5424,6 +5497,10 @@ components: type: array items: type: string + authentications: + type: array + items: + $ref: '#/components/schemas/SSHAuthentications' FTPPassivePortRange: type: object properties: diff --git a/service/service.go b/service/service.go index 816514ce..7c0a5f7f 100644 --- a/service/service.go +++ b/service/service.go @@ -263,6 +263,11 @@ func (s *Service) loadInitialData() error { } info, err := os.Stat(s.LoadDataFrom) if err != nil { + if errors.Is(err, os.ErrNotExist) { + logger.Warn(logSender, "", "unable to load initial data, the file %#v does not exist", s.LoadDataFrom) + logger.WarnToConsole("unable to load initial data, the file %#v does not exist", s.LoadDataFrom) + return nil + } return err } if info.Size() > httpd.MaxRestoreSize { diff --git a/sftpd/server.go b/sftpd/server.go index 621d1074..3323b573 100644 --- a/sftpd/server.go +++ b/sftpd/server.go @@ -203,13 +203,30 @@ func (c *Configuration) getServerConfig() *ssh.ServerConfig { return sp, nil } + serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.LoginMethodPassword) } + serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodPublicKey) return serverConfig } +func (c *Configuration) updateSupportedAuthentications() { + serviceStatus.Authentications = util.RemoveDuplicates(serviceStatus.Authentications) + + if util.IsStringInSlice(dataprovider.LoginMethodPassword, serviceStatus.Authentications) && + util.IsStringInSlice(dataprovider.SSHLoginMethodPublicKey, serviceStatus.Authentications) { + serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndPassword) + } + + if util.IsStringInSlice(dataprovider.SSHLoginMethodKeyboardInteractive, serviceStatus.Authentications) && + util.IsStringInSlice(dataprovider.SSHLoginMethodPublicKey, serviceStatus.Authentications) { + serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyAndKeyboardInt) + } +} + // Initialize the SFTP server and add a persistent listener to handle inbound SFTP connections. func (c *Configuration) Initialize(configDir string) error { + serviceStatus.Authentications = nil serverConfig := c.getServerConfig() if !c.ShouldBind() { @@ -270,6 +287,7 @@ func (c *Configuration) Initialize(configDir string) error { serviceStatus.IsActive = true serviceStatus.SSHCommands = c.EnabledSSHCommands + c.updateSupportedAuthentications() return <-exitChannel } @@ -381,6 +399,8 @@ func (c *Configuration) configureKeyboardInteractiveAuth(serverConfig *ssh.Serve return sp, nil } + + serviceStatus.Authentications = append(serviceStatus.Authentications, dataprovider.SSHLoginMethodKeyboardInteractive) } func canAcceptConnection(ip string) bool { diff --git a/sftpd/sftpd.go b/sftpd/sftpd.go index ad45f26e..9de41df6 100644 --- a/sftpd/sftpd.go +++ b/sftpd/sftpd.go @@ -38,17 +38,23 @@ type HostKey struct { // ServiceStatus defines the service status type ServiceStatus struct { - IsActive bool `json:"is_active"` - Bindings []Binding `json:"bindings"` - SSHCommands []string `json:"ssh_commands"` - HostKeys []HostKey `json:"host_keys"` + IsActive bool `json:"is_active"` + Bindings []Binding `json:"bindings"` + SSHCommands []string `json:"ssh_commands"` + HostKeys []HostKey `json:"host_keys"` + Authentications []string `json:"authentications"` } // GetSSHCommandsAsString returns enabled SSH commands as comma separated string -func (s ServiceStatus) GetSSHCommandsAsString() string { +func (s *ServiceStatus) GetSSHCommandsAsString() string { return strings.Join(s.SSHCommands, ", ") } +// GetSupportedAuthsAsString returns the supported authentications as comma separated string +func (s *ServiceStatus) GetSupportedAuthsAsString() string { + return strings.Join(s.Authentications, ", ") +} + // GetStatus returns the server status func GetStatus() ServiceStatus { return serviceStatus diff --git a/sftpd/sftpd_test.go b/sftpd/sftpd_test.go index 47365bf0..4b61748b 100644 --- a/sftpd/sftpd_test.go +++ b/sftpd/sftpd_test.go @@ -445,6 +445,8 @@ func TestBasicSFTPHandling(t *testing.T) { assert.True(t, status.IsActive) sshCommands := status.GetSSHCommandsAsString() assert.NotEmpty(t, sshCommands) + sshAuths := status.GetSupportedAuthsAsString() + assert.NotEmpty(t, sshAuths) } func TestBasicSFTPFsHandling(t *testing.T) { diff --git a/templates/webadmin/status.html b/templates/webadmin/status.html index 8667a50e..3b3cdf6a 100644 --- a/templates/webadmin/status.html +++ b/templates/webadmin/status.html @@ -21,6 +21,8 @@ Address: "{{.GetAddress}}" {{if .HasProxy}}Proxy: ON{{end}}
{{end}} + Accepted authentications: "{{.Status.SSH.GetSupportedAuthsAsString}}" +
Accepted commands: "{{.Status.SSH.GetSSHCommandsAsString}}"
{{range .Status.SSH.HostKeys}} @@ -49,9 +51,13 @@ TLS: "{{.GetTLSDescription}}" {{if .ForcePassiveIP}}
- PassiveIP: {{.ForcePassiveIP}} + Passive IP: {{.ForcePassiveIP}} {{end}}
+ {{range .PassiveIPOverrides}} + Passive IP: {{.IP}} for networks: {{.GetNetworksAsString}} +
+ {{end}} {{end}}
Passive port range: "{{.Status.FTP.PassivePortRange.Start}}-{{.Status.FTP.PassivePortRange.End}}" diff --git a/webdavd/webdavd.go b/webdavd/webdavd.go index 2f16f3e5..4cff64ef 100644 --- a/webdavd/webdavd.go +++ b/webdavd/webdavd.go @@ -26,7 +26,6 @@ const ( ) var ( - //server *webDavServer certMgr *common.CertManager serviceStatus ServiceStatus )