mirror of
https://gitlab.kube.huskypup.net/Scooby/Homelabv4.git
synced 2026-08-21 11:36:50 +00:00
116 lines
4.3 KiB
Bash
Executable File
116 lines
4.3 KiB
Bash
Executable File
#!/usr/bin/env bash
|
||
set -euo pipefail
|
||
|
||
VAULT_NS="${VAULT_NS:-vault}"
|
||
SECRET_NAME="${SECRET_NAME:-vault-init-keys}"
|
||
KV_MOUNT="${KV_MOUNT:-secret}"
|
||
JOB_NS_FOR_ROLE="${JOB_NS_FOR_ROLE:-auth-proxy}"
|
||
JOB_SA_FOR_ROLE="${JOB_SA_FOR_ROLE:-oauth2-bootstrap}"
|
||
POLICY_NAME="${POLICY_NAME:-oauth2-writer}"
|
||
ROLE_NAME="${ROLE_NAME:-eso-writer}"
|
||
|
||
# --- prereqs ---
|
||
command -v kubectl >/dev/null || { echo "kubectl not found"; exit 1; }
|
||
command -v vault >/dev/null || { echo "vault CLI not found"; exit 1; }
|
||
command -v jq >/dev/null || { echo "jq not found"; exit 1; }
|
||
|
||
# --- port-forward Vault locally ---
|
||
echo "==> Port-forwarding Vault service (ctrl-c in another terminal to stop when done)"
|
||
kubectl -n "$VAULT_NS" port-forward svc/vault 8200:8200 >/dev/null 2>&1 &
|
||
pf_pid=$!
|
||
trap 'kill $pf_pid >/dev/null 2>&1 || true' EXIT
|
||
sleep 2
|
||
|
||
export VAULT_ADDR="http://127.0.0.1:8200"
|
||
|
||
# --- check init/seal status ---
|
||
status_json="$(vault status -format=json || true)"
|
||
initialized="$(jq -r '.initialized // empty' <<<"$status_json")"
|
||
sealed="$(jq -r '.sealed // empty' <<<"$status_json")"
|
||
|
||
root_token=""
|
||
unseal_key=""
|
||
|
||
# pull existing secret if present
|
||
if kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" >/dev/null 2>&1; then
|
||
root_token="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d || true)"
|
||
unseal_key="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_UNSEAL_KEY}' | base64 -d || true)"
|
||
fi
|
||
|
||
# initialize if needed
|
||
if [[ "$initialized" != "true" ]]; then
|
||
echo "==> Vault not initialized; initializing..."
|
||
init_json="$(vault operator init -key-shares=1 -key-threshold=1 -format=json)"
|
||
root_token="$(jq -r .root_token <<<"$init_json")"
|
||
unseal_key="$(jq -r '.unseal_keys_b64[0]' <<<"$init_json")"
|
||
sealed="true"
|
||
|
||
echo "==> Storing root token & unseal key in Secret ${VAULT_NS}/${SECRET_NAME}"
|
||
kubectl -n "$VAULT_NS" create secret generic "$SECRET_NAME" \
|
||
--from-literal=VAULT_ROOT_TOKEN="$root_token" \
|
||
--from-literal=VAULT_UNSEAL_KEY="$unseal_key" \
|
||
--dry-run=client -o yaml | kubectl apply -f -
|
||
else
|
||
echo "==> Vault already initialized."
|
||
fi
|
||
|
||
# unseal if needed
|
||
if [[ "$sealed" == "true" ]]; then
|
||
[[ -n "$unseal_key" ]] || { echo "ERROR: sealed and no unseal key available"; exit 1; }
|
||
echo "==> Unsealing..."
|
||
vault operator unseal "$unseal_key" >/dev/null
|
||
fi
|
||
|
||
# login
|
||
if [[ -z "$root_token" ]]; then
|
||
echo "==> Reading root token from Secret..."
|
||
root_token="$(kubectl -n "$VAULT_NS" get secret "$SECRET_NAME" -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)"
|
||
fi
|
||
vault login "$root_token" >/dev/null
|
||
|
||
# ensure KV v2 enabled
|
||
if ! vault secrets list -format=json | jq -e "has(\"${KV_MOUNT}/\")" >/dev/null; then
|
||
echo "==> Enabling KV v2 at ${KV_MOUNT}/"
|
||
vault secrets enable -path="$KV_MOUNT" -version=2 kv >/dev/null
|
||
fi
|
||
|
||
# configure Kubernetes auth with a reviewer token from a local SA
|
||
echo "==> Ensuring reviewer SA + binding"
|
||
kubectl -n "$VAULT_NS" get sa vault-auth >/dev/null 2>&1 || kubectl -n "$VAULT_NS" create sa vault-auth
|
||
kubectl get clusterrolebinding vault-auth-delegator >/dev/null 2>&1 || \
|
||
kubectl create clusterrolebinding vault-auth-delegator \
|
||
--clusterrole=system:auth-delegator \
|
||
--serviceaccount="${VAULT_NS}:vault-auth"
|
||
|
||
reviewer_jwt="$(kubectl -n "$VAULT_NS" create token vault-auth)"
|
||
kube_ca="$(kubectl -n kube-system get configmap kube-root-ca.crt -o jsonpath='{.data.ca\.crt}')"
|
||
kube_host="https://kubernetes.default.svc:443"
|
||
|
||
vault auth enable kubernetes >/dev/null 2>&1 || true
|
||
vault write auth/kubernetes/config \
|
||
token_reviewer_jwt="$reviewer_jwt" \
|
||
kubernetes_host="$kube_host" \
|
||
kubernetes_ca_cert="$kube_ca" >/dev/null
|
||
|
||
# policy + role for ESO/oauth2 job
|
||
vault policy write "$POLICY_NAME" - >/dev/null <<'HCL'
|
||
path "secret/data/*" {
|
||
capabilities = ["create", "update", "read", "list"]
|
||
}
|
||
|
||
path "secret/metadata/*" {
|
||
capabilities = ["create", "update", "read", "list"]
|
||
}
|
||
HCL
|
||
|
||
vault write auth/kubernetes/role/${ROLE_NAME} \
|
||
bound_service_account_names="${JOB_SA_FOR_ROLE},external-secrets" \
|
||
bound_service_account_namespaces="${JOB_NS_FOR_ROLE},external-secrets" \
|
||
policies="${POLICY_NAME}" \
|
||
ttl="24h" >/dev/null
|
||
|
||
echo "==> Done."
|
||
echo "K8s Secret with init creds: ${VAULT_NS}/${SECRET_NAME}"
|
||
echo "IMPORTANT: back these up securely and delete the Secret when you’re comfortable:"
|
||
echo " kubectl -n ${VAULT_NS} delete secret ${SECRET_NAME}"
|