From cc038fbb27ab3c1d8b8e2615d084caa15f8a5b81 Mon Sep 17 00:00:00 2001 From: Janis Eccarius Date: Sat, 30 May 2026 12:52:00 +0200 Subject: [PATCH] feat(secrets): add sealed-secrets management and Grafana monitoring installation --- .claude/settings.local.json | 3 +- .../ingress-nginx/ingress-nginx.yaml | 27 ++++++ ingress-nginx/eu-central-1/values.yaml | 4 + scripts/deploy-to-cluster.sh | 15 +++- .../metrics-deployment-values.sh | 0 scripts/sealed-secrets-key.sh | 85 +++++++++++++++++++ 6 files changed, 132 insertions(+), 2 deletions(-) create mode 100644 eu-central-1/argo-apps/ingress-nginx/ingress-nginx.yaml create mode 100644 ingress-nginx/eu-central-1/values.yaml rename metrics-deployment-values.sh => scripts/metrics-deployment-values.sh (100%) mode change 100644 => 100755 create mode 100755 scripts/sealed-secrets-key.sh diff --git a/.claude/settings.local.json b/.claude/settings.local.json index bb9b45f..2683e76 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -1,7 +1,8 @@ { "permissions": { "allow": [ - "Bash(helm show *)" + "Bash(helm show *)", + "WebFetch(domain:raw.githubusercontent.com)" ] } } diff --git a/eu-central-1/argo-apps/ingress-nginx/ingress-nginx.yaml b/eu-central-1/argo-apps/ingress-nginx/ingress-nginx.yaml new file mode 100644 index 0000000..c926042 --- /dev/null +++ b/eu-central-1/argo-apps/ingress-nginx/ingress-nginx.yaml @@ -0,0 +1,27 @@ +apiVersion: argoproj.io/v1alpha1 +kind: Application +metadata: + name: ingress-nginx + namespace: argocd +spec: + project: default + destination: + namespace: ingress-nginx + server: https://kubernetes.default.svc + sources: + - repoURL: https://kubernetes.github.io/ingress-nginx + chart: ingress-nginx + targetRevision: 4.15.1 + helm: + valueFiles: + - $values/ingress-nginx/eu-central-1/values.yaml + - repoURL: git@git.janis-eccarius.de:NowChess/GitOps.git + path: ./ingress-nginx/eu-central-1 + ref: values + targetRevision: main + syncPolicy: + automated: + prune: true + selfHeal: true + syncOptions: + - CreateNamespace=true diff --git a/ingress-nginx/eu-central-1/values.yaml b/ingress-nginx/eu-central-1/values.yaml new file mode 100644 index 0000000..1d8e6ac --- /dev/null +++ b/ingress-nginx/eu-central-1/values.yaml @@ -0,0 +1,4 @@ +controller: + service: + type: LoadBalancer + externalTrafficPolicy: Cluster diff --git a/scripts/deploy-to-cluster.sh b/scripts/deploy-to-cluster.sh index 7b2358e..348f1e6 100644 --- a/scripts/deploy-to-cluster.sh +++ b/scripts/deploy-to-cluster.sh @@ -40,12 +40,25 @@ install_argocd() { install_cert_manager install_argocd -kubectl apply -f https://raw.githubusercontent.com/kubernetes/ingress-nginx/controller-v1.15.1/deploy/static/provider/baremetal/deploy.yaml +# Restore sealed-secrets key before ArgoCD syncs so existing SealedSecrets decrypt correctly. +# Export from old cluster first: ./sealed-secrets-key.sh export sealed-secrets-key-backup.yaml +echo "🔑 Restoring sealed-secrets key..." +read -rp "Path to sealed-secrets key backup (leave blank to skip): " KEY_FILE +if [[ -n "$KEY_FILE" ]]; then + ./sealed-secrets-key.sh import "$KEY_FILE" +else + echo "⚠️ Skipped. SealedSecrets will fail to decrypt until key is imported." +fi + kubectl apply -f https://github.com/kubernetes-sigs/metrics-server/releases/latest/download/components.yaml sleep 5s kubectl apply -f ../eu-central-1/root-apps-app.yaml +echo "📊 Installing Grafana monitoring..." +./metrics-deployment-values.sh +echo "✅ Grafana monitoring installed!" + clear ARGO_PW=$(kubectl -n argocd get secret argocd-initial-admin-secret -o jsonpath="{.data.password}" | base64 --decode) diff --git a/metrics-deployment-values.sh b/scripts/metrics-deployment-values.sh old mode 100644 new mode 100755 similarity index 100% rename from metrics-deployment-values.sh rename to scripts/metrics-deployment-values.sh diff --git a/scripts/sealed-secrets-key.sh b/scripts/sealed-secrets-key.sh new file mode 100755 index 0000000..4c380db --- /dev/null +++ b/scripts/sealed-secrets-key.sh @@ -0,0 +1,85 @@ +#!/usr/bin/env bash +set -euo pipefail + +NAMESPACE="kube-system" + +usage() { + echo "Manage sealed-secrets controller keys for cluster migration." + echo "" + echo "Usage:" + echo " $0 export [output-file] Export active key(s) to YAML (default: sealed-secrets-key-backup.yaml)" + echo " $0 import Import key(s) to new cluster before first ArgoCD sync" + echo "" + echo "IMPORTANT: Never commit the backup file to Git. Store in Passwords.kdbx." + exit 1 +} + +export_key() { + local out="${1:-sealed-secrets-key-backup.yaml}" + + local keys + keys=$(kubectl -n "$NAMESPACE" get secret \ + -l sealedsecrets.bitnami.com/sealed-secrets-key \ + -o name 2>/dev/null) + + if [[ -z "$keys" ]]; then + echo "Error: no sealed-secrets keys found in namespace $NAMESPACE" >&2 + exit 1 + fi + + # Strip runtime-only fields so the manifest is re-applicable + kubectl -n "$NAMESPACE" get secret \ + -l sealedsecrets.bitnami.com/sealed-secrets-key \ + -o yaml \ + | kubectl neat \ + > "$out" 2>/dev/null \ + || kubectl -n "$NAMESPACE" get secret \ + -l sealedsecrets.bitnami.com/sealed-secrets-key \ + -o yaml \ + | grep -v $'^\t' \ + | python3 -c " +import sys, yaml +docs = list(yaml.safe_load_all(sys.stdin)) +# Remove a List wrapper if present, emit individual documents +if len(docs) == 1 and docs[0].get('kind') == 'List': + docs = docs[0]['items'] +strip = {'resourceVersion', 'uid', 'selfLink', 'creationTimestamp', + 'generation', 'managedFields'} +for d in docs: + meta = d.get('metadata', {}) + for f in strip: + meta.pop(f, None) + d['metadata'] = meta +print(yaml.dump_all(docs, default_flow_style=False)) +" > "$out" + + local count + count=$(echo "$keys" | wc -l | tr -d ' ') + echo "Exported $count key(s) to: $out" + echo "" + echo "WARNING: Store this file securely (e.g. Passwords.kdbx). Never commit to Git." +} + +import_key() { + local in="${1:?'import requires a file argument — run: $0 import '}" + + if [[ ! -f "$in" ]]; then + echo "Error: file not found: $in" >&2 + exit 1 + fi + + echo "Importing sealed-secrets key(s) from: $in" + kubectl apply -f "$in" + + echo "" + echo "Key imported. If the sealed-secrets controller is already running, restart it:" + echo " kubectl -n kube-system rollout restart deployment/sealed-secrets" + echo "" + echo "Apply this BEFORE ArgoCD syncs the secrets application." +} + +case "${1:-}" in + export) export_key "${2:-}" ;; + import) import_key "${2:-}" ;; + *) usage ;; +esac