fix(openbao): complete SSH apply script for OpenBao 2.5.x issuers
Generate default CA via ssh/config/ca, split composite KUBECTL for role writes, read pubkey from config/ca, allow warden key_id in roles, prefer production kubeconfig.
This commit is contained in:
2
Makefile
2
Makefile
@@ -1,7 +1,7 @@
|
||||
SHELL := /usr/bin/env bash
|
||||
.DEFAULT_GOAL := help
|
||||
|
||||
KUBECONFIG ?= $(firstword $(wildcard $(HOME)/.kube/config-hosteurope) $(HOME)/.kube/config)
|
||||
KUBECONFIG ?= $(firstword $(wildcard $(HOME)/.kube/config) $(HOME)/.kube/config-hosteurope)
|
||||
KUBECTL_BIN ?= $(firstword $(shell command -v kubectl 2>/dev/null) $(wildcard $(HOME)/.local/bin/kubectl) kubectl)
|
||||
KUBECTL := $(KUBECTL_BIN) --kubeconfig=$(KUBECONFIG)
|
||||
HELM := helm --kubeconfig=$(KUBECONFIG)
|
||||
|
||||
@@ -8,6 +8,7 @@ roles:
|
||||
key_type: ca
|
||||
allowed_users: "*"
|
||||
allow_user_certificates: true
|
||||
allow_user_key_ids: true
|
||||
default_user: adm
|
||||
ttl: 48h
|
||||
max_ttl: 48h
|
||||
@@ -15,6 +16,7 @@ roles:
|
||||
key_type: ca
|
||||
allowed_users: "*"
|
||||
allow_user_certificates: true
|
||||
allow_user_key_ids: true
|
||||
default_user: agt
|
||||
ttl: 24h
|
||||
max_ttl: 24h
|
||||
@@ -22,6 +24,7 @@ roles:
|
||||
key_type: ca
|
||||
allowed_users: "*"
|
||||
allow_user_certificates: true
|
||||
allow_user_key_ids: true
|
||||
default_user: atm
|
||||
ttl: 8h
|
||||
max_ttl: 8h
|
||||
@@ -80,6 +80,11 @@ read_token() {
|
||||
printf '%s\n' "$token"
|
||||
}
|
||||
|
||||
kubectl_exec() {
|
||||
# shellcheck disable=SC2086
|
||||
$KUBECTL "$@"
|
||||
}
|
||||
|
||||
remote_bao() {
|
||||
local token="$1"
|
||||
shift
|
||||
@@ -87,7 +92,7 @@ remote_bao() {
|
||||
printf 'DRY-RUN: bao %s\n' "$*"
|
||||
return 0
|
||||
fi
|
||||
printf '%s\n' "$token" | $KUBECTL exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
||||
printf '%s\n' "$token" | kubectl_exec exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
||||
sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; exec bao "$@"' sh "$@"
|
||||
}
|
||||
|
||||
@@ -103,10 +108,36 @@ write_policy() {
|
||||
printf 'DRY-RUN: bao policy write %s %s\n' "$name" "$file"
|
||||
return 0
|
||||
fi
|
||||
{ printf '%s\n' "$token"; cat "$file"; } | $KUBECTL exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
||||
{ printf '%s\n' "$token"; cat "$file"; } | kubectl_exec exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
||||
sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; bao policy write "$1" -' sh "$name"
|
||||
}
|
||||
|
||||
ensure_default_issuer() {
|
||||
local token="$1"
|
||||
local issuers_out
|
||||
if [ "$DRY_RUN" -eq 1 ]; then
|
||||
printf 'DRY-RUN: bao read %s/config/issuers\n' "$SSH_MOUNT"
|
||||
printf 'DRY-RUN: bao write %s/config/ca generate_signing_key=true key_type=ed25519\n' "$SSH_MOUNT"
|
||||
return 0
|
||||
fi
|
||||
if issuers_out="$(remote_bao "$token" read "${SSH_MOUNT}/config/issuers" 2>&1)"; then
|
||||
printf 'OK: default SSH issuer already configured.\n'
|
||||
return 0
|
||||
fi
|
||||
case "$issuers_out" in
|
||||
*"no default issuer"*)
|
||||
remote_bao "$token" write "${SSH_MOUNT}/config/ca" \
|
||||
generate_signing_key=true key_type=ed25519
|
||||
printf 'OK: generated default SSH CA issuer.\n'
|
||||
;;
|
||||
*)
|
||||
printf '%s\n' "$issuers_out" >&2
|
||||
echo "ERROR: failed to read SSH issuer configuration" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
enable_ssh_engine() {
|
||||
local token="$1"
|
||||
local output status
|
||||
@@ -146,19 +177,21 @@ PY
|
||||
return 0
|
||||
fi
|
||||
python3 - "$token" "$ROLES_SPEC" "$SSH_MOUNT" "$OPENBAO_NAMESPACE" "$pod" "$KUBECTL" <<'PY'
|
||||
import shlex
|
||||
import subprocess
|
||||
import sys
|
||||
|
||||
token, spec_path, mount, namespace, pod, kubectl = sys.argv[1:7]
|
||||
import yaml
|
||||
|
||||
kubectl_parts = shlex.split(kubectl) if kubectl else ["kubectl"]
|
||||
spec = yaml.safe_load(open(spec_path))
|
||||
roles = spec.get("roles") or {}
|
||||
for role_name, params in roles.items():
|
||||
args = [f"{k}={v}" for k, v in params.items()]
|
||||
cmd = ["bao", "write", f"{mount}/roles/{role_name}"] + args
|
||||
proc = subprocess.run(
|
||||
[kubectl, "exec", "-i", "-n", namespace, pod, "--", "sh", "-c",
|
||||
kubectl_parts + ["exec", "-i", "-n", namespace, pod, "--", "sh", "-c",
|
||||
"read -r BAO_TOKEN; export BAO_TOKEN; exec bao \"$@\"", "sh"] + cmd,
|
||||
input=(token + "\n").encode(),
|
||||
capture_output=True,
|
||||
@@ -177,9 +210,12 @@ export_ca_pubkey() {
|
||||
return 0
|
||||
fi
|
||||
local pubkey
|
||||
pubkey="$(remote_bao "$token" read -field=public_key "${SSH_MOUNT}/public_key")"
|
||||
pubkey="$(remote_bao "$token" read -field=public_key "${SSH_MOUNT}/config/ca" 2>/dev/null || true)"
|
||||
if [ -z "$pubkey" ]; then
|
||||
warn "Could not read SSH CA public key from ${SSH_MOUNT}/public_key"
|
||||
pubkey="$(remote_bao "$token" read -field=public_key "${SSH_MOUNT}/public_key" 2>/dev/null || true)"
|
||||
fi
|
||||
if [ -z "$pubkey" ]; then
|
||||
warn "Could not read SSH CA public key from ${SSH_MOUNT}/config/ca or ${SSH_MOUNT}/public_key"
|
||||
return 0
|
||||
fi
|
||||
local fingerprint
|
||||
@@ -197,10 +233,10 @@ export_ca_pubkey() {
|
||||
local tmp
|
||||
tmp="$(mktemp)"
|
||||
printf '%s\n' "$pubkey" >"$tmp"
|
||||
$KUBECTL create secret generic "$K8S_CA_SECRET" \
|
||||
kubectl_exec create secret generic "$K8S_CA_SECRET" \
|
||||
--namespace "$OPENBAO_NAMESPACE" \
|
||||
--from-file=ca_user.pub="$tmp" \
|
||||
--dry-run=client -o yaml | $KUBECTL apply -f -
|
||||
--dry-run=client -o yaml | kubectl_exec apply -f -
|
||||
rm -f "$tmp"
|
||||
printf 'OK: K8s secret %s/%s updated\n' "$OPENBAO_NAMESPACE" "$K8S_CA_SECRET"
|
||||
fi
|
||||
@@ -214,6 +250,7 @@ fi
|
||||
|
||||
remote_bao "$token" status
|
||||
enable_ssh_engine "$token"
|
||||
ensure_default_issuer "$token"
|
||||
apply_roles "$token"
|
||||
write_policy "$token" warden-sign "$POLICY_DIR/warden-sign.hcl"
|
||||
remote_bao "$token" list "${SSH_MOUNT}/roles"
|
||||
|
||||
Reference in New Issue
Block a user