From 9fcff83f8f5d410d474b3ce472bd9795ed5e89df Mon Sep 17 00:00:00 2001 From: Nicola Murino Date: Thu, 25 Jan 2024 19:26:51 +0100 Subject: [PATCH] WIP new WebAdmin: status page Signed-off-by: Nicola Murino --- go.mod | 6 +- go.sum | 12 +- internal/ftpd/ftpd.go | 10 +- internal/httpd/webadmin.go | 5 +- internal/util/i18n.go | 5 + internal/vfs/vfs.go | 2 +- static/locales/en/translation.json | 33 ++- static/locales/it/translation.json | 33 ++- templates/webadmin/status.html | 309 ++++++++++++++++------------- 9 files changed, 260 insertions(+), 155 deletions(-) diff --git a/go.mod b/go.mod index 02ca9f30..ed6efe36 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/drakkan/sftpgo/v2 go 1.21 require ( - cloud.google.com/go/storage v1.36.0 + cloud.google.com/go/storage v1.37.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1 github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 @@ -13,9 +13,9 @@ require ( github.com/aws/aws-sdk-go-v2/config v1.26.6 github.com/aws/aws-sdk-go-v2/credentials v1.16.16 github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 - github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14 + github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.15 github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6 - github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 + github.com/aws/aws-sdk-go-v2/service/s3 v1.48.1 github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/bmatcuk/doublestar/v4 v4.6.1 diff --git a/go.sum b/go.sum index 280a7618..c2b07009 100644 --- a/go.sum +++ b/go.sum @@ -9,8 +9,8 @@ cloud.google.com/go/iam v1.1.5 h1:1jTsCu4bcsNsE4iiqNT5SHwrDRCfRmIaaaVFhRveTJI= cloud.google.com/go/iam v1.1.5/go.mod h1:rB6P/Ic3mykPbFio+vo7403drjlgvoWfYpJhMXEbzv8= cloud.google.com/go/kms v1.15.5 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM= cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= -cloud.google.com/go/storage v1.36.0 h1:P0mOkAcaJxhCTvAkMhxMfrTKiNcub4YmmPBtlhAyTr8= -cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= +cloud.google.com/go/storage v1.37.0 h1:WI8CsaFO8Q9KjPVtsZ5Cmi0dXV25zMoX0FklT7c3Jm4= +cloud.google.com/go/storage v1.37.0/go.mod h1:i34TiT2IhiNDmcj65PqwCjcoUX7Z5pLzS8DEmoiFq1k= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ= github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= @@ -43,8 +43,8 @@ github.com/aws/aws-sdk-go-v2/credentials v1.16.16 h1:8q6Rliyv0aUFAVtzaldUEcS+T5g github.com/aws/aws-sdk-go-v2/credentials v1.16.16/go.mod h1:UHVZrdUsv63hPXFo1H7c5fEneoVo9UXiz36QG1GEPi0= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11 h1:c5I5iH+DZcH3xOIMlz3/tCKJDaHFwYEmxvlh2fAcFo8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.14.11/go.mod h1:cRrYDYAMUohBJUtUnOhydaMHtiK/1NZ0Otc9lIb6O0Y= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14 h1:ogP1WgyvN/qxPJkgtFMD7G2eKb5p/61Jomx+nIHXUQ4= -github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.14/go.mod h1:nYd/WmIrXlBHW/5QwrZP81/Gz08wKi87nV6EI1kmqx4= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.15 h1:2MUXyGW6dVaQz6aqycpbdLIH1NMcUI6kW6vQ0RabGYg= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.15.15/go.mod h1:aHbhbR6WEQgHAiRj41EQ2W47yOYwNtIkWTXmcAtYqj8= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10 h1:vF+Zgd9s+H4vOXd5BMaPWykta2a6Ih0AKLq/X6NYKn4= github.com/aws/aws-sdk-go-v2/internal/configsources v1.2.10/go.mod h1:6BkRjejp/GR4411UGqkX8+wFMbFbqsUIimfK4XjOKR4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.5.10 h1:nYPe006ktcqUji8S2mqXf9c/7NdiKriOwMvWQHgYztw= @@ -63,8 +63,8 @@ github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10 h1:KOxnQeWy5sXyS github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.16.10/go.mod h1:jMx5INQFYFYB3lQD9W0D8Ohgq6Wnl7NYOJ2TQndbulI= github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6 h1:JWy+uLKZQR/9a3gQ+jQa28FEJ/41Z0spdbbQodaXFeA= github.com/aws/aws-sdk-go-v2/service/marketplacemetering v1.19.6/go.mod h1:T2NcfuIuXWcuwVwg3rBIW6h1cfzCdrzSn4Hs0KltND8= -github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0 h1:PJTdBMsyvra6FtED7JZtDpQrIAflYDHFoZAu/sKYkwU= -github.com/aws/aws-sdk-go-v2/service/s3 v1.48.0/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= +github.com/aws/aws-sdk-go-v2/service/s3 v1.48.1 h1:5XNlsBsEvBZBMO6p82y+sqpWg8j5aBCe+5C2GBFgqBQ= +github.com/aws/aws-sdk-go-v2/service/s3 v1.48.1/go.mod h1:4qXHrG1Ne3VGIMZPCB8OjH/pLFO94sKABIusjh0KWPU= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2 h1:A5sGOT/mukuU+4At1vkSIWAN8tPwPCoYZBp7aruR540= github.com/aws/aws-sdk-go-v2/service/secretsmanager v1.26.2/go.mod h1:qutL00aW8GSo2D0I6UEOqMvRS3ZyuBrOC1BLe5D2jPc= github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= diff --git a/internal/ftpd/ftpd.go b/internal/ftpd/ftpd.go index 9dc7fc8a..185ac6aa 100644 --- a/internal/ftpd/ftpd.go +++ b/internal/ftpd/ftpd.go @@ -228,19 +228,19 @@ func (b *Binding) HasProxy() bool { // GetTLSDescription returns the TLS mode as string func (b *Binding) GetTLSDescription() string { if certMgr == nil { - return "Disabled" + return util.I18nFTPTLSDisabled } switch b.TLSMode { case 1: - return "Explicit required" + return util.I18nFTPTLSExplicit case 2: - return "Implicit" + return util.I18nFTPTLSImplicit } if certMgr.HasCertificate(common.DefaultTLSKeyPaidID) || certMgr.HasCertificate(b.GetAddress()) { - return "Plain and explicit" + return util.I18nFTPTLSMixed } - return "Disabled" + return util.I18nFTPTLSDisabled } // PortRange defines a port range diff --git a/internal/httpd/webadmin.go b/internal/httpd/webadmin.go index 8d442c35..679a27e6 100644 --- a/internal/httpd/webadmin.go +++ b/internal/httpd/webadmin.go @@ -98,7 +98,6 @@ const ( templateMaintenance = "maintenance.html" templateMFA = "mfa.html" templateSetup = "adminsetup.html" - pageStatusTitle = "Status" pageEventRulesTitle = "Event rules" pageEventActionsTitle = "Event actions" pageMaintenanceTitle = "Maintenance" @@ -442,7 +441,7 @@ func loadAdminTemplates(templatesPath string) { filepath.Join(templatesPath, templateAdminDir, templateEventAction), } statusPaths := []string{ - filepath.Join(templatesPath, templateCommonDir, templateCommonCSS), + filepath.Join(templatesPath, templateCommonDir, templateCommonBase), filepath.Join(templatesPath, templateAdminDir, templateBase), filepath.Join(templatesPath, templateAdminDir, templateStatus), } @@ -3311,7 +3310,7 @@ func (s *httpdServer) handleWebUpdateUserPost(w http.ResponseWriter, r *http.Req func (s *httpdServer) handleWebGetStatus(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) data := statusPage{ - basePage: s.getBasePageData(pageStatusTitle, webStatusPath, r), + basePage: s.getBasePageData(util.I18nStatusTitle, webStatusPath, r), Status: getServicesStatus(), } renderAdminTemplate(w, templateStatus, data) diff --git a/internal/util/i18n.go b/internal/util/i18n.go index e3dbf1c3..f6d9df5e 100644 --- a/internal/util/i18n.go +++ b/internal/util/i18n.go @@ -67,6 +67,7 @@ const ( I18nAddIPListTitle = "title.add_ip_list" I18nUpdateIPListTitle = "title.update_ip_list" I18nDefenderTitle = "title.defender" + I18nStatusTitle = "status.desc" I18nErrorSetupInstallCode = "setup.install_code_mismatch" I18nInvalidAuth = "general.invalid_auth_request" I18nError429Message = "general.error429" @@ -225,6 +226,10 @@ const ( I18nErrorAdminSelfRole = "admin.self_role" I18nErrorIpInvalid = "ip_list.ip_invalid" I18nErrorNetInvalid = "ip_list.net_invalid" + I18nFTPTLSDisabled = "status.tls_disabled" + I18nFTPTLSExplicit = "status.tls_explicit" + I18nFTPTLSImplicit = "status.tls_implicit" + I18nFTPTLSMixed = "status.tls_mixed" ) // NewI18nError returns a I18nError wrappring the provided error diff --git a/internal/vfs/vfs.go b/internal/vfs/vfs.go index 473746ee..7832ab44 100644 --- a/internal/vfs/vfs.go +++ b/internal/vfs/vfs.go @@ -189,7 +189,7 @@ type PipeWriter interface { GetWrittenBytes() int64 } -// PipeReader defines an interface representing a SFTPGo pipe writer +// PipeReader defines an interface representing a SFTPGo pipe reader type PipeReader interface { io.Reader io.ReaderAt diff --git a/static/locales/en/translation.json b/static/locales/en/translation.json index b580d0ae..06faf88c 100644 --- a/static/locales/en/translation.json +++ b/static/locales/en/translation.json @@ -234,7 +234,10 @@ "last_login": "Last login", "previous": "Previous", "next": "Next", - "type": "Type" + "type": "Type", + "issuer": "Issuer", + "data_provider": "Database", + "driver": "Driver" }, "fs": { "view_file": "View file \"{{- path}}\"", @@ -741,5 +744,33 @@ "ip": "IP address", "ban_time": "Blocked until", "score": "Score" + }, + "status": { + "desc": "Status of services", + "ssh": "SSH/SFTP server", + "active": "Status: active", + "disabled": "Status: disabled", + "proxy_on": "PROXY protocol enabled", + "address": "Address", + "ssh_auths": "Authentication methods", + "ssh_commands": "Accepted commands", + "host_key": "Host key", + "fingeprint": "Fingerprint", + "algorithms": "Algorithms", + "algorithm": "Algorithm", + "ssh_pub_key_algo": "Public key authentication algorithms", + "ssh_mac_algo": "Message authentication code (MAC) algorithms", + "ssh_kex_algo": "Key exchange (KEX) algorithms", + "ssh_cipher_algo": "Ciphers", + "ftp": "FTP server", + "ftp_passive_range": "Passive mode port range", + "ftp_passive_ip": "Passive IP", + "tls": "TLS", + "tls_disabled": "Disabled", + "tls_explicit": "Explicit mode required (FTPES)", + "tls_implicit": "Implicit mode (FTPS), deprecated, prefer FTPES", + "tls_mixed": "Plain and explicit (FTPES) mode", + "webdav": "WebDAV server", + "rate_limiters": "Rate limiters" } } \ No newline at end of file diff --git a/static/locales/it/translation.json b/static/locales/it/translation.json index e51b1925..192c3427 100644 --- a/static/locales/it/translation.json +++ b/static/locales/it/translation.json @@ -234,7 +234,10 @@ "last_login": "Ultimo accesso", "previous": "Precedente", "next": "Successivo", - "type": "Tipo" + "type": "Tipo", + "issuer": "Emittente", + "data_provider": "Database", + "driver": "Driver" }, "fs": { "view_file": "Visualizza file \"{{- path}}\"", @@ -741,5 +744,33 @@ "ip": "Indirizzo IP", "ban_time": "Bloccato fino a", "score": "Punteggio" + }, + "status": { + "desc": "Stato dei servizi", + "ssh": "Server SSH/SFTP", + "active": "Stato: attivo", + "disabled": "Stato: disabilitato", + "proxy_on": "Protocollo PROXY abilitato", + "address": "Indirizzo", + "ssh_auths": "Metodi di autenticazione", + "ssh_commands": "Comandi accettati", + "host_key": "Chiave host", + "fingeprint": "Impronta", + "algorithms": "Algoritmi", + "algorithm": "Algoritmo", + "ssh_pub_key_algo": "Algoritmi per l'autenticazione con chiave pubblica", + "ssh_mac_algo": "Algoritmi MAC", + "ssh_kex_algo": "Algoritmi KEX", + "ssh_cipher_algo": "Cifrari", + "ftp": "Server FTP", + "ftp_passive_range": "Intervallo di porte in modalità passiva", + "ftp_passive_ip": "IP per FTP passivo", + "tls": "TLS", + "tls_disabled": "Disabilitato", + "tls_explicit": "Modalità esplicita richiesta (FTPES)", + "tls_implicit": "Modalità implicita (FTPS), sconsigliato, FTPES è preferibile", + "tls_mixed": "In chiaro e modalità esplicita (FTPES)", + "webdav": "Server WebDAV", + "rate_limiters": "Rate limiters" } } \ No newline at end of file diff --git a/templates/webadmin/status.html b/templates/webadmin/status.html index c2935fb3..6fea537d 100644 --- a/templates/webadmin/status.html +++ b/templates/webadmin/status.html @@ -1,178 +1,217 @@ {{template "base" .}} -{{define "title"}}{{.Title}}{{end}} - -{{define "page_body"}} - -
-
-
Services
+{{- define "page_body"}} +
+
+

-
+ +
+
+

SSH/SFTP server

+
-
SFTP/SSH server
-

- Status: {{ if .Status.SSH.IsActive}}"Started"{{else}}"Stopped"{{end}} - {{if .Status.SSH.IsActive}} -
- {{range .Status.SSH.Bindings}} -
- Address: "{{.GetAddress}}" {{if .HasProxy}}Proxy: ON{{end}} -
- {{end}} - Accepted authentications: "{{.Status.SSH.GetSupportedAuthsAsString}}" -
- Accepted commands: "{{.Status.SSH.GetSSHCommandsAsString}}" -
- {{range .Status.SSH.HostKeys}} -
- Host Key: "{{.Path}}" -
- Fingerprint: "{{.Fingerprint}}" -
- Algorithms: "{{.GetAlgosAsString}}" -
- {{end}} -
- Public key authentication algorithms: "{{.Status.SSH.GetPublicKeysAlgosAsString}}" -

- Message authentication algorithms: "{{.Status.SSH.GetMACsAsString}}" -

- Key exchange algorithms: "{{.Status.SSH.GetKEXsAsString}}" -

- Ciphers: "{{.Status.SSH.GetCiphersAsString}}" -
- {{end}} -

+

+ {{- if .Status.SSH.IsActive}} +
+ {{- range .Status.SSH.Bindings}} +

+ "{{.GetAddress}}" +

+ {{- if .HasProxy}} +

+ +

+ {{- end}} + {{- end}} +
+ {{- range .Status.SSH.HostKeys}} +
+

+ "{{.Path}}" +

+

+ "{{.Fingerprint}}" +

+

+ "{{.GetAlgosAsString}}" +

+
+ {{- end}} +
+

+ "{{.Status.SSH.GetSSHCommandsAsString}}" +

+

+ "{{.Status.SSH.GetSupportedAuthsAsString}}" +

+

+ "{{.Status.SSH.GetPublicKeysAlgosAsString}}" +

+

+ "{{.Status.SSH.GetMACsAsString}}" +

+

+ "{{.Status.SSH.GetKEXsAsString}}" +

+

+ "{{.Status.SSH.GetCiphersAsString}}" +

+
+ {{- end}}
-
+
+
+

FTP server

+
-
FTP server
-

- Status: {{ if .Status.FTP.IsActive}}"Started"{{else}}"Stopped"{{end}} - {{if .Status.FTP.IsActive}} -
- {{range .Status.FTP.Bindings}} -
- Address: "{{.GetAddress}}" {{if .HasProxy}}Proxy: ON{{end}} -
- TLS: "{{.GetTLSDescription}}" - {{if .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}}" - {{end}} -

+

+ {{- if .Status.FTP.IsActive}} +
+ {{- range .Status.FTP.Bindings}} +

+ "{{.GetAddress}}" +

+ {{- if .HasProxy}} +

+ +

+ {{- end}} +

+   +

+ {{- if .ForcePassiveIP}} +

+ "{{.ForcePassiveIP}}" +

+ {{- end}} + {{- range .PassiveIPOverrides}} +

+ "{{.IP}} ({{.GetNetworksAsString}})" +

+ {{- end}} + {{- end}} +
+
+

+ "{{.Status.FTP.PassivePortRange.Start}}-{{.Status.FTP.PassivePortRange.End}}" +

+
+ {{- end}}
-
+
+
+

WebDAV server

+
-
WebDAV server
-

- Status: {{ if .Status.WebDAV.IsActive}}"Started"{{else}}"Stopped"{{end}} - {{if .Status.WebDAV.IsActive}} -
- {{range .Status.WebDAV.Bindings}} -
- Address: "{{.GetAddress}}" -
- Protocol: {{if .EnableHTTPS}} HTTPS {{else}} HTTP {{end}} -
- {{end}} - {{end}} -

+

+ {{- if .Status.WebDAV.IsActive}} +
+ {{- range .Status.WebDAV.Bindings}} +

+ "{{.GetAddress}}" +

+

+ {{if .EnableHTTPS}} HTTPS {{else}} HTTP {{end}} +

+ {{- end}} +
+ {{- end}}
-
+
+
+

Allow list

+
-
Allow list
-

- Status: {{ if .Status.AllowList.IsActive}}"Enabled"{{else}}"Disabled"{{end}} -

+

-
+
+
+

Defender

+
-
Defender
-

- Status: {{ if .Status.Defender.IsActive}}"Enabled"{{else}}"Disabled"{{end}} -

+

-
+
+
+

Rate limiters

+
-
Rate limiters
-

- Status: {{ if .Status.RateLimiters.IsActive}}"Enabled"{{else}}"Disabled"{{end}} - {{if .Status.RateLimiters.IsActive}} -
- Protocols: {{.Status.RateLimiters.GetProtocolsAsString}} - {{end}} -

+

+ {{- if .Status.RateLimiters.IsActive}} +
+

+ "{{.Status.RateLimiters.GetProtocolsAsString}}" +

+
+ {{- end}}
-
+
+
+

Two-factor authentication

+
-
Multi-factor authentication
-

- Status: {{ if .Status.MFA.IsActive}}"Enabled"{{else}}"Disabled"{{end}} - {{ if .Status.MFA.IsActive}} -
- Time-based one time passwords (RFC 6238) configurations: -
-

    - {{range .Status.MFA.TOTPConfigs}} -
  • Name: "{{.Name}}", issuer: "{{.Issuer}}", HMAC algorithm: "{{.Algo}}"
  • - {{end}} -
- {{end}} -

+

+ {{- if .Status.MFA.IsActive}} + {{range .Status.MFA.TOTPConfigs}} +
+

+ "{{.Name}}" +

+

+ "{{.Issuer}}" +

+

+ "{{.Algo}}" +

+
+ {{- end}} + {{- end}}
-
+
+
+

Database

+
-
Data provider
-

- Status: {{ if .Status.DataProvider.IsActive}}"OK"{{else}}"{{.Status.DataProvider.Error}}"{{end}} -
- Driver: "{{.Status.DataProvider.Driver}}" -

+

+
+

+ "{{.Status.DataProvider.Driver}}" +

+
- -{{end}} \ No newline at end of file +{{- end}} \ No newline at end of file