Files
Wabenhain/k8s/02-secret.example.yaml
T
Nick SzaunigandClaude Opus 5 479ace1b88 docs: Konfigurationsvorlagen vollstaendig und uebernahmefaehig machen
01-configmap.yaml und 02-secret.example.yaml sind jetzt so geschrieben, dass
sie sich als Produktivkonfiguration uebernehmen lassen: in drei Abschnitte
gegliedert (ANPASSEN / Vorgaben / optional), jeder Wert mit der Angabe, was
ohne ihn nicht funktioniert. Anzupassen sind genau zwei Werte in der
ConfigMap und die mit "CHANGE-ME" markierten im Secret; alles Uebrige traegt
eine brauchbare Vorgabe oder darf leer bleiben.

Beide Dateien decken zusammen jede Umgebungsvariable ab, die die Anwendung
liest, und jeder Schluessel steht in genau einer der beiden. Bisher war das
Handarbeit und driftete: GOOGLE_PLACES_API_KEY lag in der unverschluesselten
ConfigMap, APICORE_API_URL unnoetig im Secret.

Neu ist deshalb src/lib/k8s-manifest-env.test.ts. Der Test liest die
process.env-Zugriffe unter src/ und die Schluessel der beiden Manifeste und
prueft vier Zusagen: keine gelesene Variable fehlt, kein Schluessel steht
doppelt (sonst entschiede allein die Reihenfolge der envFrom-Eintraege,
welcher Wert gewinnt), kein Schluessel ist tot, und die committete
Secret-Vorlage enthaelt keine ausgefuellten Werte.

Beim ersten Lauf hat der Test zwei Dinge gefunden. ADMIN_EMAIL und
ADMIN_PASSWORD liest nicht die App, sondern prisma/seed.ts — das ist
legitim und steht jetzt begruendet in einer Ausnahmeliste, zusammen mit
DB_PASSWORD fuer das Datenbank-StatefulSet. STRIPE_PUBLISHABLE_KEY dagegen
wird von keiner einzigen Code-Stelle gelesen: der Checkout laeuft
vollstaendig serverseitig ueber eine Stripe-Checkout-Session. Der Schluessel
ist aus Secret-Vorlage, .env.example, docker-compose.yml und README
entfernt, mit Hinweis, wofuer man ihn braeuchte.

Co-Authored-By: Claude Opus 5 (1M context) <[email protected]>
2026-09-17 03:06:18 +02:00

134 lines
6.4 KiB
YAML

