247 lines
5.9 KiB
Bash
Executable File
247 lines
5.9 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
|
|
OPENBAO_NAMESPACE="${OPENBAO_NAMESPACE:-openbao}"
|
|
OPENBAO_RELEASE="${OPENBAO_RELEASE:-openbao}"
|
|
KUBECTL="${KUBECTL:-kubectl}"
|
|
TOKEN_FILE="${OPENBAO_TOKEN_FILE:-}"
|
|
DRY_RUN=0
|
|
USE_TOKEN_HELPER=0
|
|
|
|
usage() {
|
|
cat <<'USAGE'
|
|
Usage: scripts/openbao-verify-authenticated.sh [--dry-run] [--use-token-helper]
|
|
|
|
Runs authenticated, non-mutating OpenBao readiness checks:
|
|
- audit list includes file/
|
|
- secrets list includes platform/
|
|
- auth list includes kubernetes/ and keycape/
|
|
- audit log exists and is non-empty
|
|
|
|
The token is read from OPENBAO_TOKEN_FILE or an interactive hidden prompt. The
|
|
script does not print, store, or pass the token on the command line.
|
|
|
|
With --use-token-helper, no token is read by this script. The checks rely on
|
|
the OpenBao token helper inside the pod, if a previous attended login stored a
|
|
still-valid token there.
|
|
USAGE
|
|
}
|
|
|
|
while [ "$#" -gt 0 ]; do
|
|
case "$1" in
|
|
--dry-run)
|
|
DRY_RUN=1
|
|
shift
|
|
;;
|
|
--use-token-helper)
|
|
USE_TOKEN_HELPER=1
|
|
shift
|
|
;;
|
|
-h|--help)
|
|
usage
|
|
exit 0
|
|
;;
|
|
*)
|
|
echo "ERROR: unknown argument: $1" >&2
|
|
usage >&2
|
|
exit 2
|
|
;;
|
|
esac
|
|
done
|
|
|
|
pod="${OPENBAO_RELEASE}-0"
|
|
FAILURES=0
|
|
WARNINGS=0
|
|
|
|
ok() { printf '[OK] %s\n' "$*"; }
|
|
warn() { WARNINGS=$((WARNINGS + 1)); printf '[WARN] %s\n' "$*" >&2; }
|
|
fail() { FAILURES=$((FAILURES + 1)); printf '[FAIL] %s\n' "$*" >&2; }
|
|
step() { printf '\n==> %s\n' "$*"; }
|
|
|
|
check_cmd() {
|
|
if ! command -v "${KUBECTL%% *}" >/dev/null 2>&1; then
|
|
echo "ERROR: kubectl command not found: $KUBECTL" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
run() {
|
|
# shellcheck disable=SC2086
|
|
$KUBECTL "$@"
|
|
}
|
|
|
|
read_token() {
|
|
if [ "$USE_TOKEN_HELPER" -eq 1 ]; then
|
|
printf '__USE_TOKEN_HELPER__\n'
|
|
return
|
|
fi
|
|
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
printf 'dry-run-token\n'
|
|
return
|
|
fi
|
|
|
|
if [ -n "$TOKEN_FILE" ]; then
|
|
if [ ! -f "$TOKEN_FILE" ]; then
|
|
echo "ERROR: OPENBAO_TOKEN_FILE does not exist: $TOKEN_FILE" >&2
|
|
exit 1
|
|
fi
|
|
head -n 1 "$TOKEN_FILE"
|
|
return
|
|
fi
|
|
|
|
local token
|
|
read -r -s -p "OpenBao token: " token
|
|
printf '\n' >&2
|
|
printf '%s\n' "$token"
|
|
}
|
|
|
|
remote_bao() {
|
|
local token="$1"
|
|
shift
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
case "$*" in
|
|
status)
|
|
cat <<'STATUS'
|
|
Key Value
|
|
--- -----
|
|
Sealed false
|
|
STATUS
|
|
;;
|
|
"audit list")
|
|
cat <<'AUDIT'
|
|
Path Type Description
|
|
---- ---- -----------
|
|
file/ file Default file audit device on the OpenBao audit PVC.
|
|
AUDIT
|
|
;;
|
|
"secrets list")
|
|
cat <<'SECRETS'
|
|
Path Type
|
|
---- ----
|
|
cubbyhole/ cubbyhole
|
|
identity/ identity
|
|
platform/ kv
|
|
SECRETS
|
|
;;
|
|
"auth list")
|
|
cat <<'AUTH'
|
|
Path Type
|
|
---- ----
|
|
keycape/ oidc
|
|
kubernetes/ kubernetes
|
|
token/ token
|
|
AUTH
|
|
;;
|
|
*)
|
|
printf 'DRY-RUN: bao %s\n' "$*"
|
|
;;
|
|
esac
|
|
return 0
|
|
fi
|
|
if [ "$USE_TOKEN_HELPER" -eq 1 ]; then
|
|
run exec -n "$OPENBAO_NAMESPACE" "$pod" -- bao "$@"
|
|
return $?
|
|
fi
|
|
printf '%s\n' "$token" | run exec -i -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
|
sh -c 'read -r BAO_TOKEN; export BAO_TOKEN; exec bao "$@"' sh "$@"
|
|
}
|
|
|
|
audit_log_bytes() {
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
printf '1\n'
|
|
return 0
|
|
fi
|
|
run exec -n "$OPENBAO_NAMESPACE" "$pod" -- \
|
|
sh -c 'test -s /openbao/audit/openbao-audit.log && wc -c < /openbao/audit/openbao-audit.log' \
|
|
2>/dev/null | tr -d '[:space:]'
|
|
}
|
|
|
|
require_pattern() {
|
|
local label="$1"
|
|
local output="$2"
|
|
local pattern="$3"
|
|
if printf '%s\n' "$output" | grep -Eq "$pattern"; then
|
|
ok "$label"
|
|
else
|
|
fail "$label"
|
|
fi
|
|
}
|
|
|
|
check_cmd
|
|
|
|
token="$(read_token)"
|
|
if [ -z "$token" ]; then
|
|
echo "ERROR: empty OpenBao token" >&2
|
|
exit 1
|
|
fi
|
|
|
|
before_bytes="$(audit_log_bytes || true)"
|
|
before_bytes="${before_bytes:-0}"
|
|
|
|
step "OpenBao status"
|
|
if status_output="$(remote_bao "$token" status 2>&1)"; then
|
|
printf '%s\n' "$status_output"
|
|
require_pattern "OpenBao is unsealed" "$status_output" '^Sealed[[:space:]]+false$'
|
|
else
|
|
printf '%s\n' "$status_output" >&2
|
|
fail "bao status succeeded"
|
|
fi
|
|
|
|
step "Audit devices"
|
|
if audit_output="$(remote_bao "$token" audit list 2>&1)"; then
|
|
printf '%s\n' "$audit_output"
|
|
require_pattern "file/ audit device is visible" "$audit_output" '(^|[[:space:]])file/'
|
|
else
|
|
printf '%s\n' "$audit_output" >&2
|
|
fail "bao audit list succeeded"
|
|
fi
|
|
|
|
step "Secrets engines"
|
|
if secrets_output="$(remote_bao "$token" secrets list 2>&1)"; then
|
|
printf '%s\n' "$secrets_output"
|
|
require_pattern "platform/ secrets engine is visible" "$secrets_output" '(^|[[:space:]])platform/'
|
|
else
|
|
printf '%s\n' "$secrets_output" >&2
|
|
fail "bao secrets list succeeded"
|
|
fi
|
|
|
|
step "Auth methods"
|
|
if auth_output="$(remote_bao "$token" auth list 2>&1)"; then
|
|
printf '%s\n' "$auth_output"
|
|
require_pattern "kubernetes/ auth method is visible" "$auth_output" '(^|[[:space:]])kubernetes/'
|
|
require_pattern "keycape/ auth method is visible" "$auth_output" '(^|[[:space:]])keycape/'
|
|
else
|
|
printf '%s\n' "$auth_output" >&2
|
|
fail "bao auth list succeeded"
|
|
fi
|
|
|
|
after_bytes="$(audit_log_bytes || true)"
|
|
after_bytes="${after_bytes:-0}"
|
|
|
|
step "Audit log file"
|
|
if [ "$after_bytes" -gt 0 ]; then
|
|
ok "audit log file is non-empty (${after_bytes} bytes)"
|
|
else
|
|
fail "audit log file is non-empty"
|
|
fi
|
|
|
|
if [ "$DRY_RUN" -eq 1 ]; then
|
|
ok "audit log growth check skipped in dry-run"
|
|
elif [ "$after_bytes" -gt "$before_bytes" ]; then
|
|
ok "audit log grew during authenticated checks (${before_bytes} -> ${after_bytes} bytes)"
|
|
else
|
|
warn "audit log size did not increase during this run (${before_bytes} -> ${after_bytes} bytes)"
|
|
fi
|
|
|
|
if [ "$FAILURES" -gt 0 ]; then
|
|
printf '\nAuthenticated OpenBao verification failed with %s failure(s) and %s warning(s).\n' "$FAILURES" "$WARNINGS" >&2
|
|
exit 1
|
|
fi
|
|
|
|
printf '\nAuthenticated OpenBao verification passed'
|
|
if [ "$WARNINGS" -gt 0 ]; then
|
|
printf ' with %s warning(s)' "$WARNINGS"
|
|
fi
|
|
printf '.\n'
|