diff --git a/docs/full-configuration.md b/docs/full-configuration.md index 6f60ee93..a00d4025 100644 --- a/docs/full-configuration.md +++ b/docs/full-configuration.md @@ -63,6 +63,7 @@ The configuration file contains the following sections: - `bind_address`, string. Leave blank to listen on all available network interfaces. Default: "" - `idle_timeout`, integer. Deprecated, please use the same key in `common` section. - `max_auth_tries` integer. Maximum number of authentication attempts permitted per connection. If set to a negative number, the number of attempts is unlimited. If set to zero, the number of attempts are limited to 6. + - `password_disabled`, boolean. Set to false to forbid password authentication (for example in a pubkey-only setup). - `banner`, string. Identification string used by the server. Leave empty to use the default banner. Default `SFTPGo_`, for example `SSH-2.0-SFTPGo_0.9.5` - `upload_mode` integer. Deprecated, please use the same key in `common` section. - `actions`, struct. Deprecated, please use the same key in `common` section. diff --git a/sftpd/server.go b/sftpd/server.go index 50412fdb..43e8a895 100644 --- a/sftpd/server.go +++ b/sftpd/server.go @@ -97,6 +97,8 @@ type Configuration struct { // The following SSH commands are enabled by default: "md5sum", "sha1sum", "cd", "pwd". // "*" enables all supported SSH commands. EnabledSSHCommands []string `json:"enabled_ssh_commands" mapstructure:"enabled_ssh_commands"` + // PasswordDisabled specifies whether to forbid password authentication, for example in a publickey-only setup. + PasswordDisabled bool `json:"password_disabled" mapstructure:"password_disabled"` // Absolute path to an external program or an HTTP URL to invoke for keyboard interactive authentication. // Leave empty to disable this authentication mode. KeyboardInteractiveHook string `json:"keyboard_interactive_auth_hook" mapstructure:"keyboard_interactive_auth_hook"` @@ -128,14 +130,6 @@ func (c Configuration) Initialize(configDir string) error { serverConfig := &ssh.ServerConfig{ NoClientAuth: false, MaxAuthTries: c.MaxAuthTries, - PasswordCallback: func(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { - sp, err := c.validatePasswordCredentials(conn, pass) - if err != nil { - return nil, &authenticationError{err: fmt.Sprintf("could not validate password credentials: %v", err)} - } - - return sp, nil - }, PublicKeyCallback: func(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) { sp, err := c.validatePublicKeyCredentials(conn, pubKey) if err == ssh.ErrPartialSuccess { @@ -158,6 +152,17 @@ func (c Configuration) Initialize(configDir string) error { ServerVersion: fmt.Sprintf("SSH-2.0-%v", c.Banner), } + if !c.PasswordDisabled { + serverConfig.PasswordCallback = func(conn ssh.ConnMetadata, pass []byte) (*ssh.Permissions, error) { + sp, err := c.validatePasswordCredentials(conn, pass) + if err != nil { + return nil, &authenticationError{err: fmt.Sprintf("could not validate password credentials: %v", err)} + } + + return sp, nil + } + } + if err := c.checkAndLoadHostKeys(configDir, serverConfig); err != nil { return err }