mirror of https://github.com/restic/restic.git
Merge pull request #4519 from adamantike/backend/sftp/add-args-option
backend/sftp: Add sftp.args option
This commit is contained in:
commit
ab6defbace
|
@ -0,0 +1,16 @@
|
||||||
|
Enhancement: Add config option to set SFTP command arguments
|
||||||
|
|
||||||
|
The `sftp.args` option can be passed to restic (using `-o`) to specify
|
||||||
|
custom arguments to be used by the SSH command executed by the SFTP
|
||||||
|
backend.
|
||||||
|
|
||||||
|
Before this change, a common scenario where a custom identity file was
|
||||||
|
needed for the SSH connection, required the full command to be
|
||||||
|
specified:
|
||||||
|
`-o sftp.command='ssh user@host:port -i /ssh/my_private_key -s sftp'`
|
||||||
|
|
||||||
|
With this new configuration option:
|
||||||
|
`-o sftp.args='-i /ssh/my_private_key'`
|
||||||
|
|
||||||
|
https://github.com/restic/restic/pull/4519
|
||||||
|
https://github.com/restic/restic/issues/4241
|
|
@ -119,10 +119,10 @@ user's home directory.
|
||||||
Also, if the SFTP server is enforcing domain-confined users, you can
|
Also, if the SFTP server is enforcing domain-confined users, you can
|
||||||
specify the user this way: ``user@domain@host``.
|
specify the user this way: ``user@domain@host``.
|
||||||
|
|
||||||
.. note:: Please be aware that sftp servers do not expand the tilde character
|
.. note:: Please be aware that SFTP servers do not expand the tilde character
|
||||||
(``~``) normally used as an alias for a user's home directory. If you
|
(``~``) normally used as an alias for a user's home directory. If you
|
||||||
want to specify a path relative to the user's home directory, pass a
|
want to specify a path relative to the user's home directory, pass a
|
||||||
relative path to the sftp backend.
|
relative path to the SFTP backend.
|
||||||
|
|
||||||
If you need to specify a port number or IPv6 address, you'll need to use
|
If you need to specify a port number or IPv6 address, you'll need to use
|
||||||
URL syntax. E.g., the repository ``/srv/restic-repo`` on ``[::1]`` (localhost)
|
URL syntax. E.g., the repository ``/srv/restic-repo`` on ``[::1]`` (localhost)
|
||||||
|
@ -172,9 +172,11 @@ Then use it in the backend specification:
|
||||||
|
|
||||||
Last, if you'd like to use an entirely different program to create the
|
Last, if you'd like to use an entirely different program to create the
|
||||||
SFTP connection, you can specify the command to be run with the option
|
SFTP connection, you can specify the command to be run with the option
|
||||||
``-o sftp.command="foobar"``.
|
``-o sftp.command="foobar"``. Alternatively, ``-o sftp.args`` allows
|
||||||
|
setting the arguments passed to the default SSH command (ignored when
|
||||||
|
``sftp.command`` is set)
|
||||||
|
|
||||||
.. note:: Please be aware that sftp servers close connections when no data is
|
.. note:: Please be aware that SFTP servers close connections when no data is
|
||||||
received by the client. This can happen when restic is processing huge
|
received by the client. This can happen when restic is processing huge
|
||||||
amounts of unchanged data. To avoid this issue add the following lines
|
amounts of unchanged data. To avoid this issue add the following lines
|
||||||
to the client's .ssh/config file:
|
to the client's .ssh/config file:
|
||||||
|
|
|
@ -13,8 +13,9 @@ import (
|
||||||
type Config struct {
|
type Config struct {
|
||||||
User, Host, Port, Path string
|
User, Host, Port, Path string
|
||||||
|
|
||||||
Layout string `option:"layout" help:"use this backend directory layout (default: auto-detect)"`
|
Layout string `option:"layout" help:"use this backend directory layout (default: auto-detect)"`
|
||||||
Command string `option:"command" help:"specify command to create sftp connection"`
|
Command string `option:"command" help:"specify command to create sftp connection"`
|
||||||
|
Args string `option:"args" help:"specify arguments for ssh"`
|
||||||
|
|
||||||
Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"`
|
Connections uint `option:"connections" help:"set a limit for the number of concurrent connections (default: 5)"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -213,6 +213,9 @@ func buildSSHCommand(cfg Config) (cmd string, args []string, err error) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, err
|
return "", nil, err
|
||||||
}
|
}
|
||||||
|
if cfg.Args != "" {
|
||||||
|
return "", nil, errors.New("cannot specify both sftp.command and sftp.args options")
|
||||||
|
}
|
||||||
|
|
||||||
return args[0], args[1:], nil
|
return args[0], args[1:], nil
|
||||||
}
|
}
|
||||||
|
@ -226,11 +229,19 @@ func buildSSHCommand(cfg Config) (cmd string, args []string, err error) {
|
||||||
args = append(args, "-p", port)
|
args = append(args, "-p", port)
|
||||||
}
|
}
|
||||||
if cfg.User != "" {
|
if cfg.User != "" {
|
||||||
args = append(args, "-l")
|
args = append(args, "-l", cfg.User)
|
||||||
args = append(args, cfg.User)
|
|
||||||
}
|
}
|
||||||
args = append(args, "-s")
|
|
||||||
args = append(args, "sftp")
|
if cfg.Args != "" {
|
||||||
|
a, err := backend.SplitShellStrings(cfg.Args)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, a...)
|
||||||
|
}
|
||||||
|
|
||||||
|
args = append(args, "-s", "sftp")
|
||||||
return cmd, args, nil
|
return cmd, args, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -9,38 +9,57 @@ var sshcmdTests = []struct {
|
||||||
cfg Config
|
cfg Config
|
||||||
cmd string
|
cmd string
|
||||||
args []string
|
args []string
|
||||||
|
err string
|
||||||
}{
|
}{
|
||||||
{
|
{
|
||||||
Config{User: "user", Host: "host", Path: "dir/subdir"},
|
Config{User: "user", Host: "host", Path: "dir/subdir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"host", "-l", "user", "-s", "sftp"},
|
[]string{"host", "-l", "user", "-s", "sftp"},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config{Host: "host", Path: "dir/subdir"},
|
Config{Host: "host", Path: "dir/subdir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"host", "-s", "sftp"},
|
[]string{"host", "-s", "sftp"},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config{Host: "host", Port: "10022", Path: "/dir/subdir"},
|
Config{Host: "host", Port: "10022", Path: "/dir/subdir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"host", "-p", "10022", "-s", "sftp"},
|
[]string{"host", "-p", "10022", "-s", "sftp"},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
Config{User: "user", Host: "host", Port: "10022", Path: "/dir/subdir"},
|
Config{User: "user", Host: "host", Port: "10022", Path: "/dir/subdir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"host", "-p", "10022", "-l", "user", "-s", "sftp"},
|
[]string{"host", "-p", "10022", "-l", "user", "-s", "sftp"},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config{User: "user", Host: "host", Port: "10022", Path: "/dir/subdir", Args: "-i /path/to/id_rsa"},
|
||||||
|
"ssh",
|
||||||
|
[]string{"host", "-p", "10022", "-l", "user", "-i", "/path/to/id_rsa", "-s", "sftp"},
|
||||||
|
"",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Config{Command: "ssh something", Args: "-i /path/to/id_rsa"},
|
||||||
|
"",
|
||||||
|
nil,
|
||||||
|
"cannot specify both sftp.command and sftp.args options",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IPv6 address.
|
// IPv6 address.
|
||||||
Config{User: "user", Host: "::1", Path: "dir"},
|
Config{User: "user", Host: "::1", Path: "dir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"::1", "-l", "user", "-s", "sftp"},
|
[]string{"::1", "-l", "user", "-s", "sftp"},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
// IPv6 address with zone and port.
|
// IPv6 address with zone and port.
|
||||||
Config{User: "user", Host: "::1%lo0", Port: "22", Path: "dir"},
|
Config{User: "user", Host: "::1%lo0", Port: "22", Path: "dir"},
|
||||||
"ssh",
|
"ssh",
|
||||||
[]string{"::1%lo0", "-p", "22", "-l", "user", "-s", "sftp"},
|
[]string{"::1%lo0", "-p", "22", "-l", "user", "-s", "sftp"},
|
||||||
|
"",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -48,8 +67,14 @@ func TestBuildSSHCommand(t *testing.T) {
|
||||||
for i, test := range sshcmdTests {
|
for i, test := range sshcmdTests {
|
||||||
t.Run("", func(t *testing.T) {
|
t.Run("", func(t *testing.T) {
|
||||||
cmd, args, err := buildSSHCommand(test.cfg)
|
cmd, args, err := buildSSHCommand(test.cfg)
|
||||||
if err != nil {
|
if test.err != "" {
|
||||||
t.Fatalf("%v in test %d", err, i)
|
if err.Error() != test.err {
|
||||||
|
t.Fatalf("expected error %v got %v", test.err, err.Error())
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if err != nil {
|
||||||
|
t.Fatalf("%v in test %d", err, i)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if cmd != test.cmd {
|
if cmd != test.cmd {
|
||||||
|
|
Loading…
Reference in New Issue