2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-29 00:06:32 +00:00

rclone/sftp: Improve handling of ErrDot errors

Restic now yields a more informative error message when exec.ErrDot occurs.
This commit is contained in:
Leo R. Lundgren 2022-09-20 21:26:01 +02:00
parent d6575f53ca
commit ebe9f2c969
6 changed files with 62 additions and 1 deletions

View File

@ -0,0 +1,15 @@
Enhancement: Improve handling of ErrDot errors in rclone and sftp backends
Since Go 1.19, restic can no longer implicitly run relative executables which
are found in the current directory (e.g. `rclone` if it's found in `.`). This
is a security feature of Go to prevent against running unintended and possibly
harmful executables.
The error message for this was just "cannot run executable found relative to
current directory". This has now been improved to yield a more specific error
message, informing the user how to explicitly allow running the executable
using the `-o rclone.program` and `-o sftp.command` extended options with `./`.
https://github.com/restic/restic/issues/3932
https://pkg.go.dev/os/exec#hdr-Executables_in_the_current_directory
https://go.dev/blog/path-security

View File

@ -633,6 +633,13 @@ initiate a new repository in the path ``bar`` in the remote ``foo``:
Restic takes care of starting and stopping rclone. Restic takes care of starting and stopping rclone.
.. note:: If you get an error message saying "cannot implicitly run relative
executable rclone found in current directory", this means that an
rclone executable was found in the current directory. For security
reasons restic will not run this implicitly, instead you have to
use the ``-o rclone.program=./rclone`` extended option to override
this security check and explicitly tell restic to use the executable.
As a more concrete example, suppose you have configured a remote named As a more concrete example, suppose you have configured a remote named
``b2prod`` for Backblaze B2 with rclone, with a bucket called ``yggdrasil``. ``b2prod`` for Backblaze B2 with rclone, with a bucket called ``yggdrasil``.
You can then use rclone to list files in the bucket like this: You can then use rclone to list files in the bucket like this:

View File

@ -0,0 +1,20 @@
//go:build go1.19
// +build go1.19
// This file provides a function to check whether an error from cmd.Start() is
// exec.ErrDot which was introduced in Go 1.19.
// This function is needed so that we can perform this check only for Go 1.19 and
// up, whereas for older versions we use a dummy/stub in the file errdot_old.go.
// Once the minimum Go version restic supports is 1.19, remove this file and
// replace any calls to it with the corresponding code as per below.
package backend
import (
"errors"
"os/exec"
)
func IsErrDot(err error) bool {
return errors.Is(err, exec.ErrDot)
}

View File

@ -0,0 +1,13 @@
//go:build !go1.19
// +build !go1.19
// This file provides a stub for IsErrDot() for Go versions below 1.19.
// See the corresponding file errdot_119.go for more information.
// Once the minimum Go version restic supports is 1.19, remove this file
// and perform the actions listed in errdot_119.go.
package backend
func IsErrDot(err error) bool {
return false
}

View File

@ -85,6 +85,9 @@ func run(command string, args ...string) (*StdioConn, *sync.WaitGroup, func() er
err = errW err = errW
} }
if err != nil { if err != nil {
if backend.IsErrDot(err) {
return nil, nil, nil, errors.Errorf("cannot implicitly run relative executable %v found in current directory, use -o rclone.program=./<program> to override", cmd.Path)
}
return nil, nil, nil, err return nil, nil, nil, err
} }

View File

@ -80,7 +80,10 @@ func startClient(cfg Config) (*SFTP, error) {
bg, err := backend.StartForeground(cmd) bg, err := backend.StartForeground(cmd)
if err != nil { if err != nil {
return nil, errors.Wrap(err, "cmd.Start") if backend.IsErrDot(err) {
return nil, errors.Errorf("cannot implicitly run relative executable %v found in current directory, use -o sftp.command=./<command> to override", cmd.Path)
}
return nil, err
} }
// wait in a different goroutine // wait in a different goroutine