provisioning-catalog/components/lian_build/cluster/setup-buildkit-client.sh

152 lines
5.7 KiB
Bash
Raw Normal View History

#!/bin/bash
# setup-buildkit-client.sh — declarative apply of lian-build's mTLS identity
# at the buildkit_lite CA Issuer.
#
# Source of truth: manifests/buildkit-client-cert.yaml.tmpl (this directory).
# Reads NCL-derived config from environment OR from component.json if present.
#
# Usage:
# setup-buildkit-client.sh # apply (idempotent)
# setup-buildkit-client.sh export <dir> # apply then dump ca/tls.crt/tls.key to <dir>
# setup-buildkit-client.sh revoke # delete the Certificate (and Secret)
#
# Cluster access:
# The buildkit-ca-issuer typically lives on a remote cluster (e.g. libre-daoshi)
# whose API server is not directly reachable from the operator workstation.
# This script supports two modes:
# (1) Local kubectl — set KUBECONFIG and rely on local 'kubectl'
# (2) SSH+remote kubectl — set BUILDKIT_REMOTE_HOST=root@libre-daoshi-0 to run
# 'k0s kubectl' over ssh on the node itself
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
TMPL="$SCRIPT_DIR/manifests/buildkit-client-cert.yaml.tmpl"
# Cluster access wrapper. When BUILDKIT_REMOTE_HOST is set, exec kubectl through
# ssh on the node using 'k0s kubectl' (matches the daoshi single-node k0s layout).
# Both stdin (manifest piping) and stdout (json/jsonpath output) flow transparently.
BUILDKIT_REMOTE_HOST="${BUILDKIT_REMOTE_HOST:-}"
BUILDKIT_REMOTE_KCTL="${BUILDKIT_REMOTE_KCTL:-k0s kubectl}"
_kctl() {
if [ -n "$BUILDKIT_REMOTE_HOST" ]; then
# printf %q quotes each arg safely so jsonpath braces/escapes survive the
# ssh-shell pass-through (otherwise {.data.ca\.crt} gets expanded as glob).
local quoted=""
local arg
for arg in "$@"; do
quoted+=$(printf '%q ' "$arg")
done
ssh -o BatchMode=yes "$BUILDKIT_REMOTE_HOST" "$BUILDKIT_REMOTE_KCTL $quoted"
else
kubectl "$@"
fi
}
# Stdin-piping variant — for `envsubst < tmpl | _kctl_stdin apply -f -`.
_kctl_stdin() {
if [ -n "$BUILDKIT_REMOTE_HOST" ]; then
local quoted=""
local arg
for arg in "$@"; do
quoted+=$(printf '%q ' "$arg")
done
ssh -o BatchMode=yes "$BUILDKIT_REMOTE_HOST" "$BUILDKIT_REMOTE_KCTL $quoted"
else
kubectl "$@"
fi
}
# Read overrides from environment, falling back to component.json (when present)
# for the values configured via NCL.
_load_from_component_json() {
local key="$1"
local default="$2"
local f="$SCRIPT_DIR/component.json"
if [ -f "$f" ] && command -v jq >/dev/null 2>&1; then
local v
v=$(jq -r --arg k "$key" '.buildkit_client[$k] // empty' "$f" 2>/dev/null || true)
if [ -n "$v" ] && [ "$v" != "null" ]; then
printf '%s' "$v"
return
fi
fi
printf '%s' "$default"
}
BUILDKIT_CLIENT_NAME="${BUILDKIT_CLIENT_NAME:-$(_load_from_component_json client_name "lian-build")}"
BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE="${BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE:-$(_load_from_component_json ca_issuer_namespace "build-system")}"
BUILDKIT_CLIENT_CA_ISSUER_NAME="${BUILDKIT_CLIENT_CA_ISSUER_NAME:-$(_load_from_component_json ca_issuer_name "buildkit-ca-issuer")}"
BUILDKIT_CLIENT_DURATION="${BUILDKIT_CLIENT_DURATION:-$(_load_from_component_json duration "2160h")}"
BUILDKIT_CLIENT_RENEW_BEFORE="${BUILDKIT_CLIENT_RENEW_BEFORE:-$(_load_from_component_json renew_before "360h")}"
export BUILDKIT_CLIENT_NAME BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE BUILDKIT_CLIENT_CA_ISSUER_NAME \
BUILDKIT_CLIENT_DURATION BUILDKIT_CLIENT_RENEW_BEFORE
SECRET_NAME="buildkit-client-${BUILDKIT_CLIENT_NAME}-tls"
CERT_NAME="buildkit-client-${BUILDKIT_CLIENT_NAME}"
_apply() {
if [ ! -f "$TMPL" ]; then
echo "ERROR: template missing: $TMPL" >&2
exit 1
fi
if ! _kctl get issuer "$BUILDKIT_CLIENT_CA_ISSUER_NAME" \
--namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" >/dev/null 2>&1; then
echo "ERROR: Issuer $BUILDKIT_CLIENT_CA_ISSUER_NAME not found in $BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" >&2
echo "Hint: enable mtls.cert_manager on buildkit_lite first" >&2
exit 1
fi
envsubst < "$TMPL" | _kctl_stdin apply -f -
echo "Applied Certificate $CERT_NAME in namespace $BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE"
}
_wait_for_secret() {
local timeout=60
local elapsed=0
while [ "$elapsed" -lt "$timeout" ]; do
if _kctl get secret "$SECRET_NAME" \
--namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" >/dev/null 2>&1; then
return 0
fi
sleep 2
elapsed=$((elapsed + 2))
done
echo "ERROR: secret $SECRET_NAME not issued within ${timeout}s" >&2
return 1
}
_export() {
local out_dir="${1:-${HOME}/.lian-build/buildkit-client}"
out_dir="${out_dir/#\~/$HOME}"
mkdir -p "$out_dir"
_wait_for_secret
_kctl get secret "$SECRET_NAME" --namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" \
-o jsonpath='{.data.ca\.crt}' | base64 -d > "$out_dir/ca.crt"
_kctl get secret "$SECRET_NAME" --namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" \
-o jsonpath='{.data.tls\.crt}' | base64 -d > "$out_dir/tls.crt"
_kctl get secret "$SECRET_NAME" --namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" \
-o jsonpath='{.data.tls\.key}' | base64 -d > "$out_dir/tls.key"
chmod 0600 "$out_dir/tls.key"
echo "Exported client cert material to $out_dir"
}
_revoke() {
_kctl delete certificate "$CERT_NAME" \
--namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" --ignore-not-found
_kctl delete secret "$SECRET_NAME" \
--namespace "$BUILDKIT_CLIENT_CA_ISSUER_NAMESPACE" --ignore-not-found
echo "Revoked $CERT_NAME"
}
case "${1:-apply}" in
apply) _apply ;;
export) _apply; _export "${2:-}" ;;
export-only) _export "${2:-}" ;;
revoke) _revoke ;;
*)
echo "Usage: $(basename "$0") [apply|export <dir>|export-only <dir>|revoke]" >&2
exit 1
;;
esac