provisioning/scripts/secrets-manage.nu

266 lines
7.5 KiB
Text
Raw Permalink Normal View History

#!/usr/bin/env nu
# Unified secrets management orchestrator
# Manages complete SOPS + vault-service workflow for all environments
# Usage: secrets-manage <action> [options]
use std log
def get-vault-url [] {
$env.VAULT_SERVICE_URL? // "http://localhost:9094"
}
def get-vault-token [] {
$env.VAULT_SERVICE_TOKEN? // ""
}
def check-vault-connectivity [] {
let url = get-vault-url
let health_url = $"($url)/health"
let response = (http get $health_url | complete)
if $response.exit_code != 0 {
error make {
msg: "Vault service unreachable"
label: {
text: $"Cannot connect to ($health_url)"
}
}
}
true
}
def list-environments [] {
["dev", "staging", "prod"]
}
def check-environment-setup [environment: string] {
let token = (get-vault-token)
if ($token | is-empty) {
print $"⚠️ VAULT_SERVICE_TOKEN not set (required for operations)"
}
let vault_url = (get-vault-url)
print $"Environment: ($environment)"
print $"Vault URL: ($vault_url)"
print $"Token: $(if ($token | is-empty) { "not set" } else { "set" })"
}
def status [] {
print ""
print "====== SOPS + Vault-Service Status ======"
print ""
let connectivity_ok = (check-vault-connectivity)
print $"✓ Vault-service connectivity: OK"
print ""
print "Available environments:"
list-environments | each {|env_name|
print $" • ($env_name)"
}
print ""
print "Setup:"
check-environment-setup "current"
print ""
}
def init [environment: string] {
if $environment not-in (list-environments) {
error make {
msg: "Invalid environment"
label: {
text: "Must be: dev, staging, or prod"
}
}
}
print ""
print $"====== Initialize SOPS for ($environment) ======"
print ""
print "Step 1: Checking vault-service connectivity..."
check-vault-connectivity
print "✓ Vault-service is accessible"
print ""
print "Step 2: Generating .sops.yaml configuration..."
let sops_init_result = (
nu provisioning/scripts/sops-init.nu --environment $environment --validate
)
if ($sops_init_result.success) {
print $"✓ SOPS configuration: ($sops_init_result.output_path)"
} else {
error make {
msg: "Failed to generate SOPS configuration"
label: { text: $sops_init_result }
}
}
print ""
print $"====== ($environment) environment initialized ======"
print ""
print "Next steps:"
print " 1. Verify .sops.yaml: cat $sops_init_result.output_path"
print " 2. Encrypt files: provisioning/scripts/secrets-encrypt.nu <file> --environment $environment"
print " 3. Commit to Git: git add config/secrets/$environment/"
print ""
}
def encrypt [file: string, environment: string] {
if not ($file | path exists) {
error make {
msg: "File not found"
label: { text: $file }
}
}
print ""
print $"====== Encrypt ($file) for ($environment) ======"
print ""
let result = (
nu provisioning/scripts/secrets-encrypt.nu $file --environment $environment
)
if ($result.success) {
print $"✓ Encrypted: ($result.output)"
print $"Ready to commit: git add ($result.output)"
} else {
error make { msg: "Encryption failed" }
}
print ""
}
def decrypt [file: string, environment: string] {
if not ($file | path exists) {
error make {
msg: "File not found"
label: { text: $file }
}
}
print ""
print $"====== Decrypt ($file) for ($environment) ======"
print "⚠️ SENSITIVE OPERATION: Private key will be retrieved"
print ""
let token = (get-vault-token)
if ($token | is-empty) {
error make {
msg: "VAULT_SERVICE_TOKEN required for decryption"
label: { text: "Set environment variable" }
}
}
let result = (
nu provisioning/scripts/secrets-decrypt.nu $file --environment $environment
)
if ($result.success) {
print $"✓ Decrypted: ($result.output)"
print "⚠️ REMEMBER: Delete decrypted file after use"
} else {
error make { msg: "Decryption failed" }
}
print ""
}
def rotate [environment: string] {
if $environment not-in (list-environments) {
error make {
msg: "Invalid environment"
label: { text: "Must be: dev, staging, or prod" }
}
}
print ""
print $"====== Key Rotation for ($environment) ======"
print "⚠️ This operation will:"
print " 1. Generate new Age keypair in vault-service"
print " 2. Re-encrypt all SOPS files with new key"
print " 3. Update key version tracking"
print ""
let token = (get-vault-token)
if ($token | is-empty) {
error make {
msg: "VAULT_SERVICE_TOKEN required for key rotation"
label: { text: "Set environment variable" }
}
}
let result = (
nu provisioning/scripts/secrets-rotate-keys.nu --environment $environment
)
if ($result.success) {
print $"✓ Key rotation complete"
print $" Previous version: ($result.previous_version)"
print $" New version: ($result.new_version)"
print $" Files updated: ($result.files_updated)"
if ($result.files_failed > 0) {
print $" ⚠️ Files failed: ($result.files_failed)"
}
print ""
print "Verification step:"
print $" ($result.next_steps)"
} else {
error make { msg: "Key rotation failed" }
}
print ""
}
def main [
action: string = "status"
file: string = ""
--environment: string = "dev"
] {
match $action {
"status" => (status),
"init" => (init $environment),
"encrypt" => {
if ($file | is-empty) {
error make { msg: "File required for encrypt action" }
}
(encrypt $file $environment)
},
"decrypt" => {
if ($file | is-empty) {
error make { msg: "File required for decrypt action" }
}
(decrypt $file $environment)
},
"rotate" => (rotate $environment),
"help" => {
print "Secrets Management Orchestrator"
print ""
print "Usage: secrets-manage <action> [file] [--environment dev|staging|prod]"
print ""
print "Actions:"
print " status - Show SOPS + vault-service status"
print " init - Initialize environment (generate .sops.yaml)"
print " encrypt <file> - Encrypt configuration file"
print " decrypt <file> - Decrypt SOPS file (requires token)"
print " rotate - Rotate Age keys (requires token)"
print " help - Show this help message"
print ""
print "Examples:"
print " secrets-manage status"
print " secrets-manage init --environment prod"
print " secrets-manage encrypt config.yaml --environment dev"
print " secrets-manage decrypt config.enc.yaml --environment dev"
print " secrets-manage rotate --environment prod"
},
_ => {
print $"Unknown action: ($action)"
print "Run 'secrets-manage help' for available commands"
}
}
}