Files
Wabenhain/k8s/02-secret.example.yaml
T
Nick SzaunigandClaude Opus 5.5 11910dd6dd
Docker Image bauen und veroeffentlichen / build-and-push (push) Successful in 2m25s
refactor(mail): send via the SSO app registration, drop SMTP basic auth
Mail now uses the same Entra ID app registration as "Sign in with
Microsoft" (ENTRA_TENANT_ID, ENTRA_CLIENT_ID, ENTRA_CLIENT_SECRET), so
only one enterprise application is needed. The app additionally gets
the SMTP.SendAsApp permission; Exchange still limits it to the mailbox
granted via Add-MailboxPermission.

Password (basic auth) sending is removed entirely: SMTP_HOST, SMTP_PORT,
SMTP_PASSWORD and the separate SMTP_OAUTH_* settings are gone. The
transport is fixed to smtp.office365.com:587 with required STARTTLS.
SMTP_USER (sending mailbox) and SMTP_FROM remain.

- lib/entra.ts exposes readEntraAppCredentials(), shared by SSO and mail
- diagnosis drops basic-auth-only causes; hints name the ENTRA_* values
  and warn that an expired secret breaks sign-in and mail together
- admin e-mail test, .env.example, docker-compose, k8s manifests,
  preflight and README updated

Verified against Entra ID with fake values: token error classified,
client secret never exposed, missing ENTRA_CLIENT_SECRET reported.

Co-Authored-By: Claude Opus 5.5 (1M context) <[email protected]>
2026-09-26 19:23:05 +02:00

143 lines
7.0 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"
# Mailversand ueber Microsoft 365 mit OAuth 2.0 (src/lib/mail.ts) — mit der
# App-Registrierung der Entra-ID-SSO unten (ENTRA_TENANT_ID,
# ENTRA_CLIENT_ID, ENTRA_CLIENT_SECRET), kein Passwort. Ohne diese Werte
# und SMTP_USER werden E-Mails nur ins Server-Log geschrieben: keine
# Bestellbestaetigung, keine Versandmeldung, keine Passwort-Zuruecksetzung.
# SMTP_USER ist das Postfach, mit dem gesendet wird (z. B.
# [email protected]) — dieselbe Adresse wie SMTP_FROM in
# 01-configmap.yaml. Einrichtung: README "E-Mail-Versand".
SMTP_USER: "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: ""
# Optional: eigener Key fuer die DHL-Sendungsverfolgung auf
# /bestellung-verfolgen. Leer = DHL_API_KEY wird verwendet.
DHL_TRACKING_API_KEY: ""
# Entra-ID-SSO ("Mit Microsoft anmelden"), siehe .env.example fuer die
# Einrichtung in Azure. Leer = der Anmeldeknopf erscheint gar nicht erst.
# Dieselbe App versendet auch die Shop-E-Mails (SMTP_USER oben, Berechtigung
# SMTP.SendAsApp) — ohne diese Werte also auch kein Mailversand. Das
# Client-Secret laeuft ab (hoechstens 24 Monate): Ablaufdatum im Kalender
# eintragen, sonst fallen Anmeldung UND Mails gleichzeitig aus.
# 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: ""