Backup und Restore auf Kubernetes
Summary: Konkrete Backup-/Restore-Strategie, wenn OpenBao im Helm-Chart auf Kubernetes läuft. Drei-Schichten-Modell (Raft-Snapshot + Seal-Material + Cluster-Manifests), volle Konfiguration des snapshotAgent-CronJobs aus dem Chart, Alternative als eigener CronJob, Restore-Prozedur im laufenden Cluster, Sandbox-Test-Restore und Monitoring. Komplementär zur konzeptionellen backups-Seite, die das Warum und Was beschreibt — diese Seite ist das K8s-spezifische Wie.
Sources: raw/docs/platform/k8s/helm/examples/snapshot-cronjob.md, raw/docs/commands/operator/raft.md, raw/docs/concepts/storage.md, raw/docs/configuration/storage/raft.md. Velero-, kube-state-metrics- und SealedSecrets-Schritte folgen den jeweiligen Upstream-Docs.
Last updated: 2026-05-20
0 — Die Drei-Schichten-Regel
Ein OpenBao-Backup auf Kubernetes besteht aus drei Artefakten. Wer nur eines davon sichert, hat im Ernstfall nichts.
| Schicht | Was | Wo speichern | Verlustfolge |
|---|---|---|---|
| 1 — Raft-Snapshot | Barrier-verschlüsselte Cluster-Daten zu einem konsistenten Punkt-in-Zeit | Off-Site-Bucket (S3 o. ä.) | Alle Secrets, Policies, Tokens, Auth-Roles weg |
| 2 — Seal-Material | Auto-Unseal-Key (Cloud-KMS-Ref / Transit-Seal-Token) oder Shamir-Unseal-Keys + Recovery-Keys | Cloud-KMS (cross-region), HSM oder verschlüsseltes Offline-Medium | Snapshot existiert, kann aber nie wieder entschlüsselt werden |
| 3 — Cluster-Manifests | values-ha.yaml, cert-manager-Issuer, Namespace, ServiceAccount-Bindings, NetworkPolicies | Git-Repo | Ohne die Manifeste muss man die Cluster-Topologie aus dem Gedächtnis rekonstruieren |
Hintergrund zur Trennung Daten vs. Seal: backups § “Was gesichert werden muss” und seal-unseal.
K8s-spezifischer Punkt zu Schicht 3: Auf VMs ist die Server-Konfig oft Teil eines Configuration-Management-Systems (Ansible, Salt). Auf K8s ist sie in der values-ha.yaml und in zusätzlichen Manifesten — beides muss zwingend in einem Git-Repo liegen, sonst geht beim Disaster-Restore die halbe Architektur verloren.
1 — Schicht 1, Variante A: Helm-Chart-snapshotAgent (empfohlen)
Seit Helm-Chart-Version 0.22.0 bringt der Chart einen fertigen CronJob mit (source: raw/docs/platform/k8s/helm/examples/snapshot-cronjob.md). Das ist der Default-Pfad — keine eigene Bastelei, weniger Wartung.
1.1 — S3-Credentials als Secret vorbereiten
Der Job lädt Snapshots per S3-API hoch. Credentials kommen aus einem Secret im selben Namespace wie OpenBao:
kubectl -n openbao create secret generic openbao-s3-credentials \
--from-literal=AWS_ACCESS_KEY_ID=<key-id> \
--from-literal=AWS_SECRET_ACCESS_KEY=<secret>
Wichtig: Dieses Secret ist sensibel. In Production statt kubectl create secret lieber SealedSecrets oder ExternalSecrets nehmen — damit landet das Klartext-Geheimnis nie im Git-Repo. Schicht 3 (Manifests in Git) ist nur sinnvoll, wenn die Secrets darin verschlüsselt sind.
IAM-Empfehlung für den S3-Bucket-Schlüssel:
- Nur
PutObject+ListBucket(keinDeleteObject— schützt vor Ransomware, die alte Snapshots überschreibt) - Bucket-Policy mit
aws:SecureTransport=true - Bucket-Verschlüsselung at-rest mit eigenem KMS-Key
- Object-Lock mit Compliance-Mode für Snapshots, die mind. 30 Tage unveränderlich bleiben sollen
- Lifecycle-Rule: Standard → Glacier nach 30 Tagen → Löschung nach 365 Tagen
1.2 — OpenBao-Policy und Kubernetes-Auth-Role
Der Snapshot-Endpoint liegt auf sys/storage/raft/snapshot und braucht nur read:
kubectl -n openbao exec -ti openbao-0 -- /bin/sh -c '
cat <<EOF | bao policy write snapshot -
path "sys/storage/raft/snapshot" {
capabilities = ["read"]
}
EOF
'
Kubernetes-Auth-Method aktivieren (falls noch nicht):
kubectl -n openbao exec -ti openbao-0 -- bao auth enable kubernetes
Eine Auth-Role, die ServiceAccount + Namespace des CronJobs an diese Policy bindet:
kubectl -n openbao exec -ti openbao-0 -- bao write auth/kubernetes/role/snapshot \
bound_service_account_names=openbao-snapshot-agent \
bound_service_account_namespaces=openbao \
policies=snapshot \
ttl=1h
(source: raw/docs/platform/k8s/helm/examples/snapshot-cronjob.md)
Warum ttl=1h: der Snapshot-Job läuft nur wenige Sekunden. Eine kurze TTL minimiert den Schaden, falls das Token leakt.
1.3 — values-ha.yaml um snapshotAgent erweitern
snapshotAgent:
enabled: true
schedule: "*/15 * * * *"
s3CredentialsSecret: "openbao-s3-credentials"
config:
s3Host: "s3.eu-central-1.amazonaws.com"
s3Bucket: "openbao-snapshots"
s3Uri: "s3://openbao-snapshots"
s3ExpireDays: "14"
baoAuthPath: "kubernetes"
baoRole: "snapshot"
(angelehnt an: raw/docs/platform/k8s/helm/examples/snapshot-cronjob.md)
Begründungen:
schedule: "*/15 * * * *": alle 15 Minuten. Faustregel: RPO = Schedule-Intervall. Wer keine 15 Minuten verlieren darf, geht auf*/5. Wer Cluster mit hoher Schreiblast hat, kann*/30oder0 * * * *nehmen — Snapshots sind günstig, aber jeder produziert ein Bucket-Object und einen Job-Pod.s3ExpireDays: "14": 14 Tage Retention auf dem Bucket. Das ist die kurzfristige Retention. Für mittel-/langfristige Retention (z. B. monatliche Snapshots ein Jahr lang) eine zusätzliche Bucket-Lifecycle-Rule auf S3-Seite bauen, die ausgewählte Snapshots in Glacier kopiert.baoAuthPath+baoRole: zeigen auf die Auth-Method und Role aus § 1.2.
helm upgrade ausführen:
helm upgrade openbao openbao/openbao -n openbao -f values-ha.yaml
1.4 — Verifikation
CronJob existiert?
kubectl -n openbao get cronjob
Erwartet: openbao-snapshot-agent mit Schedule */15 * * * *.
Erster Lauf-Trigger (ohne 15 Min zu warten):
kubectl -n openbao create job --from=cronjob/openbao-snapshot-agent first-snapshot-test
kubectl -n openbao logs job/first-snapshot-test -f
Erfolgsmeldung im Log: typisch snapshot saved to s3://openbao-snapshots/openbao-snapshot-<timestamp>.snap o. ä.
Im Bucket nachsehen:
aws s3 ls s3://openbao-snapshots/
Erwartet: eine .snap-Datei im niedrigen 10-MB-Bereich (für einen frischen Cluster) bis hin zu mehreren 100 MB (für Cluster mit vielen Secrets/Policies).
2 — Schicht 1, Variante B: eigener CronJob (für Sonderfälle)
Manchmal reicht der eingebaute snapshotAgent nicht — etwa wenn:
- Snapshots auf einen Nicht-S3-Endpoint (NFS, GCS, on-prem Object Storage ohne S3-API) sollen.
- Zusätzliche Verschlüsselung mit
ageodergpgvor dem Upload nötig ist (Compliance). - Snapshots in mehrere Buckets parallel gehen sollen.
- Der OpenBao-Auth-Pfad nicht
kubernetesist (z. B. AppRole für besseren Trail).
Dann legt man einen eigenen CronJob an:
apiVersion: batch/v1
kind: CronJob
metadata:
name: openbao-snapshot
namespace: openbao
spec:
schedule: "*/15 * * * *"
concurrencyPolicy: Forbid
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 5
jobTemplate:
spec:
backoffLimit: 2
template:
spec:
serviceAccountName: openbao-snapshot-agent
restartPolicy: OnFailure
containers:
- name: snapshot
image: openbao/openbao:2.0.1
env:
- name: BAO_ADDR
value: https://openbao-active.openbao.svc:8200
- name: BAO_CACERT
value: /tls/ca.crt
command:
- /bin/sh
- -c
- |
set -euo pipefail
JWT=$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)
TOKEN=$(bao write -field=token auth/kubernetes/login \
role=snapshot jwt="$JWT")
export BAO_TOKEN="$TOKEN"
TIMESTAMP=$(date -u +%Y%m%dT%H%M%SZ)
SNAP=/tmp/snapshot-${TIMESTAMP}.snap
bao operator raft snapshot save "$SNAP"
aws s3 cp "$SNAP" \
"s3://openbao-snapshots/openbao-snapshot-${TIMESTAMP}.snap" \
--sse aws:kms --sse-kms-key-id alias/openbao-backup
envFrom:
- secretRef:
name: openbao-s3-credentials
volumeMounts:
- name: tls
mountPath: /tls
readOnly: true
volumes:
- name: tls
secret:
secretName: openbao-server-tls
items:
- key: ca.crt
path: ca.crt
Begründungen der kritischen Felder:
concurrencyPolicy: Forbid: wenn ein Snapshot länger als das Intervall dauert (große Cluster, langsames S3), würde sonst der nächste Job parallel starten — beide bekämen403vom Auth-Lease-TTL oder produzierten korrupte Files.Forbidwartet stattdessen.backoffLimit: 2: zwei Retry-Versuche, dann lässt der Job es. Höher führt zu „Endless-Loop-Spam”, wenn z. B. die Auth-Role kaputt ist.successfulJobsHistoryLimit: 3/failedJobsHistoryLimit: 5: behält die letzten Jobs als Pods im Cluster — wichtig fürs Debugging nach Failures, ohne den etcd zuzumüllen.BAO_ADDR=…openbao-active.openbao.svc:8200: spricht den Leader direkt an (Hintergrund: kubernetes-service-registration). Der Snapshot-Endpoint ist Write-Forwarding-fähig, aber direkter Leader-Hit spart einen Hop.--sse aws:kms --sse-kms-key-id: Verschlüsselung at-rest mit Customer-Managed-Key. Pflicht bei jedem ernst gemeinten Setup.
Für diesen CronJob braucht es zusätzlich:
- Eine ServiceAccount
openbao-snapshot-agentim Namespaceopenbao(manifest mitliefern oder per Helm chart anlegen, falls dort vorhanden). - Die Auth-Role aus § 1.2 — bindet diese ServiceAccount an die Policy.
3 — Schicht 2: Seal-Material backupen
Je nach Seal-Mechanismus (siehe seal-unseal) sieht das anders aus:
3.1 — Auto-Unseal mit Cloud-KMS (AWS / GCP / Azure)
Der eigentliche Master-Key wird vom Cloud-KMS verwaltet — OpenBao hat ihn nie im Klartext. Backup-Anforderungen:
- Cross-Region-Replication: KMS-Key in einer zweiten Region replizieren. Bei AWS: Multi-Region-Key mit Primary in
eu-central-1und Replica ineu-west-1. Wenn die erste Region offline ist, kann der Cluster trotzdem unsealen. - Recovery Keys sichern: Auto-Unseal generiert beim
bao operator initzusätzlich Recovery Keys (Shamir-split). Die braucht man füroperator generate-rootund Seal-Migrationen. Genauso schützen wie klassische Shamir-Keys (siehe nächster Abschnitt). Dürfen niemals in K8s-Secrets oder Git landen. - IAM-Policy für den KMS-Key dokumentieren: wer welche
kms:Decrypt/kms:Encrypt-Rechte hat. Im Disaster-Restore-Cluster wird derselbe IAM-Setup gebraucht.
3.2 — Transit-Seal (zweiter OpenBao-Cluster)
Das „Seal-Material” ist hier das Auth-Token, das der primäre Cluster gegen den zweiten OpenBao verwendet, plus die Transit-Key-ID. Backup:
- Auth-Token als verschlüsseltes File offline (z. B.
age-encrypted auf einem Yubikey). - Transit-Cluster seinerseits backupen — er ist genauso disaster-recovery-relevant.
3.3 — Shamir-Unseal (PoC, nicht für Production)
Hier sind die Unseal-Keys aus bao operator init selbst das Seal-Material. Backup:
- 5 Shards auf 5 verschiedene Personen verteilen (3/5-Threshold).
- Niemals digital zusammenführen.
- Niemals in K8s-Secrets, ConfigMaps oder Git.
- Schwer-Tresor + Yubikey-verschlüsselt + Notar — die übliche Belt-and-Suspenders-Routine.
4 — Schicht 3: Manifeste und Konfiguration backupen
Drei Bestandteile:
4.1 — Git-Repo
Folgendes muss versioniert sein:
values-ha.yamlfür das OpenBao-Chart.- cert-manager-Manifeste (
Issuer,Certificate) — siehe k8s-ha-from-scratch § 10 bzw. rke2-ha-setup § 10 / k3s-ha-setup § 10. - NetworkPolicies (Cilium oder vanilla).
- StorageClass-Definitionen (falls custom).
- Snapshot-Agent-Config (falls eigener CronJob aus § 2).
- Auth-Role-Manifeste (als
kubectl exec-Skript oder Terraform).
Sensible Werte (S3-Credentials, KMS-Key-IDs, Recovery-Keys) niemals im Klartext — entweder SealedSecrets oder ExternalSecrets-Pointer.
4.2 — Velero als zweite Sicherheits-Schicht (optional)
Velero sichert ganze Namespaces inkl. Resources, PVCs und Secrets. Nicht als Ersatz für den Raft-Snapshot — Velero kennt die Barrier-Konsistenz nicht und kann keinen konsistenten Snapshot der OpenBao-Daten machen. Aber als zweite Schicht für die K8s-Resources nützlich:
velero install --provider aws --bucket openbao-velero ...
velero schedule create openbao-daily \
--schedule="0 2 * * *" \
--include-namespaces openbao \
--exclude-resources persistentvolumeclaims,persistentvolumes
Wichtig: PVCs explizit ausschließen — sonst landet ein nicht-konsistenter Stand der Raft-Volumes im Velero-Backup, der beim Restore Schaden anrichtet. Velero ist hier nur fürs Manifest-Re-Apply.
4.3 — etcd-Snapshots der K8s-Distribution
Orthogonal zum OpenBao-Backup, aber wichtig: K8s selbst muss auch wiederherstellbar sein.
- RKE2 / k3s: schreiben automatisch etcd-Snapshots nach
/var/lib/rancher/{rke2,k3s}/server/db/snapshots/(alle 12 h, 5 Snapshots Default). Diese auf S3 oder NFS wegspiegeln. - kubeadm:
etcdctl snapshot saveals CronJob auf einem Control-Plane-Node, ebenfalls nach S3.
5 — NetworkPolicy für den Snapshot-Pfad
Wenn im openbao-Namespace eine Default-Deny-Egress-NetworkPolicy aktiv ist (empfohlen), muss der Snapshot-CronJob expliziten Egress bekommen.
Mit Cilium:
apiVersion: cilium.io/v2
kind: CiliumNetworkPolicy
metadata:
name: openbao-snapshot-egress
namespace: openbao
spec:
endpointSelector:
matchLabels:
app.kubernetes.io/name: openbao-snapshot-agent
egress:
# zum OpenBao-Service (für bao snapshot save)
- toEndpoints:
- matchLabels:
app.kubernetes.io/name: openbao
component: server
toPorts:
- ports:
- port: "8200"
protocol: TCP
# zur K8s-API (für ServiceAccount-Token-Review beim Login)
- toEntities:
- kube-apiserver
# zu DNS
- toEndpoints:
- matchLabels:
io.kubernetes.pod.namespace: kube-system
k8s-app: kube-dns
toPorts:
- ports:
- port: "53"
protocol: UDP
# zu S3 (über CIDR oder FQDN-Match)
- toFQDNs:
- matchPattern: "*.amazonaws.com"
toPorts:
- ports:
- port: "443"
protocol: TCP
Warum jeder Block einzeln aufgeführt ist und nicht ein generisches “allow all egress”: jede Lücke ist ein potenzieller Exfiltrations-Pfad für ein kompromittiertes Snapshot-Token. Der CronJob darf zum OpenBao-Service hin, zur K8s-API für den Token-Review, zu DNS und zu S3 — und nichts anderes.
6 — Restore-Verfahren im laufenden Cluster
Restore ist eine seltene, folgenschwere Operation. Vor dem Ernstfall mindestens einmal in einer Sandbox üben (§ 7).
Allgemeines Verfahren (source: raw/docs/commands/operator/raft.md):
6.1 — Snapshot-File holen
TIMESTAMP=20260520T103015Z
aws s3 cp s3://openbao-snapshots/openbao-snapshot-${TIMESTAMP}.snap ./restore.snap
6.2 — In einen OpenBao-Pod kopieren
kubectl -n openbao cp ./restore.snap openbao-0:/tmp/restore.snap
6.3 — Restore auf dem Leader
Erst sicherstellen, dass openbao-0 der Leader ist:
kubectl -n openbao get pods -l openbao-active=true
Wenn ein anderer Pod Leader ist, dorthin kopieren und dort ausführen.
kubectl -n openbao exec -ti openbao-0 -- bao login <ROOT_TOKEN>
kubectl -n openbao exec -ti openbao-0 -- bao operator raft snapshot restore /tmp/restore.snap
Was hier passiert: der Restore überschreibt den aktuellen Cluster-State mit dem Inhalt des Snapshots. Es ist eine Hard-Replace-Operation, kein Merge. Die anderen Voter synchronisieren sich automatisch über Raft auf den neuen State (source: raw/docs/commands/operator/raft.md).
6.4 — Verifikation
kubectl -n openbao exec -ti openbao-0 -- bao status
kubectl -n openbao exec -ti openbao-0 -- bao operator raft list-peers
kubectl -n openbao exec -ti openbao-0 -- bao secrets list
Erwartet: Cluster unsealed, Leader-Wahl stabil, Secrets-Mounts auf dem Stand vom Snapshot-Zeitpunkt.
6.5 — Falls der Cluster komplett tot ist (Disaster Recovery)
Ablauf wenn keine drei OpenBao-Pods mehr existieren — z. B. weil die PVCs unwiederbringlich weg sind:
- K8s-Cluster ist da (aus etcd-Snapshot wiederhergestellt oder neu aufgesetzt — siehe k8s-ha-from-scratch / rke2-ha-setup / k3s-ha-setup).
- cert-manager-PKI restoren aus Git — neue Certs werden ausgestellt, weil cert-manager die Issuer-Manifeste sieht.
- Seal-Material verfügbar machen: KMS-Key zugänglich (gleiche Region/Account oder Cross-Region-Replica), oder Recovery Keys / Shamir Shards bereitstellen.
- Helm-Install mit
values-ha.yamlaus Git. Drei Pods kommen hoch, alle sealed. - Nur
openbao-0unsealen — die anderen lassen wir bewusst sealed. - Snapshot in
openbao-0kopieren, dannbao operator raft snapshot restore. openbao-0neu unsealen falls der Restore den Seal-Status zurückgesetzt hat.openbao-1undopenbao-2unsealen — sie joinen automatisch viaretry_joinund ziehen den State von openbao-0.- Verifikation wie in § 6.4.
7 — Test-Restore in einer Sandbox
Regel: ungetesteter Restore = Hoffnungswert, kein Backup. Mindestens einmal pro Quartal in einem isolierten Sandkasten durchspielen.
Wichtig: das Test-Cluster darf keinen Netzzugriff auf Produktiv-Systeme haben (DBs, Cloud-APIs). Sonst rotiert das gerestorete Cluster Credentials, die das echte Produktiv-Cluster gerade noch benutzt — Production-Outage mit Anlauf.
Konkretes Sandbox-Setup:
# values-restore-sandbox.yaml — Modifikation gegenüber Production:
global:
tlsDisable: false
server:
# eigener Namespace, kein Konflikt mit Production
# (kubectl create namespace openbao-restore-test)
ha:
enabled: true
replicas: 3
raft:
enabled: true
config: |
# exakt dieselbe Config wie Production — sonst klappt
# der TLS-Handshake mit dem gerestoreten State nicht
...
# zwei Sicherungen gegen Cred-Rotation:
# 1) NetworkPolicy default-deny egress (Sandbox-only)
# 2) Mock-Endpoints für Database-Mounts in der Config
Im Sandbox-Namespace:
- Identisches Seal-Setup wie Production (Cross-Region-KMS-Key oder kopierte Recovery-Keys).
- Cilium-NetPol mit
default deny all egress. - OpenBao-Pods hochfahren,
openbao-0mit dem Production-Snapshot restoren. - Aktiver Check:
bao read database/roles/...darf keine echten DB-Credentials erzeugen. Die NetPol sollte die ausgehende DB-Connection blocken — wenn doch eine durchgeht, ist die Isolation kaputt. - Smoketest:
bao kv get secret/fooaus einem Test-Pod heraus. - Sandbox wieder abreißen —
kubectl delete namespace openbao-restore-test.
Skript-Vorschlag für die Quartals-Übung: ein Job, der die ganze Sequenz automatisiert und in einen separaten restore-drill-Namespace deployt, am Ende einen Report ins Slack postet.
8 — Monitoring und Alerting
Ein silent-failing CronJob ist schlimmer als gar kein Backup — er gibt eine falsche Sicherheit. Was zu monitoren ist:
8.1 — CronJob-Failures via kube-state-metrics
Prometheus-Query, die einen Alarm auslöst, wenn 2 Stunden lang kein erfolgreicher Snapshot-Job gelaufen ist:
time() - kube_cronjob_status_last_successful_time{cronjob="openbao-snapshot-agent",namespace="openbao"} > 7200
Alert-Regel:
- alert: OpenBaoSnapshotStale
expr: time() - kube_cronjob_status_last_successful_time{cronjob="openbao-snapshot-agent",namespace="openbao"} > 7200
for: 5m
labels:
severity: critical
annotations:
summary: "OpenBao snapshot CronJob hat seit >2h keinen erfolgreichen Lauf"
runbook: "https://wiki.example.com/runbooks/openbao-snapshot-stale"
8.2 — Snapshot-Größen-Anomalien
Plötzlich deutlich kleinere Snapshots sind ein Warnsignal — meist Datenverlust durch Bug, manchmal ein kaputter Backup-Pfad. Bucket-Object-Size als Prometheus-Metrik per Sidecar oder per CloudWatch:
(s3_object_size_bytes{bucket="openbao-snapshots"}
/ s3_object_size_bytes{bucket="openbao-snapshots"} offset 24h)
< 0.5
Schlägt an, wenn der heutige Snapshot weniger als 50 % der Größe von vor 24 h hat.
8.3 — Bucket-Verfügbarkeit
probe_success{job="blackbox-s3", target="openbao-snapshots"} == 0
Über Prometheus Blackbox Exporter oder cloud-natives Monitoring (CloudWatch S3-Metriken).
8.4 — OpenBao Audit-Trail-Bestätigung
Jeder Snapshot-Pull erzeugt einen Audit-Log-Eintrag (sys/storage/raft/snapshot read). Wenn der Audit-Log diesen Eintrag nicht zeigt, hat zwar ein Job gelaufen, aber er hat keinen Snapshot gezogen — z. B. wegen Auth-Fehler. Audit-Logging ist ohnehin Pflicht (siehe audit) — die Snapshot-Calls beobachten ist freie Zusatz-Visibility.
9 — Antipatterns
- Snapshot ins PVC schreiben. Wenn der CronJob das
.snap-File auf ein OpenBao-eigenes PVC legt, ist es bei Volume-Loss genauso weg wie alles andere. Snapshot muss nach off-cluster. - S3-Credentials als plain
kubectl create secretohne Sealed-/External-Secrets-Schicht. Klartext im etcd, Klartext in jedem Backup, Klartext in jedem Cluster-Export. - CronJob ohne
concurrencyPolicy: Forbid. Bei großen Clustern überlappen Jobs, beide bekommenToken TTL exceededoder produzieren halbe Files. Default istAllow— explizit aufForbidsetzen. - Recovery-Keys / Shamir-Shards in einem K8s-Secret. Wenn der Cluster weg ist, sind sie auch weg. Die müssen außerhalb des K8s-Bestands liegen.
- Velero allein als Backup. Velero sichert Manifeste und PVCs, kennt aber keine Barrier-Konsistenz. Ein gerestoretes PVC kann mitten in einem Raft-Write korrupt sein. Velero ist Sekundär, nicht Primär.
- Snapshot-Pfad direkt auf den Standby-Service zeigen. Auch wenn
bao operator raft snapshot saveRequest-Forwarding kann — auf denopenbao-active-Service zeigen (kubernetes-service-registration), sonst hat man bei Failover-Race-Conditions Snapshots, die mitten im Leader-Wechsel erstellt wurden. - Snapshot-Schedule = Production-Schreibfrequenz. Wer alle 30 s in OpenBao schreibt und alle 15 min snapshottet, akzeptiert implizit 15 Minuten Datenverlust-Risiko. Schedule am RPO-Ziel ausrichten, nicht am „klingt vernünftig”.
- Kein Test-Restore. Siehe § 7. Ohne geübten Restore ist die Backup-Strategie eine Annahme, kein System.
- Snapshot-Bucket im selben AWS-Account wie der Cluster. Wenn ein kompromittiertes Cluster-Token Bucket-Zugriff bekommt (oder ein gelöschter IAM-User Bucket-Policies cascadiert), ist alles weg. Snapshots gehören in einen separaten Account / Subscription / Project.
10 — Zusammenfassung: Minimale Production-Ready-Konfiguration
Was zwingend stehen muss, bevor ein OpenBao-K8s-Cluster Production-tauglich ist:
-
snapshotAgent(oder eigener CronJob) läuft mit Schedule ≤ RPO-Ziel. - S3-Bucket im separaten Account, mit KMS-Verschlüsselung at-rest, ohne
Delete-Recht für den Cluster-Schlüssel. - Bucket-Lifecycle: kurz/mittel/lang gestaffelte Retention.
- Auto-Unseal mit Cross-Region-KMS-Key.
- Recovery Keys offline abgelegt.
-
values-ha.yaml+ alle Manifeste in Git (Secrets via SealedSecrets oder ExternalSecrets). - etcd-Snapshots der K8s-Distribution nach extern.
- Audit-Devices aktiv (siehe audit).
- Prometheus-Alerts auf stale CronJob und Snapshot-Size-Anomalien.
- Restore in Sandbox erfolgreich durchgeführt — innerhalb der letzten 90 Tage.
Related pages
- backups — konzeptionelle Grundlage: warum, was, Snapshot-Mechanik, Offline-vs.-Online
- raft — Snapshot-Internals, Konsens-Mechanik, Recovery
- seal-unseal — Auto-Unseal, Recovery-Keys, Shamir
- k8s-ha-setup — kompakter Helm-Bring-Up (Snapshot-Agent erwähnt)
- k8s-ha-from-scratch — kubeadm-End-to-End-Variante (Snapshot-Hinweis im Hardening-Teil)
- rke2-ha-setup — RKE2-Variante mit etcd-Snapshots als zusätzlicher Layer
- k3s-ha-setup — k3s-Variante mit eingebauten etcd-Snapshots
- audit — Audit-Trail für Snapshot-Calls
- upgrading — Snapshot vor jedem Upgrade als Pflicht
- kubernetes-service-registration — warum der Snapshot-Pfad auf
openbao-activezeigen sollte