--- # Periodic Vault raft snapshot, shipped to the VPS MinIO backup receiver. # This is the DR path for Vault's data independent of the unseal-key custody # story - a Ceph/cluster-loss disaster is recovered by standing up a fresh # Vault and `vault operator raft snapshot restore`ing the latest one of these, # not by anything to do with the unseal key itself. # # Requires a one-time manual step after VPS bootstrap: store the VPS MinIO # root credentials (see infrastructure/vps-standby/minio/values.yaml) into # this cluster's Vault so ESO can hand them to the CronJob: # vault kv put secret/vps-minio-credentials \ # access-key= secret-key= apiVersion: external-secrets.io/v1 kind: ExternalSecret metadata: name: vps-minio-credentials namespace: vault spec: refreshInterval: 1h secretStoreRef: kind: ClusterSecretStore name: vault-backend target: name: vps-minio-credentials creationPolicy: Owner data: - secretKey: access-key remoteRef: key: vps-minio-credentials property: access-key - secretKey: secret-key remoteRef: key: vps-minio-credentials property: secret-key --- apiVersion: v1 kind: ConfigMap metadata: name: vault-raft-snapshot-script namespace: vault data: snapshot.sh: | #!/bin/bash set -euo pipefail # VPS's Netbird address - replace with the actual peer IP/hostname once # the VPS is bootstrapped and joined to the mesh (scripts/vps-bootstrap.sh). VPS_MINIO_ENDPOINT="${VPS_MINIO_ENDPOINT:-vps-minio.netbird.internal:30900}" BUCKET="vault-raft-snapshots" SNAP_NAME="vault-raft-$(date -u +%Y%m%dT%H%M%SZ).snap" ROOT_TOKEN="$(kubectl -n vault get secret vault-init-keys -o jsonpath='{.data.VAULT_ROOT_TOKEN}' | base64 -d)" echo "==> Taking raft snapshot from vault-0..." kubectl -n vault exec vault-0 -- env VAULT_TOKEN="$ROOT_TOKEN" \ vault operator raft snapshot save "/tmp/${SNAP_NAME}" echo "==> Copying snapshot out of vault-0..." kubectl -n vault cp "vault-0:/tmp/${SNAP_NAME}" "/tmp/${SNAP_NAME}" kubectl -n vault exec vault-0 -- rm -f "/tmp/${SNAP_NAME}" echo "==> Installing mc (MinIO client)..." curl -sf https://dl.min.io/client/mc/release/linux-amd64/mc -o /tmp/mc chmod +x /tmp/mc export MC_CONFIG_DIR=/tmp/.mc /tmp/mc alias set vps-minio "http://${VPS_MINIO_ENDPOINT}" \ "${MINIO_ACCESS_KEY}" "${MINIO_SECRET_KEY}" >/dev/null echo "==> Uploading ${SNAP_NAME} to vps-minio/${BUCKET}..." /tmp/mc cp "/tmp/${SNAP_NAME}" "vps-minio/${BUCKET}/${SNAP_NAME}" rm -f "/tmp/${SNAP_NAME}" echo "==> Pruning snapshots older than 30 days..." /tmp/mc find "vps-minio/${BUCKET}" --older-than 30d --exec "/tmp/mc rm {}" || true echo "==> Done: ${SNAP_NAME}" --- apiVersion: batch/v1 kind: CronJob metadata: name: vault-raft-snapshot namespace: vault spec: schedule: "0 */6 * * *" # every 6 hours concurrencyPolicy: Forbid successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 2 template: spec: serviceAccountName: argocd-hook-sa # already has kubectl exec rights in this namespace (see vault-init-job.yaml) restartPolicy: Never securityContext: runAsNonRoot: true runAsUser: 65534 seccompProfile: type: RuntimeDefault containers: - name: raft-snapshot image: alpine/k8s:1.32.13 command: ["/bin/bash", "/scripts/snapshot.sh"] securityContext: allowPrivilegeEscalation: false capabilities: drop: ["ALL"] env: - name: MINIO_ACCESS_KEY valueFrom: secretKeyRef: name: vps-minio-credentials key: access-key - name: MINIO_SECRET_KEY valueFrom: secretKeyRef: name: vps-minio-credentials key: secret-key volumeMounts: - name: scripts mountPath: /scripts volumes: - name: scripts configMap: name: vault-raft-snapshot-script defaultMode: 0755