From df7f72cdde2419937c15377d46a311e5d734a245 Mon Sep 17 00:00:00 2001 From: Juergen Hoetzel Date: Sun, 18 Nov 2018 14:31:00 +0100 Subject: [PATCH 1/2] Add support for reading password from external command This allows reading the password from an password manager (like "pass"). Signed-off-by: Juergen Hoetzel --- cmd/restic/global.go | 47 +++++++++++++++++++++++++++++++------------- cmd/restic/main.go | 2 +- 2 files changed, 34 insertions(+), 15 deletions(-) diff --git a/cmd/restic/global.go b/cmd/restic/global.go index de8e6652d..5eecf3eba 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -34,6 +34,7 @@ import ( "github.com/restic/restic/internal/errors" "golang.org/x/crypto/ssh/terminal" + "os/exec" ) var version = "0.9.3-dev (compiled manually)" @@ -43,18 +44,19 @@ const TimeFormat = "2006-01-02 15:04:05" // GlobalOptions hold all global options for restic. type GlobalOptions struct { - Repo string - PasswordFile string - KeyHint string - Quiet bool - Verbose int - NoLock bool - JSON bool - CacheDir string - NoCache bool - CACerts []string - TLSClientCert string - CleanupCache bool + Repo string + PasswordFile string + PasswordCommand string + KeyHint string + Quiet bool + Verbose int + NoLock bool + JSON bool + CacheDir string + NoCache bool + CACerts []string + TLSClientCert string + CleanupCache bool LimitUploadKb int LimitDownloadKb int @@ -93,6 +95,7 @@ func init() { f.StringVarP(&globalOptions.Repo, "repo", "r", os.Getenv("RESTIC_REPOSITORY"), "repository to backup to or restore from (default: $RESTIC_REPOSITORY)") f.StringVarP(&globalOptions.PasswordFile, "password-file", "p", os.Getenv("RESTIC_PASSWORD_FILE"), "read the repository password from a file (default: $RESTIC_PASSWORD_FILE)") f.StringVarP(&globalOptions.KeyHint, "key-hint", "", os.Getenv("RESTIC_KEY_HINT"), "key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)") + f.StringVarP(&globalOptions.PasswordCommand, "password-command", "", os.Getenv("RESTIC_PASSWORD_COMMAND"), "specify a shell command to obtain a password (default: $RESTIC_PASSWORD_COMMAND)") f.BoolVarP(&globalOptions.Quiet, "quiet", "q", false, "do not output comprehensive progress report") f.CountVarP(&globalOptions.Verbose, "verbose", "v", "be verbose (specify --verbose multiple times or level `n`)") f.BoolVar(&globalOptions.NoLock, "no-lock", false, "do not lock the repo, this allows some operations on read-only repos") @@ -238,7 +241,23 @@ func Exitf(exitcode int, format string, args ...interface{}) { } // resolvePassword determines the password to be used for opening the repository. -func resolvePassword(opts GlobalOptions, env string) (string, error) { +func resolvePassword(opts GlobalOptions) (string, error) { + if opts.PasswordFile != "" && opts.PasswordCommand != "" { + return "", errors.Fatalf("Password file and command are mutually exclusive options") + } + if opts.PasswordCommand != "" { + args, err := backend.SplitShellStrings(opts.PasswordCommand) + if err != nil { + return "", err + } + cmd := exec.Command(args[0], args[1:]...) + cmd.Stderr = os.Stderr + output, err := cmd.Output() + if err != nil { + return "", err + } + return (strings.TrimSpace(string(output))), nil + } if opts.PasswordFile != "" { s, err := textfile.Read(opts.PasswordFile) if os.IsNotExist(errors.Cause(err)) { @@ -247,7 +266,7 @@ func resolvePassword(opts GlobalOptions, env string) (string, error) { return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile") } - if pwd := os.Getenv(env); pwd != "" { + if pwd := os.Getenv("RESTIC_PASSWORD"); pwd != "" { return pwd, nil } diff --git a/cmd/restic/main.go b/cmd/restic/main.go index 01a902b1d..e61547c5f 100644 --- a/cmd/restic/main.go +++ b/cmd/restic/main.go @@ -54,7 +54,7 @@ directories in an encrypted repository stored on different backends. if c.Name() == "version" { return nil } - pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD") + pwd, err := resolvePassword(globalOptions) if err != nil { fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err) Exit(1) From 0dc364841655141973a888436e3fc340c9a1b8c0 Mon Sep 17 00:00:00 2001 From: Alexander Neumann Date: Sun, 6 Jan 2019 20:41:38 +0100 Subject: [PATCH 2/2] Add documentation for `--password-command` --- changelog/unreleased/pull-2094 | 8 ++++++++ doc/030_preparing_a_new_repo.rst | 18 +++++++++++++----- 2 files changed, 21 insertions(+), 5 deletions(-) create mode 100644 changelog/unreleased/pull-2094 diff --git a/changelog/unreleased/pull-2094 b/changelog/unreleased/pull-2094 new file mode 100644 index 000000000..c4e8d2155 --- /dev/null +++ b/changelog/unreleased/pull-2094 @@ -0,0 +1,8 @@ +Enhancement: Run command to get password + +We've added the `--password-command` option which allows specifying a command +that restic runs every time the password for the repository is needed, so it +can be integrated with a password manager or keyring. The option can also be +set via the environment variable `$RESTIC_PASSWORD_COMMAND`. + +https://github.com/restic/restic/pull/2094 diff --git a/doc/030_preparing_a_new_repo.rst b/doc/030_preparing_a_new_repo.rst index 0131748ca..5f75e13ca 100644 --- a/doc/030_preparing_a_new_repo.rst +++ b/doc/030_preparing_a_new_repo.rst @@ -21,6 +21,19 @@ using a local repository; the remaining sections of this chapter cover all the other options. You can skip to the next chapter once you've read the relevant section here. +For automated backups, restic accepts the repository location in the +environment variable ``RESTIC_REPOSITORY``. For the password, several options +exist: + + * Setting the environment variable ``RESTIC_PASSWORD`` + + * Specifying the path to a file with the password via the option + ``--password-file`` or the environment variable ``RESTIC_PASSWORD_FILE`` + + * Configuring a program to be called when the password is needed via the + option ``--password-command`` or the environment variable + ``RESTIC_PASSWORD_COMMAND`` + Local ***** @@ -41,11 +54,6 @@ command and enter the same password twice: Remembering your password is important! If you lose it, you won't be able to access data stored in the repository. -For automated backups, restic accepts the repository location in the -environment variable ``RESTIC_REPOSITORY``. The password can be read -from a file (via the option ``--password-file`` or the environment variable -``RESTIC_PASSWORD_FILE``) or the environment variable ``RESTIC_PASSWORD``. - SFTP ****