move server version setting to common section

Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
Nicola Murino 2024-05-01 19:42:09 +02:00
parent 7b5ad6c38d
commit d3f42e39db
No known key found for this signature in database
GPG key ID: 935D2952DEC4EECF
22 changed files with 86 additions and 91 deletions

4
go.mod
View file

@ -53,7 +53,7 @@ require (
github.com/rs/xid v1.5.0 github.com/rs/xid v1.5.0
github.com/rs/zerolog v1.32.0 github.com/rs/zerolog v1.32.0
github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b
github.com/shirou/gopsutil/v3 v3.24.3 github.com/shirou/gopsutil/v3 v3.24.4
github.com/spf13/afero v1.11.0 github.com/spf13/afero v1.11.0
github.com/spf13/cobra v1.8.0 github.com/spf13/cobra v1.8.0
github.com/spf13/viper v1.18.2 github.com/spf13/viper v1.18.2
@ -73,7 +73,7 @@ require (
golang.org/x/sys v0.19.0 golang.org/x/sys v0.19.0
golang.org/x/term v0.19.0 golang.org/x/term v0.19.0
golang.org/x/time v0.5.0 golang.org/x/time v0.5.0
google.golang.org/api v0.176.1 google.golang.org/api v0.177.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1 gopkg.in/natefinch/lumberjack.v2 v2.2.1
) )

9
go.sum
View file

@ -353,8 +353,8 @@ github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b h1:BazWPub9GBUKvfM2O6MHhAwd9JbPD1i3UudhmHfGc2w= github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b h1:BazWPub9GBUKvfM2O6MHhAwd9JbPD1i3UudhmHfGc2w=
github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc= github.com/sftpgo/sdk v0.1.6-0.20240426175227-52f492b8b83b/go.mod h1:AWoY2YYe/P1ymfTlRER/meERQjCcZZTbgVPGcPQgaqc=
github.com/shirou/gopsutil/v3 v3.24.3 h1:eoUGJSmdfLzJ3mxIhmOAhgKEKgQkeOwKpz1NbhVnuPE= github.com/shirou/gopsutil/v3 v3.24.4 h1:dEHgzZXt4LMNm+oYELpzl9YCqV65Yr/6SfrvgRBtXeU=
github.com/shirou/gopsutil/v3 v3.24.3/go.mod h1:JpND7O217xa72ewWz9zN2eIIkPWsDN/3pl0H8Qt0uwg= github.com/shirou/gopsutil/v3 v3.24.4/go.mod h1:lTd2mdiOspcqLgAnr9/nGi71NkeMpWKdmhuxm9GusH8=
github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM=
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ= github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
@ -482,7 +482,6 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o= golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
@ -512,8 +511,8 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028 h1:+cNy6SZtPcJQH3LJVLOSmiC7MMxXNOb3PU/VUEz+EhU=
golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90= golang.org/x/xerrors v0.0.0-20231012003039-104605ab7028/go.mod h1:NDW/Ps6MPRej6fsCIbMTohpP40sJ/P/vI1MoTEGwX90=
google.golang.org/api v0.176.1 h1:DJSXnV6An+NhJ1J+GWtoF2nHEuqB1VNoTfnIbjNvwD4= google.golang.org/api v0.177.0 h1:8a0p/BbPa65GlqGWtUKxot4p0TV8OGOfyTjtmkXNXmk=
google.golang.org/api v0.176.1/go.mod h1:j2MaSDYcvYV1lkZ1+SMW4IeF90SrEyFA+tluDYWRrFg= google.golang.org/api v0.177.0/go.mod h1:srbhue4MLjkjbkux5p3dw/ocYOSZTaIEvf7bCOnFQDw=
google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM=
google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4=
google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc=

View file

