From 6dc28ef50ad30591c611c7918e424f35c42aa550 Mon Sep 17 00:00:00 2001 From: Stephen Harris Date: Sat, 3 Oct 2020 21:03:19 -0400 Subject: [PATCH] sftp: Allow user to optionally check server hosts key to add security Based on Issue 4087 https://github.com/rclone/rclone/issues/4087 Current behaviour is insecure. If the user specifies this value then we switch to validating the server hostkey and so can detect server changes or MITM-type attacks. --- backend/sftp/sftp.go | 21 ++++++++++++++ docs/content/sftp.md | 68 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index 093ca0276..6a0ed2262 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -32,6 +32,7 @@ import ( "github.com/rclone/rclone/lib/readers" sshagent "github.com/xanzy/ssh-agent" "golang.org/x/crypto/ssh" + "golang.org/x/crypto/ssh/knownhosts" ) const ( @@ -86,6 +87,16 @@ in the new OpenSSH format can't be used.`, Help: `Optional path to public key file. Set this if you have a signed certificate you want to use for authentication.` + env.ShellExpandHelp, + }, { + Name: "known_hosts_file", + Help: `Optional path to known_hosts file. + +Set this value to enable server host key validation.` + env.ShellExpandHelp, + Advanced: true, + Examples: []fs.OptionExample{{ + Value: "~/.ssh/known_hosts", + Help: "Use OpenSSH's known_hosts file", + }}, }, { Name: "key_use_agent", Help: `When set forces the usage of the ssh-agent. @@ -195,6 +206,7 @@ type Options struct { KeyFile string `config:"key_file"` KeyFilePass string `config:"key_file_pass"` PubKeyFile string `config:"pubkey_file"` + KnownHostsFile string `config:"known_hosts_file"` KeyUseAgent bool `config:"key_use_agent"` UseInsecureCipher bool `config:"use_insecure_cipher"` DisableHashCheck bool `config:"disable_hashcheck"` @@ -414,6 +426,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { if opt.Port == "" { opt.Port = "22" } + sshConfig := &ssh.ClientConfig{ User: opt.User, Auth: []ssh.AuthMethod{}, @@ -422,6 +435,14 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { ClientVersion: "SSH-2.0-" + fs.Config.UserAgent, } + if opt.KnownHostsFile != "" { + hostcallback, err := knownhosts.New(opt.KnownHostsFile) + if err != nil { + return nil, errors.Wrap(err, "couldn't parse known_hosts_file") + } + sshConfig.HostKeyCallback = hostcallback + } + if opt.UseInsecureCipher { sshConfig.Config.SetDefaults() sshConfig.Config.Ciphers = append(sshConfig.Config.Ciphers, "aes128-cbc", "aes192-cbc", "aes256-cbc", "3des-cbc") diff --git a/docs/content/sftp.md b/docs/content/sftp.md index c0c8cc1eb..aef29698e 100644 --- a/docs/content/sftp.md +++ b/docs/content/sftp.md @@ -148,6 +148,57 @@ Note: the cert must come first in the file. e.g. cat id_rsa-cert.pub id_rsa > merged_key ``` +### Host key validation ### + +By default rclone will not check the server's host key for validation. This +can allow an attacker to replace a server with their own and if you use +password authentication then this can lead to that password being exposed. + +Host key matching, using standard `known_hosts` files can be turned on by +enabling the `known_hosts_file` option. This can point to the file maintained +by `OpenSSH` or can point to a unique file. + +e.g. + +``` +[remote] +type = sftp +host = example.com +user = sftpuser +pass = +known_hosts_file = ~/.ssh/known_hosts +```` + +There are some limitations: + +* `rclone` will not _manage_ this file for you. If the key is missing or +wrong then the connection will be refused. +* If the server is set up for a certificate host key then the entry in +the `known_hosts` file _must_ be the `@cert-authority` entry for the CA +* Unlike `OpenSSH`, the libraries used by `rclone` do not permit (at time +of writing) multiple host keys to be listed for a server. Only the first +entry is used. + +If the host key provided by the server does not match the one in the +file (or is missing) then the connection will be aborted and an error +returned such as + + NewFs: couldn't connect SSH: ssh: handshake failed: knownhosts: key mismatch + +or + + NewFs: couldn't connect SSH: ssh: handshake failed: knownhosts: key is unknown + +If you see an error such as + + NewFs: couldn't connect SSH: ssh: handshake failed: ssh: no authorities for hostname: example.com:22 + +then it is likely the server has presented a CA signed host certificate +and you will need to add the appropriate `@cert-authority` entry. + +The `known_hosts_file` setting can be set during `rclone config` as an +advanced option. + ### ssh-agent on macOS ### Note that there seem to be various problems with using an ssh-agent on @@ -320,6 +371,23 @@ Leave blank or set to false to enable hashing (recommended), set to true to disa Here are the advanced options specific to sftp (SSH/SFTP Connection). +#### --sftp-known-hosts-file + +Optional path to known_hosts file. + +Set this value to enable server host key validation. + +Leading `~` will be expanded in the file name as will environment variables such as `${RCLONE_CONFIG_DIR}`. + + +- Config: known_hosts_file +- Env Var: RCLONE_SFTP_KNOWN_HOSTS_FILE +- Type: string +- Default: "" +- Examples: + - "~/.ssh/known_hosts" + - Use OpenSSH's known_hosts file + #### --sftp-ask-password Allow asking for SFTP password when needed.