From d9b9bbd4a87531cde949e7e82ac7025fd7072caa Mon Sep 17 00:00:00 2001 From: Pauline Middelink Date: Mon, 24 Jul 2017 22:00:44 +0200 Subject: [PATCH 1/3] Force restic to ask the password when adding a key. As `restic key add` uses the same `ReadPasswordTwice()` as the rest of restic, it is sensitive to the environment variable RESTIC_PASSWORD or --password-file= override. When asking for the new key, temporary remove these 2 overrides, forcing the password to be asked. --- cmd/restic/cmd_key.go | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/cmd/restic/cmd_key.go b/cmd/restic/cmd_key.go index fe8272457..101346426 100644 --- a/cmd/restic/cmd_key.go +++ b/cmd/restic/cmd_key.go @@ -3,6 +3,7 @@ package main import ( "context" "fmt" + "os" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" @@ -59,7 +60,16 @@ func getNewPassword(gopts GlobalOptions) (string, error) { return testKeyNewPassword, nil } - return ReadPasswordTwice(gopts, + // Since we already have an open repository, temporary remove the overrides + // to prompt the user for their passwd + oldPasswd := os.Getenv("RESTIC_PASSWORD") + defer func() { os.Setenv("RESTIC_PASSWORD", oldPasswd) }() + os.Unsetenv("RESTIC_PASSWORD") + newopts := gopts + newopts.password = "" + newopts.PasswordFile = "" + + return ReadPasswordTwice(newopts, "enter password for new key: ", "enter password again: ") } From d5615a67c8854510d6781db316e30177ab43b522 Mon Sep 17 00:00:00 2001 From: Pauline Middelink Date: Mon, 24 Jul 2017 23:01:16 +0200 Subject: [PATCH 2/3] Refactor password resolving. Instead of determining the password lazily during ReadPassword(), do so now in cobra.PersistentPreRunE() so we can store the result in the globalOptions and reuse/override when applicable without having to worry about the environment or flag options interfering. --- cmd/restic/cmd_key.go | 9 ++------- cmd/restic/global.go | 28 ++++++++++++++++------------ cmd/restic/main.go | 7 +++++++ 3 files changed, 25 insertions(+), 19 deletions(-) diff --git a/cmd/restic/cmd_key.go b/cmd/restic/cmd_key.go index 101346426..5e7ccf35c 100644 --- a/cmd/restic/cmd_key.go +++ b/cmd/restic/cmd_key.go @@ -3,7 +3,6 @@ package main import ( "context" "fmt" - "os" "github.com/restic/restic/internal/errors" "github.com/restic/restic/internal/repository" @@ -60,14 +59,10 @@ func getNewPassword(gopts GlobalOptions) (string, error) { return testKeyNewPassword, nil } - // Since we already have an open repository, temporary remove the overrides - // to prompt the user for their passwd - oldPasswd := os.Getenv("RESTIC_PASSWORD") - defer func() { os.Setenv("RESTIC_PASSWORD", oldPasswd) }() - os.Unsetenv("RESTIC_PASSWORD") + // Since we already have an open repository, temporary remove the password + // to prompt the user for the passwd. newopts := gopts newopts.password = "" - newopts.PasswordFile = "" return ReadPasswordTwice(newopts, "enter password for new key: ", diff --git a/cmd/restic/global.go b/cmd/restic/global.go index 3eabefa29..a29840a09 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -53,11 +53,6 @@ var globalOptions = GlobalOptions{ } func init() { - pw := os.Getenv("RESTIC_PASSWORD") - if pw != "" { - globalOptions.password = pw - } - var cancel context.CancelFunc globalOptions.ctx, cancel = context.WithCancel(context.Background()) AddCleanupHandler(func() error { @@ -207,6 +202,20 @@ func Exitf(exitcode int, format string, args ...interface{}) { Exit(exitcode) } +// resolvePassword determines the password to be used for opening the repository. +func resolvePassword(opts GlobalOptions, env string) (string, error) { + if opts.PasswordFile != "" { + s, err := ioutil.ReadFile(opts.PasswordFile) + return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile") + } + + if pwd := os.Getenv(env); pwd != "" { + return pwd, nil + } + + return "", nil +} + // readPassword reads the password from the given reader directly. func readPassword(in io.Reader) (password string, err error) { buf := make([]byte, 1000) @@ -238,13 +247,8 @@ func readPasswordTerminal(in *os.File, out io.Writer, prompt string) (password s // ReadPassword reads the password from a password file, the environment // variable RESTIC_PASSWORD or prompts the user. func ReadPassword(opts GlobalOptions, prompt string) (string, error) { - if opts.PasswordFile != "" { - s, err := ioutil.ReadFile(opts.PasswordFile) - return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile") - } - - if pwd := os.Getenv("RESTIC_PASSWORD"); pwd != "" { - return pwd, nil + if opts.password != "" { + return opts.password, nil } var ( diff --git a/cmd/restic/main.go b/cmd/restic/main.go index b53fc986c..195f9e2cc 100644 --- a/cmd/restic/main.go +++ b/cmd/restic/main.go @@ -36,6 +36,13 @@ directories in an encrypted repository stored on different backends. } globalOptions.extended = opts + pwd, err := resolvePassword(globalOptions, "RESTIC_PASSWORD") + if err != nil { + fmt.Fprintf(os.Stderr, "Resolving password failed: %v\n", err) + Exit(1) + } + globalOptions.password = pwd + // run the debug functions for all subcommands (if build tag "debug" is // enabled) if err := runDebug(); err != nil { From c95e2b009e58ab9deaccf35d14e90a1e9a9f4ef6 Mon Sep 17 00:00:00 2001 From: Pauline Middelink Date: Mon, 24 Jul 2017 23:15:31 +0200 Subject: [PATCH 3/3] Simpify cmd_backup and cmd_init now we have the password in gopts. --- cmd/restic/cmd_backup.go | 6 +++--- cmd/restic/cmd_init.go | 12 +++++------- cmd/restic/global.go | 8 +++----- 3 files changed, 11 insertions(+), 15 deletions(-) diff --git a/cmd/restic/cmd_backup.go b/cmd/restic/cmd_backup.go index 931988176..edc36f2df 100644 --- a/cmd/restic/cmd_backup.go +++ b/cmd/restic/cmd_backup.go @@ -249,7 +249,7 @@ func readBackupFromStdin(opts BackupOptions, gopts GlobalOptions, args []string) return errors.Fatal("filename for backup from stdin must not be empty") } - if gopts.password == "" && gopts.PasswordFile == "" { + if gopts.password == "" { return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD") } @@ -322,8 +322,8 @@ func readLinesFromFile(filename string) ([]string, error) { } func runBackup(opts BackupOptions, gopts GlobalOptions, args []string) error { - if opts.FilesFrom == "-" && gopts.password == "" && gopts.PasswordFile == "" { - return errors.Fatal("no password; either use `--password-file` option or put the password into the RESTIC_PASSWORD environment variable") + if opts.FilesFrom == "-" && gopts.password == "" { + return errors.Fatal("unable to read password from stdin when data is to be read from stdin, use --password-file or $RESTIC_PASSWORD") } fromfile, err := readLinesFromFile(opts.FilesFrom) diff --git a/cmd/restic/cmd_init.go b/cmd/restic/cmd_init.go index 47bbf4812..368cadb88 100644 --- a/cmd/restic/cmd_init.go +++ b/cmd/restic/cmd_init.go @@ -34,13 +34,11 @@ func runInit(gopts GlobalOptions, args []string) error { return errors.Fatalf("create backend at %s failed: %v\n", gopts.Repo, err) } - if gopts.password == "" { - gopts.password, err = ReadPasswordTwice(gopts, - "enter password for new backend: ", - "enter password again: ") - if err != nil { - return err - } + gopts.password, err = ReadPasswordTwice(gopts, + "enter password for new backend: ", + "enter password again: ") + if err != nil { + return err } s := repository.New(be) diff --git a/cmd/restic/global.go b/cmd/restic/global.go index a29840a09..673d8bcaf 100644 --- a/cmd/restic/global.go +++ b/cmd/restic/global.go @@ -307,11 +307,9 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) { s := repository.New(be) - if opts.password == "" { - opts.password, err = ReadPassword(opts, "enter password for repository: ") - if err != nil { - return nil, err - } + opts.password, err = ReadPassword(opts, "enter password for repository: ") + if err != nil { + return nil, err } err = s.SearchKey(context.TODO(), opts.password, maxKeys)