# Geheime Konfiguration. Vorlage OHNE echte Werte — kopieren nach
# 02-secret.yaml (per .gitignore ausgeschlossen, NIE committen), ausfuellen,
# dann "kubectl apply -f k8s/02-secret.yaml".
#
# ANPASSEN: jeder Wert, der "CHANGE-ME" enthaelt. Die leeren Werte ("")
# gehoeren zu optionalen Funktionen und koennen leer bleiben — die App faellt
# dann nachvollziehbar zurueck (siehe Kommentar an der jeweiligen Stelle) und
# stuerzt nicht ab. Nichts hier darf FEHLEN: ein fehlender und ein leerer
# Schluessel sind fuer die App zwar dasselbe, aber ein vollstaendiges Secret
# macht sofort sichtbar, welche Funktionen es ueberhaupt gibt.
#
# Gegenprobe nach dem Ausrollen: "pnpm preflight" meldet jeden Wert, der fuer
# den Live-Betrieb fehlt, als Blocker — mit Begruendung, was ohne ihn kaputt
# geht.
apiVersion: v1
kind: Secret
metadata:
name: wabenhain-secrets
namespace: wabenhain
type: Opaque
stringData:
# ---------------------------------------------------------------------
# PFLICHT — ohne diese Werte ist kein Live-Betrieb moeglich
# ---------------------------------------------------------------------
# Host "wabenhain-postgres" bzw. "wabenhain-mysql" passend zum Service aus
# 03-postgres.yaml/03-mysql.yaml waehlen; Port 5432 (Postgres) bzw. 3306
# (MySQL). Muss zu DATABASE_PROVIDER in 01-configmap.yaml passen.
#
# WICHTIG: das Passwort MUSS hier URL-encodiert sein — Sonderzeichen wie
# @ : / % # ? & werden sonst als Trennzeichen der URL gelesen, und die
# Verbindung schlaegt mit einer irrefuehrenden Meldung fehl. DB_PASSWORD
# weiter unten bleibt dagegen roh. Encodieren mit:
# node -e "console.log(encodeURIComponent('DEIN-PASSWORT'))"
DATABASE_URL: "postgresql://wabenhain:CHANGE-ME@wabenhain-postgres:5432/wabenhain?schema=public"
# Dasselbe Passwort wie in DATABASE_URL, aber UNENCODIERT. Das
# Datenbank-StatefulSet legt damit den Datenbanknutzer an (03-postgres.yaml
# bzw. 03-mysql.yaml), und die Sicherung in 07-backup-cronjob.yaml meldet
# sich damit an. Die App selbst liest diesen Wert nicht — sie kennt nur
# DATABASE_URL.
DB_PASSWORD: "CHANGE-ME"
# Signiert die Login-Cookies. Mindestens 32 Zeichen, unvorhersehbar.
# Erzeugen mit: openssl rand -base64 32
# Wird dieser Wert bekannt, ist jede Sitzung faelschbar, auch die eines
# Admins. Ihn zu aendern meldet alle Kundinnen und Kunden ab, mehr nicht.
SESSION_SECRET: "CHANGE-ME-mindestens-32-zeichen-zufaellig"
# Erstes Admin-Konto, angelegt beim Start durch "prisma db seed".
# Unvorhersehbare Adresse waehlen, NICHT "admin@..." oder "info@...":
# sonst kann sich jemand die erwartete Adresse vorher selbst registrieren
# und das Konto belegen (siehe README "Admin-Zugang").
ADMIN_EMAIL: "[email protected]"
# Mindestens 12 Zeichen. Dieses Konto darf Rueckerstattungen ausloesen und
# alle Kundendaten einsehen. Erzeugen mit:
# openssl rand -base64 24 | tr -d '=+/' | head -c 28
ADMIN_PASSWORD: "CHANGE-ME"
# Stripe im LIVE-Modus (sk_live_...). Mit einem sk_test-Schluessel sehen
# Bestellungen erfolgreich aus, ohne dass Geld fliesst.
STRIPE_SECRET_KEY: "CHANGE-ME"
# Ohne Webhook-Secret wird keine Zahlung bestaetigt: Bestellungen bleiben
# auf "NEU" stehen, es gibt keine Rechnung und keine Bestaetigungsmail.
# Stammt aus dem Webhook-Endpunkt im Stripe-Dashboard, der auf
# https://wabenhain.com/api/checkout/webhook zeigt (whsec_...).
STRIPE_WEBHOOK_SECRET: "CHANGE-ME"
# Ohne SMTP werden E-Mails nur ins Server-Log geschrieben (src/lib/mail.ts):
# keine Bestellbestaetigung, keine Versandmeldung, keine
# Passwort-Zuruecksetzung. Funktioniert mit jedem SMTP-Anbieter; Port und
# Absenderadresse stehen als SMTP_PORT/SMTP_FROM in 01-configmap.yaml.
SMTP_HOST: "CHANGE-ME"
SMTP_USER: "CHANGE-ME"
SMTP_PASSWORD: "CHANGE-ME"
# ---------------------------------------------------------------------
# EMPFOHLEN — der Shop laeuft auch ohne, aber ungeschuetzt
# ---------------------------------------------------------------------
# Cloudflare Turnstile (CAPTCHA) fuer Registrierung, Newsletter und
# Firmenanfrage. Leer = kein CAPTCHA; die Formulare bleiben nutzbar, aber
# bei einem oeffentlichen Shop sammeln sich darueber schnell Bot-Eintraege.
# Der oeffentliche Gegenpart NEXT_PUBLIC_TURNSTILE_SITE_KEY steht in
# 01-configmap.yaml — beide setzen oder beide leer lassen.
TURNSTILE_SECRET_KEY: ""
# ---------------------------------------------------------------------
# OPTIONAL — leer lassen, solange die Funktion nicht genutzt wird
# ---------------------------------------------------------------------
# Google-Places-API fuer echte Google-Rezensionen auf der Startseite.
# Leer = die Startseite zeigt die statischen Beispiel-Stimmen.
#
# Der Schluessel gehoert hierher und NICHT in 01-configmap.yaml: er ist
# abrechenbar, und eine ConfigMap ist unverschluesselt und in der
# Cluster-Oberflaeche im Klartext lesbar. Die zugehoerige GOOGLE_PLACE_ID
# ist dagegen oeffentlich und steht dort.
GOOGLE_PLACES_API_KEY: ""
# Zugangsdaten des ApiCore-Sync-Kontos (Admin -> Ernten bzw. Produkte ->
# "ApiCore-Synchronisierung"). Leer = der Sync-Knopf meldet einen
# Konfigurationsfehler, statt still nichts zu tun.
#
# ApiCore kennt keinen API-Key-Mechanismus, nur E-Mail/Passwort-Login —
# deshalb hier ein eigenes, MFA-FREIES Service-Konto eintragen, nicht einen
# echten Admin-Zugang. Noetige Leserechte: harvests:read, products:read,
# extractions:read, bottling:read und optional apiaries:read. Die Adresse
# APICORE_API_URL ist nicht geheim und steht in 01-configmap.yaml.
APICORE_SYNC_EMAIL: ""
APICORE_SYNC_PASSWORD: ""
# DHL-Versandmarken (Admin -> Bestellung -> "DHL-Versandmarke"), siehe
# .env.example fuer die vollstaendige Einrichtung. Leer = der Knopf meldet
# "noch nicht konfiguriert", der Rest des Shops ist unberuehrt. Ob Sandbox
# oder Echtbetrieb, steuert DHL_ENVIRONMENT in 01-configmap.yaml.
DHL_API_KEY: ""
DHL_API_SECRET: ""
DHL_GKP_USERNAME: ""
DHL_GKP_PASSWORD: ""
DHL_ACCOUNT_NUMBER: ""
# Entra-ID-SSO ("Mit Microsoft anmelden"), siehe .env.example fuer die
# Einrichtung in Azure. Leer = der Anmeldeknopf erscheint gar nicht erst.
# ENTRA_REDIRECT_URI muss exakt der in Azure hinterlegten Adresse
# entsprechen und auf die echte Domain zeigen:
# https://wabenhain.com/api/auth/entra/callback
ENTRA_TENANT_ID: ""
ENTRA_CLIENT_ID: ""
ENTRA_CLIENT_SECRET: ""
ENTRA_REDIRECT_URI: ""