Merge pull request #2318 from classmarkets/2175-named-keys

Allow specifying user and host when adding keys
This commit is contained in:
Alexander Neumann 2020-08-01 13:06:31 +02:00 committed by GitHub
commit 2580eef2aa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 16 deletions

View File

@ -0,0 +1,9 @@
Enhancement: Allow specifying user and host when creating keys
When adding a new key to the repository, the username and hostname for the new
key can be specified on the command line. This allows overriding the defaults,
for example if you would prefer to use the FQDN to identify the host or if you
want to add keys for several different hosts without having to run the key add
command on those hosts.
https://github.com/restic/restic/issues/2175

View File

@ -32,13 +32,19 @@ Exit status is 0 if the command was successful, and non-zero if there was any er
}, },
} }
var newPasswordFile string var (
newPasswordFile string
keyUsername string
keyHostname string
)
func init() { func init() {
cmdRoot.AddCommand(cmdKey) cmdRoot.AddCommand(cmdKey)
flags := cmdKey.Flags() flags := cmdKey.Flags()
flags.StringVarP(&newPasswordFile, "new-password-file", "", "", "the file from which to load a new password") flags.StringVarP(&newPasswordFile, "new-password-file", "", "", "the file from which to load a new password")
flags.StringVarP(&keyUsername, "user", "", "", "the username for new keys")
flags.StringVarP(&keyHostname, "host", "", "", "the hostname for new keys")
} }
func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions) error { func listKeys(ctx context.Context, s *repository.Repository, gopts GlobalOptions) error {
@ -120,7 +126,7 @@ func addKey(gopts GlobalOptions, repo *repository.Repository) error {
return err return err
} }
id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) id, err := repository.AddKey(gopts.ctx, repo, pw, keyUsername, keyHostname, repo.Key())
if err != nil { if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err) return errors.Fatalf("creating new key failed: %v\n", err)
} }
@ -151,7 +157,7 @@ func changePassword(gopts GlobalOptions, repo *repository.Repository) error {
return err return err
} }
id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) id, err := repository.AddKey(gopts.ctx, repo, pw, "", "", repo.Key())
if err != nil { if err != nil {
return errors.Fatalf("creating new key failed: %v\n", err) return errors.Fatalf("creating new key failed: %v\n", err)
} }

View File

@ -724,6 +724,28 @@ func testRunKeyAddNewKey(t testing.TB, newPassword string, gopts GlobalOptions)
rtest.OK(t, runKey(gopts, []string{"add"})) rtest.OK(t, runKey(gopts, []string{"add"}))
} }
func testRunKeyAddNewKeyUserHost(t testing.TB, gopts GlobalOptions) {
testKeyNewPassword = "john's geheimnis"
defer func() {
testKeyNewPassword = ""
keyUsername = ""
keyHostname = ""
}()
cmdKey.Flags().Parse([]string{"--user=john", "--host=example.com"})
t.Log("adding key for john@example.com")
rtest.OK(t, runKey(gopts, []string{"add"}))
repo, err := OpenRepository(gopts)
rtest.OK(t, err)
key, err := repository.SearchKey(gopts.ctx, repo, testKeyNewPassword, 1, "")
rtest.OK(t, err)
rtest.Equals(t, "john", key.Username)
rtest.Equals(t, "example.com", key.Hostname)
}
func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) { func testRunKeyPasswd(t testing.TB, newPassword string, gopts GlobalOptions) {
testKeyNewPassword = newPassword testKeyNewPassword = newPassword
defer func() { defer func() {
@ -766,6 +788,8 @@ func TestKeyAddRemove(t *testing.T) {
t.Logf("testing access with last password %q\n", env.gopts.password) t.Logf("testing access with last password %q\n", env.gopts.password)
rtest.OK(t, runKey(env.gopts, []string{"list"})) rtest.OK(t, runKey(env.gopts, []string{"list"}))
testRunCheck(t, env.gopts) testRunCheck(t, env.gopts)
testRunKeyAddNewKeyUserHost(t, env.gopts)
} }
func testFileSize(filename string, size int64) error { func testFileSize(filename string, size int64) error {

View File

@ -58,7 +58,7 @@ var (
// createMasterKey creates a new master key in the given backend and encrypts // createMasterKey creates a new master key in the given backend and encrypts
// it with the password. // it with the password.
func createMasterKey(s *Repository, password string) (*Key, error) { func createMasterKey(s *Repository, password string) (*Key, error) {
return AddKey(context.TODO(), s, password, nil) return AddKey(context.TODO(), s, password, "", "", nil)
} }
// OpenKey tries do decrypt the key specified by name with the given password. // OpenKey tries do decrypt the key specified by name with the given password.
@ -199,7 +199,7 @@ func LoadKey(ctx context.Context, s *Repository, name string) (k *Key, err error
} }
// AddKey adds a new key to an already existing repository. // AddKey adds a new key to an already existing repository.
func AddKey(ctx context.Context, s *Repository, password string, template *crypto.Key) (*Key, error) { func AddKey(ctx context.Context, s *Repository, password, username, hostname string, template *crypto.Key) (*Key, error) {
// make sure we have valid KDF parameters // make sure we have valid KDF parameters
if Params == nil { if Params == nil {
p, err := crypto.Calibrate(KDFTimeout, KDFMemory) p, err := crypto.Calibrate(KDFTimeout, KDFMemory)
@ -213,24 +213,29 @@ func AddKey(ctx context.Context, s *Repository, password string, template *crypt
// fill meta data about key // fill meta data about key
newkey := &Key{ newkey := &Key{
Created: time.Now(), Created: time.Now(),
KDF: "scrypt", Username: username,
N: Params.N, Hostname: hostname,
R: Params.R,
P: Params.P, KDF: "scrypt",
N: Params.N,
R: Params.R,
P: Params.P,
} }
hn, err := os.Hostname() if newkey.Hostname == "" {
if err == nil { newkey.Hostname, _ = os.Hostname()
newkey.Hostname = hn
} }
usr, err := user.Current() if newkey.Username == "" {
if err == nil { usr, err := user.Current()
newkey.Username = usr.Username if err == nil {
newkey.Username = usr.Username
}
} }
// generate random salt // generate random salt
var err error
newkey.Salt, err = crypto.NewSalt() newkey.Salt, err = crypto.NewSalt()
if err != nil { if err != nil {
panic("unable to read enough random bytes for salt: " + err.Error()) panic("unable to read enough random bytes for salt: " + err.Error())