Commit 884a5c558b
884a5c558b19627ca9eb25df9b29304d6aea6d62
parent: 9e4827a050
Verified · cmc
cmc <hello@cleberg.net> · 2026-09-28 22:28 UTC
backup: open the database without the key file
Ref #259
Layout: unified · split
.gitbay/wiki/Admin.org
+2
| @@ -587,6 +587,8 @@ gitbayd admin secrets rotate # new key, reseal, retire the old one (as root) |
| 587 | 587 | - Missing file: every gitbayd process that opens the database refuses |
| 588 | 588 | to run and names the path, including =serve= and, in system mode, |
| 589 | 589 | =authorized-keys=. =migrate= does not need it. |
| 590 | - Backups: =admin backup= and =admin backup --verify= do not need the |
| 591 | key file; a restore does. |
| 590 | 592 | - Wrong key: =serve= stops at startup naming the first row that does |
| 591 | 593 | not open; =secrets check= does the same without starting anything. |
| 592 | 594 | - Upgrade: the first start after the upgrade seals every value still |
cmd/gitbayd/backup.go
+8 −1
| @@ -108,7 +108,14 @@ func runBackup(cfg config.Config, out string, dbOnly bool) error { |
| 108 | 108 | defer release() |
| 109 | 109 | } |
| 110 | 110 | |
| 111 | | st, err := openStore(cfg) |
| 111 | // VACUUM INTO copies sealed values as they are, so the backup needs |
| 112 | // no key file, and it migrates nothing. store.Open would create a |
| 113 | // missing database, so its absence is checked first. |
| 114 | dbFile := filepath.Join(cfg.Server.Root, "gitbay.db") |
| 115 | if _, err := os.Stat(dbFile); err != nil { |
| 116 | return fmt.Errorf("database: %w", err) |
| 117 | } |
| 118 | st, err := store.Open(dbFile) |
| 112 | 119 | if err != nil { |
| 113 | 120 | return err |
| 114 | 121 | } |
cmd/gitbayd/backup_test.go
+27
| @@ -626,3 +626,30 @@ func TestGCRefusedDuringBackup(t *testing.T) { |
| 626 | 626 | t.Fatalf("gc during a backup: %v", err) |
| 627 | 627 | } |
| 628 | 628 | } |
| 629 | |
| 630 | // A backup and its verify need no key file: sealed values are copied as |
| 631 | // they are. A missing database is refused rather than created. |
| 632 | func TestBackupNeedsNoKeyFile(t *testing.T) { |
| 633 | cfg := testConfig(t) |
| 634 | out := filepath.Join(t.TempDir(), "b.tar.gz") |
| 635 | if err := runBackup(cfg, out, false); !errors.Is(err, fs.ErrNotExist) { |
| 636 | t.Fatalf("backup without a database: %v", err) |
| 637 | } |
| 638 | if _, err := os.Stat(filepath.Join(cfg.Server.Root, "gitbay.db")); !os.IsNotExist(err) { |
| 639 | t.Fatalf("backup created a database: %v", err) |
| 640 | } |
| 641 | s, err := openStore(cfg) |
| 642 | if err != nil { |
| 643 | t.Fatal(err) |
| 644 | } |
| 645 | s.Close() |
| 646 | if err := os.Remove(cfg.Server.SecretKeyFile); err != nil { |
| 647 | t.Fatal(err) |
| 648 | } |
| 649 | if err := runBackup(cfg, out, false); err != nil { |
| 650 | t.Fatalf("backup without the key file: %v", err) |
| 651 | } |
| 652 | if err := verifyBackup(out, ""); err != nil { |
| 653 | t.Fatalf("verify without the key file: %v", err) |
| 654 | } |
| 655 | } |