Adds KMS, secrets management, config encryption, and auth plugins to enable zero-trust security architecture across the provisioning platform.
363 lines
8.7 KiB
Plaintext
363 lines
8.7 KiB
Plaintext
#!/usr/bin/env nu
|
|
|
|
# KMS Service CLI Integration
|
|
# Provides commands to interact with the KMS service API
|
|
|
|
# Get KMS service base URL from environment or use default
|
|
def kms-url [] -> string {
|
|
$env.KMS_SERVICE_URL? | default "http://localhost:8081"
|
|
}
|
|
|
|
# Build KMS API endpoint URL
|
|
def kms-endpoint [path: string] -> string {
|
|
$"(kms-url)/api/v1/kms/(path)"
|
|
}
|
|
|
|
# Encrypt data using KMS
|
|
#
|
|
# Examples:
|
|
# "secret-data" | kms encrypt
|
|
# "api-key" | kms encrypt --context "env=prod,service=api"
|
|
export def "kms encrypt" [
|
|
--context: string # Encryption context (key1=val1,key2=val2)
|
|
--backend: string = "vault" # KMS backend (vault or aws-kms)
|
|
] -> string {
|
|
let input = $in
|
|
|
|
# Encode plaintext to base64
|
|
let plaintext_b64 = $input | encode base64
|
|
|
|
# Prepare request body
|
|
let body = {
|
|
plaintext: $plaintext_b64
|
|
context: $context
|
|
}
|
|
|
|
# Make API request
|
|
let response = http post (kms-endpoint "encrypt") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS encrypt request failed"
|
|
label: {
|
|
text: $response.stderr
|
|
span: (metadata $input).span
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS encryption failed"
|
|
label: {
|
|
text: $result.error
|
|
span: (metadata $input).span
|
|
}
|
|
}
|
|
}
|
|
|
|
$result.ciphertext
|
|
}
|
|
|
|
# Decrypt data using KMS
|
|
#
|
|
# Examples:
|
|
# "vault:v1:..." | kms decrypt
|
|
# $ciphertext | kms decrypt --context "env=prod,service=api"
|
|
export def "kms decrypt" [
|
|
--context: string # Encryption context (must match encryption context)
|
|
] -> string {
|
|
let ciphertext = $in
|
|
|
|
# Prepare request body
|
|
let body = {
|
|
ciphertext: $ciphertext
|
|
context: $context
|
|
}
|
|
|
|
# Make API request
|
|
let response = http post (kms-endpoint "decrypt") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS decrypt request failed"
|
|
label: {
|
|
text: $response.stderr
|
|
span: (metadata $ciphertext).span
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS decryption failed"
|
|
label: {
|
|
text: $result.error
|
|
span: (metadata $ciphertext).span
|
|
}
|
|
}
|
|
}
|
|
|
|
# Decode base64 plaintext
|
|
$result.plaintext | decode base64
|
|
}
|
|
|
|
# Generate a data key for envelope encryption
|
|
#
|
|
# Examples:
|
|
# kms generate-key
|
|
# kms generate-key --key-spec AES_128
|
|
export def "kms generate-key" [
|
|
--key-spec: string = "AES_256" # Key specification (AES_128, AES_256, RSA_2048, RSA_4096)
|
|
] -> record {
|
|
let body = {
|
|
key_spec: $key_spec
|
|
}
|
|
|
|
let response = http post (kms-endpoint "generate-key") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS generate-key request failed"
|
|
label: {
|
|
text: $response.stderr
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS key generation failed"
|
|
label: {
|
|
text: $result.error
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
plaintext: $result.plaintext
|
|
ciphertext: $result.ciphertext
|
|
key_id: $result.key_id
|
|
}
|
|
}
|
|
|
|
# Rotate KMS key
|
|
#
|
|
# Examples:
|
|
# kms rotate-key "arn:aws:kms:..."
|
|
export def "kms rotate-key" [
|
|
key_id: string # Key ID to rotate
|
|
] -> record {
|
|
let body = {
|
|
key_id: $key_id
|
|
}
|
|
|
|
let response = http post (kms-endpoint "rotate-key") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS rotate-key request failed"
|
|
label: {
|
|
text: $response.stderr
|
|
span: (metadata $key_id).span
|
|
}
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS key rotation failed"
|
|
label: {
|
|
text: $result.error
|
|
span: (metadata $key_id).span
|
|
}
|
|
}
|
|
}
|
|
|
|
{
|
|
new_key_id: $result.new_key_id
|
|
success: $result.success
|
|
}
|
|
}
|
|
|
|
# Check KMS service health
|
|
#
|
|
# Examples:
|
|
# kms health
|
|
export def "kms health" [] -> record {
|
|
let response = http get (kms-endpoint "health") | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
return {
|
|
status: "unhealthy"
|
|
error: $response.stderr
|
|
}
|
|
}
|
|
|
|
$response.stdout | from json
|
|
}
|
|
|
|
# Get KMS service status
|
|
#
|
|
# Examples:
|
|
# kms status
|
|
export def "kms status" [] -> record {
|
|
let response = http get (kms-endpoint "status") | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS status request failed"
|
|
label: {
|
|
text: $response.stderr
|
|
}
|
|
}
|
|
}
|
|
|
|
$response.stdout | from json
|
|
}
|
|
|
|
# Encrypt file using KMS
|
|
#
|
|
# Examples:
|
|
# kms encrypt-file config.yaml
|
|
# kms encrypt-file secrets.json --output secrets.enc --context "env=prod"
|
|
export def "kms encrypt-file" [
|
|
input_file: path # File to encrypt
|
|
--output: path # Output file (default: <input>.enc)
|
|
--context: string # Encryption context
|
|
] {
|
|
if not ($input_file | path exists) {
|
|
error make {
|
|
msg: "Input file not found"
|
|
label: {
|
|
text: $input_file
|
|
span: (metadata $input_file).span
|
|
}
|
|
}
|
|
}
|
|
|
|
let output_file = if ($output | is-empty) {
|
|
$"($input_file).enc"
|
|
} else {
|
|
$output
|
|
}
|
|
|
|
print $"Encrypting ($input_file)..."
|
|
|
|
# Read file and encrypt
|
|
let plaintext = open --raw $input_file
|
|
let ciphertext = $plaintext | kms encrypt --context $context
|
|
|
|
# Save encrypted data
|
|
$ciphertext | save --raw $output_file
|
|
|
|
print $"✓ Encrypted file saved to ($output_file)"
|
|
}
|
|
|
|
# Decrypt file using KMS
|
|
#
|
|
# Examples:
|
|
# kms decrypt-file config.yaml.enc
|
|
# kms decrypt-file secrets.enc --output secrets.json --context "env=prod"
|
|
export def "kms decrypt-file" [
|
|
input_file: path # File to decrypt
|
|
--output: path # Output file (default: remove .enc extension)
|
|
--context: string # Encryption context (must match)
|
|
] {
|
|
if not ($input_file | path exists) {
|
|
error make {
|
|
msg: "Input file not found"
|
|
label: {
|
|
text: $input_file
|
|
span: (metadata $input_file).span
|
|
}
|
|
}
|
|
}
|
|
|
|
let output_file = if ($output | is-empty) {
|
|
if ($input_file | str ends-with ".enc") {
|
|
$input_file | str replace ".enc" ""
|
|
} else {
|
|
$"($input_file).dec"
|
|
}
|
|
} else {
|
|
$output
|
|
}
|
|
|
|
print $"Decrypting ($input_file)..."
|
|
|
|
# Read encrypted data and decrypt
|
|
let ciphertext = open --raw $input_file
|
|
let plaintext = $ciphertext | kms decrypt --context $context
|
|
|
|
# Save decrypted data
|
|
$plaintext | save --raw $output_file
|
|
|
|
print $"✓ Decrypted file saved to ($output_file)"
|
|
}
|
|
|
|
# Envelope encrypt data (AWS KMS only)
|
|
#
|
|
# Examples:
|
|
# "large-data" | kms envelope encrypt
|
|
export def "kms envelope encrypt" [] -> string {
|
|
let input = $in
|
|
let plaintext_b64 = $input | encode base64
|
|
|
|
let body = { plaintext: $plaintext_b64 }
|
|
let response = http post (kms-endpoint "envelope/encrypt") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS envelope encrypt request failed"
|
|
label: { text: $response.stderr }
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS envelope encryption failed"
|
|
label: { text: $result.error }
|
|
}
|
|
}
|
|
|
|
$result.ciphertext
|
|
}
|
|
|
|
# Envelope decrypt data (AWS KMS only)
|
|
#
|
|
# Examples:
|
|
# $envelope_ciphertext | kms envelope decrypt
|
|
export def "kms envelope decrypt" [] -> string {
|
|
let ciphertext = $in
|
|
|
|
let body = { ciphertext: $ciphertext }
|
|
let response = http post (kms-endpoint "envelope/decrypt") $body | complete
|
|
|
|
if $response.exit_code != 0 {
|
|
error make {
|
|
msg: "KMS envelope decrypt request failed"
|
|
label: { text: $response.stderr }
|
|
}
|
|
}
|
|
|
|
let result = $response.stdout | from json
|
|
if ($result | get -i error | is-not-empty) {
|
|
error make {
|
|
msg: "KMS envelope decryption failed"
|
|
label: { text: $result.error }
|
|
}
|
|
}
|
|
|
|
$result.plaintext | decode base64
|
|
}
|