feat(cert-manager): Vorlagen für Zielsysteme, Modulversion 1.2.0 und Anleitungen
- Sieben Vorlagen (Nginx, Apache ab/vor 2.4.8, Windows/IIS, Nginx Proxy Manager, HAProxy, Tomcat) mit Dateien und Einrichtungszeilen - Reiter „Vorlagen“ mit ZIP samt Anleitung, Schnipsel und Kopieren - Modulversion 1.2.0, Modul-Changelog, CHANGELOG und drei Anleitungen Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -870,3 +870,75 @@ Nextcloud (`ratelimit.protection.enabled`) nur für den Lauf aus und stellt die
|
||||
`user:setting anna files_sharing default_accept`). Alles setzt der `trap` zurück. Der Zähler von
|
||||
Tessera liegt im Prozess: `all` verbraucht 7 der 10 Plätze, nach jedem `all`-Lauf
|
||||
`docker compose restart api`.
|
||||
|
||||
**Zertifikat-Manager, Arbeitsbereich und Ketten (quick-261009-ikt):** Der Zertifikat-Manager
|
||||
(`apps/api/src/cert-manager`, `apps/web/src/app/(portal)/modules/cert-manager`) hält **nichts**
|
||||
auf dem Server. Die Oberfläche führt eine Liste von Dateien nur im Arbeitsspeicher des Browsers
|
||||
(`use-cert-workspace.ts`, rein mit `working-set.ts`) und schickt bei jeder Änderung die ganze Liste
|
||||
an `POST analyze` (multipart, höchstens 30 Dateien zu je 5 MiB, zusammen 20 MiB); ältere Antworten
|
||||
verwirft ein Anfragezähler. Es gibt genau drei POST-Routen, alle ohne Zustand: `analyze`, `build`
|
||||
(JSON) und, ab dem Abschnitt zum Nachladen fehlender Zertifikate, `fetch-issuer`. Fehler tragen
|
||||
immer einen Code im Körper (`{ code, message }`, Liste in `cert-types.ts`), den die Oberfläche unter
|
||||
`certManager.errors.<code>` übersetzt.
|
||||
|
||||
*Ein Parser.* **`node:crypto` entscheidet alles über Zertifikate und Schlüssel** (`X509Certificate`,
|
||||
`createPrivateKey`, `createPublicKey`, `KeyObject#export`), denn nur so laufen RSA **und** EC durch
|
||||
denselben Weg. `node-forge` bleibt ausschließlich für PKCS#12 (Lesen und Schreiben) und als
|
||||
allgemeiner ASN.1-Leser und -Schreiber (PKCS#7 lesen und bauen, Zertifikatsanfragen lesen). Kein
|
||||
Produktivcode ruft `certificateFromPem`, `certificateFromAsn1`, `certificationRequestFromAsn1`,
|
||||
`messageFromPem` oder `messageFromAsn1` auf, denn diese forge-Leser können nur RSA; das prüft das
|
||||
Gate in der Aufgabenkette per `grep`. Die Erkennung (`cert-model.ts`, `detectBlob`) ist eine feste
|
||||
Stufenfolge (ZIP, PEM-Blöcke, DER-Zertifikat, PKCS#12, PKCS#7, Schlüssel, Anfrage), jede Stufe in
|
||||
`try/catch`: eine kaputte Datei wird `unknown`, nie ein Fehler der Anfrage. Erkannt wird am Inhalt,
|
||||
nie an der Dateiendung.
|
||||
|
||||
*Ketten.* `cert-chain.ts` (`buildChains`) nimmt als Aussteller nur Zertifikate, für die
|
||||
`C.checkIssued(I)` **und** `C.verify(I.publicKey)` gelten. `checkIssued` allein vergleicht nur Namen
|
||||
und Schlüsselkennungen; ein gleichnamiges Zwischenzertifikat mit anderem Schlüssel (Fixture
|
||||
`rsa-inter-decoy`) besteht es und würde ohne die Unterschriftsprüfung fälschlich genommen.
|
||||
Selbstsigniert heißt: Unterschrift mit dem eigenen Schlüssel stimmt und (`checkIssued` gegen sich
|
||||
selbst oder Aussteller gleich Inhaber). Mehrere Wege rangiert die Funktion fest (endet bei einer
|
||||
Wurzel der Liste, weniger abgelaufene, kürzer, späteres Ablaufdatum, dann SHA-256), damit das
|
||||
Ergebnis nie vom Zufall abhängt. Schlüssel und Anfragen ordnet `matchKeys` mit
|
||||
`checkPrivateKey` bzw. dem Vergleich der öffentlichen Schlüssel zu, nie nach Namen. `build`
|
||||
baut die Reihenfolge **immer neu** aus den gesendeten Zertifikaten; eine vom Browser mitgeschickte
|
||||
Reihenfolge gäbe es nicht einmal als Feld.
|
||||
|
||||
*PFX schreiben.* forge schreibt von sich aus nur RSA. `cert-pkcs12.ts` (`writePkcs12`) tauscht
|
||||
deshalb für die Dauer **eines** synchronen Aufrufs drei Funktionen von `forge.pki`
|
||||
(`privateKeyToAsn1`, `wrapRsaPrivateKey`, `certificateToAsn1`) gegen Durchreicher aus und stellt
|
||||
sie im `finally` wieder her; ASN.1 für Zertifikate und Schlüssel kommt aus `node:crypto`, die
|
||||
Verschlüsselung und den MAC macht weiterhin forge. Das ist sicher, weil Node einfädig ist und der
|
||||
Aufruf nicht abgibt; das Spec prüft die Wiederherstellung auch nach einem Fehler. Profile:
|
||||
`compat` (3DES, SHA-1, Vorgabe, lesbar bis Windows Server 2016) und `modern` (AES-256, der MAC bleibt
|
||||
bei forge SHA-1). Die Vorlagen für IIS und Tomcat nutzen dieselbe Funktion (`cert-templates.ts`);
|
||||
die Vorlagen für Dateien bauen aus denselben Bausteinen wie `buildOutput` und kennen `cert-output.ts`
|
||||
bewusst nicht (sonst entstünde eine Importschleife).
|
||||
|
||||
*ZIP-Grenzen.* `zip-expand.ts` prüft **alles vor dem ersten Entpacken** an den Kopfdaten: höchstens
|
||||
100 Einträge, 1 MiB je Eintrag, Verhältnis entpackt zu gepackt höchstens 100, Summe 20 MiB, nur eine
|
||||
Ebene (ein ZIP im ZIP wird gemeldet, nicht geöffnet), verschlüsselte Einträge werden gemeldet.
|
||||
Eintragsnamen dienen nur der Anzeige und werden nie als Dateipfad benutzt.
|
||||
|
||||
*Grenze des Anfragekörpers von `build` (D-26).* Die Express-Voreinstellung von 100 kB reicht für die
|
||||
größte erlaubte `build`-Anfrage nicht (Zertifikat, 20 Pool-Zertifikate, Schlüssel und Anfrage zu je
|
||||
16 384 Zeichen ergeben rund 384 kB). Deshalb hat nur diese Route einen eigenen JSON-Leser mit
|
||||
512 KiB (`cert-json-body.ts`), den `main.ts` per `app.use(CERT_BUILD_ROUTE, certBuildJsonBody,
|
||||
certBuildBodyErrors)` **vor** `app.listen` einhängt (Nest registriert seine eigenen Leser erst in
|
||||
`init()`). Zwei Fallen: Erstens liegt `express` nicht in `apps/api/node_modules`, der Leser kommt
|
||||
deshalb über `createRequire(...)` aus der Kopie, die auch `@nestjs/platform-express` lädt. Zweitens
|
||||
**darf die Funktion nicht `jsonParser` heißen**: Nests `ExpressAdapter` überspringt seinen eigenen
|
||||
JSON-Leser für **alle** Routen, sobald schon eine Schicht mit diesem Funktionsnamen im Router liegt
|
||||
(`isMiddlewareApplied`); deshalb heißt sie `certBuildJsonBody`. Eine größere Anfrage bekommt 413 mit
|
||||
Code `tooLarge`, kaputtes JSON 400 mit `invalidInput`; alle anderen Routen behalten die 100 kB. Das
|
||||
Spec `cert-json-body.spec.ts` rechnet die größte DTO-Anfrage aus den Konstanten in
|
||||
`dto/cert-build.dto.ts` aus und bleibt rot, wenn jemand dort eine Obergrenze erhöht, ohne die
|
||||
Grenze mitzuziehen.
|
||||
|
||||
*Fixtures.* Die Testdaten liegen unter `apps/api/src/cert-manager/__fixtures__/` und entstehen mit
|
||||
`make-fixtures.sh` (OpenSSL 3.4 oder neuer; Passwort aller geschützten Dateien `Test-Pass-123`, die
|
||||
Schlüssel der CAs werden nach der Erzeugung gelöscht). Dateinamen enden **nie** auf `.key`, denn die
|
||||
`.gitignore` ignoriert `*.key` wegen des Updater-Schlüssels; Schlüssel heißen `-key.pem` oder
|
||||
`-key-….der`. Die Specs lesen die Dateien mit `readFileSync` und rufen OpenSSL nie auf (die CI hat
|
||||
es nicht); die Live-Prüfung mit OpenSSL-Gegenprobe (`openssl verify`, `pkcs12 -info`,
|
||||
`pkcs7 -print_certs`, `pkey`) liegt im Skript `e2e-cert.sh` der Aufgabe.
|
||||
|
||||
Reference in New Issue
Block a user