# ING-06 — TLS/STARTTLS-Absicherung: Prüfprotokoll Datum: 2026-09-01 Host: 192.168.1.131 (Build/Test/Lint), rsync + ssh Pakete: `mail/internal/tlscert` (neu, gemeinsam genutzt), `mail/internal/imap`, `mail/internal/pop3`, `mail/internal/smtp` ## Umsetzung Neues Paket `tlscert` kapselt die für alle drei Protokollserver gemeinsame TLS-Grundlage: - `Store` hält das aktuell aktive Zertifikat hinter `GetCertificate` (wird von `crypto/tls` bei JEDEM neuen Handshake aufgerufen). `Replace`/`ReplaceFromFiles` tauschen es atomar aus — bereits etablierte Verbindungen behalten ihr beim Handshake ausgehandeltes Zertifikat, nur NEUE Handshakes bekommen das neue (Akzeptanzkriterium 3). - `Store.Config()` liefert eine gehärtete `tls.Config`: `MinVersion: tls.VersionTLS12`, für TLS 1.2 ausschließlich AEAD-Cipher-Suiten (kein CBC, kein RC4, kein 3DES) — TLS 1.3 hat ohnehin nur starke, feste Suiten (Akzeptanzkriterium 2). - `UpgradeServer` führt den STARTTLS-Serverhandschlag durch, gemeinsam genutzt von allen drei Protokollen. **IMAP** (`STARTTLS`, RFC 3501 §6.2.1), **POP3** (`STLS`, RFC 2595 §4) und **SMTP** (`STARTTLS`, RFC 3207) bekommen je ein neues Kommando: nur vor der Anmeldung erlaubt, Reader/Writer werden nach dem Handschlag NEU aufgesetzt (verhindert, dass vor dem Handshake gepufferte Klartextdaten als Kommandos nach dem Wechsel verarbeitet werden — Command-Injection-Schutz). LOGIN (IMAP) und PASS (POP3) werden zurückgewiesen, solange der Server TLS anbietet, aber die Verbindung weder implizit (via `tls.Conn`) noch per STARTTLS/STLS verschlüsselt ist (Akzeptanzkriterium 1). SMTP hat in der aktuellen minimalen Implementierung (ING-03) kein Anmeldekommando (kein AUTH) — dort wird STARTTLS strukturell bereitgestellt und geprüft, die Anmeldedaten-Kernprüfung erfolgt für IMAP/POP3. Implizites TLS (z. B. Port 993/995/465) benötigt KEINE Codeänderung: `Server.Serve` nimmt jeden `net.Listener` entgegen, ein mit `tls.NewListener` gewrapptes Listener liefert bereits `*tls.Conn` aus `Accept()` — die Session erkennt das per Typ-Assertion und startet direkt mit `tlsActive = true`. Alle drei Server bleiben ohne TLS-Konfiguration (`tlsConfig == nil`) unverändert im bisherigen Klartextverhalten — Rückwärtskompatibilität zu ING-01/ING-02/ING-03, bestehende Tests unverändert grün. ## Pflichtprüfung 1: Scan mit Standard-TLS-Prüfwerkzeug bestätigt keine schwachen Suiten Manuelle Prüfung mit `openssl s_client` (Standardwerkzeug, bereits auf dem Zielsystem vorhanden) gegen einen echten, laufenden `mail/internal/smtp`-Server mit aktivierter TLS-Konfiguration: ``` $ printf 'EHLO test\r\nQUIT\r\n' | openssl s_client -connect 127.0.0.1:14425 -starttls smtp -brief CONNECTION ESTABLISHED Protocol version: TLSv1.3 Ciphersuite: TLS_AES_128_GCM_SHA256 ... 250 STARTTLS DONE ``` → Reguläre Verbindung: TLS 1.3, starke AEAD-Suite. Erzwungener Versuch mit ausschließlich schwachen TLS-1.2-CBC-Suiten: ``` $ openssl s_client -connect 127.0.0.1:14425 -starttls smtp -tls1_2 \ -cipher 'ECDHE-RSA-AES256-SHA:ECDHE-RSA-AES128-SHA:AES128-SHA:AES256-SHA' ... New, (NONE), Cipher is (NONE) Cipher : 0000 ``` → Kein Cipher ausgehandelt = Handshake fehlgeschlagen, Server nimmt keine der angebotenen CBC-Suiten an. **Ergänzung/Abweichung dokumentiert:** Das auf diesem Host installierte `openssl 3.5.6` verweigert es, TLS 1.0/1.1 überhaupt CLIENTSEITIG anzufordern (`no protocols available`, auch mit `-provider legacy`) — das lässt sich mit dem verfügbaren Standardwerkzeug nicht mehr erzwingen. Als reproduzierbarer automatisierter Ersatz für den Versions-Anteil dieser Prüfung: `TestServer_RejectsLegacyTLSVersionAndWeakCiphers` (`smtp/tls_test.go`, echter TCP-Client über `crypto/tls`, `MaxVersion: tls.VersionTLS11`) gegen den echten Server — Handshake schlägt fehl. Zweiter Subtest erzwingt clientseitig ausschließlich `TLS_RSA_WITH_AES_128_CBC_SHA` — Handshake schlägt ebenfalls fehl. Zusätzlich `TestConfig_HardenedDefaults` (`tlscert/tlscert_test.go`) prüft die `tls.Config` direkt gegen eine Liste bekannter schwacher Suiten. Ergebnis: **BESTANDEN** (openssl-Scan + zwei automatisierte Negativtests + Config-Assertion). ## Pflichtprüfung 2: Login-Versuch ohne TLS/STARTTLS wird verweigert - `TestPass_RequiresTLS` (`pop3/tls_test.go`): PASS ohne vorheriges STLS liefert `-ERR`. - `TestLogin_RequiresTLS` (`imap/tls_test.go`): LOGIN ohne vorheriges STARTTLS liefert `NO`. - Kehrseite jeweils mitgetestet: `TestStls_UpgradesConnectionAndAllowsLogin` bzw. `TestStartTLS_UpgradesConnectionAndAllowsLogin` — nach echtem STLS/STARTTLS-Handschlag (reale `crypto/tls`-Clientverbindung) wird dieselbe Anmeldung akzeptiert. - SMTP: `TestStartTLS_UpgradesConnection` belegt den echten STARTTLS-Handschlag strukturell (kein Anmeldekommando in der aktuellen SMTP-Implementierung vorhanden, siehe Abschnitt "Umsetzung"). Ergebnis: **BESTANDEN**. ## Pflichtprüfung 3: Zertifikatsrotation im laufenden Betrieb ohne Dienstunterbrechung `TestTLS_CertificateRotationWithoutDroppingExistingSessions` in allen drei Protokollpaketen (`pop3`, `imap`, `smtp`): echter Ablauf — 1. Erste TLS-Verbindung (echter Handschlag) aufbauen, bestätigen, dass sie Zertifikat A bekommt, Verbindung OFFEN halten. 2. `store.Replace(certB)` — Rotation im laufenden Betrieb. 3. Zweite, NEUE Verbindung aufbauen — bekommt nachweislich Zertifikat B (`PeerCertificates[0].Raw` verglichen). 4. Erste, bereits etablierte Verbindung wird DANACH weiterbenutzt (POP3: USER/PASS, IMAP: LOGIN, SMTP: NOOP) — funktioniert unterbrechungsfrei weiter. Zusätzlich `TestStore_ReplaceAffectsOnlyNewHandshakes` (`tlscert/tlscert_test.go`) auf Store-Ebene. Ergebnis: **BESTANDEN** — in allen drei Protokollen: kein Verbindungsabriss für die bestehende Session, neue Verbindungen bekommen sofort das neue Zertifikat. ## Akzeptanzkriterien 1. **Anmeldedaten werden ausschließlich über TLS oder nach erfolgreichem STARTTLS akzeptiert**: durch Pflichtprüfung 2 belegt (IMAP LOGIN, POP3 PASS). 2. **Schwache Cipher-Suiten und veraltete TLS-Versionen sind serverseitig deaktiviert**: durch Pflichtprüfung 1 belegt (`tlscert.Store.Config()`: `MinVersion: TLS12`, ausschließlich AEAD-Suiten für TLS 1.2). 3. **Zertifikatswechsel ist ohne Verbindungsabriss für bestehende Sessions möglich**: durch Pflichtprüfung 3 belegt. ## Build/Vet/Lint/Test — Gesamtmodul ``` go build ./... → OK go vet ./... → OK golangci-lint run ./... → 0 issues go test ./... -p 1 (TEST_TENANT_DSN, TEST_MANTICORE_URL gesetzt) → alle Pakete ok, inkl. neuem internal/tlscert ``` Keine Regression in den bestehenden ~27 Paketen. Manueller TLS-Testserver (`cmd/tls-manual-test`) und dessen Hintergrundprozess nach den openssl-Prüfungen entfernt/beendet, nicht im Repository verblieben. ## Ergebnis ING-06 erfüllt alle Akzeptanzkriterien mit echten, ausgeführten Nachweisen. Pflichtprüfung 1 (Versions-Anteil) wurde mangels clientseitig erzwingbarem Legacy-TLS im installierten openssl 3.5.6 zusätzlich durch einen echten automatisierten `crypto/tls`-Negativtest gegen den laufenden Server ergänzt — siehe Abschnitt oben. Freigeschaltet: QA-04.