#!/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/triage-oidc hermes/agent-tokens" ""
write_policy_and_role "hermes-agent" "hermes" "hermes-agent,hermes-switchyard" \
  "hermes/agent-oidc hermes/agent-tokens hermes/chat-telegram" ""
write_policy_and_role "hermes-chat" "hermes" "hermes-chat" \
  "hermes/chat-oidc hermes/chat-telegram 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/chat-oidc hermes/chat-telegram hermes/agent-oidc hermes/triage-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"
