mirror of
https://github.com/drakkan/sftpgo.git
synced 2024-11-25 00:50:31 +00:00
move server version setting to common section
Signed-off-by: Nicola Murino <nicola.murino@gmail.com>
This commit is contained in:
parent
7b5ad6c38d
commit
d3f42e39db
22 changed files with 86 additions and 91 deletions
4
go.mod
4
go.mod
|
@ -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
9
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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++ {
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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()
|
||||||
|
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
@ -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,
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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 != "" {
|
||||||
|
|
|
@ -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()
|
||||||
|
}
|
||||||
|
|
|
@ -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(),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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": {
|
||||||
|
|
|
@ -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>
|
||||||
|
|
Loading…
Reference in a new issue