81 lines
4.4 KiB
Text
81 lines
4.4 KiB
Text
|
|
#!/usr/bin/env nu
|
||
|
|
# rotate_keys/run.nu — validate, propose, sign, activate, verify, revoke
|
||
|
|
|
||
|
|
def main [
|
||
|
|
--workspace: string = ""
|
||
|
|
--key-type: string = ""
|
||
|
|
--new-key-path: string = ""
|
||
|
|
--new-pubkey-path: string = ""
|
||
|
|
--ops-vm-host: string = ""
|
||
|
|
--dry-run
|
||
|
|
]: nothing -> nothing {
|
||
|
|
let ws = if ($workspace | is-not-empty) { $workspace } else { $env.PLAYBOOK_PARAM_WORKSPACE? | default "" }
|
||
|
|
let ktype = if ($key_type | is-not-empty) { $key_type } else { $env.PLAYBOOK_PARAM_KEY_TYPE? | default "" }
|
||
|
|
let new_key = if ($new_key_path | is-not-empty) { $new_key_path } else { $env.PLAYBOOK_PARAM_NEW_KEY_PATH? | default "" }
|
||
|
|
let new_pubkey = if ($new_pubkey_path | is-not-empty) { $new_pubkey_path } else { $env.PLAYBOOK_PARAM_NEW_PUBKEY_PATH? | default "" }
|
||
|
|
let vm_host = if ($ops_vm_host | is-not-empty) { $ops_vm_host } else { $env.PLAYBOOK_PARAM_OPS_VM_HOST? | default "" }
|
||
|
|
|
||
|
|
if ($ws | is-empty) { error make { msg: "workspace is required" } }
|
||
|
|
if ($ktype | is-empty) { error make { msg: "key-type is required (ops-controller | keeper | operator)" } }
|
||
|
|
if ($new_key | is-empty) { error make { msg: "new-key-path is required" } }
|
||
|
|
|
||
|
|
let valid_types = ["ops-controller", "keeper", "operator"]
|
||
|
|
if not ($valid_types | any { |t| $t == $ktype }) {
|
||
|
|
error make { msg: $"Invalid key-type '($ktype)'. Must be one of: ($valid_types | str join ', ')" }
|
||
|
|
}
|
||
|
|
|
||
|
|
let playbook_dir = $env.PLAYBOOK_DIR? | default ($env.FILE_PWD | path expand)
|
||
|
|
let dry_flag = if $dry_run { "--dry-run" } else { "" }
|
||
|
|
|
||
|
|
print $"[rotate_keys] workspace=($ws) key-type=($ktype) dry-run=($dry_run)"
|
||
|
|
print ""
|
||
|
|
|
||
|
|
print "[step 1/7] validating new keypair..."
|
||
|
|
let r1 = do { ^nu ($playbook_dir | path join "steps/validate_new_key.nu") --key-type $ktype --new-key-path $new_key --new-pubkey-path $new_pubkey $dry_flag } | complete
|
||
|
|
if $r1.exit_code != 0 { error make { msg: $"Key validation failed: ($r1.stderr | str trim)" } }
|
||
|
|
print $r1.stdout
|
||
|
|
|
||
|
|
print "[step 2/7] proposing delegation patch..."
|
||
|
|
let r2 = do { ^nu ($playbook_dir | path join "steps/propose_delegation_patch.nu") --workspace $ws --key-type $ktype --new-pubkey-path $new_pubkey $dry_flag } | complete
|
||
|
|
if $r2.exit_code != 0 { error make { msg: $"Delegation patch proposal failed: ($r2.stderr | str trim)" } }
|
||
|
|
print $r2.stdout
|
||
|
|
|
||
|
|
print "[step 3/7] awaiting quorum signatures on delegation patch..."
|
||
|
|
let r3 = do { ^nu ($playbook_dir | path join "steps/sign_delegation_patch.nu") --workspace $ws --key-type $ktype $dry_flag } | complete
|
||
|
|
if $r3.exit_code != 0 { error make { msg: $"Delegation patch signing failed: ($r3.stderr | str trim)" } }
|
||
|
|
print $r3.stdout
|
||
|
|
|
||
|
|
print "[step 4/7] activating new key..."
|
||
|
|
let r4 = do { ^nu ($playbook_dir | path join "steps/activate_new_key.nu") --workspace $ws --key-type $ktype --new-key-path $new_key --ops-vm-host $vm_host $dry_flag } | complete
|
||
|
|
if $r4.exit_code != 0 {
|
||
|
|
print $"ERROR: key activation failed — initiating rollback: ($r4.stderr | str trim)"
|
||
|
|
print "ROLLBACK REQUIRED: restore old key from backup and re-run propose/sign/activate cycle"
|
||
|
|
error make { msg: "activate_new_key failed — manual rollback required" }
|
||
|
|
}
|
||
|
|
print $r4.stdout
|
||
|
|
|
||
|
|
print "[step 5/7] verifying continuity..."
|
||
|
|
let r5 = do { ^nu ($playbook_dir | path join "steps/verify_continuity.nu") --workspace $ws --key-type $ktype $dry_flag } | complete
|
||
|
|
if $r5.exit_code != 0 {
|
||
|
|
print $" WARNING: continuity verification failed: ($r5.stderr | str trim)"
|
||
|
|
} else {
|
||
|
|
print $r5.stdout
|
||
|
|
}
|
||
|
|
|
||
|
|
print "[step 6/7] revoking old key..."
|
||
|
|
let r6 = do { ^nu ($playbook_dir | path join "steps/revoke_old_key.nu") --workspace $ws --key-type $ktype $dry_flag } | complete
|
||
|
|
if $r6.exit_code != 0 {
|
||
|
|
print $" WARNING: old key revocation reported errors: ($r6.stderr | str trim)"
|
||
|
|
} else {
|
||
|
|
print $r6.stdout
|
||
|
|
}
|
||
|
|
|
||
|
|
print "[step 7/7] emitting key-rotation audit event..."
|
||
|
|
let r7 = do { ^nu ($playbook_dir | path join "steps/emit_audit.nu") --workspace $ws --event-type "key_rotation" --key-type $ktype $dry_flag } | complete
|
||
|
|
if $r7.exit_code != 0 { print $" WARNING: audit emit failed: ($r7.stderr | str trim)" }
|
||
|
|
|
||
|
|
print ""
|
||
|
|
print "[rotate_keys] complete"
|
||
|
|
print $"Verify: new ($ktype) key is active, old key rejected by target service"
|
||
|
|
}
|