395 lines
13 KiB
Plaintext
Raw Normal View History

# Configuration Encryption CLI Commands
# Provides user-friendly commands for config encryption operations
use encryption.nu *
use accessor.nu *
# Encrypt a configuration file
export def "config encrypt" [
file: path # Configuration file to encrypt
--output (-o): path # Output path (default: <file>.enc)
--kms (-k): string = "age" # KMS backend: age, aws-kms, vault, cosmian
--in-place (-i) # Encrypt in-place (overwrites original)
--debug (-d) # Enable debug output
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
print $"🔒 Encrypting configuration file: ($file)"
print $" Backend: ($kms)"
if $in_place {
print $" Mode: In-place (will overwrite original)"
} else if ($output | is-not-empty) {
print $" Output: ($output)"
} else {
print $" Output: ($file).enc"
}
try {
if $in_place {
encrypt-config $file --kms=$kms --in-place --debug=$debug
} else {
encrypt-config $file $output --kms=$kms --debug=$debug
}
} catch { |err|
print $"❌ Encryption failed: ($err.msg)"
}
}
# Decrypt a configuration file
export def "config decrypt" [
file: path # Encrypted configuration file
--output (-o): path # Output path (default: removes .enc extension)
--in-place (-i) # Decrypt in-place (overwrites original)
--debug (-d) # Enable debug output
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
print $"🔓 Decrypting configuration file: ($file)"
try {
if $in_place {
decrypt-config $file --in-place --debug=$debug
} else {
decrypt-config $file $output --debug=$debug
}
} catch { |err|
print $"❌ Decryption failed: ($err.msg)"
}
}
# Edit encrypted configuration file securely
export def "config edit-secure" [
file: path # Encrypted configuration file
--editor (-e): string # Editor to use (default: $EDITOR or vim)
--debug (-d) # Enable debug output
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
try {
if ($editor | is-not-empty) {
edit-encrypted-config $file --editor=$editor --debug=$debug
} else {
edit-encrypted-config $file --debug=$debug
}
} catch { |err|
print $"❌ Edit failed: ($err.msg)"
}
}
# Rotate encryption keys for a configuration file
export def "config rotate-keys" [
file: path # Encrypted configuration file
new_key: string # New key ID or recipient
--debug (-d) # Enable debug output
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
print $"🔄 Rotating encryption keys"
print $" File: ($file)"
print $" New key: ($new_key)"
try {
rotate-encryption-keys $file $new_key --debug=$debug
} catch { |err|
print $"❌ Key rotation failed: ($err.msg)"
}
}
# Check if a configuration file is encrypted
export def "config is-encrypted" [
file: path # Configuration file to check
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
let encrypted = (is-encrypted-config $file)
if $encrypted {
print $"🔒 File is encrypted: ($file)"
} else {
print $"🔓 File is not encrypted: ($file)"
}
$encrypted
}
# Validate encryption configuration and setup
export def "config validate-encryption" [] {
print $"🔍 Validating encryption configuration..."
print ""
let validation = (validate-encryption-config)
# Show summary
print $"📊 Encryption Configuration Summary"
print $"=================================="
print $" SOPS installed: ($validation.summary.sops_installed)"
print $" Age backend: ($validation.summary.age_backend)"
print $" KMS enabled: ($validation.summary.kms_enabled)"
print $" Errors: ($validation.summary.total_errors)"
print $" Warnings: ($validation.summary.total_warnings)"
print ""
# Show errors
if ($validation.errors | length) > 0 {
print $"❌ Errors:"
for error in $validation.errors {
print $" • ($error.message)"
}
print ""
}
# Show warnings
if ($validation.warnings | length) > 0 {
print $"⚠️ Warnings:"
for warning in $validation.warnings {
print $" • ($warning.message)"
}
print ""
}
if $validation.valid {
print $"✅ Encryption configuration is valid"
} else {
print $"❌ Encryption configuration has errors"
}
$validation
}
# Scan directory for unencrypted sensitive configurations
export def "config scan-sensitive" [
directory: path = "." # Directory to scan
--recursive (-r) # Scan recursively
] {
if not ($directory | path exists) {
print $"❌ Directory not found: ($directory)"
return
}
print $"🔍 Scanning for unencrypted sensitive configs in: ($directory)"
let results = (scan-unencrypted-configs $directory --recursive=$recursive)
if ($results | is-empty) {
print $"✅ No unencrypted sensitive configs found"
} else {
print $"\n⚠ Found ($results | length) unencrypted sensitive configs:"
print ""
print $results
print ""
print $"💡 Run 'config encrypt-all ($directory)' to encrypt them"
}
$results
}
# Encrypt all sensitive configurations in directory
export def "config encrypt-all" [
directory: path = "." # Directory to encrypt
--kms (-k): string = "age" # KMS backend: age, aws-kms, vault, cosmian
--recursive (-r) # Scan recursively
--dry-run (-n) # Dry run (no actual encryption)
] {
if not ($directory | path exists) {
print $"❌ Directory not found: ($directory)"
return
}
try {
encrypt-sensitive-configs $directory --kms=$kms --recursive=$recursive --dry-run=$dry_run
} catch { |err|
print $"❌ Bulk encryption failed: ($err.msg)"
}
}
# Show encryption information for configuration
export def "config encryption-info" [
file: path # Configuration file
] {
if not ($file | path exists) {
print $"❌ File not found: ($file)"
return
}
print $"📋 Encryption Information"
print $"========================"
print $" File: ($file)"
let encrypted = (is-encrypted-config $file)
print $" Encrypted: ($encrypted)"
if $encrypted {
# Try to extract SOPS metadata
try {
let content = (open $file --raw)
if ($content | str contains "sops:") {
print $" Type: SOPS encrypted"
# Extract some metadata (without decrypting)
if ($content | str contains "age:") {
print $" Backend: Age"
}
if ($content | str contains "kms:") {
print $" Backend: AWS KMS"
}
if ($content | str contains "vault:") {
print $" Backend: Vault"
}
}
} catch {
print $" Type: Encrypted (unknown format)"
}
} else {
let sensitive = (contains-sensitive-data $file)
print $" Contains sensitive data: ($sensitive)"
if $sensitive {
print ""
print $"⚠️ This file contains sensitive data but is not encrypted!"
print $"💡 Run 'config encrypt ($file)' to encrypt it"
}
}
}
# Initialize encryption setup (generate keys, create SOPS config)
export def "config init-encryption" [
--kms (-k): string = "age" # KMS backend to initialize: age, aws-kms, vault
--force (-f) # Force re-initialization
] {
print $"🔧 Initializing encryption setup with ($kms)"
print ""
match $kms {
"age" => {
# Check if age is installed
let age_check = (^which age | complete)
if $age_check.exit_code != 0 {
print $"❌ Age is not installed"
print $"💡 Install with: brew install age"
return
}
# Generate age key if not exists
let age_key_file = ($env.HOME | path join ".config" | path join "sops" | path join "age" | path join "keys.txt")
let age_key_dir = ($age_key_file | path dirname)
if ($age_key_file | path exists) and not $force {
print $"✅ Age key already exists: ($age_key_file)"
print $" Use --force to regenerate"
} else {
# Create directory
mkdir $age_key_dir
# Generate new age key
print $"🔑 Generating new Age key..."
let key_output = (^age-keygen -o $age_key_file | complete)
if $key_output.exit_code == 0 {
print $"✅ Age key generated: ($age_key_file)"
# Extract recipient
let key_content = (open $age_key_file --raw)
let recipient = ($key_content | lines | where ($it | str starts-with "# public key:") | first | split row ": " | get 1)
print $" Public key (recipient): ($recipient)"
print ""
print $"💡 Set this environment variable:"
print $" export SOPS_AGE_RECIPIENTS=($recipient)"
} else {
print $"❌ Failed to generate Age key"
return
}
}
# Create .sops.yaml if not exists
let sops_config = ($env.PWD | path join ".sops.yaml")
if not ($sops_config | path exists) or $force {
print $"📝 Creating SOPS configuration: ($sops_config)"
let key_content = (open $age_key_file --raw)
let recipient = ($key_content | lines | where ($it | str starts-with "# public key:") | first | split row ": " | get 1)
let sops_yaml = $"creation_rules:
- path_regex: .*\\.enc\\.yaml$
age: ($recipient)
- path_regex: .*\\.enc\\.yml$
age: ($recipient)
- path_regex: .*\\.enc\\.toml$
age: ($recipient)
- path_regex: .*\\.enc\\.json$
age: ($recipient)
- path_regex: workspace/.*/config/secure\\.yaml$
age: ($recipient)
"
$sops_yaml | save --force $sops_config
print $"✅ SOPS configuration created"
}
}
"aws-kms" => {
print $"⚠️ AWS KMS requires manual configuration"
print $"💡 Follow these steps:"
print $" 1. Create KMS key in AWS Console"
print $" 2. Update .sops.yaml with KMS ARN"
print $" 3. Configure AWS credentials"
}
"vault" | "cosmian" => {
print $"⚠️ ($kms) KMS requires manual configuration"
print $"💡 Configure KMS settings in config file"
}
_ => {
print $"❌ Unknown KMS backend: ($kms)"
print $" Supported: age, aws-kms, vault, cosmian"
}
}
print ""
print $"✅ Encryption initialization completed"
}
# Main help command
export def main [] {
print "Configuration Encryption Commands"
print "================================="
print ""
print "Encryption/Decryption:"
print " config encrypt <file> Encrypt configuration file"
print " config decrypt <file> Decrypt configuration file"
print " config edit-secure <file> Edit encrypted file securely"
print " config rotate-keys <file> Rotate encryption keys"
print ""
print "Information:"
print " config is-encrypted <file> Check if file is encrypted"
print " config encryption-info <file> Show encryption details"
print " config validate-encryption Validate encryption setup"
print ""
print "Bulk Operations:"
print " config scan-sensitive <dir> Find unencrypted sensitive configs"
print " config encrypt-all <dir> Encrypt all sensitive configs"
print ""
print "Setup:"
print " config init-encryption Initialize encryption (generate keys)"
print ""
print "Examples:"
print " config encrypt workspace/config/secure.yaml"
print " config edit-secure workspace/config/secure.enc.yaml"
print " config scan-sensitive workspace/config --recursive"
print " config init-encryption --kms age"
}