95 lines
3.0 KiB
Go
95 lines
3.0 KiB
Go
package cmd
|
|
|
|
import (
|
|
"context"
|
|
"fmt"
|
|
|
|
"gitea.hostxtra.co.uk/vantage/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()
|
|
|
|
// The key is read unconditionally. Archive-only mode - no MONGO_URI,
|
|
// which is what a scheduled check uses - must still compare the key
|
|
// in hand against the archive's fingerprint; leaving it unset there
|
|
// reported "KEY_ENCRYPTION_KEY is not set" for a key that was set
|
|
// and correct.
|
|
opt := backup.VerifyOptions{Archive: archive, KeyHex: keyFromEnv()}
|
|
|
|
// 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
|
|
} 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")
|
|
},
|
|
}
|
|
}
|