# SecretumVault Plugin Wrapper with HTTP Fallback # Provides high-level functions for SecretumVault operations with graceful HTTP fallback use ../config/accessor.nu * # Check if SecretumVault plugin is available def is-plugin-available []: nothing -> bool { (which secretumvault | length) > 0 } # Check if SecretumVault plugin is enabled in config def is-plugin-enabled []: nothing -> bool { config-get "plugins.secretumvault_enabled" true } # Get SecretumVault service URL def get-secretumvault-url []: nothing -> string { config-get "kms.secretumvault.server_url" "http://localhost:8200" } # Get SecretumVault auth token def get-secretumvault-token []: nothing -> string { let token = ( if ($env.SECRETUMVAULT_TOKEN? != null) { $env.SECRETUMVAULT_TOKEN } else { "" } ) if ($token | is-empty) { config-get "kms.secretumvault.auth_token" "" } else { $token } } # Get SecretumVault mount point def get-secretumvault-mount-point []: nothing -> string { config-get "kms.secretumvault.mount_point" "transit" } # Get default SecretumVault key name def get-secretumvault-key-name []: nothing -> string { config-get "kms.secretumvault.key_name" "provisioning-master" } # Helper to safely execute a closure and return null on error def try-plugin [callback: closure]: nothing -> any { do -i $callback } # Encrypt data using SecretumVault plugin export def plugin-secretumvault-encrypt [ plaintext: string --key-id: string = "" # Encryption key ID ] { let enabled = is-plugin-enabled let available = is-plugin-available let key_name = if ($key_id | is-empty) { get-secretumvault-key-name } else { $key_id } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($key_id | is-empty) { [encrypt $plaintext] } else { [encrypt $plaintext --key-id $key_id] } secretumvault ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault encrypt failed, falling back to HTTP" } # HTTP fallback - call SecretumVault service directly print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let sv_token = (get-secretumvault-token) let mount_point = (get-secretumvault-mount-point) let url = $"($sv_url)/v1/($mount_point)/encrypt/($key_name)" if ($sv_token | is-empty) { error make { msg: "SecretumVault authentication failed" label: { text: "SECRETUMVAULT_TOKEN not set" span: (metadata $plaintext).span } } } let result = (do -i { let plaintext_b64 = ($plaintext | encode base64) let body = {plaintext: $plaintext_b64} http post -H ["X-Vault-Token" $sv_token] $url $body }) if $result != null { return $result } error make { msg: "SecretumVault encryption failed" label: { text: $"Failed to encrypt data with key ($key_name)" span: (metadata $plaintext).span } } } # Decrypt data using SecretumVault plugin export def plugin-secretumvault-decrypt [ ciphertext: string --key-id: string = "" # Encryption key ID ] { let enabled = is-plugin-enabled let available = is-plugin-available let key_name = if ($key_id | is-empty) { get-secretumvault-key-name } else { $key_id } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($key_id | is-empty) { [decrypt $ciphertext] } else { [decrypt $ciphertext --key-id $key_id] } secretumvault ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault decrypt failed, falling back to HTTP" } # HTTP fallback - call SecretumVault service directly print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let sv_token = (get-secretumvault-token) let mount_point = (get-secretumvault-mount-point) let url = $"($sv_url)/v1/($mount_point)/decrypt/($key_name)" if ($sv_token | is-empty) { error make { msg: "SecretumVault authentication failed" label: { text: "SECRETUMVAULT_TOKEN not set" span: (metadata $ciphertext).span } } } let result = (do -i { let body = {ciphertext: $ciphertext} let response = (http post -H ["X-Vault-Token" $sv_token] $url $body) if ($response.data.plaintext? != null) { { plaintext: ($response.data.plaintext | decode base64), key_id: ($response.data.key_id? // $key_name) } } else { $response } }) if $result != null { return $result } error make { msg: "SecretumVault decryption failed" label: { text: $"Failed to decrypt data with key ($key_name)" span: (metadata $ciphertext).span } } } # Generate data key using SecretumVault plugin export def plugin-secretumvault-generate-key [ --bits: int = 256 # Key size in bits (128, 256, 2048, 4096) --key-id: string = "" # Encryption key ID ] { let enabled = is-plugin-enabled let available = is-plugin-available let key_name = if ($key_id | is-empty) { get-secretumvault-key-name } else { $key_id } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($key_id | is-empty) { [generate-key --bits $bits] } else { [generate-key --bits $bits --key-id $key_id] } secretumvault ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault generate-key failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let sv_token = (get-secretumvault-token) let mount_point = (get-secretumvault-mount-point) let url = $"($sv_url)/v1/($mount_point)/datakey/plaintext/($key_name)" if ($sv_token | is-empty) { error make { msg: "SecretumVault authentication failed" label: { text: "SECRETUMVAULT_TOKEN not set" } } } let result = (do -i { let body = {bits: $bits} http post -H ["X-Vault-Token" $sv_token] $url $body }) if $result != null { return $result } error make { msg: "SecretumVault key generation failed" label: { text: $"Failed to generate key with ($bits) bits" } } } # Check SecretumVault health using plugin export def plugin-secretumvault-health []: nothing -> record { let enabled = is-plugin-enabled let available = is-plugin-available if $enabled and $available { let plugin_result = (try-plugin { secretumvault health }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault health check failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let url = $"($sv_url)/v1/sys/health" let result = (do -i { http get $url }) if $result != null { return $result } { healthy: false status: "unavailable" message: "SecretumVault service unreachable" } } # Get SecretumVault version using plugin export def plugin-secretumvault-version []: nothing -> string { let enabled = is-plugin-enabled let available = is-plugin-available if $enabled and $available { let plugin_result = (try-plugin { secretumvault version }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault version failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let url = $"($sv_url)/v1/sys/health" let result = (do -i { let response = (http get $url) $response.version? // "unknown" }) if $result != null { return $result } "unavailable" } # Rotate encryption key using plugin export def plugin-secretumvault-rotate-key [ --key-id: string = "" # Key ID to rotate ] { let enabled = is-plugin-enabled let available = is-plugin-available let key_name = if ($key_id | is-empty) { get-secretumvault-key-name } else { $key_id } if $enabled and $available { let plugin_result = (try-plugin { let args = if ($key_id | is-empty) { [rotate-key] } else { [rotate-key --key-id $key_id] } secretumvault ...$args }) if $plugin_result != null { return $plugin_result } print "⚠️ Plugin SecretumVault rotate-key failed, falling back to HTTP" } # HTTP fallback print "⚠️ Using HTTP fallback (plugin not available)" let sv_url = (get-secretumvault-url) let sv_token = (get-secretumvault-token) let mount_point = (get-secretumvault-mount-point) let url = $"($sv_url)/v1/($mount_point)/keys/($key_name)/rotate" if ($sv_token | is-empty) { error make { msg: "SecretumVault authentication failed" label: { text: "SECRETUMVAULT_TOKEN not set" span: (metadata $key_name).span } } } let result = (do -i { http post -H ["X-Vault-Token" $sv_token] $url }) if $result != null { return $result } error make { msg: "SecretumVault key rotation failed" label: { text: $"Failed to rotate key ($key_name)" span: (metadata $key_name).span } } } # Get SecretumVault plugin status and configuration export def plugin-secretumvault-info []: nothing -> record { let plugin_available = is-plugin-available let plugin_enabled = is-plugin-enabled let sv_url = get-secretumvault-url let mount_point = get-secretumvault-mount-point let key_name = get-secretumvault-key-name let has_token = (not (get-secretumvault-token | is-empty)) { plugin_available: $plugin_available plugin_enabled: $plugin_enabled service_url: $sv_url mount_point: $mount_point default_key: $key_name authenticated: $has_token mode: (if ($plugin_enabled and $plugin_available) { "plugin (native)" } else { "http fallback" }) } } # Encrypt configuration file using SecretumVault export def encrypt-config-file [ config_file: string --output: string = "" # Output file path (default: .enc) --key-id: string = "" # Encryption key ID ] { let out_file = if ($output | is-empty) { $"($config_file).enc" } else { $output } let result = (do -i { let content = (open $config_file --raw) let encrypted = (plugin-secretumvault-encrypt $content --key-id $key_id) # Save encrypted content if ($encrypted | type) == "record" { $encrypted.ciphertext | save --force $out_file } else { $encrypted | save --force $out_file } print $"✅ Configuration encrypted to: ($out_file)" { success: true input_file: $config_file output_file: $out_file key_id: (if ($key_id | is-empty) { (get-secretumvault-key-name) } else { $key_id }) } }) if $result == null { error make { msg: "Failed to encrypt configuration file" label: { text: "Check file permissions and SecretumVault service" span: (metadata $config_file).span } } } $result } # Decrypt configuration file using SecretumVault export def decrypt-config-file [ encrypted_file: string --output: string = "" # Output file path (default: .dec) --key-id: string = "" # Encryption key ID ] { let out_file = if ($output | is-empty) { let base_name = ($encrypted_file | str replace '.enc' '') $"($base_name).dec" } else { $output } let result = (do -i { let encrypted_content = (open $encrypted_file --raw) let decrypted = (plugin-secretumvault-decrypt $encrypted_content --key-id $key_id) # Save decrypted content if ($decrypted | type) == "record" { if ($decrypted.plaintext? != null) { $decrypted.plaintext | save --force $out_file } else { $decrypted | to json | save --force $out_file } } else { $decrypted | save --force $out_file } print $"✅ Configuration decrypted to: ($out_file)" { success: true input_file: $encrypted_file output_file: $out_file key_id: (if ($key_id | is-empty) { (get-secretumvault-key-name) } else { $key_id }) } }) if $result == null { error make { msg: "Failed to decrypt configuration file" label: { text: "Check file permissions and SecretumVault service" span: (metadata $encrypted_file).span } } } $result }