# KMS Plugin Wrapper with HTTP Fallback # Provides graceful degradation to HTTP/CLI when nu_plugin_kms is unavailable use ../config/accessor.nu * # Check if KMS plugin is available def is-plugin-available []: nothing -> bool { (which kms | length) > 0 } # Check if KMS plugin is enabled in config def is-plugin-enabled []: nothing -> bool { config-get "plugins.kms_enabled" true } # Get KMS service base URL def get-kms-url []: nothing -> string { config-get "platform.kms_service.url" "http://localhost:8090" } # Get default KMS backend def get-default-backend []: nothing -> string { config-get "security.kms.backend" "rustyvault" } # Helper to safely execute a closure and return null on error def try-plugin [callback: closure]: nothing -> any { do -i $callback } # Encrypt data using KMS export def plugin-kms-encrypt [ data: string --backend: string = "" # rustyvault, age, vault, cosmian, aws-kms --context: string = "" # Additional authenticated data --key-id: string = "" # Specific key ID ]: nothing -> record { let enabled = is-plugin-enabled let available = is-plugin-available let backend_name = if ($backend | is-empty) { get-default-backend } else { $backend } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($context | is-empty) and ($key_id | is-empty) { [encrypt $data --backend $backend_name] } else if ($context | is-empty) { [encrypt $data --backend $backend_name --key-id $key_id] } else if ($key_id | is-empty) { [encrypt $data --backend $backend_name --context $context] } else { [encrypt $data --backend $backend_name --context $context --key-id $key_id] } kms ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS encrypt failed, falling back to HTTP/CLI" } # HTTP fallback - call KMS service directly print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/encrypt" let result = (do -i { let body = {data: $data, backend: $backend_name} http post $url $body }) if $result != null { return $result } error make { msg: "KMS encryption failed" label: { text: $"Failed to encrypt data with backend ($backend_name)" span: (metadata $data).span } } } # Decrypt data using KMS export def plugin-kms-decrypt [ ciphertext: string --backend: string = "" # rustyvault, age, vault, cosmian, aws-kms --context: string = "" # Additional authenticated data --key-id: string = "" # Specific key ID ]: nothing -> string { let enabled = is-plugin-enabled let available = is-plugin-available let backend_name = if ($backend | is-empty) { get-default-backend } else { $backend } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($context | is-empty) and ($key_id | is-empty) { [decrypt $ciphertext --backend $backend_name] } else if ($context | is-empty) { [decrypt $ciphertext --backend $backend_name --key-id $key_id] } else if ($key_id | is-empty) { [decrypt $ciphertext --backend $backend_name --context $context] } else { [decrypt $ciphertext --backend $backend_name --context $context --key-id $key_id] } kms ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS decrypt failed, falling back to HTTP/CLI" } # HTTP fallback - call KMS service directly print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/decrypt" let result = (do -i { let body = {ciphertext: $ciphertext, backend: $backend_name} http post $url $body }) if $result != null { return $result } error make { msg: "KMS decryption failed" label: { text: $"Failed to decrypt data with backend ($backend_name)" span: (metadata $ciphertext).span } } } # Generate new encryption key export def plugin-kms-generate-key [ --backend: string = "" # rustyvault, age, vault, cosmian, aws-kms --key-type: string = "aes256" # aes256, rsa2048, rsa4096, ed25519 --name: string = "" # Key name/alias ]: nothing -> record { let enabled = is-plugin-enabled let available = is-plugin-available let backend_name = if ($backend | is-empty) { get-default-backend } else { $backend } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($name | is-empty) { [generate-key --backend $backend_name --key-type $key_type] } else { [generate-key --backend $backend_name --key-type $key_type --name $name] } kms ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS generate-key failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/keys/generate" let body = if ($name | is-empty) { {backend: $backend_name, key_type: $key_type} } else { {backend: $backend_name, key_type: $key_type, name: $name} } let result = (do -i { http post $url $body }) if $result != null { return $result } error make { msg: "KMS key generation failed" label: { text: $"Failed to generate key with backend ($backend_name)" } } } # Get KMS service status export def plugin-kms-status []: nothing -> record { let enabled = is-plugin-enabled let available = is-plugin-available if $enabled and $available { let plugin_result = (try-plugin { kms status }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS status failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/health" let result = (do -i { http get $url }) if $result != null { return $result } { status: "unavailable" message: "KMS service unreachable" } } # List available KMS backends export def plugin-kms-backends []: nothing -> table { let enabled = is-plugin-enabled let available = is-plugin-available if $enabled and $available { let plugin_result = (try-plugin { kms backends }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS backends failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/backends" let result = (do -i { let response = (http get $url) $response.backends? | default [] }) if $result != null { return $result } # Return known backends as fallback [ {name: "rustyvault", available: true, description: "RustyVault KMS (primary)"} {name: "age", available: true, description: "Age encryption"} {name: "vault", available: false, description: "HashiCorp Vault"} {name: "cosmian", available: false, description: "Cosmian KMS"} {name: "aws-kms", available: false, description: "AWS Key Management Service"} ] } # Rotate encryption key export def plugin-kms-rotate-key [ key_id: string --backend: string = "" # rustyvault, age, vault, cosmian, aws-kms ]: nothing -> record { let enabled = is-plugin-enabled let available = is-plugin-available let backend_name = if ($backend | is-empty) { get-default-backend } else { $backend } if $enabled and $available { let plugin_result = (try-plugin { kms rotate-key $key_id --backend $backend_name }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS rotate-key failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/keys/rotate" let result = (do -i { http post $url {backend: $backend_name, key_id: $key_id} }) if $result != null { return $result } error make { msg: "KMS key rotation failed" label: { text: $"Failed to rotate key ($key_id) with backend ($backend_name)" span: (metadata $key_id).span } } } # List encryption keys export def plugin-kms-list-keys [ --backend: string = "" # rustyvault, age, vault, cosmian, aws-kms ]: nothing -> table { let enabled = is-plugin-enabled let available = is-plugin-available let backend_name = if ($backend | is-empty) { get-default-backend } else { $backend } if $enabled and $available { let plugin_result = (try-plugin { kms list-keys --backend $backend_name }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin KMS list-keys failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let kms_url = (get-kms-url) let url = $"($kms_url)/api/keys?backend=($backend_name)" let result = (do -i { let response = (http get $url) $response.keys? | default [] }) if $result != null { return $result } [] } # Get KMS plugin status and configuration export def plugin-kms-info []: nothing -> record { let plugin_available = is-plugin-available let plugin_enabled = is-plugin-enabled let default_backend = get-default-backend let kms_url = get-kms-url { plugin_available: $plugin_available plugin_enabled: $plugin_enabled default_backend: $default_backend kms_service_url: $kms_url mode: (if ($plugin_enabled and $plugin_available) { "plugin" } else { "http" }) } }