sftp: add support for using ssh key files #1494

Update docs about macOS and ssh-agent #1218
This commit is contained in:
Nick Craig-Wood 2017-06-23 16:25:35 +01:00
parent b44d0ea088
commit d55f8f0492
2 changed files with 67 additions and 20 deletions

View File

@ -21,17 +21,14 @@ Here is an example of making a SFTP configuration. First run
rclone config
This will guide you through an interactive setup process. You will
need your account number (a short hex number) and key (a long hex
number) which you can get from the SFTP control panel.
This will guide you through an interactive setup process.
```
No remotes found - make a new one
n) New remote
r) Rename remote
c) Copy remote
s) Set configuration password
q) Quit config
n/r/c/s/q> n
n/s/q> n
name> remote
Type of storage to configure.
Choose a number from below, or type in your own value
@ -63,6 +60,8 @@ Choose a number from below, or type in your own value
\ "sftp"
14 / Yandex Disk
\ "yandex"
15 / http Connection
\ "http"
Storage> sftp
SSH host to connect to
Choose a number from below, or type in your own value
@ -70,21 +69,24 @@ Choose a number from below, or type in your own value
\ "example.com"
host> example.com
SSH username, leave blank for current username, ncw
user>
user> sftpuser
SSH port, leave blank to use default (22)
port>
SSH password, leave blank to use ssh-agent
SSH password, leave blank to use ssh-agent.
y) Yes type in my own password
g) Generate random password
n) No leave this optional password blank
y/g/n> n
Path to unencrypted PEM-encoded private key file, leave blank to use ssh-agent.
key_file>
Remote config
--------------------
[remote]
host = example.com
user =
user = sftpuser
port =
pass =
key_file =
--------------------
y) Yes this is OK
e) Edit this remote
@ -111,6 +113,34 @@ excess files in the directory.
rclone sync /home/local/directory remote:directory
### SSH Authentication ###
The SFTP remote supports 3 authentication methods
* Password
* Key file
* ssh-agent
Key files should be unencrypted PEM-encoded private key files. For
instance `/home/$USER/.ssh/id_rsa`.
If you don't specify `pass` or `key_file` then it will attempt to
contact an ssh-agent.
### ssh-agent on macOS ###
Note that there seem to be various problems with using an ssh-agent on
macOS due to recent changes in the OS. The most effective work-around
seems to be to start an ssh-agent in each session, eg
eval `ssh-agent -s` && ssh-add -A
And then at the end of the session
eval `ssh-agent -k`
These commands can be used in scripts of course.
### Modified time ###
Modified times are stored on the server to 1 second precision.

View File

@ -6,6 +6,7 @@ package sftp
import (
"io"
"io/ioutil"
"os"
"path"
"time"
@ -40,9 +41,13 @@ func init() {
Optional: true,
}, {
Name: "pass",
Help: "SSH password, leave blank to use ssh-agent",
Help: "SSH password, leave blank to use ssh-agent.",
Optional: true,
IsPassword: true,
}, {
Name: "key_file",
Help: "Path to unencrypted PEM-encoded private key file, leave blank to use ssh-agent.",
Optional: true,
}},
}
fs.Register(fsi)
@ -79,6 +84,7 @@ func NewFs(name, root string) (fs.Fs, error) {
host := fs.ConfigFileGet(name, "host")
port := fs.ConfigFileGet(name, "port")
pass := fs.ConfigFileGet(name, "pass")
keyFile := fs.ConfigFileGet(name, "key_file")
if user == "" {
user = os.Getenv("USER")
}
@ -91,7 +97,9 @@ func NewFs(name, root string) (fs.Fs, error) {
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
Timeout: fs.Config.ConnectTimeout,
}
if pass == "" {
// Add ssh agent-auth if no password or file specified
if pass == "" && keyFile == "" {
sshAgentClient, _, err := sshagent.New()
if err != nil {
return nil, errors.Wrap(err, "couldn't connect to ssh-agent")
@ -100,22 +108,31 @@ func NewFs(name, root string) (fs.Fs, error) {
if err != nil {
return nil, errors.Wrap(err, "couldn't read ssh agent signers")
}
/*
for i, signer := range signers {
if 2*i < len(signers) {
signers[i] = signers[len(signers)-i-1]
signers[len(signers)-i-1] = signer
}
}
*/
config.Auth = append(config.Auth, ssh.PublicKeys(signers...))
} else {
}
// Load key file if specified
if keyFile != "" {
key, err := ioutil.ReadFile(keyFile)
if err != nil {
return nil, errors.Wrap(err, "failed to read private key file")
}
signer, err := ssh.ParsePrivateKey(key)
if err != nil {
return nil, errors.Wrap(err, "failed to parse private key file")
}
config.Auth = append(config.Auth, ssh.PublicKeys(signer))
}
// Auth from password if specified
if pass != "" {
clearpass, err := fs.Reveal(pass)
if err != nil {
return nil, err
}
config.Auth = append(config.Auth, ssh.Password(clearpass))
}
sshClient, err := ssh.Dial("tcp", host+":"+port, config)
if err != nil {
return nil, errors.Wrap(err, "couldn't connect ssh")