Some checks failed
Tests / Declarative: Post Actions testing.tests.test_repo_structure.test_knowledge_service_mirror_matches_source failed
The Claude subscription OAuth token was created as a manual kubectl Secret in the interest of demo time, with migration to Vault agreed as follow-up. The value now lives at kv/atlas/hermes/agent-tokens and is injected as a file. The hermes role gains that path and binds the hermes-triage service account the deployment actually runs as; it previously bound only hermes-vault. The init container prefers the Vault file and falls back to the Secret, so this can be rolled back by removing the annotations alone, and the Secret should be deleted once Vault has been serving it for a while. Vault was reachable all along without the operator credential: Ariadne already holds a vault-admin Kubernetes auth role, which is how the value was written. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
317 lines
11 KiB
Bash
317 lines
11 KiB
Bash
#!/usr/bin/env sh
|
|
set -eu
|
|
|
|
log() { echo "[vault-k8s-auth] $*"; }
|
|
|
|
vault_cmd() {
|
|
for attempt in 1 2 3 4 5 6; do
|
|
set +e
|
|
output="$(vault "$@" 2>&1)"
|
|
status=$?
|
|
set -e
|
|
if [ "${status}" -eq 0 ]; then
|
|
printf '%s' "${output}"
|
|
return 0
|
|
fi
|
|
log "vault command failed; retrying (${attempt}/6)"
|
|
sleep $((attempt * 2))
|
|
done
|
|
log "vault command failed; giving up"
|
|
return 1
|
|
}
|
|
|
|
ensure_token() {
|
|
if [ -n "${VAULT_TOKEN:-}" ]; then
|
|
return
|
|
fi
|
|
role="${VAULT_K8S_ROLE:-vault}"
|
|
jwt="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
|
|
if ! VAULT_TOKEN="$(vault_cmd write -field=token auth/kubernetes/login role="${role}" jwt="${jwt}")"; then
|
|
log "kubernetes auth login failed; set VAULT_TOKEN or fix role ${role}"
|
|
exit 1
|
|
fi
|
|
export VAULT_TOKEN
|
|
}
|
|
|
|
if ! status_json="$(vault_cmd status -format=json)"; then
|
|
log "vault status failed; check VAULT_ADDR and VAULT_TOKEN"
|
|
exit 1
|
|
fi
|
|
|
|
if ! printf '%s' "${status_json}" | grep -q '"initialized":[[:space:]]*true'; then
|
|
log "vault not initialized; skipping"
|
|
exit 0
|
|
fi
|
|
|
|
if printf '%s' "${status_json}" | grep -q '"sealed":[[:space:]]*true'; then
|
|
log "vault sealed; skipping"
|
|
exit 0
|
|
fi
|
|
|
|
ensure_token
|
|
|
|
k8s_host="https://${KUBERNETES_SERVICE_HOST}:443"
|
|
k8s_ca="$(cat /var/run/secrets/kubernetes.io/serviceaccount/ca.crt)"
|
|
k8s_token="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
|
|
role_ttl="${VAULT_K8S_ROLE_TTL:-1h}"
|
|
token_reviewer_jwt="${VAULT_K8S_TOKEN_REVIEWER_JWT:-}"
|
|
|
|
if [ -z "${token_reviewer_jwt}" ] && [ -n "${VAULT_K8S_TOKEN_REVIEWER_JWT_FILE:-}" ] && [ -r "${VAULT_K8S_TOKEN_REVIEWER_JWT_FILE}" ]; then
|
|
token_reviewer_jwt="$(cat "${VAULT_K8S_TOKEN_REVIEWER_JWT_FILE}")"
|
|
fi
|
|
if [ -z "${token_reviewer_jwt}" ]; then
|
|
token_reviewer_jwt="${k8s_token}"
|
|
fi
|
|
|
|
if ! vault_cmd auth list -format=json | grep -q '"kubernetes/"'; then
|
|
log "enabling kubernetes auth"
|
|
vault_cmd auth enable kubernetes
|
|
fi
|
|
|
|
log "configuring kubernetes auth"
|
|
vault_cmd write auth/kubernetes/config \
|
|
token_reviewer_jwt="${token_reviewer_jwt}" \
|
|
kubernetes_host="${k8s_host}" \
|
|
kubernetes_ca_cert="${k8s_ca}"
|
|
|
|
write_raw_policy() {
|
|
name="$1"
|
|
body="$2"
|
|
log "writing policy ${name}"
|
|
printf '%s\n' "${body}" | vault_cmd policy write "${name}" -
|
|
}
|
|
|
|
write_policy_and_role() {
|
|
role="$1"
|
|
namespace="$2"
|
|
service_accounts="$3"
|
|
read_paths="$4"
|
|
write_paths="$5"
|
|
extra_rules="${6:-}"
|
|
|
|
policy_body=""
|
|
for path in ${read_paths}; do
|
|
policy_body="${policy_body}
|
|
path \"kv/data/atlas/${path}\" {
|
|
capabilities = [\"read\"]
|
|
}
|
|
path \"kv/metadata/atlas/${path}\" {
|
|
capabilities = [\"list\"]
|
|
}
|
|
"
|
|
done
|
|
for path in ${write_paths}; do
|
|
policy_body="${policy_body}
|
|
path \"kv/data/atlas/${path}\" {
|
|
capabilities = [\"create\", \"update\", \"read\"]
|
|
}
|
|
path \"kv/metadata/atlas/${path}\" {
|
|
capabilities = [\"list\"]
|
|
}
|
|
"
|
|
done
|
|
if [ -n "${extra_rules}" ]; then
|
|
policy_body="${policy_body}
|
|
${extra_rules}
|
|
"
|
|
fi
|
|
|
|
log "writing policy ${role}"
|
|
printf '%s\n' "${policy_body}" | vault_cmd policy write "${role}" -
|
|
|
|
log "writing role ${role}"
|
|
vault_cmd write "auth/kubernetes/role/${role}" \
|
|
bound_service_account_names="${service_accounts}" \
|
|
bound_service_account_namespaces="${namespace}" \
|
|
policies="${role}" \
|
|
ttl="${role_ttl}"
|
|
}
|
|
|
|
vault_admin_policy='
|
|
path "sys/auth" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/auth/*" {
|
|
capabilities = ["create", "update", "delete", "sudo", "read"]
|
|
}
|
|
path "auth/kubernetes/*" {
|
|
capabilities = ["create", "update", "read"]
|
|
}
|
|
path "auth/oidc/*" {
|
|
capabilities = ["create", "update", "read"]
|
|
}
|
|
path "sys/policies/acl" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "sys/policies/acl/*" {
|
|
capabilities = ["create", "update", "read"]
|
|
}
|
|
path "sys/internal/ui/mounts" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/internal/ui/mounts/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/mounts" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/mounts/auth/*" {
|
|
capabilities = ["read", "update", "sudo"]
|
|
}
|
|
path "sys/storage/raft/configuration" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/storage/raft/snapshot" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "kv/data/atlas/vault/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "kv/metadata/atlas/vault/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/data/*" {
|
|
capabilities = ["create", "update", "read", "delete", "patch"]
|
|
}
|
|
path "kv/metadata" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/metadata/*" {
|
|
capabilities = ["read", "list", "delete"]
|
|
}
|
|
path "kv/data/atlas/shared/*" {
|
|
capabilities = ["create", "update", "read", "patch"]
|
|
}
|
|
path "kv/metadata/atlas/shared/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
'
|
|
|
|
write_raw_policy "vault-admin" "${vault_admin_policy}"
|
|
dev_kv_policy='
|
|
path "sys/internal/ui/mounts" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "sys/internal/ui/mounts/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "kv/metadata" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/metadata/atlas" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/metadata/atlas/shared" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/metadata/atlas/shared/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
path "kv/data/atlas/shared/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
'
|
|
write_raw_policy "dev-kv" "${dev_kv_policy}"
|
|
log "writing role vault-admin"
|
|
vault_cmd write "auth/kubernetes/role/vault-admin" \
|
|
bound_service_account_names="vault-admin,ariadne" \
|
|
bound_service_account_namespaces="vault,maintenance" \
|
|
policies="vault-admin" \
|
|
ttl="${role_ttl}"
|
|
|
|
write_policy_and_role "outline" "outline" "outline-vault" \
|
|
"outline/* shared/postmark-relay" ""
|
|
write_policy_and_role "planka" "planka" "planka-vault" \
|
|
"planka/* shared/postmark-relay" ""
|
|
write_policy_and_role "bstein-dev-home" "bstein-dev-home" "bstein-dev-home,bstein-dev-home-vault-sync" \
|
|
"portal/* shared/chat-ai-keys-runtime shared/portal-e2e-client shared/postmark-relay mailu/mailu-initial-account-secret shared/harbor-pull" ""
|
|
write_policy_and_role "gitea" "gitea" "gitea-vault" \
|
|
"gitea/*" ""
|
|
write_policy_and_role "vaultwarden" "vaultwarden" "vaultwarden-vault" \
|
|
"vaultwarden/* mailu/mailu-initial-account-secret" ""
|
|
write_policy_and_role "sso" "sso" "sso-vault,sso-vault-sync,mas-secrets-ensure" \
|
|
"sso/* portal/bstein-dev-home-keycloak-admin shared/keycloak-admin shared/portal-e2e-client shared/postmark-relay shared/harbor-pull" ""
|
|
write_policy_and_role "mailu-mailserver" "mailu-mailserver" "mailu-vault-sync" \
|
|
"mailu/* shared/postmark-relay shared/harbor-pull" ""
|
|
write_policy_and_role "harbor" "harbor" "harbor-vault-sync" \
|
|
"harbor/* shared/harbor-pull" ""
|
|
write_policy_and_role "nextcloud" "nextcloud" "nextcloud-vault" \
|
|
"nextcloud/* shared/keycloak-admin shared/postmark-relay" ""
|
|
write_policy_and_role "comms" "comms" "comms-vault,atlasbot" \
|
|
"comms/* shared/chat-ai-keys-runtime shared/harbor-pull" ""
|
|
write_policy_and_role "jenkins" "jenkins" "jenkins,jenkins-vault-sync" \
|
|
"jenkins/* shared/harbor-pull quality/sonarqube-oidc" ""
|
|
write_policy_and_role "monitoring" "monitoring" "monitoring-vault-sync" \
|
|
"monitoring/* shared/postmark-relay shared/harbor-pull" ""
|
|
write_policy_and_role "logging" "logging" "logging-vault-sync" \
|
|
"logging/* shared/harbor-pull" ""
|
|
write_policy_and_role "pegasus" "jellyfin" "pegasus-vault-sync" \
|
|
"pegasus/* shared/harbor-pull" ""
|
|
write_policy_and_role "crypto" "crypto" "crypto-vault-sync" \
|
|
"crypto/* shared/harbor-pull" ""
|
|
write_policy_and_role "health" "health" "health-vault-sync" \
|
|
"health/*" ""
|
|
write_policy_and_role "game-stream" "game-stream" "game-stream-vault" \
|
|
"game-stream/*" ""
|
|
write_policy_and_role "hermes" "hermes" "hermes-vault,hermes-triage" \
|
|
"hermes/operator-oidc hermes/agent-tokens" ""
|
|
write_policy_and_role "veles" "veles" "veles-backend,veles-generator,veles-postgres,veles-vault-sync" \
|
|
"veles/* shared/harbor-pull shared/postmark-relay" ""
|
|
write_policy_and_role "veles-sim" "veles" "veles-sim" \
|
|
"veles/codex-cli" ""
|
|
write_policy_and_role "veles-secrets" "veles" "veles-secrets-ensure" \
|
|
"shared/postmark-relay" \
|
|
"veles/*"
|
|
write_policy_and_role "cassandra" "cassandra" "cassandra-backend,cassandra-generator,cassandra-postgres,cassandra-vault-sync" \
|
|
"cassandra/* shared/harbor-pull shared/postmark-relay" ""
|
|
write_policy_and_role "cassandra-sim" "cassandra" "cassandra-sim" \
|
|
"cassandra/codex-cli" ""
|
|
write_policy_and_role "cassandra-secrets" "cassandra" "cassandra-secrets-ensure" \
|
|
"shared/postmark-relay veles/app-secrets" \
|
|
"cassandra/*"
|
|
write_policy_and_role "cassandra-migration" "cassandra" "cassandra-db-migration" \
|
|
"veles/veles-db cassandra/cassandra-db" ""
|
|
write_policy_and_role "maintenance" "maintenance" "ariadne,maintenance-vault-sync,metis" \
|
|
"maintenance/ariadne-db maintenance/metis-oidc maintenance/soteria-oidc maintenance/metis-ssh-keys maintenance/metis-runtime portal/atlas-portal-db portal/bstein-dev-home-keycloak-admin mailu/mailu-db-secret mailu/mailu-initial-account-secret nextcloud/nextcloud-db nextcloud/nextcloud-admin health/wger-admin finance/firefly-secrets comms/mas-admin-client-runtime comms/atlasbot-credentials-runtime comms/synapse-db comms/synapse-admin vault/vault-oidc-config shared/harbor-pull shared/soteria-restic harbor/harbor-core" "" \
|
|
'
|
|
path "kv/data/atlas/nodes/*" {
|
|
capabilities = ["read"]
|
|
}
|
|
path "kv/metadata/atlas/nodes/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
'
|
|
write_policy_and_role "maintenance-metis-token-sync" "maintenance" "metis-token-sync" \
|
|
"" \
|
|
"maintenance/metis-runtime"
|
|
write_policy_and_role "finance" "finance" "finance-vault" \
|
|
"finance/* shared/postmark-relay" ""
|
|
write_policy_and_role "finance-secrets" "finance" "finance-secrets-ensure" \
|
|
"" \
|
|
"finance/*"
|
|
write_policy_and_role "longhorn" "longhorn-system" "longhorn-vault,longhorn-vault-sync" \
|
|
"longhorn/* shared/harbor-pull" ""
|
|
write_policy_and_role "postgres" "postgres" "postgres-vault" \
|
|
"postgres/postgres-db" ""
|
|
write_policy_and_role "vault" "vault" "vault" \
|
|
"vault/*" ""
|
|
|
|
write_policy_and_role "sso-secrets" "sso" "mas-secrets-ensure" \
|
|
"shared/keycloak-admin shared/postmark-relay maintenance/metis-ssh-keys" \
|
|
"harbor/harbor-oidc vault/vault-oidc-config comms/synapse-oidc logging/oauth2-proxy-logs-oidc finance/actual-oidc maintenance/metis-oidc maintenance/soteria-oidc maintenance/metis-ssh-keys veles/veles-oidc cassandra/cassandra-oidc gitea/gitea-veles-oidc gitea/gitea-cassandra-oidc hermes/operator-oidc" \
|
|
'
|
|
path "kv/data/atlas/nodes/*" {
|
|
capabilities = ["create", "update", "read"]
|
|
}
|
|
path "kv/metadata/atlas/nodes/*" {
|
|
capabilities = ["list"]
|
|
}
|
|
'
|
|
write_policy_and_role "crypto-secrets" "crypto" "crypto-secrets-ensure" \
|
|
"" \
|
|
"crypto/wallet-monero-temp-rpc-auth"
|
|
write_policy_and_role "comms-secrets" "comms" \
|
|
"comms-secrets-ensure,mas-db-ensure,mas-admin-client-secret-writer,othrys-synapse-signingkey-job" \
|
|
"" \
|
|
"comms/turn-shared-secret comms/livekit-api comms/synapse-redis comms/synapse-macaroon comms/atlasbot-credentials-runtime comms/synapse-db comms/synapse-admin comms/synapse-registration comms/mas-db comms/mas-admin-client-runtime comms/mas-secrets-runtime comms/othrys-synapse-signingkey"
|