293 lines
10 KiB
Bash
293 lines
10 KiB
Bash
#!/usr/bin/env bash
|
|
|
|
set -euo pipefail
|
|
|
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
REPO_ROOT="$(cd "${SCRIPT_DIR}/../.." && pwd)"
|
|
|
|
source "${REPO_ROOT}/scripts/vault/dev/provider-lib.sh"
|
|
|
|
usage() {
|
|
cat <<'EOF'
|
|
Usage: scripts/ci/reconcile-vault-dev.sh <command>
|
|
|
|
Commands:
|
|
prepare-infra
|
|
reconcile-transit
|
|
reconcile-workload
|
|
apply-apps
|
|
EOF
|
|
}
|
|
|
|
log() {
|
|
printf '[reconcile-vault-dev] %s\n' "$*"
|
|
}
|
|
|
|
require_cmd() {
|
|
local cmd="$1"
|
|
if ! command -v "$cmd" >/dev/null 2>&1; then
|
|
echo "$cmd is required" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
require_env() {
|
|
local name="$1"
|
|
if [[ -z "${!name:-}" ]]; then
|
|
echo "$name must be set" >&2
|
|
exit 1
|
|
fi
|
|
}
|
|
|
|
wait_http_ready() {
|
|
local url="$1"
|
|
local label="$2"
|
|
if ! curl -fsS --retry 30 --retry-delay 2 --retry-connrefused "$url" >/dev/null 2>&1; then
|
|
echo "${label} is not reachable at ${url}" >&2
|
|
return 1
|
|
fi
|
|
}
|
|
|
|
start_port_forward() {
|
|
local namespace="$1"
|
|
local service="$2"
|
|
local local_port="$3"
|
|
local remote_port="$4"
|
|
local log_file="$5"
|
|
|
|
kubectl -n "$namespace" port-forward "svc/${service}" "${local_port}:${remote_port}" >"$log_file" 2>&1 &
|
|
local pf_pid=$!
|
|
trap 'kill "$pf_pid" >/dev/null 2>&1 || true' EXIT
|
|
printf '%s\n' "$pf_pid"
|
|
}
|
|
|
|
use_port_forward() {
|
|
[[ "${RECONCILE_USE_PORT_FORWARD:-true}" == "true" ]]
|
|
}
|
|
|
|
transit_login() {
|
|
VAULT_ADDR="${TRANSIT_VAULT_ADDR}" \
|
|
vault write -field=token auth/approle/login \
|
|
role_id="${VAULT_TRANSIT_DEV_ROLE_ID}" \
|
|
secret_id="${VAULT_TRANSIT_DEV_SECRET_ID}"
|
|
}
|
|
|
|
ensure_transit_state_resource() {
|
|
local address="$1"
|
|
local import_id="$2"
|
|
|
|
if ! terraform -chdir="${REPO_ROOT}/terraform/vault-transit/reconcile" state show "$address" >/dev/null 2>&1; then
|
|
log "Importing missing vault-transit state for ${address}"
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault-transit/reconcile" import "$address" "$import_id"
|
|
fi
|
|
}
|
|
|
|
ensure_workload_state_resource() {
|
|
local address="$1"
|
|
local import_id="$2"
|
|
|
|
if ! terraform -chdir="${REPO_ROOT}/terraform/vault/reconcile" state show "$address" >/dev/null 2>&1; then
|
|
log "Importing missing workload-vault state for ${address}"
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault/reconcile" import "$address" "$import_id"
|
|
fi
|
|
}
|
|
|
|
prepare_infra() {
|
|
require_cmd kubectl
|
|
require_env TF_STATE_DIR
|
|
|
|
mkdir -p "${TF_STATE_DIR}"
|
|
"${REPO_ROOT}/scripts/vault/dev/apply-argocd-dev-infra.sh"
|
|
|
|
kubectl -n vault-transit wait --for=create deployment/vault-transit --timeout=300s
|
|
kubectl -n vault-transit wait --for=condition=available deployment/vault-transit --timeout=300s
|
|
kubectl -n vault wait --for=create deployment/vault --timeout=300s
|
|
kubectl -n vault wait --for=condition=available deployment/vault --timeout=300s
|
|
}
|
|
|
|
reconcile_transit() {
|
|
require_cmd kubectl
|
|
require_cmd vault
|
|
require_cmd terraform
|
|
require_cmd curl
|
|
require_cmd jq
|
|
require_env TF_STATE_DIR
|
|
require_env TRANSIT_VAULT_ADDR
|
|
require_env VAULT_TRANSIT_DEV_ROLE_ID
|
|
require_env VAULT_TRANSIT_DEV_SECRET_ID
|
|
|
|
if use_port_forward; then
|
|
start_port_forward vault-transit vault-transit 18200 8200 /tmp/vault-transit-port-forward.log >/dev/null
|
|
fi
|
|
wait_http_ready "${TRANSIT_VAULT_ADDR}/v1/sys/health" "Transit provider Vault API"
|
|
|
|
VAULT_ADDR="${TRANSIT_VAULT_ADDR}" "${REPO_ROOT}/scripts/vault-transit/dev/ensure-unsealed.sh"
|
|
|
|
local transit_tf_token
|
|
transit_tf_token="$(transit_login)"
|
|
|
|
VAULT_ADDR="${TRANSIT_VAULT_ADDR}" \
|
|
VAULT_TOKEN="${transit_tf_token}" \
|
|
vault policy write \
|
|
vault-transit-automation-dev \
|
|
"${REPO_ROOT}/runbooks/vault-transit/dev/policies/vault-transit-automation-dev.hcl"
|
|
|
|
transit_tf_token="$(transit_login)"
|
|
|
|
local transit_state_path="${TF_STATE_DIR}/vault-transit-reconcile.tfstate"
|
|
|
|
TF_VAR_vault_addr="${TRANSIT_VAULT_ADDR}" \
|
|
TF_VAR_vault_token="${transit_tf_token}" \
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault-transit/reconcile" init \
|
|
-input=false \
|
|
-reconfigure \
|
|
-backend-config="path=${transit_state_path}"
|
|
|
|
if ! terraform -chdir="${REPO_ROOT}/terraform/vault-transit/reconcile" state list >/dev/null 2>&1; then
|
|
echo "vault-transit Terraform state is unreadable. Re-seed ${transit_state_path}." >&2
|
|
exit 1
|
|
fi
|
|
|
|
export TF_VAR_vault_addr="${TRANSIT_VAULT_ADDR}"
|
|
export TF_VAR_vault_token="${transit_tf_token}"
|
|
|
|
ensure_transit_state_resource vault_policy.workload_vault_transit_dev workload-vault-transit-dev
|
|
ensure_transit_state_resource vault_policy.vault_transit_admin_dev vault-transit-admin-dev
|
|
ensure_transit_state_resource vault_policy.vault_transit_automation_dev vault-transit-automation-dev
|
|
ensure_transit_state_resource vault_approle_auth_backend_role.workflow auth/approle/role/vault-transit-dev-workflow
|
|
|
|
TF_VAR_vault_addr="${TRANSIT_VAULT_ADDR}" \
|
|
TF_VAR_vault_token="${transit_tf_token}" \
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault-transit/reconcile" apply -input=false -auto-approve
|
|
}
|
|
|
|
reconcile_workload() {
|
|
require_cmd kubectl
|
|
require_cmd vault
|
|
require_cmd terraform
|
|
require_cmd curl
|
|
require_env TF_STATE_DIR
|
|
require_env TRANSIT_VAULT_ADDR
|
|
require_env WORKLOAD_VAULT_ADDR
|
|
require_env VAULT_TRANSIT_DEV_ROLE_ID
|
|
require_env VAULT_TRANSIT_DEV_SECRET_ID
|
|
|
|
if use_port_forward; then
|
|
start_port_forward vault vault 8200 8200 /tmp/vault-workload-port-forward.log >/dev/null
|
|
fi
|
|
wait_http_ready "${WORKLOAD_VAULT_ADDR}/v1/sys/health" "Workload Vault API"
|
|
|
|
VAULT_ADDR="${WORKLOAD_VAULT_ADDR}" "${REPO_ROOT}/scripts/vault/dev/ensure-unsealed.sh"
|
|
|
|
local transit_tf_token
|
|
transit_tf_token="$(transit_login)"
|
|
|
|
local workload_role_id
|
|
local workload_secret_id
|
|
workload_role_id="$(
|
|
VAULT_ADDR="${TRANSIT_VAULT_ADDR}" \
|
|
VAULT_TOKEN="${transit_tf_token}" \
|
|
vault kv get -field=VAULT_WORKLOAD_DEV_ROLE_ID kv/dev/workload/bootstrap 2>/dev/null || true
|
|
)"
|
|
workload_secret_id="$(
|
|
VAULT_ADDR="${TRANSIT_VAULT_ADDR}" \
|
|
VAULT_TOKEN="${transit_tf_token}" \
|
|
vault kv get -field=VAULT_WORKLOAD_DEV_SECRET_ID kv/dev/workload/bootstrap 2>/dev/null || true
|
|
)"
|
|
|
|
if [[ -z "${workload_role_id}" || -z "${workload_secret_id}" ]]; then
|
|
echo "Workload Vault bootstrap AppRole is missing from provider Vault." >&2
|
|
echo "Execute scripts/vault/dev/bootstrap-runbook.sh manually with a privileged workload token to seed kv/dev/workload/bootstrap." >&2
|
|
exit 1
|
|
fi
|
|
|
|
local workload_tf_token
|
|
workload_tf_token="$(
|
|
VAULT_ADDR="${WORKLOAD_VAULT_ADDR}" \
|
|
vault write -field=token auth/approle/login \
|
|
role_id="${workload_role_id}" \
|
|
secret_id="${workload_secret_id}"
|
|
)"
|
|
|
|
local workload_state_path="${TF_STATE_DIR}/vault-reconcile.tfstate"
|
|
|
|
TF_VAR_workload_vault_addr="${WORKLOAD_VAULT_ADDR}" \
|
|
TF_VAR_workload_vault_token="${workload_tf_token}" \
|
|
TF_VAR_transit_vault_addr="${TRANSIT_VAULT_ADDR}" \
|
|
TF_VAR_transit_vault_token="${transit_tf_token}" \
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault/reconcile" init \
|
|
-input=false \
|
|
-reconfigure \
|
|
-backend-config="path=${workload_state_path}"
|
|
|
|
export TF_VAR_workload_vault_addr="${WORKLOAD_VAULT_ADDR}"
|
|
export TF_VAR_workload_vault_token="${workload_tf_token}"
|
|
export TF_VAR_transit_vault_addr="${TRANSIT_VAULT_ADDR}"
|
|
export TF_VAR_transit_vault_token="${transit_tf_token}"
|
|
|
|
ensure_workload_state_resource vault_policy.auth_server auth-server-dev
|
|
ensure_workload_state_resource vault_policy.auth_db_migration auth-db-migration-dev
|
|
ensure_workload_state_resource vault_policy.postgres postgres-dev
|
|
ensure_workload_state_resource vault_policy.keycloak keycloak-dev
|
|
ensure_workload_state_resource vault_policy.keycloak_client_sync keycloak-client-sync-dev
|
|
ensure_workload_state_resource vault_policy.postgres_operator postgres-operator-dev
|
|
ensure_workload_state_resource vault_policy.keycloak_operator keycloak-operator-dev
|
|
ensure_workload_state_resource vault_policy.platform_admin platform-admin-dev
|
|
ensure_workload_state_resource vault_policy.workload_automation workload-automation-dev
|
|
ensure_workload_state_resource vault_kubernetes_auth_backend_role.auth_server auth/kubernetes/role/auth-server-dev
|
|
ensure_workload_state_resource vault_kubernetes_auth_backend_role.auth_db_migration auth/kubernetes/role/auth-db-migration-dev
|
|
ensure_workload_state_resource vault_kubernetes_auth_backend_role.postgres auth/kubernetes/role/postgres-dev
|
|
ensure_workload_state_resource vault_kubernetes_auth_backend_role.keycloak auth/kubernetes/role/keycloak-dev
|
|
ensure_workload_state_resource vault_kubernetes_auth_backend_role.keycloak_client_sync auth/kubernetes/role/keycloak-client-sync-dev
|
|
ensure_workload_state_resource vault_approle_auth_backend_role.workflow auth/approle/role/workload-dev-workflow
|
|
ensure_workload_state_resource vault_database_secret_backend_connection.platform_postgres database/config/platform-postgres-dev
|
|
ensure_workload_state_resource vault_database_secret_backend_role.auth_db_migration database/roles/auth-db-migration-dev
|
|
ensure_workload_state_resource vault_database_secret_backend_role.postgres_operator database/roles/postgres-operator-dev
|
|
ensure_workload_state_resource vault_kv_secret_v2.platform_postgres_superuser kv/data/dev/platform/postgres/superuser
|
|
ensure_workload_state_resource vault_kv_secret_v2.platform_postgres_auth_server kv/data/dev/platform/postgres/auth-server
|
|
ensure_workload_state_resource vault_kv_secret_v2.platform_postgres_keycloak kv/data/dev/platform/postgres/keycloak
|
|
ensure_workload_state_resource vault_kv_secret_v2.platform_keycloak_bootstrap_admin kv/data/dev/platform/keycloak/bootstrap-admin
|
|
ensure_workload_state_resource vault_kv_secret_v2.platform_keycloak_client_auth_server kv/data/dev/platform/keycloak/client-auth-server
|
|
|
|
TF_VAR_workload_vault_addr="${WORKLOAD_VAULT_ADDR}" \
|
|
TF_VAR_workload_vault_token="${workload_tf_token}" \
|
|
TF_VAR_transit_vault_addr="${TRANSIT_VAULT_ADDR}" \
|
|
TF_VAR_transit_vault_token="${transit_tf_token}" \
|
|
terraform -chdir="${REPO_ROOT}/terraform/vault/reconcile" apply -input=false -auto-approve
|
|
|
|
if kubectl -n vault get deployment -l app.kubernetes.io/name=vault-agent-injector -o name | grep -q .; then
|
|
kubectl -n vault wait --for=condition=available deployment -l app.kubernetes.io/name=vault-agent-injector --timeout=180s
|
|
fi
|
|
}
|
|
|
|
apply_apps() {
|
|
require_cmd kubectl
|
|
"${REPO_ROOT}/scripts/vault/dev/apply-argocd-dev-apps.sh"
|
|
}
|
|
|
|
main() {
|
|
require_cmd bash
|
|
require_cmd kubectl
|
|
|
|
case "${1:-}" in
|
|
prepare-infra)
|
|
prepare_infra
|
|
;;
|
|
reconcile-transit)
|
|
reconcile_transit
|
|
;;
|
|
reconcile-workload)
|
|
reconcile_workload
|
|
;;
|
|
apply-apps)
|
|
apply_apps
|
|
;;
|
|
*)
|
|
usage >&2
|
|
exit 1
|
|
;;
|
|
esac
|
|
}
|
|
|
|
main "$@"
|