provisioning-core/nulib/platform/sops/lib.nu

222 lines
8.7 KiB
Text
Raw Permalink Normal View History

use std
use primitives/io/interface.nu [_ansi _print]
use primitives/infra/init.nu [get-provisioning-use-sops get-workspace-path get-provisioning-infra-path]
use primitives/io/error.nu [throw-error]
def find_file [start_path: string, match_path: string, only_first: bool] {
mut found_path = ""
mut search_path = $start_path
let home_root = ($env.HOME | path dirname)
while $found_path == "" and $search_path != "/" and $search_path != $home_root {
if $search_path == "" { break }
let res = if $only_first {
(^find $search_path -type f -name $match_path -print -quit | complete)
} else {
(^find $search_path -type f -name $match_path err> (if $nu.os-info.name == "windows" { "NUL" } else { "/dev/null" }) | complete)
}
if $res.exit_code == 0 { $found_path = ($res.stdout | str trim) }
$search_path = ($search_path | path dirname)
}
$found_path
}
export def run_cmd_sops [task: string, cmd: string, source_path: string, error_exit: bool] {
let str_cmd = $"-($cmd)"
let use_sops_value = (get-provisioning-use-sops | into string)
let res = if ($use_sops_value | str contains "age") {
if $env.SOPS_AGE_RECIPIENTS? != null {
(with-env {SOPS_AGE_KEY_FILE: (get-sops-age-key-file)} {
do {^sops $str_cmd --config (find-sops-key) --age $env.SOPS_AGE_RECIPIENTS $source_path} | complete
})
} else {
if $error_exit {
(throw-error $"🛑 Sops with age error" $"(_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green)($source_path)(_ansi reset)"
"on_sops decrypt" --span (metadata $task).span)
} else {
_print $"🛑 Sops with age error (_ansi red)no AGE_RECIPIENTS(_ansi reset) for (_ansi green_bold)($source_path)(_ansi reset)"
return ""
}
}
} else {
(^sops $str_cmd --config (find-sops-key) $source_path | complete)
}
if $res.exit_code != 0 {
if $error_exit {
(throw-error $"🛑 Sops error" $"(_ansi red)($source_path)(_ansi reset) ($res.stdout)"
$"on_sops ($task)" --span (metadata $res).span)
} else {
_print $"🛑 Sops error (_ansi red)($source_path)(_ansi reset) ($res.exit_code)"
return ""
}
}
$res.stdout
}
export def on_sops [
task: string
source_path: string
output_path?: string
...args
--check (-c)
--error_exit
--quiet
] {
match $task {
"sed" => {
if (is_sops_file $source_path) {
^sops $source_path
} else {
(throw-error $"🛑 File (_ansi green_italic)($source_path)(_ansi reset) exists"
$"No (_ansi yellow_bold)sops(_ansi reset) content found "
"on_sops sed"
--span (metadata $source_path).span)
}
}
"is_sops" | "i" => { return (is_sops_file $source_path) }
"encrypt" | "encode" | "e" => {
if not ($source_path | path exists) {
if not $quiet { _print $"🛑 No file ($source_path) found to decrypt with sops " }
return ""
}
if (is_sops_file $source_path) {
if not $quiet { _print $"🛑 File ($source_path) alredy with sops " }
return (open -r $source_path)
}
let result = (run_cmd_sops "encrypt" "e" $source_path $error_exit)
if ($output_path | is-not-empty) {
$result | save -f $output_path
if not $quiet { _print $"Result saved in ($output_path) " }
}
return $result
}
"generate" | "gen" | "g" => { generate_sops_file $source_path $output_path $quiet }
"decrypt" | "decode" | "d" => {
if not ($source_path | path exists) {
if not $quiet { _print $"🛑 No file ($source_path) found to decrypt with sops " }
return ""
}
if not (is_sops_file $source_path) {
if not $quiet { _print $"🛑 File ($source_path) does not have sops info " }
return (open -r $source_path)
}
let result = (run_cmd_sops "decrypt" "d" $source_path $error_exit)
if ($output_path | is-not-empty) {
$result | save -f $output_path
if not $quiet { _print $"Result saved in ($output_path) " }
}
return $result
}
_ => {
(throw-error $"🛑 Option " $"(_ansi red)($task)(_ansi reset) undefined")
return ""
}
}
}
export def generate_sops_file [source_path: string, target_path: string, quiet: bool] {
let result = (on_sops "encrypt" $source_path --error_exit)
if result == "" {
_print $"🛑 File ($source_path) not sops generated"
return false
}
$result | save -f $target_path
if not $quiet { _print $"($source_path) generated for 'sops' " }
true
}
export def generate_sops_settings [mode: string, target: string, file: string] {
_print ""
}
export def edit_sop [items: list<string>] {
_print ""
}
export def is_sops_file [target: string] {
if not ($target | path exists) {
(throw-error $"🛑 File (_ansi green_italic)($target)(_ansi reset)"
$"(_ansi red_bold)Not found(_ansi reset)"
$"is_sops_file ($target)"
--span (metadata $target).span)
}
let file_sops = (open $target --raw)
if ($file_sops | find "sops" | length) == 0 { return false }
if ($file_sops | find "ENC[" | length) == 0 { return false }
true
}
export def decode_sops_file [source: string, target: string, quiet: bool] {
if $quiet {
on_sops "decrypt" $source --quiet
} else {
on_sops "decrypt" $source
} | save --force $target
}
export def get_def_sops [current_path: string] {
let use_sops = (get-provisioning-use-sops)
if ($use_sops | is-empty) { return "" }
let start_path = if ($current_path | path exists) {
$current_path
} else {
$"((get-workspace-path))/($current_path)"
}
let sops_file = "sops.yaml"
mut provisioning_sops = (find_file $start_path $sops_file true)
if $provisioning_sops == "" and ($env.HOME | path join ".config" | path join "provisioning" | path join $sops_file | path exists) {
$provisioning_sops = ($env.HOME | path join ".config" | path join "provisioning" | path join $sops_file)
}
if $provisioning_sops == "" and ($env.HOME | path join ".provisioning" | path join $sops_file | path exists) {
$provisioning_sops = ($env.HOME | path join ".provisioning" | path join $sops_file)
}
if $provisioning_sops == "" {
_print $"❗Error no (_ansi red_bold)($sops_file)(_ansi reset) file for secure operations found "
exit 1
}
($provisioning_sops | default "")
}
export def get_def_age [current_path: string] {
let use_sops = (get-provisioning-use-sops | into string)
if not ($use_sops | str contains "age") { return "" }
let kage_file = ".kage"
let start_path = if ($current_path | path exists) {
$current_path
} else {
((get-provisioning-infra-path) | path join $current_path)
}
let provisioning_kage = (find_file $start_path $kage_file true)
let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".config" | path join "provisioning " | path join $kage_file | path exists) {
($env.HOME | path join ".config" | path join "provisioning " | path join $kage_file)
} else {
$provisioning_kage
}
let provisioning_kage = if $provisioning_kage == "" and ($env.HOME | path join ".provisioning " | path join $kage_file | path exists) {
($env.HOME | path join ".provisioning " | path join $kage_file)
} else {
$provisioning_kage
}
let workspace_path = (get-workspace-path)
let provisioning_kage = if $provisioning_kage == "" and ($workspace_path | is-not-empty) and (($workspace_path | path join ".provisioning" | path join $kage_file) | path exists) {
($workspace_path | path join ".provisioning" | path join $kage_file)
} else {
$provisioning_kage
}
if $provisioning_kage == "" {
_print $"❗Error no (_ansi red_bold)($kage_file)(_ansi reset) file for secure operations found "
exit 1
}
($provisioning_kage | default "")
}
export def find-sops-key [] {
let from_env = ($env.PROVISIONING_SOPS? | default "")
if ($from_env | is-not-empty) { return $from_env }
let search_path = ($env.CURRENT_KLOUD_PATH? | default ($env.PROVISIONING_WORKSPACE_PATH? | default $env.PWD))
get_def_sops $search_path
}
export def get-sops-age-key-file [] {
$env.SOPS_AGE_KEY_FILE? | default ($env.PROVISIONING_KAGE? | default "")
}