Large combined batch of 23 files refactored from star-imports to selective.
Grouped because two sub-batches accumulated in staging without intermediate
commit.
=== Orchestrator facades (Layer 3) ===
ai/mod.nu [12 symbols from ai/lib.nu]
config/loader.nu [14 symbols from loader/mod.nu]
config/accessor/mod.nu [15 symbols from accessor/functions.nu]
sops/mod.nu [11 symbols from sops/lib.nu]
user/mod.nu [16 symbols from user/config.nu]
=== Selective imports ===
defs/lists.nu utils/on_select (kept, selective)
services/manager.nu (all dead dropped)
webhook/ai_webhook.nu ai/lib [4] + settings/lib
kms/lib.nu utils/error + utils/interface + plugins/kms
gitea/locking.nu api_client [8]
gitea/workspace_git.nu api_client [3]
gitea/extension_publish.nu api_client [8] + config/loader
infra_validator/rules_engine.nu config_loader [3]
plugins/kms.nu config/accessor/core [config-get]
coredns/api_client.nu config/loader [get-config]
=== Dangling imports removed (target file does not exist) ===
coredns/docker.nu ../utils/log.nu → deleted (uses corefile.nu [2])
coredns/zones.nu ../utils/log.nu → deleted (uses corefile.nu [1])
coredns/service.nu ../utils/log.nu → deleted (uses corefile.nu [2])
coredns/corefile.nu ../utils/log.nu → deleted
=== Broken paths cleaned up ===
project/detect.nu Former `use ../../../lib_provisioning *` resolved to
non-existent path (core/lib_provisioning). Silent no-op at runtime.
Removed. Error count went 19 -> 17.
=== Dead imports dropped ===
utils/ssh.nu config/accessor DROPPED (dead)
utils/init.nu config/accessor DROPPED (dead)
infra_validator/agent_interface.nu report_generator DROPPED (dead)
=== Dynamic imports preserved ===
providers/loader.nu line 179 `use ($provider_entry.entry_point) *` is
intentional runtime dispatch — not convertible to selective.
Validation: all files match pre-existing baseline. Gitea subsystem has
known pre-existing 50-error noise (transitive); independent of this work.
Refs: ADR-025
378 lines
10 KiB
Text
378 lines
10 KiB
Text
# KMS Plugin Wrapper with HTTP Fallback
|
|
# Provides graceful degradation to HTTP/CLI when nu_plugin_kms is unavailable
|
|
|
|
# Selective imports (ADR-025 Phase 3 Layer 2).
|
|
use lib_provisioning/config/accessor/core.nu [config-get]
|
|
|
|
# Check if KMS plugin is available (registered with Nushell)
|
|
def is-plugin-available [] {
|
|
let installed = (version | get installed_plugins)
|
|
$installed | str contains "kms"
|
|
}
|
|
|
|
# Check if KMS plugin is enabled in config
|
|
def is-plugin-enabled [] {
|
|
config-get "plugins.kms_enabled" true
|
|
}
|
|
|
|
# Get KMS service base URL
|
|
def get-kms-url [] {
|
|
config-get "platform.kms_service.url" "http://localhost:8090"
|
|
}
|
|
|
|
# Get default KMS backend
|
|
def get-default-backend [] {
|
|
config-get "security.kms.backend" "rustyvault"
|
|
}
|
|
|
|
# Helper to safely execute a closure and return null on error
|
|
def try-plugin [callback: closure] {
|
|
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
|
|
] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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
|
|
] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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
|
|
] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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 [] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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 [] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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
|
|
] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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
|
|
] {
|
|
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 "⚠️ nu_plugin_kms not available - using HTTP fallback for key management"
|
|
|
|
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 [] {
|
|
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" })
|
|
}
|
|
}
|