Add safe repository password rotation

This commit is contained in:
Mikei386
2026-06-15 20:13:10 +02:00
parent 3579756cc9
commit 9249fad65a
12 changed files with 262 additions and 30 deletions
+48 -18
View File
@@ -106,6 +106,19 @@ func (r *Runner) Unlock(ctx context.Context, repo model.Repository) error {
return r.run(ctx, repo, []string{"unlock"}, nil, nil)
}
func (r *Runner) ChangePassword(ctx context.Context, repo model.Repository, currentPassword, newPassword string) error {
newPasswordPath, cleanup, err := r.writePasswordFile("restic-new-password-*", newPassword)
if err != nil {
return err
}
defer cleanup()
return r.runWithInputEnvPassword(ctx, repo, []string{"key", "passwd", "--new-password-file", newPasswordPath}, nil, nil, nil, nil, currentPassword)
}
func (r *Runner) TestPassword(ctx context.Context, repo model.Repository, password string) error {
return r.runWithInputEnvPassword(ctx, repo, []string{"snapshots", "--json"}, nil, nil, nil, nil, password)
}
func (r *Runner) Backup(ctx context.Context, repo model.Repository, job model.Job, sources []string, progress ProgressCallback) (Summary, error) {
args := []string{"backup", "--json", "--compression", job.Compression}
for _, tag := range append([]string{"urbm", "job:" + job.ID}, job.Tags...) {
@@ -283,28 +296,17 @@ func (r *Runner) runWithInputAndEnv(ctx context.Context, repo model.Repository,
if err != nil {
return fmt.Errorf("authentication: load repository password: %w", err)
}
secretDir := filepath.Join(r.RuntimeDir, "secrets")
if err := os.MkdirAll(secretDir, 0700); err != nil {
return err
}
f, err := os.CreateTemp(secretDir, "restic-password-*")
return r.runWithInputEnvPassword(ctx, repo, args, onLine, capture, input, environment, password)
}
func (r *Runner) runWithInputEnvPassword(ctx context.Context, repo model.Repository, args []string, onLine func([]byte), capture *[]byte, input io.Reader, environment []string, password string) error {
passwordPath, cleanup, err := r.writePasswordFile("restic-password-*", password)
if err != nil {
return err
}
passwordPath := f.Name()
defer os.Remove(passwordPath)
if err := f.Chmod(0600); err != nil {
f.Close()
return err
}
if _, err := io.WriteString(f, password); err != nil {
f.Close()
return err
}
if err := f.Close(); err != nil {
return err
}
defer cleanup()
secretDir := filepath.Join(r.RuntimeDir, "secrets")
globalArgs := []string{"-r", repositoryLocation(repo)}
credentialPath := ""
if repo.Type == model.RepositorySFTP && repo.CredentialRef != "" {
@@ -397,6 +399,34 @@ func (r *Runner) runWithInputAndEnv(ctx context.Context, repo model.Repository,
return nil
}
func (r *Runner) writePasswordFile(pattern, password string) (string, func(), error) {
secretDir := filepath.Join(r.RuntimeDir, "secrets")
if err := os.MkdirAll(secretDir, 0700); err != nil {
return "", nil, err
}
f, err := os.CreateTemp(secretDir, pattern)
if err != nil {
return "", nil, err
}
passwordPath := f.Name()
cleanup := func() { _ = os.Remove(passwordPath) }
if err := f.Chmod(0600); err != nil {
f.Close()
cleanup()
return "", nil, err
}
if _, err := io.WriteString(f, password); err != nil {
f.Close()
cleanup()
return "", nil, err
}
if err := f.Close(); err != nil {
cleanup()
return "", nil, err
}
return passwordPath, cleanup, nil
}
func backupEnvironment(job model.Job) []string {
if job.CPUCores <= 0 {
return nil