forked from TrueCloudLab/restic
Merge pull request 2094 from juergenhoetzel/password-command
Add support for reading password from external command
This commit is contained in:
commit
4609b5c24d
4 changed files with 55 additions and 20 deletions
8
changelog/unreleased/pull-2094
Normal file
8
changelog/unreleased/pull-2094
Normal 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
|
|
@ -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
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
|
|
|
@ -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
|
||||||
****
|
****
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue