resolveGlobals can fail for two distinct reasons — no MongoDB URI, or no resolvable database name — and verify.go was printing a hardcoded no-URI note regardless of which one occurred, misleading an operator whose URI was fine but whose database name could not be resolved.
91 lines
2.7 KiB
Go
91 lines
2.7 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.hostxtra.co.uk/mrhid6/vantage/shared/backup"
|
|
"github.com/spf13/cobra"
|
|
"go.mongodb.org/mongo-driver/v2/mongo"
|
|
)
|
|
|
|
func newVerifyCmd() *cobra.Command {
|
|
return &cobra.Command{
|
|
Use: "verify ARCHIVE",
|
|
Short: "Check an archive against the key in hand",
|
|
Long: "verify checks that an archive is intact and that the\n" +
|
|
"KEY_ENCRYPTION_KEY in this environment matches the one it was made\n" +
|
|
"with.\n\n" +
|
|
"Given --mongo-uri it goes further and opens a real ciphertext value\n" +
|
|
"from that database. A fingerprint proves two archives agree about a\n" +
|
|
"key; only the probe proves the key you hold reads the data.\n\n" +
|
|
"Exit status is non-zero when anything is wrong, so this is the command\n" +
|
|
"to put on a schedule.",
|
|
Args: cobra.ExactArgs(1),
|
|
RunE: func(c *cobra.Command, args []string) error {
|
|
ctx := c.Context()
|
|
|
|
archive, err := backup.Open(args[0])
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer archive.Close()
|
|
|
|
opt := backup.VerifyOptions{Archive: archive}
|
|
|
|
// A database is optional here. resolveGlobals fails without a URI or
|
|
// without a resolvable database name, and either error is a signal to
|
|
// verify the archive alone rather than a reason to stop.
|
|
var client *mongo.Client
|
|
if g, gerr := resolveGlobals(c); gerr == nil {
|
|
client, err = connect(ctx, g)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer client.Disconnect(context.Background())
|
|
opt.Client = client
|
|
opt.Database = g.Database
|
|
opt.KeyHex = g.KeyHex
|
|
} else {
|
|
fmt.Fprintf(c.ErrOrStderr(),
|
|
"note: %v, so this checks the archive and the key only\n", gerr)
|
|
}
|
|
|
|
rep, err := backup.Verify(ctx, opt)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
out := c.OutOrStdout()
|
|
fmt.Fprintln(out, "Archive intact, every member matches its checksum")
|
|
if rep.ArchiveFingerprint != nil {
|
|
fmt.Fprintf(out, "Archive key %s\n", *rep.ArchiveFingerprint)
|
|
}
|
|
if rep.KeyFingerprint != nil {
|
|
fmt.Fprintf(out, "Your key %s\n", *rep.KeyFingerprint)
|
|
}
|
|
if rep.KeyMatchesArchive {
|
|
fmt.Fprintln(out, "Key match yes")
|
|
}
|
|
switch {
|
|
case rep.ProbeDecrypted:
|
|
fmt.Fprintf(out, "Live probe decrypted a value from %s\n", rep.ProbeCollection)
|
|
case rep.ProbeAttempted:
|
|
fmt.Fprintf(out, "Live probe FAILED against %s\n", rep.ProbeCollection)
|
|
case opt.Client != nil:
|
|
fmt.Fprintln(out, "Live probe skipped; this database stores no ciphertext yet")
|
|
}
|
|
|
|
if rep.OK() {
|
|
fmt.Fprintln(out, "\nThis archive will restore.")
|
|
return nil
|
|
}
|
|
fmt.Fprintln(out)
|
|
for _, p := range rep.Problems {
|
|
fmt.Fprintln(out, "problem:", p)
|
|
}
|
|
return fmt.Errorf("verification failed")
|
|
},
|
|
}
|
|
}
|