forked from TrueCloudLab/restic
Add key hinting (#2097)
This commit is contained in:
parent
d53595e43c
commit
1688713400
7 changed files with 39 additions and 6 deletions
12
changelog/unreleased/issue-2097
Normal file
12
changelog/unreleased/issue-2097
Normal file
|
@ -0,0 +1,12 @@
|
||||||
|
Enhancement: Add key hinting
|
||||||
|
|
||||||
|
Added a new option `--key-hint` and corresponding environment variable
|
||||||
|
`RESTIC_KEY_HINT`. The key hint is a key ID to try decrypting first, before
|
||||||
|
other keys in the repository.
|
||||||
|
|
||||||
|
This change will benefit repositories with many keys; if the correct key hint
|
||||||
|
is supplied then restic only needs to check one key. If the key hint is
|
||||||
|
incorrect (the key does not exist, or the password is incorrect) then restic
|
||||||
|
will check all keys, as usual.
|
||||||
|
|
||||||
|
https://github.com/restic/restic/issues/2097
|
|
@ -45,6 +45,7 @@ const TimeFormat = "2006-01-02 15:04:05"
|
||||||
type GlobalOptions struct {
|
type GlobalOptions struct {
|
||||||
Repo string
|
Repo string
|
||||||
PasswordFile string
|
PasswordFile string
|
||||||
|
KeyHint string
|
||||||
Quiet bool
|
Quiet bool
|
||||||
Verbose int
|
Verbose int
|
||||||
NoLock bool
|
NoLock bool
|
||||||
|
@ -91,6 +92,7 @@ func init() {
|
||||||
f := cmdRoot.PersistentFlags()
|
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.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.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")
|
||||||
|
@ -353,7 +355,7 @@ func OpenRepository(opts GlobalOptions) (*repository.Repository, error) {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
err = s.SearchKey(opts.ctx, opts.password, maxKeys)
|
err = s.SearchKey(opts.ctx, opts.password, maxKeys, opts.KeyHint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
|
@ -47,6 +47,7 @@ Usage help is available:
|
||||||
--cleanup-cache auto remove old cache directories
|
--cleanup-cache auto remove old cache directories
|
||||||
-h, --help help for restic
|
-h, --help help for restic
|
||||||
--json set output mode to JSON for commands that support it
|
--json set output mode to JSON for commands that support it
|
||||||
|
--key-hint string key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
|
||||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||||
--no-cache do not use a local cache
|
--no-cache do not use a local cache
|
||||||
|
@ -97,6 +98,7 @@ command:
|
||||||
--cache-dir string set the cache directory. (default: use system default cache directory)
|
--cache-dir string set the cache directory. (default: use system default cache directory)
|
||||||
--cleanup-cache auto remove old cache directories
|
--cleanup-cache auto remove old cache directories
|
||||||
--json set output mode to JSON for commands that support it
|
--json set output mode to JSON for commands that support it
|
||||||
|
--key-hint string key ID of key to try decrypting first (default: $RESTIC_KEY_HINT)
|
||||||
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
--limit-download int limits downloads to a maximum rate in KiB/s. (default: unlimited)
|
||||||
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
--limit-upload int limits uploads to a maximum rate in KiB/s. (default: unlimited)
|
||||||
--no-cache do not use a local cache
|
--no-cache do not use a local cache
|
||||||
|
|
|
@ -330,7 +330,7 @@ func TestCheckerModifiedData(t *testing.T) {
|
||||||
|
|
||||||
beError := &errorBackend{Backend: repo.Backend()}
|
beError := &errorBackend{Backend: repo.Backend()}
|
||||||
checkRepo := repository.New(beError)
|
checkRepo := repository.New(beError)
|
||||||
test.OK(t, checkRepo.SearchKey(context.TODO(), test.TestPassword, 5))
|
test.OK(t, checkRepo.SearchKey(context.TODO(), test.TestPassword, 5, ""))
|
||||||
|
|
||||||
chkr := checker.New(checkRepo)
|
chkr := checker.New(checkRepo)
|
||||||
|
|
||||||
|
|
|
@ -112,9 +112,26 @@ func OpenKey(ctx context.Context, s *Repository, name string, password string) (
|
||||||
// given password. If none could be found, ErrNoKeyFound is returned. When
|
// given password. If none could be found, ErrNoKeyFound is returned. When
|
||||||
// maxKeys is reached, ErrMaxKeysReached is returned. When setting maxKeys to
|
// maxKeys is reached, ErrMaxKeysReached is returned. When setting maxKeys to
|
||||||
// zero, all keys in the repo are checked.
|
// zero, all keys in the repo are checked.
|
||||||
func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int) (k *Key, err error) {
|
func SearchKey(ctx context.Context, s *Repository, password string, maxKeys int, keyHint string) (k *Key, err error) {
|
||||||
checked := 0
|
checked := 0
|
||||||
|
|
||||||
|
if len(keyHint) > 0 {
|
||||||
|
id, err := restic.Find(s.Backend(), restic.KeyFile, keyHint)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
key, err := OpenKey(ctx, s, id, password)
|
||||||
|
|
||||||
|
if err == nil {
|
||||||
|
debug.Log("successfully opened hinted key %v", id)
|
||||||
|
return key, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
debug.Log("could not open hinted key %v", id)
|
||||||
|
} else {
|
||||||
|
debug.Log("Could not find hinted key %v", keyHint)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
listCtx, cancel := context.WithCancel(ctx)
|
listCtx, cancel := context.WithCancel(ctx)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
|
|
||||||
|
|
|
@ -510,8 +510,8 @@ func LoadIndex(ctx context.Context, repo restic.Repository, id restic.ID) (*Inde
|
||||||
|
|
||||||
// SearchKey finds a key with the supplied password, afterwards the config is
|
// SearchKey finds a key with the supplied password, afterwards the config is
|
||||||
// read and parsed. It tries at most maxKeys key files in the repo.
|
// read and parsed. It tries at most maxKeys key files in the repo.
|
||||||
func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int) error {
|
func (r *Repository) SearchKey(ctx context.Context, password string, maxKeys int, keyHint string) error {
|
||||||
key, err := SearchKey(ctx, r, password, maxKeys)
|
key, err := SearchKey(ctx, r, password, maxKeys, keyHint)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
|
@ -99,7 +99,7 @@ func TestOpenLocal(t testing.TB, dir string) (r restic.Repository) {
|
||||||
}
|
}
|
||||||
|
|
||||||
repo := New(be)
|
repo := New(be)
|
||||||
err = repo.SearchKey(context.TODO(), test.TestPassword, 10)
|
err = repo.SearchKey(context.TODO(), test.TestPassword, 10, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Fatal(err)
|
t.Fatal(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue