Merge #45: Beispielkonfiguration entschärfen
This commit was merged in pull request #51.
This commit is contained in:
+13
-3
@@ -103,7 +103,17 @@ VERTRAUE_PROXY=true
|
||||
# --- Objektspeicher (S3-kompatibel, EU!) ------------------------------------
|
||||
# Wird ab Etappe 3 verwendet. Beispiel: Hetzner Object Storage, Falkenstein.
|
||||
# ACHTUNG: Mit dem Anbieter muss ein AV-Vertrag nach Art. 28 DSGVO bestehen.
|
||||
S3_ENDPOINT=https://fsn1.your-objectstorage.com
|
||||
# Bleibt AUSKOMMENTIERT, solange das mitgelieferte MinIO benutzt wird.
|
||||
#
|
||||
# Die docker-compose.yml setzt dann von selbst http://minio:9000 - aber
|
||||
# nur, wenn die Variable leer ist. Steht hier ein Wert, gewinnt er, und
|
||||
# die Anwendung spricht mit einem fremden Host statt mit dem Speicher
|
||||
# nebenan. Genau das ist am 31.08.2026 passiert: Jede Ablage scheiterte
|
||||
# monatelang mit "NoSuchBucket", und die Zugangs-ID ging bei jedem Versuch
|
||||
# nach draussen.
|
||||
#
|
||||
# Nur setzen, wenn ein EXTERNER Objektspeicher benutzt wird, z.B.:
|
||||
# S3_ENDPOINT=https://fsn1.your-objectstorage.com
|
||||
S3_REGION=eu-central
|
||||
S3_BUCKET=betreuungs-cockpit
|
||||
S3_ACCESS_KEY_ID=
|
||||
@@ -131,5 +141,5 @@ SMTP_FROM="Betreuungs-Cockpit <cockpit@example.de>"
|
||||
|
||||
# --- Erstanmeldung ----------------------------------------------------------
|
||||
# Wird nur vom Seed benutzt, um den ersten Betreuer-Zugang anzulegen.
|
||||
SEED_ADMIN_EMAIL=fw@wildeflorian.de
|
||||
SEED_ADMIN_PASSWORT=bitte-beim-ersten-login-aendern
|
||||
SEED_ADMIN_EMAIL=
|
||||
SEED_ADMIN_PASSWORT=
|
||||
|
||||
@@ -218,6 +218,9 @@ INDEX_KEY=<aus Schritt 2>
|
||||
# --- Objektspeicher ---
|
||||
S3_ACCESS_KEY_ID=<aus Schritt 2>
|
||||
S3_SECRET_ACCESS_KEY=<aus Schritt 2>
|
||||
# S3_ENDPOINT bleibt AUSKOMMENTIERT, solange du das mitgelieferte MinIO
|
||||
# benutzt. Nur dann setzt die docker-compose.yml von selbst
|
||||
# http://minio:9000 - steht dort ein Wert, gewinnt er.
|
||||
|
||||
# --- Sicherung ---
|
||||
SICHERUNG_PASSWORT=<aus Schritt 2>
|
||||
|
||||
@@ -0,0 +1,100 @@
|
||||
import { readFileSync } from 'node:fs'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
|
||||
/**
|
||||
* Die Beispielkonfiguration darf keine Falle enthalten.
|
||||
*
|
||||
* `.env.example` wird nach `.env` kopiert und dann Zeile für Zeile
|
||||
* ausgefüllt. Was schon einen Wert hat, wird dabei leicht überlesen — und
|
||||
* genau das ist am 31.08.2026 passiert:
|
||||
*
|
||||
* S3_ENDPOINT=https://fsn1.your-objectstorage.com
|
||||
*
|
||||
* Die Zeile war als Beispiel gedacht. Sie stand aber aktiv in der `.env`,
|
||||
* und damit sprach die Anwendung monatelang mit einem fremden Anbieter
|
||||
* statt mit dem Objektspeicher nebenan. Jede Ablage scheiterte mit
|
||||
* `NoSuchBucket`, und die Zugangs-ID des lokalen Speichers ging bei jedem
|
||||
* Versuch nach draußen.
|
||||
*
|
||||
* Verschärfend: Die `docker-compose.yml` hatte längst einen brauchbaren
|
||||
* Standard (`${S3_ENDPOINT:-http://minio:9000}`). Der greift aber nur bei
|
||||
* leerer Variable — die Beispieldatei hebelte ihn aus. Der Mechanismus,
|
||||
* der den Fehler verhindern sollte, war durch die Beispieldatei
|
||||
* wirkungslos.
|
||||
*
|
||||
* Diese Tests halten fest: Ein Wert steht in der Beispieldatei nur, wenn
|
||||
* er für die mitgelieferte Zusammenstellung auch richtig ist.
|
||||
*/
|
||||
|
||||
const beispiel = readFileSync('.env.example', 'utf8')
|
||||
|
||||
/** Alle aktiven Zuweisungen - Kommentarzeilen zählen nicht. */
|
||||
function zuweisungen(): [string, string][] {
|
||||
return beispiel
|
||||
.split('\n')
|
||||
.map((zeile) => zeile.trim())
|
||||
.filter((zeile) => zeile !== '' && !zeile.startsWith('#'))
|
||||
.map((zeile) => {
|
||||
const stelle = zeile.indexOf('=')
|
||||
return [zeile.slice(0, stelle), zeile.slice(stelle + 1)] as [string, string]
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Rechner, die zur mitgelieferten Zusammenstellung gehören. Alles andere
|
||||
* ist ein fremder Host und hat in der Beispieldatei nichts verloren.
|
||||
*/
|
||||
const EIGENE_HOSTS = ['localhost', '127.0.0.1', '0.0.0.0', 'minio', 'db']
|
||||
|
||||
describe('Beispielkonfiguration', () => {
|
||||
it('zeigt mit keinem aktiven Wert auf einen fremden Host', () => {
|
||||
const fremde = zuweisungen()
|
||||
.filter(([, wert]) => /https?:\/\//.test(wert))
|
||||
.filter(([, wert]) => {
|
||||
const treffer = wert.match(/https?:\/\/([^:/"']+)/)
|
||||
return treffer ? !EIGENE_HOSTS.includes(treffer[1]!) : false
|
||||
})
|
||||
.map(([name, wert]) => `${name}=${wert}`)
|
||||
|
||||
expect(
|
||||
fremde,
|
||||
'Diese Zeilen zeigen nach draußen. Wer die Datei kopiert und die ' +
|
||||
'Zeile übersieht, schickt seine Daten an einen fremden Rechner.',
|
||||
).toEqual([])
|
||||
})
|
||||
|
||||
it('lässt S3_ENDPOINT auskommentiert, damit der Compose-Standard greift', () => {
|
||||
const aktiv = zuweisungen().some(([name]) => name === 'S3_ENDPOINT')
|
||||
|
||||
expect(
|
||||
aktiv,
|
||||
'Mit gesetztem S3_ENDPOINT greift ${S3_ENDPOINT:-http://minio:9000} ' +
|
||||
'aus der docker-compose.yml nicht mehr.',
|
||||
).toBe(false)
|
||||
})
|
||||
|
||||
it('erklärt trotzdem, wann man S3_ENDPOINT braucht', () => {
|
||||
// Auskommentieren allein hilft nicht - dann sucht jemand mit externem
|
||||
// Speicher vergeblich nach der Einstellung.
|
||||
expect(beispiel).toMatch(/#\s*S3_ENDPOINT=/)
|
||||
expect(beispiel).toMatch(/EXTERNER Objektspeicher/)
|
||||
})
|
||||
|
||||
it('enthält keine persönliche E-Mail-Adresse', () => {
|
||||
// Stand einmal drin: die Adresse des Betreibers als Vorgabe fuer den
|
||||
// Seed. In einer mitgelieferten Beispieldatei hat sie nichts zu suchen.
|
||||
const adressen = zuweisungen()
|
||||
// Verbindungszeichenfolgen tragen ein "@" vor dem Rechnernamen -
|
||||
// das ist keine E-Mail-Adresse.
|
||||
.filter(([, wert]) => !wert.includes('://'))
|
||||
.filter(([, wert]) => /[\w.+-]+@[\w-]+\.[a-z]{2,}/i.test(wert))
|
||||
.filter(([, wert]) => !/example\.(de|com|org)/.test(wert))
|
||||
.map(([name, wert]) => `${name}=${wert}`)
|
||||
|
||||
expect(adressen).toEqual([])
|
||||
})
|
||||
|
||||
it('findet überhaupt Zuweisungen - sonst prüft der Test nichts', () => {
|
||||
expect(zuweisungen().length).toBeGreaterThan(15)
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user