Merge pull request #2910 from mtdcr/repository-file

Add new option --repository-file (default: $RESTIC_REPOSITORY_FILE)
This commit is contained in:
MichaelEischer 2020-10-01 01:04:23 +02:00 committed by GitHub
commit f361ed66de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 88 additions and 6 deletions

View file

@ -0,0 +1,10 @@
Enhancement: New option --repository-file
We've added a new command-line option --repository-file as an alternative
to -r. This allows to read the repository URL from a file in order to
prevent certain types of information leaks, especially for URLs containing
credentials.
https://github.com/restic/restic/issues/1458
https://github.com/restic/restic/issues/2900
https://github.com/restic/restic/pull/2910

View file

@ -52,7 +52,12 @@ func runInit(opts InitOptions, gopts GlobalOptions, args []string) error {
return err
}
be, err := create(gopts.Repo, gopts.extended)
repo, err := ReadRepo(gopts)
if err != nil {
return err
}
be, err := create(repo, gopts.extended)
if err != nil {
return errors.Fatalf("create repository at %s failed: %v\n", location.StripPassword(gopts.Repo), err)
}

View file

@ -49,6 +49,7 @@ type backendWrapper func(r restic.Backend) (restic.Backend, error)
// GlobalOptions hold all global options for restic.
type GlobalOptions struct {
Repo string
RepositoryFile string
PasswordFile string
PasswordCommand string
KeyHint string
@ -101,6 +102,7 @@ func init() {
f := cmdRoot.PersistentFlags()
f.StringVarP(&globalOptions.Repo, "repo", "r", os.Getenv("RESTIC_REPOSITORY"), "`repository` to backup to or restore from (default: $RESTIC_REPOSITORY)")
f.StringVarP(&globalOptions.RepositoryFile, "repository-file", "", os.Getenv("RESTIC_REPOSITORY_FILE"), "`file` to read the repository location from (default: $RESTIC_REPOSITORY_FILE)")
f.StringVarP(&globalOptions.PasswordFile, "password-file", "p", os.Getenv("RESTIC_PASSWORD_FILE"), "`file` to read the repository password from (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"), "shell `command` to obtain the repository password from (default: $RESTIC_PASSWORD_COMMAND)")
@ -382,15 +384,41 @@ func ReadPasswordTwice(gopts GlobalOptions, prompt1, prompt2 string) (string, er
return pw1, nil
}
func ReadRepo(opts GlobalOptions) (string, error) {
if opts.Repo == "" && opts.RepositoryFile == "" {
return "", errors.Fatal("Please specify repository location (-r or --repository-file)")
}
repo := opts.Repo
if opts.RepositoryFile != "" {
if repo != "" {
return "", errors.Fatal("Options -r and --repository-file are mutually exclusive, please specify only one")
}
s, err := textfile.Read(opts.RepositoryFile)
if os.IsNotExist(errors.Cause(err)) {
return "", errors.Fatalf("%s does not exist", opts.RepositoryFile)
}
if err != nil {
return "", err
}
repo = strings.TrimSpace(string(s))
}
return repo, nil
}
const maxKeys = 20
// OpenRepository reads the password and opens the repository.
func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
if opts.Repo == "" {
return nil, errors.Fatal("Please specify repository location (-r)")
repo, err := ReadRepo(opts)
if err != nil {
return nil, err
}
be, err := open(opts.Repo, opts, opts.extended)
be, err := open(repo, opts, opts.extended)
if err != nil {
return nil, err
}

View file

@ -2,8 +2,11 @@ package main
import (
"bytes"
"io/ioutil"
"path/filepath"
"testing"
"github.com/restic/restic/internal/test"
rtest "github.com/restic/restic/internal/test"
)
@ -26,3 +29,33 @@ func Test_PrintFunctionsRespectsGlobalStdout(t *testing.T) {
buf.Reset()
}
}
func TestReadRepo(t *testing.T) {
tempDir, cleanup := test.TempDir(t)
defer cleanup()
// test --repo option
var opts GlobalOptions
opts.Repo = tempDir
repo, err := ReadRepo(opts)
rtest.OK(t, err)
rtest.Equals(t, tempDir, repo)
// test --repository-file option
foo := filepath.Join(tempDir, "foo")
err = ioutil.WriteFile(foo, []byte(tempDir+"\n"), 0666)
rtest.OK(t, err)
var opts2 GlobalOptions
opts2.RepositoryFile = foo
repo, err = ReadRepo(opts2)
rtest.OK(t, err)
rtest.Equals(t, tempDir, repo)
var opts3 GlobalOptions
opts3.RepositoryFile = foo + "-invalid"
repo, err = ReadRepo(opts3)
if err == nil {
t.Fatal("must not read repository path from invalid file path")
}
}

View file

@ -22,8 +22,10 @@ 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:
environment variable ``RESTIC_REPOSITORY``. Restic can also read the repository
location from a file specified via the ``--repository-file`` option or the
environment variable ``RESTIC_REPOSITORY_FILE``. For the password, several
options exist:
* Setting the environment variable ``RESTIC_PASSWORD``

View file

@ -407,6 +407,7 @@ environment variables. The following lists these environment variables:
.. code-block:: console
RESTIC_REPOSITORY_FILE Name of file containing the repository location (replaces --repository-file)
RESTIC_REPOSITORY Location of repository (replaces -r)
RESTIC_PASSWORD_FILE Location of password file (replaces --password-file)
RESTIC_PASSWORD The actual password for the repository

View file

@ -60,6 +60,7 @@ Usage help is available:
-p, --password-file file file to read the repository password from (default: $RESTIC_PASSWORD_FILE)
-q, --quiet do not output comprehensive progress report
-r, --repo repository repository to backup to or restore from (default: $RESTIC_REPOSITORY)
--repository-file file file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)
--tls-client-cert file path to a file containing PEM encoded TLS client certificate and private key
-v, --verbose n be verbose (specify --verbose multiple times or level --verbose=n)
@ -92,6 +93,7 @@ command:
--exclude-caches excludes cache directories that are marked with a CACHEDIR.TAG file. See https://bford.info/cachedir/ for the Cache Directory Tagging Standard
--exclude-file file read exclude patterns from a file (can be specified multiple times)
--exclude-if-present filename[:header] takes filename[:header], exclude contents of directories containing filename (except filename itself) if header of that file is as provided (can be specified multiple times)
--exclude-larger-than size max size of the files to be backed up (allowed suffixes: k/K, m/M, g/G, t/T)
--files-from file read the files to backup from file (can be combined with file args/can be specified multiple times)
-f, --force force re-reading the target files/directories (overrides the "parent" flag)
-h, --help help for backup
@ -122,6 +124,7 @@ command:
-p, --password-file file file to read the repository password from (default: $RESTIC_PASSWORD_FILE)
-q, --quiet do not output comprehensive progress report
-r, --repo repository repository to backup to or restore from (default: $RESTIC_REPOSITORY)
--repository-file file file to read the repository location from (default: $RESTIC_REPOSITORY_FILE)
--tls-client-cert file path to a file containing PEM encoded TLS client certificate and private key
-v, --verbose n be verbose (specify --verbose multiple times or level --verbose=n)