apiVersion: batch/v1 kind: CronJob metadata: name: naviya-etcd-backup namespace: kube-system spec: schedule: "17 3 * * *" timeZone: Etc/UTC concurrencyPolicy: Forbid startingDeadlineSeconds: 3600 successfulJobsHistoryLimit: 3 failedJobsHistoryLimit: 3 jobTemplate: spec: backoffLimit: 1 activeDeadlineSeconds: 1800 ttlSecondsAfterFinished: 86400 template: metadata: labels: app.kubernetes.io/name: naviya-etcd-backup spec: automountServiceAccountToken: false hostNetwork: true dnsPolicy: ClusterFirstWithHostNet restartPolicy: Never nodeSelector: node-role.kubernetes.io/control-plane: "" tolerations: - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule securityContext: seccompProfile: type: RuntimeDefault initContainers: - name: snapshot image: registry.k8s.io/etcd:3.6.8-0 imagePullPolicy: IfNotPresent command: - etcdctl args: - --endpoints=https://127.0.0.1:2379 - --cacert=/etcd-certs/ca.crt - --cert=/etcd-certs/healthcheck-client.crt - --key=/etcd-certs/healthcheck-client.key - snapshot - save - /work/snapshot.db securityContext: runAsUser: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumeMounts: - name: etcd-certs mountPath: /etcd-certs readOnly: true - name: work mountPath: /work - name: validate image: registry.k8s.io/etcd:3.6.8-0 imagePullPolicy: IfNotPresent command: - etcdutl args: - --write-out=table - snapshot - status - /work/snapshot.db securityContext: runAsUser: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumeMounts: - name: work mountPath: /work readOnly: true containers: - name: encrypt image: docker.io/alpine/openssl@sha256:61cec9c1f221755bf995f1f309211b222164599e2d1943b666f878ef644d3a0e imagePullPolicy: IfNotPresent command: ["sh", "-ec"] args: - | umask 077 stamp="$(date -u +%Y%m%dT%H%M%SZ)" name="naviya-etcd-${stamp}-${NODE_NAME}.db.enc" target="/offsite/daily/${name}" tmp="${target}.tmp.$$" checksum="${target}.sha256" checksum_tmp="${checksum}.tmp.$$" verification="/work/verification.db" cleanup() { rm -f \ "$tmp" \ "$checksum_tmp" \ "$verification" } trap cleanup EXIT openssl enc \ -aes-256-cbc \ -salt \ -pbkdf2 \ -iter 600000 \ -md sha256 \ -in /work/snapshot.db \ -out "$tmp" \ -pass file:/key/passphrase test -s "$tmp" openssl enc \ -d \ -aes-256-cbc \ -pbkdf2 \ -iter 600000 \ -md sha256 \ -in "$tmp" \ -out "$verification" \ -pass file:/key/passphrase snapshot_sha="$(sha256sum /work/snapshot.db | awk '{print $1}')" verification_sha="$(sha256sum "$verification" | awk '{print $1}')" test "$snapshot_sha" = "$verification_sha" rm -f "$verification" echo "Chiffrement et déchiffrement de contrôle : OK" mv "$tmp" "$target" encrypted_sha="$(sha256sum "$target" | awk '{print $1}')" printf '%s %s\n' \ "$encrypted_sha" \ "$name" \ > "$checksum_tmp" mv "$checksum_tmp" "$checksum" ( cd /offsite/daily sha256sum -c "$(basename "$checksum")" ) echo "Snapshot etcd quotidien validé : $target" copy_tmp="" copy_checksum_tmp="" cleanup() { rm -f \ "$tmp" \ "$checksum_tmp" \ "$verification" test -z "$copy_tmp" || rm -f "$copy_tmp" test -z "$copy_checksum_tmp" || rm -f "$copy_checksum_tmp" } copy_archive() { tier="$1" tier_dir="/offsite/${tier}" tier_target="${tier_dir}/${name}" copy_tmp="${tier_target}.tmp.$$" tier_checksum="${tier_target}.sha256" copy_checksum_tmp="${tier_checksum}.tmp.$$" cp "$target" "$copy_tmp" copy_sha="$(sha256sum "$copy_tmp" | awk '{print $1}')" test "$copy_sha" = "$encrypted_sha" mv "$copy_tmp" "$tier_target" printf '%s %s\n' \ "$encrypted_sha" \ "$name" \ > "$copy_checksum_tmp" mv \ "$copy_checksum_tmp" \ "$tier_checksum" ( cd "$tier_dir" sha256sum -c "$(basename "$tier_checksum")" ) copy_tmp="" copy_checksum_tmp="" echo "Copie ${tier} validée : $tier_target" } if test "$(date -u +%u)" = "7"; then copy_archive weekly fi if test "$(date -u +%d)" = "01"; then copy_archive monthly fi find /offsite/daily \ -type f \ \( -name '*.db.enc' -o -name '*.db.enc.sha256' \) \ -mtime +14 \ -delete find /offsite/weekly \ -type f \ \( -name '*.db.enc' -o -name '*.db.enc.sha256' \) \ -mtime +56 \ -delete find /offsite/monthly \ -type f \ \( -name '*.db.enc' -o -name '*.db.enc.sha256' \) \ -mtime +370 \ -delete find /offsite \ -type f \ -name '*.tmp.*' \ -mtime +1 \ -delete trap - EXIT echo "Sauvegarde etcd chiffrée terminée avec succès" env: - name: NODE_NAME valueFrom: fieldRef: fieldPath: spec.nodeName resources: requests: cpu: 25m memory: 32Mi limits: cpu: 500m memory: 256Mi securityContext: runAsUser: 0 allowPrivilegeEscalation: false readOnlyRootFilesystem: true capabilities: drop: ["ALL"] volumeMounts: - name: work mountPath: /work - name: offsite mountPath: /offsite - name: encryption-key mountPath: /key readOnly: true volumes: - name: work emptyDir: {} - name: etcd-certs hostPath: path: /etc/kubernetes/pki/etcd type: Directory - name: offsite persistentVolumeClaim: claimName: naviya-etcd-backups-offsite - name: encryption-key secret: secretName: naviya-etcd-backup-encryption defaultMode: 0400