forked from TrueCloudLab/rclone
sftp: add support for using ssh key files #1494
Update docs about macOS and ssh-agent #1218
This commit is contained in:
parent
b44d0ea088
commit
d55f8f0492
2 changed files with 67 additions and 20 deletions
|
@ -21,17 +21,14 @@ Here is an example of making a SFTP configuration. First run
|
||||||
|
|
||||||
rclone config
|
rclone config
|
||||||
|
|
||||||
This will guide you through an interactive setup process. You will
|
This will guide you through an interactive setup process.
|
||||||
need your account number (a short hex number) and key (a long hex
|
|
||||||
number) which you can get from the SFTP control panel.
|
|
||||||
```
|
```
|
||||||
No remotes found - make a new one
|
No remotes found - make a new one
|
||||||
n) New remote
|
n) New remote
|
||||||
r) Rename remote
|
|
||||||
c) Copy remote
|
|
||||||
s) Set configuration password
|
s) Set configuration password
|
||||||
q) Quit config
|
q) Quit config
|
||||||
n/r/c/s/q> n
|
n/s/q> n
|
||||||
name> remote
|
name> remote
|
||||||
Type of storage to configure.
|
Type of storage to configure.
|
||||||
Choose a number from below, or type in your own value
|
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"
|
\ "sftp"
|
||||||
14 / Yandex Disk
|
14 / Yandex Disk
|
||||||
\ "yandex"
|
\ "yandex"
|
||||||
|
15 / http Connection
|
||||||
|
\ "http"
|
||||||
Storage> sftp
|
Storage> sftp
|
||||||
SSH host to connect to
|
SSH host to connect to
|
||||||
Choose a number from below, or type in your own value
|
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"
|
\ "example.com"
|
||||||
host> example.com
|
host> example.com
|
||||||
SSH username, leave blank for current username, ncw
|
SSH username, leave blank for current username, ncw
|
||||||
user>
|
user> sftpuser
|
||||||
SSH port, leave blank to use default (22)
|
SSH port, leave blank to use default (22)
|
||||||
port>
|
port>
|
||||||
SSH password, leave blank to use ssh-agent
|
SSH password, leave blank to use ssh-agent.
|
||||||
y) Yes type in my own password
|
y) Yes type in my own password
|
||||||
g) Generate random password
|
g) Generate random password
|
||||||
n) No leave this optional password blank
|
n) No leave this optional password blank
|
||||||
y/g/n> n
|
y/g/n> n
|
||||||
|
Path to unencrypted PEM-encoded private key file, leave blank to use ssh-agent.
|
||||||
|
key_file>
|
||||||
Remote config
|
Remote config
|
||||||
--------------------
|
--------------------
|
||||||
[remote]
|
[remote]
|
||||||
host = example.com
|
host = example.com
|
||||||
user =
|
user = sftpuser
|
||||||
port =
|
port =
|
||||||
pass =
|
pass =
|
||||||
|
key_file =
|
||||||
--------------------
|
--------------------
|
||||||
y) Yes this is OK
|
y) Yes this is OK
|
||||||
e) Edit this remote
|
e) Edit this remote
|
||||||
|
@ -111,6 +113,34 @@ excess files in the directory.
|
||||||
|
|
||||||
rclone sync /home/local/directory remote: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 time ###
|
||||||
|
|
||||||
Modified times are stored on the server to 1 second precision.
|
Modified times are stored on the server to 1 second precision.
|
||||||
|
|
39
sftp/sftp.go
39
sftp/sftp.go
|
@ -6,6 +6,7 @@ package sftp
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"time"
|
"time"
|
||||||
|
@ -40,9 +41,13 @@ func init() {
|
||||||
Optional: true,
|
Optional: true,
|
||||||
}, {
|
}, {
|
||||||
Name: "pass",
|
Name: "pass",
|
||||||
Help: "SSH password, leave blank to use ssh-agent",
|
Help: "SSH password, leave blank to use ssh-agent.",
|
||||||
Optional: true,
|
Optional: true,
|
||||||
IsPassword: 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)
|
fs.Register(fsi)
|
||||||
|
@ -79,6 +84,7 @@ func NewFs(name, root string) (fs.Fs, error) {
|
||||||
host := fs.ConfigFileGet(name, "host")
|
host := fs.ConfigFileGet(name, "host")
|
||||||
port := fs.ConfigFileGet(name, "port")
|
port := fs.ConfigFileGet(name, "port")
|
||||||
pass := fs.ConfigFileGet(name, "pass")
|
pass := fs.ConfigFileGet(name, "pass")
|
||||||
|
keyFile := fs.ConfigFileGet(name, "key_file")
|
||||||
if user == "" {
|
if user == "" {
|
||||||
user = os.Getenv("USER")
|
user = os.Getenv("USER")
|
||||||
}
|
}
|
||||||
|
@ -91,7 +97,9 @@ func NewFs(name, root string) (fs.Fs, error) {
|
||||||
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
HostKeyCallback: ssh.InsecureIgnoreHostKey(),
|
||||||
Timeout: fs.Config.ConnectTimeout,
|
Timeout: fs.Config.ConnectTimeout,
|
||||||
}
|
}
|
||||||
if pass == "" {
|
|
||||||
|
// Add ssh agent-auth if no password or file specified
|
||||||
|
if pass == "" && keyFile == "" {
|
||||||
sshAgentClient, _, err := sshagent.New()
|
sshAgentClient, _, err := sshagent.New()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't connect to ssh-agent")
|
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 {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't read ssh agent signers")
|
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...))
|
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)
|
clearpass, err := fs.Reveal(pass)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
config.Auth = append(config.Auth, ssh.Password(clearpass))
|
config.Auth = append(config.Auth, ssh.Password(clearpass))
|
||||||
}
|
}
|
||||||
|
|
||||||
sshClient, err := ssh.Dial("tcp", host+":"+port, config)
|
sshClient, err := ssh.Dial("tcp", host+":"+port, config)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "couldn't connect ssh")
|
return nil, errors.Wrap(err, "couldn't connect ssh")
|
||||||
|
|
Loading…
Add table
Reference in a new issue