Files
nomad/command/operator_snapshot_redact.go
Tim Gross a7f2cb879e command line tools for redacting keyring from snapshots (#24023)
In #23977 we moved the keyring into Raft, which can expose key material in Raft
snapshots when using the less-secure AEAD keyring instead of KMS. This changeset
adds tools for redacting this material from snapshots:

* The `operator snapshot state` command gains the ability to display key
  metadata (only), which respects the `-filter` option.
* The `operator snapshot save` command gains a `-redact` option that removes key
  material from the snapshot after it's downloaded.
* A new `operator snapshot redact` command allows removing key material from an
  existing snapshot.
2024-09-20 15:30:14 -04:00

96 lines
2.3 KiB
Go

// Copyright (c) HashiCorp, Inc.
// SPDX-License-Identifier: BUSL-1.1
package command
import (
"fmt"
"io"
"os"
"strings"
"github.com/hashicorp/nomad/helper/raftutil"
"github.com/posener/complete"
)
type OperatorSnapshotRedactCommand struct {
Meta
}
func (c *OperatorSnapshotRedactCommand) Help() string {
helpText := `
Usage: nomad operator snapshot redact [options] <file>
Removes key material from an existing snapshot file created by the operator
snapshot save command, when using the AEAD keyring provider. When using a KMS
keyring provider, no cleartext key material is stored in snapshots and this
command is not necessary. Note that this command requires loading the entire
snapshot into memory locally and overwrites the existing snapshot.
This is useful for situations where you need to transmit a snapshot without
exposing key material.
General Options:
` + generalOptionsUsage(usageOptsDefault|usageOptsNoNamespace)
return strings.TrimSpace(helpText)
}
func (c *OperatorSnapshotRedactCommand) AutocompleteFlags() complete.Flags {
return complete.Flags{}
}
func (c *OperatorSnapshotRedactCommand) AutocompleteArgs() complete.Predictor {
return complete.PredictFiles("*")
}
func (c *OperatorSnapshotRedactCommand) Synopsis() string {
return "Redacts an existing snapshot of Nomad server state"
}
func (c *OperatorSnapshotRedactCommand) Name() string { return "operator snapshot redact" }
func (c *OperatorSnapshotRedactCommand) Run(args []string) int {
if len(args) != 1 {
c.Ui.Error("This command takes one argument: <file>")
c.Ui.Error(commandErrorText(c))
return 1
}
path := args[0]
f, err := os.Open(path)
if err != nil {
c.Ui.Error(fmt.Sprintf("Error opening snapshot file: %s", err))
return 1
}
defer f.Close()
tmpFile, err := os.Create(path + ".tmp")
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to create temporary file: %v", err))
return 1
}
_, err = io.Copy(tmpFile, f)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to copy snapshot to temporary file: %v", err))
return 1
}
err = raftutil.RedactSnapshot(tmpFile)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to redact snapshot: %v", err))
return 1
}
err = os.Rename(tmpFile.Name(), path)
if err != nil {
c.Ui.Error(fmt.Sprintf("Failed to finalize snapshot file: %v", err))
return 1
}
c.Ui.Output("Snapshot redacted")
return 0
}