github.com/mckael/restic@v0.8.3/cmd/restic/cmd_key.go (about) 1 package main 2 3 import ( 4 "context" 5 "fmt" 6 7 "github.com/restic/restic/internal/errors" 8 "github.com/restic/restic/internal/repository" 9 "github.com/restic/restic/internal/restic" 10 11 "github.com/spf13/cobra" 12 ) 13 14 var cmdKey = &cobra.Command{ 15 Use: "key [list|add|remove|passwd] [ID]", 16 Short: "Manage keys (passwords)", 17 Long: ` 18 The "key" command manages keys (passwords) for accessing the repository. 19 `, 20 DisableAutoGenTag: true, 21 RunE: func(cmd *cobra.Command, args []string) error { 22 return runKey(globalOptions, args) 23 }, 24 } 25 26 func init() { 27 cmdRoot.AddCommand(cmdKey) 28 } 29 30 func listKeys(ctx context.Context, s *repository.Repository) error { 31 tab := NewTable() 32 tab.Header = fmt.Sprintf(" %-10s %-10s %-10s %s", "ID", "User", "Host", "Created") 33 tab.RowFormat = "%s%-10s %-10s %-10s %s" 34 35 err := s.List(ctx, restic.KeyFile, func(id restic.ID, size int64) error { 36 k, err := repository.LoadKey(ctx, s, id.String()) 37 if err != nil { 38 Warnf("LoadKey() failed: %v\n", err) 39 return nil 40 } 41 42 var current string 43 if id.String() == s.KeyName() { 44 current = "*" 45 } else { 46 current = " " 47 } 48 tab.Rows = append(tab.Rows, []interface{}{current, id.Str(), 49 k.Username, k.Hostname, k.Created.Format(TimeFormat)}) 50 return nil 51 }) 52 if err != nil { 53 return err 54 } 55 56 return tab.Write(globalOptions.stdout) 57 } 58 59 // testKeyNewPassword is used to set a new password during integration testing. 60 var testKeyNewPassword string 61 62 func getNewPassword(gopts GlobalOptions) (string, error) { 63 if testKeyNewPassword != "" { 64 return testKeyNewPassword, nil 65 } 66 67 // Since we already have an open repository, temporary remove the password 68 // to prompt the user for the passwd. 69 newopts := gopts 70 newopts.password = "" 71 72 return ReadPasswordTwice(newopts, 73 "enter password for new key: ", 74 "enter password again: ") 75 } 76 77 func addKey(gopts GlobalOptions, repo *repository.Repository) error { 78 pw, err := getNewPassword(gopts) 79 if err != nil { 80 return err 81 } 82 83 id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) 84 if err != nil { 85 return errors.Fatalf("creating new key failed: %v\n", err) 86 } 87 88 Verbosef("saved new key as %s\n", id) 89 90 return nil 91 } 92 93 func deleteKey(ctx context.Context, repo *repository.Repository, name string) error { 94 if name == repo.KeyName() { 95 return errors.Fatal("refusing to remove key currently used to access repository") 96 } 97 98 h := restic.Handle{Type: restic.KeyFile, Name: name} 99 err := repo.Backend().Remove(ctx, h) 100 if err != nil { 101 return err 102 } 103 104 Verbosef("removed key %v\n", name) 105 return nil 106 } 107 108 func changePassword(gopts GlobalOptions, repo *repository.Repository) error { 109 pw, err := getNewPassword(gopts) 110 if err != nil { 111 return err 112 } 113 114 id, err := repository.AddKey(gopts.ctx, repo, pw, repo.Key()) 115 if err != nil { 116 return errors.Fatalf("creating new key failed: %v\n", err) 117 } 118 119 h := restic.Handle{Type: restic.KeyFile, Name: repo.KeyName()} 120 err = repo.Backend().Remove(gopts.ctx, h) 121 if err != nil { 122 return err 123 } 124 125 Verbosef("saved new key as %s\n", id) 126 127 return nil 128 } 129 130 func runKey(gopts GlobalOptions, args []string) error { 131 if len(args) < 1 || (args[0] == "remove" && len(args) != 2) || (args[0] != "remove" && len(args) != 1) { 132 return errors.Fatal("wrong number of arguments") 133 } 134 135 ctx, cancel := context.WithCancel(gopts.ctx) 136 defer cancel() 137 138 repo, err := OpenRepository(gopts) 139 if err != nil { 140 return err 141 } 142 143 switch args[0] { 144 case "list": 145 lock, err := lockRepo(repo) 146 defer unlockRepo(lock) 147 if err != nil { 148 return err 149 } 150 151 return listKeys(ctx, repo) 152 case "add": 153 lock, err := lockRepo(repo) 154 defer unlockRepo(lock) 155 if err != nil { 156 return err 157 } 158 159 return addKey(gopts, repo) 160 case "remove": 161 lock, err := lockRepoExclusive(repo) 162 defer unlockRepo(lock) 163 if err != nil { 164 return err 165 } 166 167 id, err := restic.Find(repo.Backend(), restic.KeyFile, args[1]) 168 if err != nil { 169 return err 170 } 171 172 return deleteKey(gopts.ctx, repo, id) 173 case "passwd": 174 lock, err := lockRepoExclusive(repo) 175 defer unlockRepo(lock) 176 if err != nil { 177 return err 178 } 179 180 return changePassword(gopts, repo) 181 } 182 183 return nil 184 }