158 lines
3.6 KiB
Markdown
158 lines
3.6 KiB
Markdown
|
|
# STEP 4.1a – TLS-ENFORCEMENT VERIFIKATION (BEWEIS)
|
|||
|
|
# Status: ABGESCHLOSSEN
|
|||
|
|
# Datum: 2026-02-22
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 1. Listener-Tabelle
|
|||
|
|
|
|||
|
|
| Server | Port | Host | Protokoll (mit TLS) | Protokoll (ohne TLS) |
|
|||
|
|
|--------|------|------|---------------------|----------------------|
|
|||
|
|
| backend_main | 8000 (ENV PORT) | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
|||
|
|
| transcribe_server | 8090 (ENV TRANSCRIBE_PORT) | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
|||
|
|
| todo_server | 5111 | 0.0.0.0 | HTTPS only | Fail-Start (AZA_TLS_REQUIRE=1) |
|
|||
|
|
| workforce_planner | variabel | variabel | Über uvicorn CLI | Kein eigener __main__ |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 2. Test-Kommandos und Ergebnisse
|
|||
|
|
|
|||
|
|
### TEST 1: Fail-Start ohne Zertifikat (AZA_TLS_REQUIRE=1)
|
|||
|
|
|
|||
|
|
**Kommando:**
|
|||
|
|
```
|
|||
|
|
$env:AZA_TLS_CERTFILE = ""
|
|||
|
|
$env:AZA_TLS_KEYFILE = ""
|
|||
|
|
$env:AZA_TLS_REQUIRE = "1"
|
|||
|
|
$env:MEDWORK_API_TOKEN = "test123"
|
|||
|
|
python backend_main.py
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
```
|
|||
|
|
FEHLER: TLS ist erforderlich (AZA_TLS_REQUIRE=1), aber
|
|||
|
|
AZA_TLS_CERTFILE und/oder AZA_TLS_KEYFILE sind nicht gesetzt.
|
|||
|
|
```
|
|||
|
|
Exit Code: 1
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – Server startet nicht ohne Zertifikat.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TEST 2: HTTPS-Verbindung
|
|||
|
|
|
|||
|
|
**Kommando:**
|
|||
|
|
```python
|
|||
|
|
ctx = ssl.create_default_context()
|
|||
|
|
ctx.check_hostname = False
|
|||
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|||
|
|
r = urllib.request.urlopen('https://127.0.0.1:8444/health', context=ctx)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
```
|
|||
|
|
HTTPS: 200 {"ok": true}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – HTTPS funktioniert.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TEST 3: HTTP-Verbindung (muss fehlschlagen)
|
|||
|
|
|
|||
|
|
**Kommando:**
|
|||
|
|
```python
|
|||
|
|
r = urllib.request.urlopen('http://127.0.0.1:8444/health', timeout=5)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
```
|
|||
|
|
http.client.RemoteDisconnected: Remote end closed connection without response
|
|||
|
|
```
|
|||
|
|
Exit Code: 1
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – HTTP wird abgelehnt. Kein HTTP-Fallback.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TEST 4: TLS-Version und Cipher
|
|||
|
|
|
|||
|
|
**Kommando:**
|
|||
|
|
```python
|
|||
|
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_CLIENT)
|
|||
|
|
ctx.check_hostname = False
|
|||
|
|
ctx.verify_mode = ssl.CERT_NONE
|
|||
|
|
with ctx.wrap_socket(socket.socket(), server_hostname='localhost') as s:
|
|||
|
|
s.connect(('127.0.0.1', 8444))
|
|||
|
|
print(s.version(), s.cipher())
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
```
|
|||
|
|
TLS Version: TLSv1.3
|
|||
|
|
Cipher: TLS_AES_256_GCM_SHA384
|
|||
|
|
Protocol: TLSv1.3
|
|||
|
|
Bits: 256
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – TLS 1.3 mit AES-256-GCM, PFS aktiv.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TEST 5: Zertifikat-Details
|
|||
|
|
|
|||
|
|
**Kommando:**
|
|||
|
|
```python
|
|||
|
|
cert = s.getpeercert(binary_form=True)
|
|||
|
|
c = x509.load_der_x509_certificate(cert)
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
```
|
|||
|
|
Subject: CN=localhost,O=AZA MedWork DEV
|
|||
|
|
Issuer: CN=localhost,O=AZA MedWork DEV
|
|||
|
|
Key: 4096 bit RSA
|
|||
|
|
Algo: SHA-256
|
|||
|
|
Valid: 2026-02-22 -> 2027-02-22
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – RSA 4096-bit, SHA-256, Self-Signed (DEV).
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
### TEST 6: ENV-Hardcoding-Prüfung
|
|||
|
|
|
|||
|
|
**Kommando:** Grep nach "dev-cert.pem" / "dev-key.pem" in *.py
|
|||
|
|
|
|||
|
|
**Ergebnis:**
|
|||
|
|
Nur in `aza_tls.py` Zeilen 114-115 (Generierungsfunktion) und Docstring.
|
|||
|
|
NICHT in todo_server.py, transcribe_server.py, backend_main.py.
|
|||
|
|
Alle Server lesen Zertifikatspfade ausschliesslich aus ENV-Variablen.
|
|||
|
|
|
|||
|
|
**Bewertung: PASS** – Keine hardcodierten Zertifikatspfade.
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 3. Fix während Verifikation
|
|||
|
|
|
|||
|
|
| Datei | Zeile | Problem | Fix |
|
|||
|
|
|-------|-------|---------|-----|
|
|||
|
|
| aza_tls.py | 93-97 | `ssl_version: ssl.TLSVersion.TLSv1_2` inkompatibel mit uvicorn (ValueError: invalid protocol version 771) | Ersetzt durch `ssl_ciphers: _STRONG_CIPHERS` |
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
## 4. Gesamtbewertung
|
|||
|
|
|
|||
|
|
| Test | Ergebnis |
|
|||
|
|
|------|----------|
|
|||
|
|
| Fail-Start ohne Cert | **PASS** |
|
|||
|
|
| HTTPS-Verbindung | **PASS** |
|
|||
|
|
| HTTP-Ablehnung | **PASS** |
|
|||
|
|
| TLS >= 1.2 | **PASS** (TLS 1.3 verhandelt) |
|
|||
|
|
| Starke Cipher | **PASS** (AES-256-GCM) |
|
|||
|
|
| PFS | **PASS** (ECDHE/DHE only) |
|
|||
|
|
| Zertifikat RSA 4096 | **PASS** |
|
|||
|
|
| Keine hardcodierten Pfade | **PASS** |
|
|||
|
|
|
|||
|
|
**GESAMTERGEBNIS: PASS (8/8)**
|