add Vault→Woodpecker secret sync CronJob (Part E)
Syncs secrets from Vault KV at secret/ci/global to Woodpecker global secrets via REST API every 6 hours. Authenticates via K8s SA JWT (woodpecker-sync role). New repos just add secrets to Vault and use from_secret: in pipeline files. Also removes k8s-dashboard static admin token — use vault write kubernetes/creds/dashboard-admin instead.
This commit is contained in:
parent
850ab5277f
commit
f82ece8d1f
1 changed files with 112 additions and 0 deletions
|
|
@ -208,6 +208,118 @@ resource "kubernetes_cluster_role_binding" "woodpecker_default" {
|
|||
}
|
||||
}
|
||||
|
||||
# --- Vault → Woodpecker Secret Sync ---
|
||||
# Syncs secrets from Vault KV (secret/ci/global) to Woodpecker global secrets via API.
|
||||
# Runs every 6 hours. Secrets are created/updated via Woodpecker REST API.
|
||||
|
||||
resource "kubernetes_config_map" "vault_woodpecker_sync" {
|
||||
metadata {
|
||||
name = "vault-woodpecker-sync"
|
||||
namespace = kubernetes_namespace.woodpecker.metadata[0].name
|
||||
}
|
||||
|
||||
data = {
|
||||
"sync.sh" = <<-'SCRIPT'
|
||||
#!/bin/sh
|
||||
set -e
|
||||
VAULT_ADDR="http://vault-active.vault.svc.cluster.local:8200"
|
||||
WP_API="http://woodpecker-server.woodpecker.svc.cluster.local:8000/api"
|
||||
|
||||
# Authenticate to Vault via K8s SA
|
||||
SA_TOKEN=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
|
||||
VAULT_TOKEN=$(curl -sf -X POST "$VAULT_ADDR/v1/auth/kubernetes/login" \
|
||||
-d "{\"role\":\"woodpecker-sync\",\"jwt\":\"$SA_TOKEN\"}" | jq -r .auth.client_token)
|
||||
|
||||
if [ -z "$VAULT_TOKEN" ] || [ "$VAULT_TOKEN" = "null" ]; then
|
||||
echo "ERROR: Failed to authenticate to Vault"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Get Woodpecker API token from Vault
|
||||
WP_TOKEN=$(curl -sf -H "X-Vault-Token: $VAULT_TOKEN" \
|
||||
"$VAULT_ADDR/v1/secret/data/ci/global" | jq -r '.data.data.woodpecker_api_token // empty')
|
||||
|
||||
if [ -z "$WP_TOKEN" ]; then
|
||||
echo "ERROR: No woodpecker_api_token in secret/ci/global"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Sync global secrets
|
||||
SECRETS=$(curl -sf -H "X-Vault-Token: $VAULT_TOKEN" \
|
||||
"$VAULT_ADDR/v1/secret/data/ci/global" | jq -r '.data.data | to_entries[] | select(.key != "woodpecker_api_token") | @base64')
|
||||
|
||||
synced=0
|
||||
for entry in $SECRETS; do
|
||||
NAME=$(echo "$entry" | base64 -d | jq -r .key)
|
||||
VALUE=$(echo "$entry" | base64 -d | jq -r .value)
|
||||
|
||||
# Try PATCH first (update), fall back to POST (create)
|
||||
STATUS=$(curl -sf -o /dev/null -w "%{http_code}" -X PATCH "$WP_API/secrets/$NAME" \
|
||||
-H "Authorization: Bearer $WP_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"$NAME\",\"value\":\"$VALUE\",\"events\":[\"push\",\"tag\",\"deployment\"]}" 2>/dev/null || echo "000")
|
||||
|
||||
if [ "$STATUS" != "200" ]; then
|
||||
curl -sf -X POST "$WP_API/secrets" \
|
||||
-H "Authorization: Bearer $WP_TOKEN" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"name\":\"$NAME\",\"value\":\"$VALUE\",\"events\":[\"push\",\"tag\",\"deployment\"]}" > /dev/null
|
||||
fi
|
||||
synced=$((synced + 1))
|
||||
done
|
||||
echo "Synced $synced global secrets from Vault to Woodpecker"
|
||||
SCRIPT
|
||||
}
|
||||
}
|
||||
|
||||
resource "kubernetes_cron_job_v1" "vault_secret_sync" {
|
||||
metadata {
|
||||
name = "vault-woodpecker-sync"
|
||||
namespace = kubernetes_namespace.woodpecker.metadata[0].name
|
||||
}
|
||||
spec {
|
||||
schedule = "0 */6 * * *"
|
||||
successful_jobs_history_limit = 3
|
||||
failed_jobs_history_limit = 3
|
||||
concurrency_policy = "Forbid"
|
||||
job_template {
|
||||
metadata {}
|
||||
spec {
|
||||
template {
|
||||
metadata {}
|
||||
spec {
|
||||
container {
|
||||
name = "sync"
|
||||
image = "curlimages/curl"
|
||||
command = ["/bin/sh", "/scripts/sync.sh"]
|
||||
volume_mount {
|
||||
name = "sync-script"
|
||||
mount_path = "/scripts"
|
||||
}
|
||||
resources {
|
||||
requests = {
|
||||
cpu = "10m"
|
||||
memory = "32Mi"
|
||||
}
|
||||
limits = {
|
||||
memory = "64Mi"
|
||||
}
|
||||
}
|
||||
}
|
||||
volume {
|
||||
name = "sync-script"
|
||||
config_map {
|
||||
name = kubernetes_config_map.vault_woodpecker_sync.metadata[0].name
|
||||
}
|
||||
}
|
||||
restart_policy = "OnFailure"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module "ingress" {
|
||||
source = "../../modules/kubernetes/ingress_factory"
|
||||
namespace = kubernetes_namespace.woodpecker.metadata[0].name
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue