WIP new WebAdmin: status page

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-01-25 19:26:51 +01:00
parent eec9c449d4
commit 9fcff83f8f
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
9 changed files with 260 additions and 155 deletions

6
go.mod
View file

@ -3,7 +3,7 @@ module github.com/drakkan/sftpgo/v2
go 1.21 go 1.21
require ( 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/azcore v1.9.1
github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.2.1
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 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/config v1.26.6
github.com/aws/aws-sdk-go-v2/credentials v1.16.16 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/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/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/secretsmanager v1.26.2
github.com/aws/aws-sdk-go-v2/service/sts v1.26.7 github.com/aws/aws-sdk-go-v2/service/sts v1.26.7
github.com/bmatcuk/doublestar/v4 v4.6.1 github.com/bmatcuk/doublestar/v4 v4.6.1

12
go.sum
View file

@ -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/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 h1:pj1sRfut2eRbD9pFRjNnPNg/CzJPuQAzUujMIM1vVeM=
cloud.google.com/go/kms v1.15.5/go.mod h1:cU2H5jnp6G2TDpUGZyqTCoy1n16fbubHZjmVXSMtwDI= 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.37.0 h1:WI8CsaFO8Q9KjPVtsZ5Cmi0dXV25zMoX0FklT7c3Jm4=
cloud.google.com/go/storage v1.36.0/go.mod h1:M6M/3V/D3KpzMTJyPOR/HU6n2Si5QdaXYEsng2xgOs8= 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 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 h1:lGlwhPtrX6EVml1hO0ivjkUxsSyl4dsiw9qcA1k/3IQ=
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.9.1/go.mod h1:RKUqNu35KJYcVG/fqTRqmuXJZYNhYkBrnC/hX7yGbTA= 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/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 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/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.15 h1:2MUXyGW6dVaQz6aqycpbdLIH1NMcUI6kW6vQ0RabGYg=
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/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 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/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= 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/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 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/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.1 h1:5XNlsBsEvBZBMO6p82y+sqpWg8j5aBCe+5C2GBFgqBQ=
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/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 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/secretsmanager v1.26.2/go.mod h1:qutL00aW8GSo2D0I6UEOqMvRS3ZyuBrOC1BLe5D2jPc=
github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow= github.com/aws/aws-sdk-go-v2/service/sso v1.18.7 h1:eajuO3nykDPdYicLlP3AGgOyVN3MOlFmZv7WGTuJPow=

View file

@ -228,19 +228,19 @@ func (b *Binding) HasProxy() bool {
// GetTLSDescription returns the TLS mode as string // GetTLSDescription returns the TLS mode as string
func (b *Binding) GetTLSDescription() string { func (b *Binding) GetTLSDescription() string {
if certMgr == nil { if certMgr == nil {
return "Disabled" return util.I18nFTPTLSDisabled
} }
switch b.TLSMode { switch b.TLSMode {
case 1: case 1:
return "Explicit required" return util.I18nFTPTLSExplicit
case 2: case 2:
return "Implicit" return util.I18nFTPTLSImplicit
} }
if certMgr.HasCertificate(common.DefaultTLSKeyPaidID) || certMgr.HasCertificate(b.GetAddress()) { 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 // PortRange defines a port range

View file

@ -98,7 +98,6 @@ const (
templateMaintenance = "maintenance.html" templateMaintenance = "maintenance.html"
templateMFA = "mfa.html" templateMFA = "mfa.html"
templateSetup = "adminsetup.html" templateSetup = "adminsetup.html"
pageStatusTitle = "Status"
pageEventRulesTitle = "Event rules" pageEventRulesTitle = "Event rules"
pageEventActionsTitle = "Event actions" pageEventActionsTitle = "Event actions"
pageMaintenanceTitle = "Maintenance" pageMaintenanceTitle = "Maintenance"
@ -442,7 +441,7 @@ func loadAdminTemplates(templatesPath string) {
filepath.Join(templatesPath, templateAdminDir, templateEventAction), filepath.Join(templatesPath, templateAdminDir, templateEventAction),
} }
statusPaths := []string{ statusPaths := []string{
filepath.Join(templatesPath, templateCommonDir, templateCommonCSS), filepath.Join(templatesPath, templateCommonDir, templateCommonBase),
filepath.Join(templatesPath, templateAdminDir, templateBase), filepath.Join(templatesPath, templateAdminDir, templateBase),
filepath.Join(templatesPath, templateAdminDir, templateStatus), 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) { func (s *httpdServer) handleWebGetStatus(w http.ResponseWriter, r *http.Request) {
r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize) r.Body = http.MaxBytesReader(w, r.Body, maxRequestSize)
data := statusPage{ data := statusPage{
basePage: s.getBasePageData(pageStatusTitle, webStatusPath, r), basePage: s.getBasePageData(util.I18nStatusTitle, webStatusPath, r),
Status: getServicesStatus(), Status: getServicesStatus(),
} }
renderAdminTemplate(w, templateStatus, data) renderAdminTemplate(w, templateStatus, data)

View file

@ -67,6 +67,7 @@ const (
I18nAddIPListTitle = "title.add_ip_list" I18nAddIPListTitle = "title.add_ip_list"
I18nUpdateIPListTitle = "title.update_ip_list" I18nUpdateIPListTitle = "title.update_ip_list"
I18nDefenderTitle = "title.defender" I18nDefenderTitle = "title.defender"
I18nStatusTitle = "status.desc"
I18nErrorSetupInstallCode = "setup.install_code_mismatch" I18nErrorSetupInstallCode = "setup.install_code_mismatch"
I18nInvalidAuth = "general.invalid_auth_request" I18nInvalidAuth = "general.invalid_auth_request"
I18nError429Message = "general.error429" I18nError429Message = "general.error429"
@ -225,6 +226,10 @@ const (
I18nErrorAdminSelfRole = "admin.self_role" I18nErrorAdminSelfRole = "admin.self_role"
I18nErrorIpInvalid = "ip_list.ip_invalid" I18nErrorIpInvalid = "ip_list.ip_invalid"
I18nErrorNetInvalid = "ip_list.net_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 // NewI18nError returns a I18nError wrappring the provided error

View file

@ -189,7 +189,7 @@ type PipeWriter interface {
GetWrittenBytes() int64 GetWrittenBytes() int64
} }
// PipeReader defines an interface representing a SFTPGo pipe writer // PipeReader defines an interface representing a SFTPGo pipe reader
type PipeReader interface { type PipeReader interface {
io.Reader io.Reader
io.ReaderAt io.ReaderAt

View file

@ -234,7 +234,10 @@
"last_login": "Last login", "last_login": "Last login",
"previous": "Previous", "previous": "Previous",
"next": "Next", "next": "Next",
"type": "Type" "type": "Type",
"issuer": "Issuer",
"data_provider": "Database",
"driver": "Driver"
}, },
"fs": { "fs": {
"view_file": "View file \"{{- path}}\"", "view_file": "View file \"{{- path}}\"",
@ -741,5 +744,33 @@
"ip": "IP address", "ip": "IP address",
"ban_time": "Blocked until", "ban_time": "Blocked until",
"score": "Score" "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"
} }
} }

View file

@ -234,7 +234,10 @@
"last_login": "Ultimo accesso", "last_login": "Ultimo accesso",
"previous": "Precedente", "previous": "Precedente",
"next": "Successivo", "next": "Successivo",
"type": "Tipo" "type": "Tipo",
"issuer": "Emittente",
"data_provider": "Database",
"driver": "Driver"
}, },
"fs": { "fs": {
"view_file": "Visualizza file \"{{- path}}\"", "view_file": "Visualizza file \"{{- path}}\"",
@ -741,5 +744,33 @@
"ip": "Indirizzo IP", "ip": "Indirizzo IP",
"ban_time": "Bloccato fino a", "ban_time": "Bloccato fino a",
"score": "Punteggio" "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"
} }
} }

View file

@ -1,178 +1,217 @@
<!-- <!--
Copyright (C) 2019 Nicola Murino Copyright (C) 2024 Nicola Murino
This program is free software: you can redistribute it and/or modify This WebUI uses the KeenThemes Mega Bundle, a proprietary theme:
it under the terms of the GNU Affero General Public License as published
by the Free Software Foundation, version 3.
This program is distributed in the hope that it will be useful, https://keenthemes.com/products/templates-mega-bundle
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License KeenThemes HTML/CSS/JS components are allowed for use only within the
along with this program. If not, see <https://www.gnu.org/licenses/>. SFTPGo product and restricted to be used in a resealable HTML template
that can compete with KeenThemes products anyhow.
This WebUI is allowed for use only within the SFTPGo product and
therefore cannot be used in derivative works/products without an
explicit grant from the SFTPGo Team (support@sftpgo.com).
--> -->
{{template "base" .}} {{template "base" .}}
{{define "title"}}{{.Title}}{{end}} {{- define "page_body"}}
<div class="card shadow-sm">
{{define "page_body"}} <div class="card-header bg-light">
<h3 data-i18n="{{.Title}}" class="card-title section-title"></h3>
<div class="card shadow mb-4">
<div class="card-header py-3">
<h6 class="m-0 font-weight-bold text-primary">Services</h6>
</div> </div>
<div class="card-body"> <div class="card-body">
<div class="card mb-4 {{ if .Status.SSH.IsActive}}border-left-success{{else}}border-left-info{{end}}">
<div class="card">
<div class="card-header bg-light">
<h3 data-i18n="status.ssh" class="card-title section-title-inner">SSH/SFTP server</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">SFTP/SSH server</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.SSH.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text"> {{- if .Status.SSH.IsActive}}
Status: {{ if .Status.SSH.IsActive}}"Started"{{else}}"Stopped"{{end}} <div class="d-flex flex-column">
{{if .Status.SSH.IsActive}} {{- range .Status.SSH.Bindings}}
<br> <p class="fs-5 fw-semibold">
{{range .Status.SSH.Bindings}} <span class="text-success" data-i18n="status.address"></span> "{{.GetAddress}}"
<br> </p>
Address: "{{.GetAddress}}" {{if .HasProxy}}Proxy: ON{{end}} {{- if .HasProxy}}
<br> <p class="fs-5 fw-semibold">
{{end}} <span class="text-success" data-i18n="status.proxy_on"></span>
Accepted authentications: "{{.Status.SSH.GetSupportedAuthsAsString}}" </p>
<br> {{- end}}
Accepted commands: "{{.Status.SSH.GetSSHCommandsAsString}}" {{- end}}
<br> </div>
{{range .Status.SSH.HostKeys}} {{- range .Status.SSH.HostKeys}}
<br> <div class="d-flex flex-column mt-10">
Host Key: "{{.Path}}" <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="status.host_key"></span> "{{.Path}}"
Fingerprint: "{{.Fingerprint}}" </p>
<br> <p class="fs-5 fw-semibold">
Algorithms: "{{.GetAlgosAsString}}" <span class="text-success" data-i18n="status.fingeprint"></span> "{{.Fingerprint}}"
<br> </p>
{{end}} <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="status.algorithms"></span> "{{.GetAlgosAsString}}"
Public key authentication algorithms: "{{.Status.SSH.GetPublicKeysAlgosAsString}}" </p>
<br><br> </div>
Message authentication algorithms: "{{.Status.SSH.GetMACsAsString}}" {{- end}}
<br><br> <div class="d-flex flex-column mt-10">
Key exchange algorithms: "{{.Status.SSH.GetKEXsAsString}}" <p class="fs-5 fw-semibold">
<br><br> <span class="text-success" data-i18n="status.ssh_commands"></span> "{{.Status.SSH.GetSSHCommandsAsString}}"
Ciphers: "{{.Status.SSH.GetCiphersAsString}}" </p>
<br> <p class="fs-5 fw-semibold">
{{end}} <span class="text-success" data-i18n="status.ssh_auths"></span> "{{.Status.SSH.GetSupportedAuthsAsString}}"
</p> </p>
<p class="fs-5 fw-semibold">
<span class="text-success" data-i18n="status.ssh_pub_key_algo"></span> "{{.Status.SSH.GetPublicKeysAlgosAsString}}"
</p>
<p class="fs-5 fw-semibold">
<span class="text-success" data-i18n="status.ssh_mac_algo"></span> "{{.Status.SSH.GetMACsAsString}}"
</p>
<p class="fs-5 fw-semibold">
<span class="text-success" data-i18n="status.ssh_kex_algo"></span> "{{.Status.SSH.GetKEXsAsString}}"
</p>
<p class="fs-5 fw-semibold">
<span class="text-success" data-i18n="status.ssh_cipher_algo"></span> "{{.Status.SSH.GetCiphersAsString}}"
</p>
</div>
{{- end}}
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.FTP.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="status.ftp" class="card-title section-title-inner">FTP server</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">FTP server</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.FTP.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text"> {{- if .Status.FTP.IsActive}}
Status: {{ if .Status.FTP.IsActive}}"Started"{{else}}"Stopped"{{end}} <div class="d-flex flex-column">
{{if .Status.FTP.IsActive}} {{- range .Status.FTP.Bindings}}
<br> <p class="fs-5 fw-semibold">
{{range .Status.FTP.Bindings}} <span class="text-success" data-i18n="status.address"></span> "{{.GetAddress}}"
<br> </p>
Address: "{{.GetAddress}}" {{if .HasProxy}}Proxy: ON{{end}} {{- if .HasProxy}}
<br> <p class="fs-5 fw-semibold">
TLS: "{{.GetTLSDescription}}" <span class="text-success" data-i18n="status.proxy_on"></span>
{{if .ForcePassiveIP}} </p>
<br> {{- end}}
Passive IP: {{.ForcePassiveIP}} <p class="fs-5 fw-semibold">
{{end}} <span class="text-success" data-i18n="status.tls"></span>&nbsp;<span data-i18n="{{.GetTLSDescription}}"></span>
<br> </p>
{{range .PassiveIPOverrides}} {{- if .ForcePassiveIP}}
Passive IP: {{.IP}} for networks: {{.GetNetworksAsString}} <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="status.ftp_passive_ip"></span> "{{.ForcePassiveIP}}"
{{end}} </p>
{{end}} {{- end}}
<br> {{- range .PassiveIPOverrides}}
Passive port range: "{{.Status.FTP.PassivePortRange.Start}}-{{.Status.FTP.PassivePortRange.End}}" <p class="fs-5 fw-semibold">
{{end}} <span class="text-success" data-i18n="status.ftp_passive_ip"></span> "{{.IP}} ({{.GetNetworksAsString}})"
</p> </p>
{{- end}}
{{- end}}
</div>
<div class="d-flex flex-column mt-10">
<p class="fs-5 fw-semibold">
<span class="text-success" data-i18n="status.ftp_passive_range"></span> "{{.Status.FTP.PassivePortRange.Start}}-{{.Status.FTP.PassivePortRange.End}}"
</p>
</div>
{{- end}}
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.WebDAV.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="status.webdav" class="card-title section-title-inner">WebDAV server</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">WebDAV server</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.WebDAV.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text"> {{- if .Status.WebDAV.IsActive}}
Status: {{ if .Status.WebDAV.IsActive}}"Started"{{else}}"Stopped"{{end}} <div class="d-flex flex-column">
{{if .Status.WebDAV.IsActive}} {{- range .Status.WebDAV.Bindings}}
<br> <p class="fs-5 fw-semibold">
{{range .Status.WebDAV.Bindings}} <span class="text-success" data-i18n="status.address"></span> "{{.GetAddress}}"
<br> </p>
Address: "{{.GetAddress}}" <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="general.protocol"></span> {{if .EnableHTTPS}} HTTPS {{else}} HTTP {{end}}
Protocol: {{if .EnableHTTPS}} HTTPS {{else}} HTTP {{end}} </p>
<br> {{- end}}
{{end}} </div>
{{end}} {{- end}}
</p>
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.AllowList.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="iplist.allow_list" class="card-title section-title-inner">Allow list</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">Allow list</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.AllowList.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text">
Status: {{ if .Status.AllowList.IsActive}}"Enabled"{{else}}"Disabled"{{end}}
</p>
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.Defender.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="iplist.defender_list" class="card-title section-title-inner">Defender</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">Defender</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.Defender.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text">
Status: {{ if .Status.Defender.IsActive}}"Enabled"{{else}}"Disabled"{{end}}
</p>
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.RateLimiters.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="status.rate_limiters" class="card-title section-title-inner">Rate limiters</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">Rate limiters</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.RateLimiters.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text"> {{- if .Status.RateLimiters.IsActive}}
Status: {{ if .Status.RateLimiters.IsActive}}"Enabled"{{else}}"Disabled"{{end}} <div class="d-flex flex-column">
{{if .Status.RateLimiters.IsActive}} <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="iplist.protocols"></span> "{{.Status.RateLimiters.GetProtocolsAsString}}"
Protocols: {{.Status.RateLimiters.GetProtocolsAsString}} </p>
{{end}} </div>
</p> {{- end}}
</div> </div>
</div> </div>
<div class="card mb-4 {{ if .Status.MFA.IsActive}}border-left-success{{else}}border-left-info{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="title.two_factor_auth" class="card-title section-title-inner">Two-factor authentication</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">Multi-factor authentication</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.MFA.IsActive}}data-i18n="status.active"{{else}}data-i18n="status.disabled"{{end}}></p>
<p class="card-text"> {{- if .Status.MFA.IsActive}}
Status: {{ if .Status.MFA.IsActive}}"Enabled"{{else}}"Disabled"{{end}} {{range .Status.MFA.TOTPConfigs}}
{{ if .Status.MFA.IsActive}} <div class="d-flex flex-column">
<br> <p class="fs-5 fw-semibold">
Time-based one time passwords (RFC 6238) configurations: <span class="text-success" data-i18n="general.configuration"></span> "{{.Name}}"
<br> </p>
<ul> <p class="fs-5 fw-semibold">
{{range .Status.MFA.TOTPConfigs}} <span class="text-success" data-i18n="general.issuer"></span> "{{.Issuer}}"
<li>Name: "{{.Name}}", issuer: "{{.Issuer}}", HMAC algorithm: "{{.Algo}}"</li> </p>
{{end}} <p class="fs-5 fw-semibold">
</ul> <span class="text-success" data-i18n="status.algorithm"></span> "{{.Algo}}"
{{end}} </p>
</p> </div>
{{- end}}
{{- end}}
</div> </div>
</div> </div>
<div class="card mb-2 {{ if .Status.DataProvider.IsActive}}border-left-success{{else}}border-left-warning{{end}}"> <div class="card mt-10">
<div class="card-header bg-light">
<h3 data-i18n="general.data_provider" class="card-title section-title-inner">Database</h3>
</div>
<div class="card-body"> <div class="card-body">
<h6 class="card-title font-weight-bold">Data provider</h6> <p class="fs-3 fw-semibold mb-4" {{if .Status.DataProvider.IsActive}}data-i18n="status.active"{{else}}{{.Status.DataProvider.Error}}{{end}}></p>
<p class="card-text"> <div class="d-flex flex-column">
Status: {{ if .Status.DataProvider.IsActive}}"OK"{{else}}"{{.Status.DataProvider.Error}}"{{end}} <p class="fs-5 fw-semibold">
<br> <span class="text-success" data-i18n="general.driver"></span> "{{.Status.DataProvider.Driver}}"
Driver: "{{.Status.DataProvider.Driver}}" </p>
</p> </div>
</div> </div>
</div> </div>
</div> </div>
</div> </div>
{{- end}}
{{end}}