Add safe repository password rotation
This commit is contained in:
@@ -128,6 +128,82 @@ func (s *Service) PutSecret(id, kind, value string) error {
|
||||
|
||||
func (s *Service) DeleteSecret(id string) error { return s.secrets.Delete(id) }
|
||||
|
||||
func (s *Service) ChangeRepositoryPassword(ctx context.Context, repoID, newPassword string) error {
|
||||
if err := s.validatePasswordChange(newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
repo, ok := s.repository(repoID)
|
||||
if !ok {
|
||||
return errors.New("validation: unknown repository")
|
||||
}
|
||||
oldPassword, err := s.secrets.Get(repo.PasswordRef)
|
||||
if err != nil {
|
||||
return fmt.Errorf("authentication: load current repository password: %w", err)
|
||||
}
|
||||
if oldPassword == newPassword {
|
||||
return errors.New("validation: Das neue Repository-Passwort muss sich vom bisherigen Passwort unterscheiden")
|
||||
}
|
||||
mounted, err := s.mounts.Prepare(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.mounts.Cleanup(context.Background(), mounted)
|
||||
if err := s.restic.ChangePassword(ctx, mounted.Repository, oldPassword, newPassword); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := s.secrets.Put(repo.PasswordRef, "restic-password", newPassword); err != nil {
|
||||
rollbackCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
rollbackErr := s.restic.ChangePassword(rollbackCtx, mounted.Repository, newPassword, oldPassword)
|
||||
if rollbackErr != nil {
|
||||
return fmt.Errorf("internal: Neues Repository-Passwort wurde aktiviert, konnte aber nicht in URBM gespeichert werden; automatischer Rollback schlug ebenfalls fehl: %v; ursprünglicher Speicherfehler: %w", rollbackErr, err)
|
||||
}
|
||||
return fmt.Errorf("internal: Neues Passwort konnte nicht in URBM gespeichert werden; Repository wurde erfolgreich auf das bisherige Passwort zurückgesetzt: %w", err)
|
||||
}
|
||||
if _, err := s.restic.Snapshots(ctx, mounted.Repository); err != nil {
|
||||
rollbackCtx, cancel := context.WithTimeout(context.Background(), 5*time.Minute)
|
||||
defer cancel()
|
||||
rollbackErr := s.restic.ChangePassword(rollbackCtx, mounted.Repository, newPassword, oldPassword)
|
||||
secretErr := s.secrets.Put(repo.PasswordRef, "restic-password", oldPassword)
|
||||
if rollbackErr != nil || secretErr != nil {
|
||||
return fmt.Errorf("internal: Prüfung des neuen Passworts fehlgeschlagen und Rollback war unvollständig (repository: %v, secret: %v): %w", rollbackErr, secretErr, err)
|
||||
}
|
||||
return fmt.Errorf("authentication: Prüfung des neuen Passworts fehlgeschlagen; Änderung wurde zurückgesetzt: %w", err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) AdoptRepositoryPassword(ctx context.Context, repoID, password string) error {
|
||||
if err := s.validatePasswordChange(password); err != nil {
|
||||
return err
|
||||
}
|
||||
repo, ok := s.repository(repoID)
|
||||
if !ok {
|
||||
return errors.New("validation: unknown repository")
|
||||
}
|
||||
mounted, err := s.mounts.Prepare(ctx, repo)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer s.mounts.Cleanup(context.Background(), mounted)
|
||||
if err := s.restic.TestPassword(ctx, mounted.Repository, password); err != nil {
|
||||
return fmt.Errorf("authentication: Das angegebene Passwort wurde vom Repository nicht akzeptiert: %w", err)
|
||||
}
|
||||
return s.secrets.Put(repo.PasswordRef, "restic-password", password)
|
||||
}
|
||||
|
||||
func (s *Service) validatePasswordChange(password string) error {
|
||||
if len(password) < 8 {
|
||||
return errors.New("validation: Das Repository-Passwort muss mindestens 8 Zeichen lang sein")
|
||||
}
|
||||
for _, run := range s.queue.Snapshot() {
|
||||
if run.Status == "queued" || run.Status == "running" || run.Status == "paused" {
|
||||
return errors.New("validation: Repository-Passwörter können nur geändert werden, wenn keine Aufgabe wartet, läuft oder pausiert ist")
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *Service) EnqueueJob(jobID string, priority int) (model.Run, error) {
|
||||
job, ok := s.job(jobID)
|
||||
if !ok {
|
||||
|
||||
Reference in New Issue
Block a user