Files
kcrypt-challenger/cmd/discovery/main.go
Dimitris Karakasilis 2ef72d3c0a Use a KairosLogger consistently
in plugin mode: log only to a file and journal and in "debug" level by
default

in cli mode: respect the `--debug` flag and write to the stdout

Signed-off-by: Dimitris Karakasilis <dimitris@karakasilis.me>
2025-09-18 14:29:48 +03:00

267 lines
8.9 KiB
Go

package main
import (
"flag"
"fmt"
"os"
"strings"
"github.com/jaypipes/ghw/pkg/block"
"github.com/kairos-io/kairos-challenger/cmd/discovery/client"
"github.com/kairos-io/kairos-sdk/kcrypt/bus"
"github.com/kairos-io/kairos-sdk/types"
"github.com/mudler/go-pluggable"
)
func main() {
// Check if we're being called as a plugin or CLI mode
if len(os.Args) > 1 && isEventDefined(os.Args[1]) {
// Plugin mode - use the go-pluggable interface
exitCode := RunPluginMode()
os.Exit(exitCode)
} else {
// CLI mode - use flags
exitCode := RunCLIMode(os.Args[1:])
os.Exit(exitCode)
}
}
// RunPluginMode implements the go-pluggable interface
// Returns exit code for testability
func RunPluginMode() int {
// In plugin mode, use quiet=true to log to file instead of console and debug level for detailed logs
// This will write debug logs to /var/log/kairos/kcrypt-discovery-challenger.log
logger := types.NewKairosLogger("kcrypt-discovery-challenger", "debug", true)
c, err := client.NewClientWithLogger(logger)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating client: %v\n", err)
return 1
}
err = c.Start()
if err != nil {
fmt.Fprintf(os.Stderr, "Error starting plugin: %v\n", err)
return 1
}
return 0
}
// RunCLIMode implements the CLI interface with flags
// Takes args slice and returns exit code for testability
func RunCLIMode(args []string) int {
// Create a new FlagSet for testability
fs := flag.NewFlagSet("kcrypt-discovery-challenger", flag.ContinueOnError)
var (
partitionName = fs.String("partition-name", "", "Name of the partition (at least one identifier required)")
partitionUUID = fs.String("partition-uuid", "", "UUID of the partition (at least one identifier required)")
partitionLabel = fs.String("partition-label", "", "Filesystem label of the partition (at least one identifier required)")
attempts = fs.Int("attempts", 30, "Number of attempts to get the passphrase")
challengerServer = fs.String("challenger-server", "", "URL of the challenger server (overrides config)")
enableMDNS = fs.Bool("mdns", false, "Enable mDNS discovery (overrides config)")
serverCertificate = fs.String("certificate", "", "Server certificate for verification (overrides config)")
debug = fs.Bool("debug", false, "Enable debug logging to show configuration values")
showHelp = fs.Bool("help", false, "Show this help message")
showVersion = fs.Bool("version", false, "Show version information")
)
fs.Usage = func() {
usageText := fmt.Sprintf(`Usage: kcrypt-discovery-challenger [options]
kcrypt-challenger discovery client - Get decryption passphrases for encrypted partitions
This tool can work in two modes:
1. Plugin mode: kcrypt-discovery-challenger %s < partition_data.json
2. CLI mode: kcrypt-discovery-challenger [at least one of --partition-name, --partition-uuid, or --partition-label]
CLI Options:
`, bus.EventDiscoveryPassword)
fmt.Fprint(os.Stderr, usageText)
fs.PrintDefaults()
examplesText := fmt.Sprintf(`
Examples:
# Get passphrase using partition name only
kcrypt-discovery-challenger --partition-name=/dev/sda2
# Get passphrase using UUID only
kcrypt-discovery-challenger --partition-uuid=12345-abcde
# Get passphrase using filesystem label only
kcrypt-discovery-challenger --partition-label=encrypted-data
# Get passphrase with multiple identifiers (provides more options for matching)
kcrypt-discovery-challenger --partition-name=/dev/sda2 --partition-uuid=12345-abcde --partition-label=encrypted-data
# Get passphrase with custom server (override config)
kcrypt-discovery-challenger --partition-label=encrypted-data --challenger-server=https://my-server.com:8082
# Plugin mode (for integration with kcrypt)
echo '{"data": "{\"name\": \"/dev/sda2\", \"uuid\": \"12345-abcde\", \"label\": \"encrypted-data\"}"}' | kcrypt-discovery-challenger %s
Configuration:
The client reads configuration from Kairos configuration files in /oem, /sysroot/oem, or /tmp/oem
Key configuration options under kcrypt.challenger:
- challenger_server: URL of the challenger server
- mdns: Enable mDNS discovery
- certificate: Server certificate for verification
`, bus.EventDiscoveryPassword)
fmt.Fprint(os.Stderr, examplesText)
}
err := fs.Parse(args)
if err != nil {
return 1
}
// Create logger based on debug flag
var logger types.KairosLogger
if *debug {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "debug", false)
} else {
logger = types.NewKairosLogger("kcrypt-discovery-challenger", "error", false)
}
if *showHelp {
fs.Usage()
return 0
}
if *showVersion {
fmt.Println("kcrypt-challenger discovery client")
fmt.Println("Part of the Kairos project: https://github.com/kairos-io/kcrypt-challenger")
return 0
}
// Validate required flags - at least one identifier must be provided
if *partitionName == "" && *partitionUUID == "" && *partitionLabel == "" {
fmt.Fprintf(os.Stderr, "Error: At least one of partition-name, partition-uuid, or partition-label must be provided\n\n")
fs.Usage()
return 1
}
// Create client with potential CLI overrides
c, err := createClientWithOverrides(*challengerServer, *enableMDNS, *serverCertificate, logger, args)
if err != nil {
fmt.Fprintf(os.Stderr, "Error creating client: %v\n", err)
return 1
}
// Create partition object
partition := &block.Partition{
Name: *partitionName,
UUID: *partitionUUID,
FilesystemLabel: *partitionLabel,
}
// Log partition information
logger.Debugf("Partition details:")
logger.Debugf(" Name: %s", partition.Name)
logger.Debugf(" UUID: %s", partition.UUID)
logger.Debugf(" Label: %s", partition.FilesystemLabel)
logger.Debugf(" Attempts: %d", *attempts)
// Get the passphrase using the same backend logic as the plugin
fmt.Fprintf(os.Stderr, "Requesting passphrase for partition %s (UUID: %s, Label: %s)...\n",
*partitionName, *partitionUUID, *partitionLabel)
passphrase, err := c.GetPassphrase(partition, *attempts)
if err != nil {
fmt.Fprintf(os.Stderr, "Error getting passphrase: %v\n", err)
return 1
}
// Output the passphrase to stdout (this is what tools expect)
fmt.Print(passphrase)
fmt.Fprintf(os.Stderr, "\nPassphrase retrieved successfully\n")
return 0
}
// isEventDefined checks whether an event is defined in the bus.
// It accepts strings or EventType, returns a boolean indicating that
// the event was defined among the events emitted by the bus.
func isEventDefined(i interface{}) bool {
checkEvent := func(e pluggable.EventType) bool {
if e == bus.EventDiscoveryPassword {
return true
}
return false
}
switch f := i.(type) {
case string:
return checkEvent(pluggable.EventType(f))
case pluggable.EventType:
return checkEvent(f)
default:
return false
}
}
// createClientWithOverrides creates a client and applies CLI flag overrides to the config
func createClientWithOverrides(serverURL string, enableMDNS bool, certificate string, logger types.KairosLogger, args []string) (*client.Client, error) {
// Start with the default config from files and pass the logger
c, err := client.NewClientWithLogger(logger)
if err != nil {
return nil, err
}
// Log the original configuration values
logger.Debugf("Original configuration:")
logger.Debugf(" Server: %s", c.Config.Kcrypt.Challenger.Server)
logger.Debugf(" MDNS: %t", c.Config.Kcrypt.Challenger.MDNS)
logger.Debugf(" Certificate: %s", maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate))
// Apply CLI overrides if provided
if serverURL != "" {
logger.Debugf("Overriding server URL: %s -> %s", c.Config.Kcrypt.Challenger.Server, serverURL)
c.Config.Kcrypt.Challenger.Server = serverURL
}
// For boolean flags, check if the flag was explicitly provided in the args
mdnsSet := false
for _, arg := range args {
if arg == "-mdns" || arg == "--mdns" ||
strings.HasPrefix(arg, "-mdns=") || strings.HasPrefix(arg, "--mdns=") {
mdnsSet = true
break
}
}
if mdnsSet {
logger.Debugf("Overriding MDNS setting: %t -> %t", c.Config.Kcrypt.Challenger.MDNS, enableMDNS)
c.Config.Kcrypt.Challenger.MDNS = enableMDNS
}
if certificate != "" {
logger.Debugf("Overriding certificate: %s -> %s",
maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate),
maskSensitiveString(certificate))
c.Config.Kcrypt.Challenger.Certificate = certificate
}
// Log the final configuration values
logger.Debugf("Final configuration:")
logger.Debugf(" Server: %s", c.Config.Kcrypt.Challenger.Server)
logger.Debugf(" MDNS: %t", c.Config.Kcrypt.Challenger.MDNS)
logger.Debugf(" Certificate: %s", maskSensitiveString(c.Config.Kcrypt.Challenger.Certificate))
return c, nil
}
// maskSensitiveString masks certificate paths/content for logging
func maskSensitiveString(s string) string {
if s == "" {
return "<empty>"
}
if len(s) <= 10 {
return strings.Repeat("*", len(s))
}
// Show first 3 and last 3 characters with * in between
return s[:3] + strings.Repeat("*", len(s)-6) + s[len(s)-3:]
}