2
2
mirror of https://github.com/octoleo/restic.git synced 2024-11-26 14:56:29 +00:00

Merge pull request #613 from restic/read-password-from-file

Read password from file
This commit is contained in:
Alexander Neumann 2016-09-12 20:37:08 +02:00
commit ceb4a3ecc0
5 changed files with 61 additions and 26 deletions

View File

@ -116,7 +116,8 @@ 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` and also the password in the variable
environment variable `RESTIC_REPOSITORY`. The password can be read from a file
(via the option `--password-file`) or the environment variable
`RESTIC_PASSWORD`.
## Password prompt on Windows

View File

@ -20,9 +20,12 @@ func (cmd CmdInit) Execute(args []string) error {
}
if cmd.global.password == "" {
cmd.global.password = cmd.global.ReadPasswordTwice(
cmd.global.password, err = cmd.global.ReadPasswordTwice(
"enter password for new backend: ",
"enter password again: ")
if err != nil {
return err
}
}
s := repository.New(be)

View File

@ -56,9 +56,9 @@ func (cmd CmdKey) listKeys(s *repository.Repository) error {
return tab.Write(cmd.global.stdout)
}
func (cmd CmdKey) getNewPassword() string {
func (cmd CmdKey) getNewPassword() (string, error) {
if cmd.newPassword != "" {
return cmd.newPassword
return cmd.newPassword, nil
}
return cmd.global.ReadPasswordTwice(
@ -67,7 +67,12 @@ func (cmd CmdKey) getNewPassword() string {
}
func (cmd CmdKey) addKey(repo *repository.Repository) error {
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
pw, err := cmd.getNewPassword()
if err != nil {
return err
}
id, err := repository.AddKey(repo, pw, repo.Key())
if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err)
}
@ -92,7 +97,12 @@ func (cmd CmdKey) deleteKey(repo *repository.Repository, name string) error {
}
func (cmd CmdKey) changePassword(repo *repository.Repository) error {
id, err := repository.AddKey(repo, cmd.getNewPassword(), repo.Key())
pw, err := cmd.getNewPassword()
if err != nil {
return err
}
id, err := repository.AddKey(repo, pw, repo.Key())
if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err)
}

View File

@ -3,6 +3,7 @@ package main
import (
"fmt"
"io"
"io/ioutil"
"os"
"restic"
"runtime"
@ -28,11 +29,12 @@ var compiledAt = "unknown time"
// GlobalOptions holds all those options that can be set for every command.
type GlobalOptions struct {
Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"`
CacheDir string ` long:"cache-dir" description:"Directory to use as a local cache"`
Quiet bool `short:"q" long:"quiet" default:"false" description:"Do not output comprehensive progress report"`
NoLock bool ` long:"no-lock" default:"false" description:"Do not lock the repo, this allows some operations on read-only repos."`
Options []string `short:"o" long:"option" description:"Specify options in the form 'foo.key=value'"`
Repo string `short:"r" long:"repo" description:"Repository directory to backup to/restore from"`
PasswordFile string `short:"p" long:"password-file" description:"Read the repository password from a file"`
CacheDir string ` long:"cache-dir" description:"Directory to use as a local cache"`
Quiet bool `short:"q" long:"quiet" default:"false" description:"Do not output comprehensive progress report"`
NoLock bool ` long:"no-lock" default:"false" description:"Do not lock the repo, this allows some operations on read-only repos."`
Options []string `short:"o" long:"option" description:"Specify options in the form 'foo.key=value'"`
password string
stdout io.Writer
@ -185,7 +187,7 @@ func readPassword(in io.Reader) (password string, err error) {
buf = buf[:n]
if err != nil && errors.Cause(err) != io.ErrUnexpectedEOF {
return "", err
return "", errors.Wrap(err, "ReadFull")
}
return strings.TrimRight(string(buf), "\r\n"), nil
@ -199,15 +201,25 @@ func readPasswordTerminal(in *os.File, out io.Writer, prompt string) (password s
buf, err := terminal.ReadPassword(int(in.Fd()))
fmt.Fprintln(out)
if err != nil {
return "", err
return "", errors.Wrap(err, "ReadPassword")
}
password = string(buf)
return password, nil
}
// ReadPassword reads the password from stdin.
func (o GlobalOptions) ReadPassword(prompt string) string {
// ReadPassword reads the password from a password file, the environment
// variable RESTIC_PASSWORD or prompts the user.
func (o GlobalOptions) ReadPassword(prompt string) (string, error) {
if o.PasswordFile != "" {
s, err := ioutil.ReadFile(o.PasswordFile)
return strings.TrimSpace(string(s)), errors.Wrap(err, "Readfile")
}
if pwd := os.Getenv("RESTIC_PASSWORD"); pwd != "" {
return pwd, nil
}
var (
password string
err error
@ -220,26 +232,33 @@ func (o GlobalOptions) ReadPassword(prompt string) string {
}
if err != nil {
o.Exitf(2, "unable to read password: %v", err)
return "", errors.Wrap(err, "unable to read password")
}
if len(password) == 0 {
o.Exitf(1, "an empty password is not a password")
return "", errors.Fatal("an empty password is not a password")
}
return password
return password, nil
}
// ReadPasswordTwice calls ReadPassword two times and returns an error when the
// passwords don't match.
func (o GlobalOptions) ReadPasswordTwice(prompt1, prompt2 string) string {
pw1 := o.ReadPassword(prompt1)
pw2 := o.ReadPassword(prompt2)
if pw1 != pw2 {
o.Exitf(1, "passwords do not match")
func (o GlobalOptions) ReadPasswordTwice(prompt1, prompt2 string) (string, error) {
pw1, err := o.ReadPassword(prompt1)
if err != nil {
return "", err
}
pw2, err := o.ReadPassword(prompt2)
if err != nil {
return "", err
}
return pw1
if pw1 != pw2 {
return "", errors.Fatal("passwords do not match")
}
return pw1, nil
}
const maxKeys = 20
@ -258,7 +277,10 @@ func (o GlobalOptions) OpenRepository() (*repository.Repository, error) {
s := repository.New(be)
if o.password == "" {
o.password = o.ReadPassword("enter password for repository: ")
o.password, err = o.ReadPassword("enter password for repository: ")
if err != nil {
return nil, err
}
}
err = s.SearchKey(o.password, maxKeys)

View File

@ -28,7 +28,6 @@ func main() {
// defer profile.Start(profile.MemProfileRate(100000), profile.ProfilePath(".")).Stop()
// defer profile.Start(profile.CPUProfile, profile.ProfilePath(".")).Stop()
globalOpts.Repo = os.Getenv("RESTIC_REPOSITORY")
globalOpts.password = os.Getenv("RESTIC_PASSWORD")
debug.Log("restic", "main %#v", os.Args)