provisioning/taskservs/etcd/default/prepare

464 lines
20 KiB
Plaintext
Raw Permalink Normal View History

#!/usr/bin/env nu
# Info: Prepare for etcd installation
# Author: JesusPerezLorenzo
# Release: 1.0.2
# Date: 26-02-2024
use lib_provisioning/cmd/env.nu *
use lib_provisioning/cmd/lib.nu *
use lib_provisioning/utils/ui.nu *
use lib_provisioning/utils/files.nu find_file
use lib_provisioning/sops *
def get_domain_name [
defs: record
source: string
] {
match $source {
"$defaults" => $defs.server.main_domain,
_ => $source
}
}
def openssl_ecc_cert [
defs: record
src: string
run_root: string
cluster_name: string
hostname: string
signature: string
long_sign: int
] {
let etcd_cn = ( $defs.taskserv.cn | default "")
let ca_signature = ($defs.taskserv.ca_sign | default "")
let ssl_curve = ($defs.taskserv.ssl_curve | default "")
let sign_sha = ($defs.taskserv.sign_sha | default "")
let sign_cipher = ($defs.taskserv.cipher | default "")
let sign_days = ($defs.taskserv.sign_days | default "")
let on_error = { |msg: string|
print $"๐Ÿ›‘ (_ansi red)Error(_ansi reset) (_ansi yellow)ECC(_ansi reset): ($msg)"
rm -f ($src | path join "pass")
}
^openssl ecparam -genkey -name $ssl_curve -out ($src | path join $"($cluster_name).key") | ignore
let res = (^openssl req -new $"-SHA($sign_sha)" -key ($src | path join $"($cluster_name).key") -nodes
-out ($src | path join $"($cluster_name).csr")
-subj $"/CN=($etcd_cn)" -config ($src | path join "openssl.conf") -extensions ssl_peer
| complete )
if $res.exit_code != 0 {
do $on_error $"openssl csr error ($res.stdout)"
exit 1
}
let res = (^openssl x509 -req $"-SHA($sign_sha)" -in ($src | path join $"($cluster_name).csr")
-CA ($src | path join "ca.crt") -CAkey ($src | path join "ca.key")
-CAcreateserial -out ($src | path join $"($cluster_name).crt") -days $sign_days
-extensions ssl_peer -extfile ($src | path join "openssl.conf")
| complete )
if $res.exit_code != 0 {
do $on_error $"openssl x509 req error ($res.exit_code)($res.stdout)"
exit 1
}
^openssl ecparam -genkey -name $ssl_curve -out ($src | path join $"($hostname).key") | ignore
let res = (^openssl req -noenc -new $"-SHA($sign_sha)" -key ($src | path join $"($hostname).key")
-nodes -out ($src | path join $"($hostname).csr")
-subj $"/CN=($etcd_cn)" -config ($src | path join "openssl.conf") -extensions ssl_peer | complete )
if res.exit_code != 0 and not ($src | path join $"($hostname).csr" | path exists) {
do $on_error $"๐Ÿ›‘ openssl req csr error ($res.exit_code) ($res.stdout)"
exit 1
}
let res = (^openssl x509 -req -noenc $"-SHA($sign_sha)" -in ($src | path join $"($hostname).csr")
-CA ($src | path join "ca.crt") -CAkey ($src | path join "ca.key")
-CAcreateserial -out ($src | path join $"($hostname).crt") -days $sign_days
-extensions ssl_peer -extfile ($src | path join "openssl.conf")
| complete )
if res.exit_code != 0 and not ($src | path join $"($hostname).crt" | path exists) {
do $on_error $"๐Ÿ›‘ openssl x509 req error ($res.stdout)"
exit 1
}
}
def openssl_rsa_cert [
defs: record
src: string
run_root: string
cluster_name: string
hostname: string
signature: string
long_sign: int
] {
let etcd_cn = ( $defs.taskserv.cn | default "")
let sign_cipher = ($defs.taskserv.cipher | default "")
let sign_days = ($defs.taskserv.sign_days | default "")
let on_error = { |msg: string|
print $"๐Ÿ›‘ (_ansi red)Error(_ansi reset) (_ansi yellow)RSA(_ansi reset): ($msg)"
rm -f ($src | path join "pass")
}
if not ($src | path join "pass" | path exists) { $defs.taskserv.sign_pass | save -f ($src | path join "pass") }
^openssl genrsa -passout $"file:($src | path join "pass")" $sign_cipher -out ($src | path join $"($cluster_name)_p.key") $long_sign
^openssl rsa -in "$src/$cluster_name"_p.key -out ($src | path join $"($cluster_name).key")
if not ($src | path join "openssl.conf" | path exists) {
do $on_error $"openssl.con not found in ($src |path join "openssl.conf")"
exit 1
}
let res = (^openssl req -newkey rsa:($long_sign) -passout $"file:($src | path join "pass")" -key ($src | path join $"($cluster_name).key")
-out ($src | path join $"($cluster_name).csr")
-subj $"/CN=($etcd_cn)" -config ($src | path join "openssl.conf") -extensions ssl_client
| complete)
if $res.exit_code != 0 {
do $on_error $"openssl req error ($res.exit_code) ($res.stdout)"
exit 1
}
print $"openssl gemrsa error ($res.exit_code) ($res.stdout)"
(^openssl x509 -req -in ($src | path join $"($cluster_name).csr") -CA ($src | path join "ca.crt")
-CAkey ($src | path join "ca.key") -out ($src | path join $"($cluster_name).crt") -days $sign_days
-extensions ssl_client -extfile ($src | path join "openssl.conf")
)
let res = (^openssl genrsa -passout $"file:($src | path join "pass")" $sign_cipher
-out ($src | path join $"($hostname)_p.key") $long_sign
| complete)
if $res.exit_code != 0 {
do $on_error $"openssl genrsa error ($res.exit_code) ($res.stdout)"
exit 1
}
^openssl rsa -in ($src | path join $"($hostname)_p.key") -out ($src | path join $"($hostname).key")
if not ($src | path join "openssl.conf" | path exists) {
print $"openssl.con not found in ($src | path join "openssl.conf") "
rm -f ($src | path join "pass")
exit 1
}
let res = (^openssl req -newkey rsa:$long_sign -passout $"file:($src | path join "pass")"
-key ($src | path join $"($hostname).key") -out ($src | path join $"($hostname).csr")
-subj $"/CN=($etcd_cn)" -config ($src | path join "openssl.conf") -extensions ssl_peer
| complete)
if $res.exit_code == 0 {
do $on_error $"openssl req key error ($res.exit_code) ($res.stdout)"
exit 1
}
let res = (^openssl x509 -req -in ($src | path join $"($hostname).csr") -CA ($src | path join "ca.crt") -CAkey ($src | path join "ca.key")
-out ($src | path join $"($hostname).crt") -days $sign_days
-extensions ssl_peer -extfile ($src | path join "openssl.conf")
| complete)
if $res.exit_code != 0 {
do $on_error $"openssl x509 req cst error ($res.exit_code) ($res.stdout)"
exit 1
}
rm -f ($src | path join "pass")
}
def openssl_mode [
defs: record
src: string
run_root: string
cluster_name: string
hostname: string
signature: string
long_sign: int
] {
let etcd_cn = ( $defs.taskserv.cn | default "")
let ca_signature = ($defs.taskserv.ca_sign | default "")
let ssl_curve = ($defs.taskserv.ssl_curve | default "")
let sign_sha = ($defs.taskserv.sign_sha | default "")
let sign_cipher = ($defs.taskserv.cipher | default "")
let sign_days = ($defs.taskserv.sign_days | default "")
let ca_sign_days = ($defs.taskserv.ca_sign_days | default "")
mut openssl = (^bash -c "type -P openssl")
if $openssl == "" {
^sudo apt install openssl -y
$openssl = (^bash -c "type -P openssl")
}
if openssl == "" { print $"openssl not installed " ; exit 1 }
if not ($src | path join "openssl.conf" | path exists) and ($run_root | path join "openssl.conf.tpl" | path exists) {
cp ($run_root | path join "openssl.conf.tpl") ($src | path join "openssl.conf")
if ($src | path join "openssl_conf_alt_names" | path exists ) {
open ($src | path join "openssl_conf_alt_names") -r | save -a ($src | path join "openssl.conf")
}
}
print $"CA signature: ($ca_signature)"
if not ($src | path join "ca.key" | path exists) {
sops_cmd "decrypt" ($src | path join "ca.key") ($src | path join "ca.key") --error_exit
#sudo mv "$src/ca.key.$$" "$src/ca.key"
}
if $ca_signature == "ECC" {
if not ($src | path join "ca.key" | path exists) and not ($src| path join "ca.crt" | path exists) {
^openssl ecparam -genkey -name $ssl_curve -out ($src | path join "ca.key")
let res = (^openssl req -x509 -extensions v3_ca -config ($src | path join "openssl.conf") -new $"-SHA($sign_sha)"
-nodes -key ($src | path join "ca.key") -days $ca_sign_days
-out ($src | path join "ca.crt") -subj $"/CN=($etcd_cn)"
| complete )
if $res.exit_code != 0 {
print $"๐Ÿ›‘ openssl key ($ca_signature) error ($res.stdout)"
exit 1
}
}
} else if not ($src | path join "ca.key" | path exists) and not ($src |path join "ca.crt" | path exists) {
$defs.taskserv.sign_pass | save -f ($src | path join "pass")
^openssl genrsa -passout $"file:($src | path join "pass")" $sign_cipher -out ($src | path join "ca_p.key") $long_sign
^openssl rsa -in ($src |path join "ca_p.key") -out ($src | path join "ca.key")
let res = (^openssl req -x509 -extensions v3_ca -config ($src | path join "openssl.conf") -newkey rsa:($long_sign)
-nodes -key ($src | path join "ca.key") -days $sign_days -out ($src | path join "ca.crt") -subj $"CN=($etcd_cn)"
| complete )
if $res.exit_code != 0 {
print $"๐Ÿ›‘ openssl ca ($ca_signature) error ($res.stdout)"
exit 1
}
}
print $"Certs signature: ($signature)"
if not ($src | path join $"($cluster_name).crt" | path exists) or not ($src | path join $"($cluster_name).key" | path exists) {
match $signature {
"ECC" => {
(openssl_ecc_cert $defs $src $run_root $cluster_name $hostname $signature $long_sign)
},
_ => {
(openssl_rsa_cert $defs $src $run_root $cluster_name $hostname $signature $long_sign)
},
}
}
copy_certs $defs $src $run_root $cluster_name $signature
}
def cfssl_mode [
defs: record
src: string
run_root: string
cluster_name: string
hostname: string
signature: string
long_sign: int
] {
let domain_name = (get_domain_name $defs ($defs.taskserv.domain_name | default ""))
let source_name = $"($cluster_name | default "").($domain_name)"
let ORG = $env.PWD
let etcd_c = ($defs.taskserv.c | default "")
mut CFSSL = (^bash -c "type -P cfssl")
if "$CFSSL" == "" {
let cfssl_install_bin = ($env.PROVISIONING | path join "core"| path join "bin" | path join "cfssl-install.sh")
if ($cfssl_install_bin | path exists) { ^$cfssl_install_bin }
$CFSSL = (^bash -c "type -P cfssl")
}
if "$CFSSL" == "" { print $"cfssl not installed " ; exit 1 }
let CFSSLJSON = (^bash -c "type -P cfssljson")
let csr_json_file = ($src | path join "csr.json")
if not ($csr_json_file) {
"{" | tee { save -f $csr_json_file } | ignore
$"\"hosts\": [" | tee { save -a $csr_json_file } | ignore
for server in $defs.defs.servers {
let ip = ($server.network_private_ip | default "")
if $ip == "" { continue }
$"\"($server.hostname)\",\"($server.hostname).($domain_name)\",\"($ip)\"," | tee { save -a $csr_json_file } | ignore
}
if $source_name != "" and $source_name != $"($cluster_name).($domain_name)" {
print $"\"($source_name)\","| tee { save -a ($src | path join "csr.json") } | ignore
}
$"\"${domain_name}\", \"$cluster_name\"],\"key\": {" | tee { save -a $csr_json_file } | ignore
if $signature == "ECC" {
$"\"algo\": \"ecdsa\",\"size\": ($long_sign) " | tee { save -a $csr_json_file } | ignore
} else {
$"\"algo\": \"rsa\",\"size\": ($long_sign) " | tee { save -a $csr_json_file } | ignore
}
$"}, \"names\": [{ \"C\":\"($etcd_c)\", \"CN\": \"($domain_name)\" }]" | tee { save -a $csr_json_file } | ignore
$"}" | tee { save -a $csr_json_file } | ignore
#sudo echo '{"CN":"CA","key":{"algo":"rsa","size":2048}}' | cfssl gencert -initca - | cfssljson -bare ca -
#$sudo echo '{"signing":{"default":{"expiry":"43800h","usages":["signing","key encipherment","server auth","client auth"]}}}' \&ca-config.json
}
if not ( $"($cluster_name).key" | path exists) {
cd $src
if ((^($CFSSL) genkey -initca csr.json | ^($CFSSLJSON) -bare ca) | complete).exit_code == 0 {
if ((^($CFSSL) gencert -ca ca.pem -ca-key ca-key.pem csr.json
| ^($CFSSLJSON) -bare $cluster_name) | complete).exit_code == 0 {
mv ca.pem ca.crt
sudo mv ca-key.pem ca.key
mv $"($cluster_name).pem" $"($cluster_name).crt"
sudo mv $"($cluster_name)-key.pem" $"($cluster_name).key"
for server in $defs.defs.servers {
cp $"($cluster_name).crt" $"($server.hostname).crt"
sudo cp $"($cluster_name).key" $"($server.hostname).key"
}
cd $ORG
copy_certs $defs $src $run_root $cluster_name $signature
}
}
cd $ORG
} else {
copy_certs $defs $src $run_root $cluster_name $signature
}
}
export def make_certs [
defs: record
src: string
run_root: string
cluster_name: string
signature: string
ssl_mode: string
settings_root: string
long_sign: int
] {
if $signature == "" { print $"No signatures found" ; return 1 }
if not ($src | path exists) { print $"Directory ($src) not found" ; return 1 }
let hostname = ($defs.server.hostname | default "")
if $hostname == "" { print $"hostname not found in ($env.PROVISIONING_VARS)" ; exit 1 }
let servers_list = ($defs.defs.servers | select "hostname" | flatten | get -i "hostname")
match $ssl_mode {
"open" | "openssl" => {
openssl_mode $defs $src $run_root $cluster_name $hostname $signature $long_sign
},
"cf" | "cfssl" => {
cfssl_mode $defs $src $run_root $cluster_name $hostname $signature $long_sign
},
}
}
export def etcd_conf [
defs: record
src: string
run_root: string
cluster_name: string
signature: string
ssl_mode: string
] {
if not ($src | path exists) { mkdir $src }
let domain_name = (get_domain_name $defs ($defs.taskserv.domain_name | default ""))
let etcd_cn = ( $defs.taskserv.cn | default "")
let source_name = $"($cluster_name | default "").($domain_name)"
if $domain_name == "" or $domain_name == "" { print $"No names \( cluster_name and domain \) are defined" ; return 1 }
if $env.PROVISIONING_DEBUG { print $"nodeport: ($defs.taskserv.peer_port) \nprotocol: ($defs.taskserv.etcd_protocol) \n" }
let conf_alt_names_path = ($src | path join "openssl_conf_alt_names")
let setup_tpl_path = ($src | path join "setup.tpl")
mut n = 0
match $ssl_mode {
"open"| "openssl" => {
rm -f $conf_alt_names_path $setup_tpl_path
if $defs.taskserv.use_localhost {
if $env.PROVISIONING_DEBUG { print $"localhost: 127.0.0.1" }
match $ssl_mode {
"open"| "openssl" => {
$n += 1
$"DNS.$n = localhost" | tee { save -a $conf_alt_names_path } | ignore
$"IP.$n = 127.0.0.1" | tee { save -a $conf_alt_names_path } | ignore
}
}
}
$n += 1
$"DNS.($n) = ($cluster_name)" | tee { save -a $conf_alt_names_path } | ignore
$n += 1
$"DNS.($n) = ($etcd_cn)" | tee { save -a $conf_alt_names_path } | ignore
}
}
mut cluster_list = ""
for server in $defs.defs.servers {
let ip = ($server.network_private_ip | default "")
if $ip == "" { continue }
if $env.PROVISIONING_DEBUG { print $"($server.hostname): ($ip)" }
if $cluster_list != "" { $cluster_list += "," }
$cluster_list += $"($server.hostname)=($defs.taskserv.etcd_protocol)://($ip):($defs.taskserv.peer_port)"
$n += 1
match $ssl_mode {
"open"| "openssl" => {
$"export Node($n)_IP=($ip)" | tee { save -a $setup_tpl_path } | ignore
$"DNS.($n) = ($server.hostname)" | tee { save -a $conf_alt_names_path } | ignore
$"IP.($n) = ($ip)" | tee { save -a $conf_alt_names_path } | ignore
$n += 1
$"DNS.($n) = ($server.hostname).($domain_name)" | tee { save -a $conf_alt_names_path } | ignore
}
}
}
match $ssl_mode {
"open"| "openssl" => {
if $source_name != "" and $source_name != $"($cluster_name).($domain_name)" {
$n += 1
print $"DNS.($n) = ($source_name)" | tee { save -a $conf_alt_names_path } | ignore
}
}
}
if $env.PROVISIONING_DEBUG { print $"\ncluster_list: ($cluster_list)" }
return 0
}
export def copy_certs [
defs: record
src: string
run_root: string
cluster_name: string
signature: string
] {
print $"Copy certs to ($run_root) ..."
let hostname = $defs.server.hostname
if $hostname == "" { print $"hostname not found for ($env.PROVISIONING_VARS)" ; exit 1 }
if (glob ($src | path join "*.csr") | length) > 0 {
rm -f ...(glob ($src | path join "*.csr"))
}
if not ($run_root | path join "certs" | path exists) { mkdir ($run_root | path join "certs") }
for name in [ ca $hostname $cluster_name] {
if not ($src | path join $"($name).key" | path exists) { continue }
if (sops_cmd "is_sops" ($src | path join $"($name).key")) {
let content = (sops_cmd "decrypt" ($src | path join $"($name).key") --error_exit)
if $content != "" { $content | save -f ($run_root | path join "certs" | path join $"($name).key") }
} else {
cp ($src | path join $"($name).key") ($run_root | path join "certs" | path join $"($name).key" )
sops_cmd "encrypt" ($src | path join $"($name).key") --error_exit | save -f ($src | path join $"($name).key")
}
chmod 400 ($src | path join $"($name).key") ($run_root | path join "certs" | path join $"($name).key")
if ($src | path join $"($name).crt" | path exists) {
cp ($src | path join $"($name).crt") ($run_root | path join "certs")
}
}
if ($src | path join $"($cluster_name).crt" | path exists) {
#if not ($run_root | path join "certs" | path join $"($cluster_name).crt" | path exists) {
# cp ($src | path join $"($cluster_name).crt") ($run_root | path join "certs")
#}
if not ($run_root | path join "certs" | path join $"($hostname).crt" | path exists) {
cp ($src | path join $"($cluster_name).crt") ($run_root | path join "certs" | path join $"($hostname).crt")
}
if not ($run_root | path join "certs" | path join $"($hostname).key" | path exists) {
cp ($run_root | path join "certs" | path join $"($cluster_name).key") ($run_root | path join "certs" | path join $"($hostname).key")
}
print $"Certificate for ($hostname) signed ($signature) in ($src) copy to deployment"
}
if (glob ($run_root | path join "openssl.*") | length) > 0 {
rm -r ...(glob ($run_root | path join "openssl.*"))
}
}
def main [] {
print $"(_ansi green_bold)ETCD(_ansi reset) with ($env.PROVISIONING_VARS?) "
let run_root = $env.PROVISIONING_WK_ENV_PATH
let defs = load_defs
let src = ($env.PROVISIONING_SETTINGS_SRC_PATH | path join "resources" | path join $defs.taskserv.prov_path)
if not ($env.PROVISIONING_SETTINGS_SRC_PATH | path join "resources" | path exists) {
^mkdir -p ($env.PROVISIONING_SETTINGS_SRC_PATH | path join "resources")
}
let provision_path = ($defs.taskserv.prov_path | default "" | str replace "~" $env.HOME)
if $provision_path == "" {
print $"๐Ÿ›‘ prov_path not found taskserv definition"
exit 1
}
let cluster_name = $defs.taskserv.cluster_name | default ""
if $cluster_name == "" {
print $"๐Ÿ›‘ cluster_name not foundi taskserv definition"
exit 1
}
let domain_name = (get_domain_name $defs ($defs.taskserv.domain_name | default ""))
if $domain_name == "" {
print $"๐Ÿ›‘ domain_name nor found in settings"
exit 1
}
let source_name = $"($cluster_name | default "").($domain_name)"
let settings_root = ($env.PROVISIONING_SETTINGS_SRC_PATH | default "" )
let signature = ($defs.taskserv.ssl_sign | default "")
let ssl_mode = ($defs.taskserv.ssl_mode | default "")
let long_sign = ($defs.taskserv.long_sign | default 0)
if ($env.PROVISIONING_SETTINGS_SRC_PATH | path join $provision_path | path join $"($cluster_name).crt" | path exists) {
copy_certs $defs $src $run_root $cluster_name $signature
} else {
if not ($env.PROVISIONING_SETTINGS_SRC_PATH | path join $provision_path | path exists) {
^mkdir -p ($env.PROVISIONING_SETTINGS_SRC_PATH | path join $provision_path)
}
etcd_conf $defs $src $run_root $cluster_name $signature $ssl_mode
make_certs $defs $src $run_root $cluster_name $signature $ssl_mode $settings_root $long_sign
}
}