From 17acae2b0000f0c42ae3bcd72604cc32a0c43a75 Mon Sep 17 00:00:00 2001 From: Stephen Harris Date: Thu, 24 Sep 2020 14:51:35 -0400 Subject: [PATCH] sftp: allow cert based auth via optional pubkey Discussion at https://forum.rclone.org/t/ssh-certificate-based-authentication-does-not-work/19222 Basically we allow the user to specify their own public key cert rather than letting the SSH client extract the pubkey from the private key. This allows certificate based authentication to work. --- backend/sftp/sftp.go | 40 +++++++++++++++++++++++++++++++++++++++- docs/content/sftp.md | 34 +++++++++++++++++++++++++++++++++- 2 files changed, 72 insertions(+), 2 deletions(-) diff --git a/backend/sftp/sftp.go b/backend/sftp/sftp.go index c0cb928f5..0cda21c2f 100644 --- a/backend/sftp/sftp.go +++ b/backend/sftp/sftp.go @@ -82,6 +82,11 @@ func init() { Only PEM encrypted key files (old OpenSSH format) are supported. Encrypted keys in the new OpenSSH format can't be used.`, IsPassword: true, + }, { + Name: "pubkey_file", + Help: `Optional path to public key file. + +Set this if you have a signed certificate you want to use for authentication.` + env.ShellExpandHelp, }, { Name: "key_use_agent", Help: `When set forces the usage of the ssh-agent. @@ -190,6 +195,7 @@ type Options struct { KeyPem string `config:"key_pem"` KeyFile string `config:"key_file"` KeyFilePass string `config:"key_file_pass"` + PubKeyFile string `config:"pubkey_file"` KeyUseAgent bool `config:"key_use_agent"` UseInsecureCipher bool `config:"use_insecure_cipher"` DisableHashCheck bool `config:"disable_hashcheck"` @@ -438,6 +444,7 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { } keyFile := env.ShellExpand(opt.KeyFile) + pubkeyFile := env.ShellExpand(opt.PubKeyFile) //keyPem := env.ShellExpand(opt.KeyPem) // Add ssh agent-auth if no password or file or key PEM specified if (opt.Pass == "" && keyFile == "" && !opt.AskPassword && opt.KeyPem == "") || opt.KeyUseAgent { @@ -507,7 +514,38 @@ func NewFs(name, root string, m configmap.Mapper) (fs.Fs, error) { if err != nil { return nil, errors.Wrap(err, "failed to parse private key file") } - sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(signer)) + + // If a public key has been specified then use that + if pubkeyFile != "" { + certfile, err := ioutil.ReadFile(pubkeyFile) + if err != nil { + return nil, errors.Wrap(err, "unable to read cert file") + } + + pk, _, _, _, err := ssh.ParseAuthorizedKey(certfile) + if err != nil { + return nil, errors.Wrap(err, "unable to parse cert file") + } + + // And the signer for this, which includes the private key signer + // This is what we'll pass to the ssh client. + // Normally the ssh client will use the public key built + // into the private key, but we need to tell it to use the user + // specified public key cert. This signer is specific to the + // cert and will include the private key signer. Now ssh + // knows everything it needs. + cert, ok := pk.(*ssh.Certificate) + if !ok { + return nil, errors.New("public key file is not a certificate file: " + pubkeyFile) + } + pubsigner, err := ssh.NewCertSigner(cert, signer) + if err != nil { + return nil, errors.Wrap(err, "error generating cert signer") + } + sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(pubsigner)) + } else { + sshConfig.Auth = append(sshConfig.Auth, ssh.PublicKeys(signer)) + } } // Auth from password if specified diff --git a/docs/content/sftp.md b/docs/content/sftp.md index bc13b1909..a354c29e2 100644 --- a/docs/content/sftp.md +++ b/docs/content/sftp.md @@ -102,7 +102,7 @@ excess files in the directory. The SFTP remote supports three authentication methods: * Password - * Key file + * Key file, including certificate signed keys * ssh-agent Key files should be PEM-encoded private key files. For instance `/home/$USER/.ssh/id_rsa`. @@ -128,6 +128,26 @@ Using an ssh-agent is the only way to load encrypted OpenSSH keys at the moment. If you set the `--sftp-ask-password` option, rclone will prompt for a password when needed and no password has been configured. +If you have a certificate then you can provide the path to the public key that contains the certificate. For example: + +``` +[remote] +type = sftp +host = example.com +user = sftpuser +key_file = ~/id_rsa +pubkey_file = ~/id_rsa-cert.pub +```` + +If you concatenate a cert with a private key then you can specify the +merged file in both places. + +Note: the cert must come first in the file. e.g. + +``` +cat id_rsa-cert.pub id_rsa > merged_key +``` + ### ssh-agent on macOS ### Note that there seem to be various problems with using an ssh-agent on @@ -234,6 +254,18 @@ in the new OpenSSH format can't be used. - Type: string - Default: "" +#### --sftp-pubkey-file + +Optional path to public key file; set this if you have a signed certificate you want to use for authentication. + +Leading `~` will be expanded in the file name as will environment variables such as `${RCLONE_CONFIG_DIR}`. + + +- Config: pubkey_file +- Env Var: RCLONE_SFTP_PUBKEY_FILE +- Type: string +- Default: "" + #### --sftp-key-use-agent When set forces the usage of the ssh-agent.