Merge pull request 2094 from juergenhoetzel/password-command

Add support for reading password from external command
This commit is contained in:
Alexander Neumann 2019-01-06 21:14:52 +01:00
commit 4609b5c24d
4 changed files with 55 additions and 20 deletions

View File

@ -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

View File

@ -34,6 +34,7 @@ import (
"github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/errors"
"golang.org/x/crypto/ssh/terminal" "golang.org/x/crypto/ssh/terminal"
"os/exec"
) )
var version = "0.9.3-dev (compiled manually)" 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. // GlobalOptions hold all global options for restic.
type GlobalOptions struct { type GlobalOptions struct {
Repo string Repo string
PasswordFile string PasswordFile string
KeyHint string PasswordCommand string
Quiet bool KeyHint string
Verbose int Quiet bool
NoLock bool Verbose int
JSON bool NoLock bool
CacheDir string JSON bool
NoCache bool CacheDir string
CACerts []string NoCache bool
TLSClientCert string CACerts []string
CleanupCache bool TLSClientCert string
CleanupCache bool
LimitUploadKb int LimitUploadKb int
LimitDownloadKb 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.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.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.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.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.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") f.BoolVar(&globalOptions.NoLock, "no-lock", false, "do not lock the repo, this allows some operations on read-only repos")
@ -236,7 +239,23 @@ func Exitf(exitcode int, format string, args ...interface{}) {
} }
// resolvePassword determines the password to be used for opening the repository. // 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 != "" { if opts.PasswordFile != "" {
s, err := textfile.Read(opts.PasswordFile) s, err := textfile.Read(opts.PasswordFile)
if os.IsNotExist(errors.Cause(err)) { if os.IsNotExist(errors.Cause(err)) {
@ -245,7 +264,7 @@ func resolvePassword(opts GlobalOptions, env string) (string, error) {
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile") 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 return pwd, nil
} }

View File

@ -54,7 +54,7 @@ directories in an encrypted repository stored on different backends.
if c.Name() == "version" { if c.Name() == "version" {
return nil return nil
} }
pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD") pwd, err := resolvePassword(globalOptions)
if err != nil { if err != nil {
fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err) fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err)
Exit(1) Exit(1)

View File

@ -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 other options. You can skip to the next chapter once you've read the relevant
section here. 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 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 Remembering your password is important! If you lose it, you won't be
able to access data stored in the repository. 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 SFTP
**** ****