@ -489,7 +489,7 @@ func (c *Configuration) setup() (*account, *lego.Client, error) {
config := lego.NewConfig(&account) config := lego.NewConfig(&account)
config.CADirURL = c.CAEndpoint config.CADirURL = c.CAEndpoint
config.Certificate.KeyType = certcrypto.KeyType(c.KeyType) config.Certificate.KeyType = certcrypto.KeyType(c.KeyType)
config.UserAgent = fmt.Sprintf("SFTPGo/%v", version.Get().Version) config.UserAgent = version.GetServerVersion("/", false)
client, err := lego.NewClient(config) client, err := lego.NewClient(config)
if err != nil { if err != nil {
acmeLog(logger.LevelError, "unable to get ACME client: %v", err) acmeLog(logger.LevelError, "unable to get ACME client: %v", err)
@ -555,7 +555,7 @@ func (c *Configuration) register(client *lego.Client) (*registration.Resource, e
func (c *Configuration) tryRecoverRegistration(privateKey crypto.PrivateKey) (*registration.Resource, error) { func (c *Configuration) tryRecoverRegistration(privateKey crypto.PrivateKey) (*registration.Resource, error) {
config := lego.NewConfig(&account{key: privateKey}) config := lego.NewConfig(&account{key: privateKey})
config.CADirURL = c.CAEndpoint config.CADirURL = c.CAEndpoint
config.UserAgent = fmt.Sprintf("SFTPGo/%v", version.Get().Version) config.UserAgent = version.GetServerVersion("/", false)
client, err := lego.NewClient(config) client, err := lego.NewClient(config)
if err != nil { if err != nil {

View file

@ -42,6 +42,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/plugin" "github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/smtp" "github.com/drakkan/sftpgo/v2/internal/smtp"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/vfs" "github.com/drakkan/sftpgo/v2/internal/vfs"
) )
@ -168,6 +169,7 @@ var (
func Initialize(c Configuration, isShared int) error { func Initialize(c Configuration, isShared int) error {
isShuttingDown.Store(false) isShuttingDown.Store(false)
util.SetUmask(c.Umask) util.SetUmask(c.Umask)
version.SetConfig(c.ServerVersion)
Config = c Config = c
Config.Actions.ExecuteOn = util.RemoveDuplicates(Config.Actions.ExecuteOn, true) Config.Actions.ExecuteOn = util.RemoveDuplicates(Config.Actions.ExecuteOn, true)
Config.Actions.ExecuteSync = util.RemoveDuplicates(Config.Actions.ExecuteSync, true) Config.Actions.ExecuteSync = util.RemoveDuplicates(Config.Actions.ExecuteSync, true)
@ -577,6 +579,8 @@ type Configuration struct {
RateLimitersConfig []RateLimiterConfig `json:"rate_limiters" mapstructure:"rate_limiters"` RateLimitersConfig []RateLimiterConfig `json:"rate_limiters" mapstructure:"rate_limiters"`
// Umask for new uploads. Leave blank to use the system default. // Umask for new uploads. Leave blank to use the system default.
Umask string `json:"umask" mapstructure:"umask"` Umask string `json:"umask" mapstructure:"umask"`
// Defines the server version
ServerVersion string `json:"server_version" mapstructure:"server_version"`
// Metadata configuration // Metadata configuration
Metadata MetadataConfig `json:"metadata" mapstructure:"metadata"` Metadata MetadataConfig `json:"metadata" mapstructure:"metadata"`
idleTimeoutAsDuration time.Duration idleTimeoutAsDuration time.Duration

View file

@ -38,6 +38,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/kms" "github.com/drakkan/sftpgo/v2/internal/kms"
"github.com/drakkan/sftpgo/v2/internal/plugin" "github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/vfs" "github.com/drakkan/sftpgo/v2/internal/vfs"
) )
@ -1768,6 +1769,21 @@ func TestALPNProtocols(t *testing.T) {
assert.Equal(t, []string{"h2", "http/1.1"}, protocols) assert.Equal(t, []string{"h2", "http/1.1"}, protocols)
} }
func TestServerVersion(t *testing.T) {
appName := "SFTPGo"
version.SetConfig("")
v := version.GetServerVersion("_", false)
assert.Equal(t, fmt.Sprintf("%s_%s", appName, version.Get().Version), v)
v = version.GetServerVersion("-", true)
assert.Equal(t, fmt.Sprintf("%s-%s-", appName, version.Get().Version), v)
version.SetConfig("short")
v = version.GetServerVersion("_", false)
assert.Equal(t, appName, v)
v = version.GetServerVersion("_", true)
assert.Equal(t, appName+"_", v)
version.SetConfig("")
}
func BenchmarkBcryptHashing(b *testing.B) { func BenchmarkBcryptHashing(b *testing.B) {
bcryptPassword := "bcryptpassword" bcryptPassword := "bcryptpassword"
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {

View file

@ -41,7 +41,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/smtp" "github.com/drakkan/sftpgo/v2/internal/smtp"
"github.com/drakkan/sftpgo/v2/internal/telemetry" "github.com/drakkan/sftpgo/v2/internal/telemetry"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/webdavd" "github.com/drakkan/sftpgo/v2/internal/webdavd"
) )
@ -58,7 +57,6 @@ const (
var ( var (
globalConf globalConfig globalConf globalConfig
defaultFTPDBanner = fmt.Sprintf("SFTPGo %v ready", version.Get().Version)
defaultInstallCodeHint = "Installation code" defaultInstallCodeHint = "Installation code"
defaultSFTPDBinding = sftpd.Binding{ defaultSFTPDBinding = sftpd.Binding{
Address: "", Address: "",
@ -231,6 +229,7 @@ func Init() {
}, },
RateLimitersConfig: []common.RateLimiterConfig{defaultRateLimiter}, RateLimitersConfig: []common.RateLimiterConfig{defaultRateLimiter},
Umask: "", Umask: "",
ServerVersion: "",
Metadata: common.MetadataConfig{ Metadata: common.MetadataConfig{
Read: 0, Read: 0,
}, },
@ -254,7 +253,6 @@ func Init() {
SFTPD: sftpd.Configuration{ SFTPD: sftpd.Configuration{
Bindings: []sftpd.Binding{defaultSFTPDBinding}, Bindings: []sftpd.Binding{defaultSFTPDBinding},
MaxAuthTries: 0, MaxAuthTries: 0,
Banner: "",
HostKeys: []string{}, HostKeys: []string{},
HostCertificates: []string{}, HostCertificates: []string{},
HostKeyAlgorithms: []string{}, HostKeyAlgorithms: []string{},
@ -272,7 +270,6 @@ func Init() {
}, },
FTPD: ftpd.Configuration{ FTPD: ftpd.Configuration{
Bindings: []ftpd.Binding{defaultFTPDBinding}, Bindings: []ftpd.Binding{defaultFTPDBinding},
Banner: defaultFTPDBanner,
BannerFile: "", BannerFile: "",
ActiveTransfersPortNon20: true, ActiveTransfersPortNon20: true,
PassivePortRange: ftpd.PortRange{ PassivePortRange: ftpd.PortRange{
@ -757,9 +754,6 @@ func isExternalAuthScopeValid() bool {
} }
func resetInvalidConfigs() { func resetInvalidConfigs() {
if strings.TrimSpace(globalConf.FTPD.Banner) == "" {
globalConf.FTPD.Banner = defaultFTPDBanner
}
if strings.TrimSpace(globalConf.HTTPDConfig.Setup.InstallationCodeHint) == "" { if strings.TrimSpace(globalConf.HTTPDConfig.Setup.InstallationCodeHint) == "" {
globalConf.HTTPDConfig.Setup.InstallationCodeHint = defaultInstallCodeHint globalConf.HTTPDConfig.Setup.InstallationCodeHint = defaultInstallCodeHint
} }
@ -1996,6 +1990,7 @@ func setViperDefaults() {
viper.SetDefault("common.defender.entries_soft_limit", globalConf.Common.DefenderConfig.EntriesSoftLimit) viper.SetDefault("common.defender.entries_soft_limit", globalConf.Common.DefenderConfig.EntriesSoftLimit)
viper.SetDefault("common.defender.entries_hard_limit", globalConf.Common.DefenderConfig.EntriesHardLimit) viper.SetDefault("common.defender.entries_hard_limit", globalConf.Common.DefenderConfig.EntriesHardLimit)
viper.SetDefault("common.umask", globalConf.Common.Umask) viper.SetDefault("common.umask", globalConf.Common.Umask)
viper.SetDefault("common.server_version", globalConf.Common.ServerVersion)
viper.SetDefault("common.metadata.read", globalConf.Common.Metadata.Read) viper.SetDefault("common.metadata.read", globalConf.Common.Metadata.Read)
viper.SetDefault("acme.email", globalConf.ACME.Email) viper.SetDefault("acme.email", globalConf.ACME.Email)
viper.SetDefault("acme.key_type", globalConf.ACME.KeyType) viper.SetDefault("acme.key_type", globalConf.ACME.KeyType)
@ -2008,7 +2003,6 @@ func setViperDefaults() {
viper.SetDefault("acme.http01_challenge.proxy_header", globalConf.ACME.HTTP01Challenge.ProxyHeader) viper.SetDefault("acme.http01_challenge.proxy_header", globalConf.ACME.HTTP01Challenge.ProxyHeader)
viper.SetDefault("acme.tls_alpn01_challenge.port", globalConf.ACME.TLSALPN01Challenge.Port) viper.SetDefault("acme.tls_alpn01_challenge.port", globalConf.ACME.TLSALPN01Challenge.Port)
viper.SetDefault("sftpd.max_auth_tries", globalConf.SFTPD.MaxAuthTries) viper.SetDefault("sftpd.max_auth_tries", globalConf.SFTPD.MaxAuthTries)
viper.SetDefault("sftpd.banner", globalConf.SFTPD.Banner)
viper.SetDefault("sftpd.host_keys", globalConf.SFTPD.HostKeys) viper.SetDefault("sftpd.host_keys", globalConf.SFTPD.HostKeys)
viper.SetDefault("sftpd.host_certificates", globalConf.SFTPD.HostCertificates) viper.SetDefault("sftpd.host_certificates", globalConf.SFTPD.HostCertificates)
viper.SetDefault("sftpd.host_key_algorithms", globalConf.SFTPD.HostKeyAlgorithms) viper.SetDefault("sftpd.host_key_algorithms", globalConf.SFTPD.HostKeyAlgorithms)
@ -2023,7 +2017,6 @@ func setViperDefaults() {
viper.SetDefault("sftpd.keyboard_interactive_authentication", globalConf.SFTPD.KeyboardInteractiveAuthentication) viper.SetDefault("sftpd.keyboard_interactive_authentication", globalConf.SFTPD.KeyboardInteractiveAuthentication)
viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook) viper.SetDefault("sftpd.keyboard_interactive_auth_hook", globalConf.SFTPD.KeyboardInteractiveHook)
viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication) viper.SetDefault("sftpd.password_authentication", globalConf.SFTPD.PasswordAuthentication)
viper.SetDefault("ftpd.banner", globalConf.FTPD.Banner)
viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile) viper.SetDefault("ftpd.banner_file", globalConf.FTPD.BannerFile)
viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20) viper.SetDefault("ftpd.active_transfers_port_non_20", globalConf.FTPD.ActiveTransfersPortNon20)
viper.SetDefault("ftpd.passive_port_range.start", globalConf.FTPD.PassivePortRange.Start) viper.SetDefault("ftpd.passive_port_range.start", globalConf.FTPD.PassivePortRange.Start)

View file

@ -19,7 +19,6 @@ import (
"encoding/json" "encoding/json"
"os" "os"
"path/filepath" "path/filepath"
"strings"
"testing" "testing"
"github.com/sftpgo/sdk/kms" "github.com/sftpgo/sdk/kms"
@ -31,7 +30,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/common" "github.com/drakkan/sftpgo/v2/internal/common"
"github.com/drakkan/sftpgo/v2/internal/config" "github.com/drakkan/sftpgo/v2/internal/config"
"github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/ftpd"
"github.com/drakkan/sftpgo/v2/internal/httpclient" "github.com/drakkan/sftpgo/v2/internal/httpclient"
"github.com/drakkan/sftpgo/v2/internal/httpd" "github.com/drakkan/sftpgo/v2/internal/httpd"
"github.com/drakkan/sftpgo/v2/internal/mfa" "github.com/drakkan/sftpgo/v2/internal/mfa"
@ -124,42 +122,6 @@ func TestReadEnvFiles(t *testing.T) {
os.RemoveAll(envd) os.RemoveAll(envd)
} }
func TestEmptyBanner(t *testing.T) {
reset()
confName := tempConfigName + ".json"
configFilePath := filepath.Join(configDir, confName)
err := config.LoadConfig(configDir, "")
assert.NoError(t, err)
sftpdConf := config.GetSFTPDConfig()
sftpdConf.Banner = " "
c := make(map[string]sftpd.Configuration)
c["sftpd"] = sftpdConf
jsonConf, _ := json.Marshal(c)
err = os.WriteFile(configFilePath, jsonConf, os.ModePerm)
assert.NoError(t, err)
err = config.LoadConfig(configDir, confName)
assert.NoError(t, err)
sftpdConf = config.GetSFTPDConfig()
assert.Empty(t, strings.TrimSpace(sftpdConf.Banner))
err = os.Remove(configFilePath)
assert.NoError(t, err)
ftpdConf := config.GetFTPDConfig()
ftpdConf.Banner = " "
c1 := make(map[string]ftpd.Configuration)
c1["ftpd"] = ftpdConf
jsonConf, _ = json.Marshal(c1)
err = os.WriteFile(configFilePath, jsonConf, os.ModePerm)
assert.NoError(t, err)
err = config.LoadConfig(configDir, confName)
assert.NoError(t, err)
ftpdConf = config.GetFTPDConfig()
assert.NotEmpty(t, strings.TrimSpace(ftpdConf.Banner))
err = os.Remove(configFilePath)
assert.NoError(t, err)
}
func TestEnabledSSHCommands(t *testing.T) { func TestEnabledSSHCommands(t *testing.T) {
reset() reset()

View file

@ -262,10 +262,7 @@ type ServiceStatus struct {
type Configuration struct { type Configuration struct {
// Addresses and ports to bind to // Addresses and ports to bind to
Bindings []Binding `json:"bindings" mapstructure:"bindings"` Bindings []Binding `json:"bindings" mapstructure:"bindings"`
// Greeting banner displayed when a connection first comes in // The contents of the specified file, if any, are diplayed when someone connects to the server.
Banner string `json:"banner" mapstructure:"banner"`
// the contents of the specified file, if any, are diplayed when someone connects to the server.
// If set, it overrides the banner string provided by the banner option
BannerFile string `json:"banner_file" mapstructure:"banner_file"` BannerFile string `json:"banner_file" mapstructure:"banner_file"`
// If files containing a certificate and matching private key for the server are provided the server will accept // If files containing a certificate and matching private key for the server are provided the server will accept
// both plain FTP an explicit FTP over TLS. // both plain FTP an explicit FTP over TLS.

View file

@ -37,6 +37,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/common" "github.com/drakkan/sftpgo/v2/internal/common"
"github.com/drakkan/sftpgo/v2/internal/dataprovider" "github.com/drakkan/sftpgo/v2/internal/dataprovider"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/vfs" "github.com/drakkan/sftpgo/v2/internal/vfs"
) )
@ -440,7 +441,7 @@ func TestInitialization(t *testing.T) {
c.CertificateKeyFile = "" c.CertificateKeyFile = ""
c.BannerFile = "afile" c.BannerFile = "afile"
server := NewServer(c, configDir, binding, 0) server := NewServer(c, configDir, binding, 0)
assert.Equal(t, "", server.initialMsg) assert.Equal(t, version.GetServerVersion("_", false), server.initialMsg)
_, err = server.GetTLSConfig() _, err = server.GetTLSConfig()
assert.Error(t, err) assert.Error(t, err)

View file

@ -48,10 +48,11 @@ type Server struct {
// NewServer returns a new FTP server driver // NewServer returns a new FTP server driver
func NewServer(config *Configuration, configDir string, binding Binding, id int) *Server { func NewServer(config *Configuration, configDir string, binding Binding, id int) *Server {
binding.setCiphers() binding.setCiphers()
vers := version.GetServerVersion("_", false)
server := &Server{ server := &Server{
config: config, config: config,
initialMsg: config.Banner, initialMsg: vers,
statusBanner: fmt.Sprintf("SFTPGo %v FTP Server", version.Get().Version), statusBanner: fmt.Sprintf("%s FTP Server", vers),
binding: binding, binding: binding,
ID: id, ID: id,
} }

View file

@ -1087,6 +1087,7 @@ func (s *httpdServer) updateContextFromCookie(r *http.Request) *http.Request {
func (s *httpdServer) parseHeaders(next http.Handler) http.Handler { func (s *httpdServer) parseHeaders(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Server", version.GetServerVersion("/", false))
ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr) ipAddr := util.GetIPFromRemoteAddress(r.RemoteAddr)
var ip net.IP var ip net.IP
isUnixSocket := filepath.IsAbs(s.binding.Address) isUnixSocket := filepath.IsAbs(s.binding.Address)

View file

@ -16,7 +16,6 @@ package httpd
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"strings" "strings"
@ -118,11 +117,10 @@ func hasPrefixAndSuffix(key, prefix, suffix string) bool {
} }
func getCommonBasePage(r *http.Request) commonBasePage { func getCommonBasePage(r *http.Request) commonBasePage {
v := version.Get()
return commonBasePage{ return commonBasePage{
CSPNonce: secure.CSPNonce(r.Context()), CSPNonce: secure.CSPNonce(r.Context()),
StaticURL: webStaticFilesPath, StaticURL: webStaticFilesPath,
Version: fmt.Sprintf("v%v-%v", v.Version, v.CommitHash), Version: version.GetServerVersion(" ", true),
} }
} }

View file

@ -32,7 +32,6 @@ import (
"github.com/drakkan/sftpgo/v2/internal/logger" "github.com/drakkan/sftpgo/v2/internal/logger"
"github.com/drakkan/sftpgo/v2/internal/sftpd" "github.com/drakkan/sftpgo/v2/internal/sftpd"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
"github.com/drakkan/sftpgo/v2/internal/webdavd" "github.com/drakkan/sftpgo/v2/internal/webdavd"
) )
@ -232,9 +231,6 @@ func configurePortableFTPService(port int, cert, key string) {
} else { } else {
ftpConf.Bindings[0].Port = 0 ftpConf.Bindings[0].Port = 0
} }
if ftpConf.Banner == "" {
ftpConf.Banner = fmt.Sprintf("SFTPGo portable %v ready", version.Get().Version)
}
ftpConf.Bindings[0].CertificateFile = cert ftpConf.Bindings[0].CertificateFile = cert
ftpConf.Bindings[0].CertificateKeyFile = key ftpConf.Bindings[0].CertificateKeyFile = key
config.SetFTPDConfig(ftpConf) config.SetFTPDConfig(ftpConf)

View file

@ -107,8 +107,6 @@ func (b *Binding) HasProxy() bool {
// Configuration for the SFTP server // Configuration for the SFTP server
type Configuration struct { type Configuration struct {
// Identification string used by the server
Banner string `json:"banner" mapstructure:"banner"`
// Addresses and ports to bind to // Addresses and ports to bind to
Bindings []Binding `json:"bindings" mapstructure:"bindings"` Bindings []Binding `json:"bindings" mapstructure:"bindings"`
// Maximum number of authentication attempts permitted per connection. // Maximum number of authentication attempts permitted per connection.
@ -227,13 +225,6 @@ func (c *Configuration) ShouldBind() bool {
return false return false
} }
func (c *Configuration) getServerVersion() string {
if c.Banner == "short" {
return "SSH-2.0-SFTPGo"
}
return fmt.Sprintf("SSH-2.0-SFTPGo_%v", version.Get().Version)
}
func (c *Configuration) getServerConfig() *ssh.ServerConfig { func (c *Configuration) getServerConfig() *ssh.ServerConfig {
serverConfig := &ssh.ServerConfig{ serverConfig := &ssh.ServerConfig{
NoClientAuth: false, NoClientAuth: false,
@ -251,7 +242,7 @@ func (c *Configuration) getServerConfig() *ssh.ServerConfig {
return sp, nil return sp, nil
}, },
ServerVersion: c.getServerVersion(), ServerVersion: fmt.Sprintf("SSH-2.0-%s", version.GetServerVersion("_", false)),
} }
if c.PasswordAuthentication { if c.PasswordAuthentication {

View file

@ -323,9 +323,8 @@ func (c *Config) getMailClientOptions() []mail.Option {
func (c *Config) getSMTPClientAndMsg(to, bcc []string, subject, body string, contentType EmailContentType, func (c *Config) getSMTPClientAndMsg(to, bcc []string, subject, body string, contentType EmailContentType,
attachments ...*mail.File) (*mail.Client, *mail.Msg, error) { attachments ...*mail.File) (*mail.Client, *mail.Msg, error) {
version := version.Get()
msg := mail.NewMsg() msg := mail.NewMsg()
msg.SetUserAgent(fmt.Sprintf("SFTPGo-%s-%s", version.Version, version.CommitHash)) msg.SetUserAgent(version.GetServerVersion(" ", true))
var from string var from string
if c.From != "" { if c.From != "" {

View file

@ -17,7 +17,10 @@ package version
import "strings" import "strings"
const version = "2.5.99-dev" const (
version = "2.5.99-dev"
appName = "SFTPGo"
)
var ( var (
commit = "" commit = ""
@ -25,6 +28,10 @@ var (
info Info info Info
) )
var (
config string
)
// Info defines version details // Info defines version details
type Info struct { type Info struct {
Version string `json:"version"` Version string `json:"version"`
@ -69,3 +76,33 @@ func AddFeature(feature string) {
func Get() Info { func Get() Info {
return info return info
} }
// SetConfig sets the version configuration
func SetConfig(val string) {
config = val
}
// GetServerVersion returns the server version according to the configuration
// and the provided parameters.
func GetServerVersion(separator string, addHash bool) string {
var sb strings.Builder
sb.WriteString(appName)
if config != "short" {
sb.WriteString(separator)
sb.WriteString(info.Version)
}
if addHash {
sb.WriteString(separator)
sb.WriteString(info.CommitHash)
}
return sb.String()
}
// GetVersionHash returns the server identification string with the commit hash.
func GetVersionHash() string {
var sb strings.Builder
sb.WriteString(appName)
sb.WriteString("-")
sb.WriteString(info.CommitHash)
return sb.String()
}

View file

@ -1133,11 +1133,10 @@ func checkDirectoryMarkers(contentType string, metadata map[string]*string) bool
} }
func getAzContainerClientOptions() *container.ClientOptions { func getAzContainerClientOptions() *container.ClientOptions {
version := version.Get()
return &container.ClientOptions{ return &container.ClientOptions{
ClientOptions: azcore.ClientOptions{ ClientOptions: azcore.ClientOptions{
Telemetry: policy.TelemetryOptions{ Telemetry: policy.TelemetryOptions{
ApplicationID: fmt.Sprintf("SFTPGo-%s", version.CommitHash), ApplicationID: version.GetVersionHash(),
}, },
}, },
} }

View file

@ -125,7 +125,7 @@ func NewS3Fs(connectionID, localTempDir, mountPath string, s3Config S3FsConfig)
awsConfig.Credentials = creds awsConfig.Credentials = creds
} }
fs.svc = s3.NewFromConfig(awsConfig, func(o *s3.Options) { fs.svc = s3.NewFromConfig(awsConfig, func(o *s3.Options) {
o.AppID = fmt.Sprintf("SFTPGo-%s", version.Get().CommitHash) o.AppID = version.GetVersionHash()
o.UsePathStyle = fs.config.ForcePathStyle o.UsePathStyle = fs.config.ForcePathStyle
if fs.config.Endpoint != "" { if fs.config.Endpoint != "" {
o.BaseEndpoint = aws.String(fs.config.Endpoint) o.BaseEndpoint = aws.String(fs.config.Endpoint)

View file

@ -977,7 +977,7 @@ func (c *sftpConnection) openConnNoLock() error {
return nil return nil
}, },
Timeout: 15 * time.Second, Timeout: 15 * time.Second,
ClientVersion: fmt.Sprintf("SSH-2.0-SFTPGo_%v", version.Get().Version), ClientVersion: fmt.Sprintf("SSH-2.0-%s", version.GetServerVersion("_", false)),
} }
signer, err := c.getKeySigner() signer, err := c.getKeySigner()
if err != nil { if err != nil {

View file

@ -41,6 +41,7 @@ import (
"github.com/drakkan/sftpgo/v2/internal/metric" "github.com/drakkan/sftpgo/v2/internal/metric"
"github.com/drakkan/sftpgo/v2/internal/plugin" "github.com/drakkan/sftpgo/v2/internal/plugin"
"github.com/drakkan/sftpgo/v2/internal/util" "github.com/drakkan/sftpgo/v2/internal/util"
"github.com/drakkan/sftpgo/v2/internal/version"
) )
type webDavServer struct { type webDavServer struct {
@ -165,6 +166,7 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
} }
}() }()
w.Header().Set("Server", version.GetServerVersion("/", false))
ipAddr := s.checkRemoteAddress(r) ipAddr := s.checkRemoteAddress(r)
common.Connections.AddClientConnection(ipAddr) common.Connections.AddClientConnection(ipAddr)
@ -194,7 +196,7 @@ func (s *webDavServer) ServeHTTP(w http.ResponseWriter, r *http.Request) {
user, isCached, lockSystem, loginMethod, err := s.authenticate(r, ipAddr) user, isCached, lockSystem, loginMethod, err := s.authenticate(r, ipAddr)
if err != nil { if err != nil {
if !s.binding.DisableWWWAuthHeader { if !s.binding.DisableWWWAuthHeader {
w.Header().Set("WWW-Authenticate", "Basic realm=\"SFTPGo WebDAV\"") w.Header().Set("WWW-Authenticate", fmt.Sprintf("Basic realm=\"%s WebDAV\"", version.GetServerVersion("_", false)))
} }
http.Error(w, fmt.Sprintf("Authentication error: %v", err), http.StatusUnauthorized) http.Error(w, fmt.Sprintf("Authentication error: %v", err), http.StatusUnauthorized)
return return

View file

@ -23,6 +23,7 @@
"allowlist_status": 0, "allowlist_status": 0,
"allow_self_connections": 0, "allow_self_connections": 0,
"umask": "", "umask": "",
"server_version": "",
"metadata": { "metadata": {
"read": 0 "read": 0
}, },
@ -83,7 +84,6 @@
} }
], ],
"max_auth_tries": 0, "max_auth_tries": 0,
"banner": "",
"host_keys": [], "host_keys": [],
"host_certificates": [], "host_certificates": [],
"host_key_algorithms": [], "host_key_algorithms": [],
@ -129,7 +129,6 @@
"debug": false "debug": false
} }
], ],
"banner": "",
"banner_file": "", "banner_file": "",
"active_transfers_port_non_20": true, "active_transfers_port_non_20": true,
"passive_port_range": { "passive_port_range": {

View file

@ -881,7 +881,7 @@ explicit grant from the SFTPGo Team (support@sftpgo.com).
<div id="kt_app_footer" class="app-footer"> <div id="kt_app_footer" class="app-footer">
<div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3 align-items-center justify-content-center"> <div class="app-container container-fluid d-flex flex-column flex-md-row flex-center flex-md-stack py-3 align-items-center justify-content-center">
<div class="text-gray-900 order-2 order-md-1"> <div class="text-gray-900 order-2 order-md-1">
<span class="text-gray-700 fw-semibold me-1">SFTPGo {{.Version}}</span> <span class="text-gray-700 fw-semibold me-1">{{.Version}}</span>
</div> </div>
</div> </div>
</div> </div>