init
This commit is contained in:
@@ -0,0 +1,198 @@
|
||||
# Architektur
|
||||
|
||||
## Leitprinzip — Hexagonale Architektur
|
||||
|
||||
Die Anwendung folgt dem Muster **Ports & Adapters** (Alistair Cockburn). Ziel
|
||||
ist die strikte Trennung der fachlichen Logik (Domain) von technischer
|
||||
Infrastruktur (HSM, CA, DB, SMTP, HTTP). Konsequenzen:
|
||||
|
||||
- Die Domäne ist frei von asynchroner Laufzeit, ORM-Typen, HTTP-Clients und
|
||||
XML-Bibliotheken. Sie enthält reine Daten und reine Funktionen.
|
||||
- Use-Cases ("Anwendungsdienste") orchestrieren die Domäne, indem sie Ports
|
||||
konsumieren.
|
||||
- Adapter sind austauschbar. Für Tests gibt es In-Memory-Adapter; für die
|
||||
Lab-Umgebung gibt es SoftHSM-/SQLite-/SMTP-/SOAP-Adapter.
|
||||
|
||||
```
|
||||
┌──────────────────────────────────────┐
|
||||
│ Domain │
|
||||
│ Certificate, Gateway, Policies │
|
||||
└───────────────▲──────────────────────┘
|
||||
│
|
||||
┌────────────────────┴──────────────────────┐
|
||||
│ Ports │
|
||||
│ Inbound (UseCases) Outbound (Traits) │
|
||||
└─────▲─────────────────────────▲───────────┘
|
||||
│ │
|
||||
┌────────────────┴─────┐ ┌─────────┴─────────────────┐
|
||||
│ Inbound │ │ Outbound │
|
||||
│ (treibt die App) │ │ (wird von der App │
|
||||
│ │ │ getrieben) │
|
||||
│ • Axum HTTP-Server │ │ • SoftHsmAdapter │
|
||||
│ • Tokio-Cron │ │ • SubCaSoapAdapter │
|
||||
│ │ │ • SqliteAdapter │
|
||||
│ │ │ • SmtpAdapter │
|
||||
│ │ │ • SystemClock │
|
||||
└──────────────────────┘ └───────────────────────────┘
|
||||
```
|
||||
|
||||
## Schichten
|
||||
|
||||
### Domain (`src/domain/`)
|
||||
|
||||
Keine externen Abhängigkeiten außer `time` (Datum/Zeit-Typen).
|
||||
|
||||
- `certificate.rs` — `Certificate`, `CertificateRequest`, `CertificateUsage`,
|
||||
reine Methoden wie `days_until_expiry`, `is_expiring_within`.
|
||||
- `gateway.rs` — `Gateway`.
|
||||
|
||||
### Ports (`src/ports/`)
|
||||
|
||||
Traits, die die Domäne nach außen exponiert bzw. von außen erwartet.
|
||||
|
||||
**Outbound-Ports** (`outbound.rs`) — was die Domäne von der Außenwelt braucht:
|
||||
|
||||
| Port | Zweck |
|
||||
| ------------------- | ------------------------------------------- |
|
||||
| `HsmPort` | Keypair-Generierung, CSR- und XML-Signatur. |
|
||||
| `CertificateCaPort` | Asynchrone Zertifikatsanfrage an Sub-CA. |
|
||||
| `StoragePort` | Persistenz von Zertifikaten + Pending Reqs. |
|
||||
| `NotificationPort` | Versand von Alert-Mails. |
|
||||
| `ClockPort` | Testbare Zeitquelle für Ablauflogik. |
|
||||
|
||||
Jeder Port hat seinen eigenen Fehlertyp (`HsmError`, `CaError`,
|
||||
`StorageError`, `NotificationError`) via `thiserror`. Auf Use-Case-Ebene wird
|
||||
auf `UseCaseError` aggregiert; auf App-Ebene auf `anyhow::Result`.
|
||||
|
||||
**Inbound-Ports** (`inbound.rs`) — was die Domäne anbietet:
|
||||
|
||||
- `RenewExpiringCertificates` — wird vom Cron getrieben.
|
||||
- `HandleCaCallback` — wird vom HTTP-Webhook getrieben.
|
||||
|
||||
### Adapters (`src/adapters/`)
|
||||
|
||||
Konkrete Implementierungen der Outbound-Ports:
|
||||
|
||||
| Adapter | Crates | Anmerkung |
|
||||
| -------------------- | ---------------------------- | ------------------------------------------------------ |
|
||||
| `SoftHsmAdapter` | `cryptoki` | PKCS#11. Blocking-Aufrufe via `spawn_blocking`. |
|
||||
| `SubCaSoapAdapter` | `reqwest` (rustls), `quick-xml` | mTLS-Client. SOAP per Hand (kleine Surface). |
|
||||
| `SqliteAdapter` | `sqlx` | Migrationen unter `migrations/`. |
|
||||
| `SmtpAdapter` | `lettre` | SMTP via Tokio + rustls. |
|
||||
| `SystemClock` | `time` | Triviale `now()`-Implementierung. |
|
||||
|
||||
### Builders (`src/builders/`)
|
||||
|
||||
Kapseln das Erzeugen komplexer Payloads:
|
||||
|
||||
- `SoapRequestBuilder` — TR-03129-4 `RequestCertificate`-Envelope inkl.
|
||||
`callbackIndicator=callback_possible`, eindeutiger `messageID`,
|
||||
Base64-kodierter CSR im Feld `certReq`.
|
||||
- `InitialConfigBuilder` — Erzeugt `iconfig.xml` (TR-03109-1) und ruft den
|
||||
`HsmPort` zur Signatur (`iconfig.sig`) auf. Wichtig: Signatur erfolgt auf
|
||||
den kanonischen (C14N) Bytes, **nicht** auf pretty-printed XML.
|
||||
|
||||
### Composition Root (`src/app.rs`)
|
||||
|
||||
`AppState` hält `Arc<dyn Port>` für alle Outbound-Ports. `app::run()` baut die
|
||||
Adapter, wired sie in den AppState, startet Axum-Router und Tokio-Cron.
|
||||
|
||||
## Datenflüsse
|
||||
|
||||
### Zertifikatserneuerung (Cron-getrieben)
|
||||
|
||||
```
|
||||
Cron ──► RenewExpiringCertificates::run(days=30)
|
||||
│
|
||||
├─► StoragePort::get_expiring_certificates(now, 30)
|
||||
│
|
||||
└─► für jedes Cert:
|
||||
├─► HsmPort::generate_key_pair("gw-<id>-<usage>")
|
||||
├─► CSR bauen, HsmPort::sign_csr(...)
|
||||
├─► SoapRequestBuilder::build_request_certificate(...)
|
||||
├─► CertificateCaPort::request_certificate(csr)
|
||||
│ └─ liefert messageID
|
||||
└─► StoragePort::save_pending_request(messageID, gateway_id)
|
||||
```
|
||||
|
||||
Die CA antwortet synchron nur mit `returnCode=ok_syntax`. Das eigentliche
|
||||
Zertifikat kommt asynchron über den Callback.
|
||||
|
||||
### Callback-Annahme (HTTP-getrieben)
|
||||
|
||||
```
|
||||
CA ──► POST /pki/callback (mTLS, SOAP)
|
||||
│
|
||||
└─► Axum Handler
|
||||
├─► mTLS-Client-Cert prüfen
|
||||
├─► SOAP-Signatur prüfen
|
||||
├─► messageID + certificateSeq extrahieren
|
||||
└─► HandleCaCallback::handle(messageID, certificateSeq)
|
||||
├─► StoragePort: pending → resolved
|
||||
├─► StoragePort::update_certificate(...)
|
||||
└─► NotificationPort::send_alert(...) bei Fehler
|
||||
```
|
||||
|
||||
**Polling ist laut BSI verboten.** Der Callback ist der einzige Weg.
|
||||
|
||||
### Initial-Konfiguration
|
||||
|
||||
```
|
||||
CLI/HTTP-Trigger ──► InitialConfigBuilder
|
||||
├─► XML bauen (quick-xml)
|
||||
├─► C14N
|
||||
├─► HsmPort::sign_xml(GWADM_SIG_PRV-Label, c14n)
|
||||
│ └─ liefert iconfig.sig (XML-DSig)
|
||||
└─► tar(iconfig.xml, iconfig.sig) → iconfig.tar
|
||||
```
|
||||
|
||||
## Querschnittsthemen
|
||||
|
||||
### Fehlerbehandlung
|
||||
|
||||
- Adapter werfen ihren eigenen `thiserror`-Typ.
|
||||
- Use-Cases mappen auf `UseCaseError`.
|
||||
- `main.rs` / `app::run` arbeitet mit `anyhow::Result`.
|
||||
- Keine `Result<_, String>` in Ports — strukturierte Fehler sind testbar und
|
||||
matchbar.
|
||||
|
||||
### Async-Modell
|
||||
|
||||
- `HsmPort` ist **synchron**, weil PKCS#11 nativ blockierend ist. Adapter
|
||||
ruft `tokio::task::spawn_blocking` an den richtigen Stellen.
|
||||
- Alle anderen Ports sind `async_trait`.
|
||||
- Eine Tokio-Runtime trägt Axum und Cron. `tokio-cron-scheduler` läuft im
|
||||
selben Runtime.
|
||||
|
||||
### Nebenläufigkeit der Cron-Jobs
|
||||
|
||||
`tokio-cron-scheduler` startet einen Job auch dann, wenn der vorherige Lauf
|
||||
noch läuft. Erneuerungs-Job daher mit `Semaphore(1)` schützen, um doppelte
|
||||
`RequestCertificate`-Aufrufe für dasselbe Gateway zu verhindern.
|
||||
|
||||
### Testbarkeit
|
||||
|
||||
- Domäne wird ohne Adapter getestet.
|
||||
- Pro Port existiert ein In-Memory-Adapter unter `#[cfg(test)]`.
|
||||
- Integrationstests gegen SoftHSM2 laufen in Docker (siehe
|
||||
[`development.md`](development.md)).
|
||||
|
||||
## Umsetzungsreihenfolge
|
||||
|
||||
1. **Domain + Ports** stehen — Code kompiliert.
|
||||
2. **In-Memory-Adapter** für jeden Port → erste Use-Case-Tests grün.
|
||||
3. **`SoftHsmAdapter`** — riskantestes Stück, früh validieren.
|
||||
4. **`SoapRequestBuilder` + Mock-CA** (Wiremock o.ä.).
|
||||
5. **`SqliteAdapter`** + Migrationen.
|
||||
6. **Axum** Router (Callback + GUI) + Cron-Wiring.
|
||||
7. **`SmtpAdapter`** zuletzt.
|
||||
|
||||
## Bewusste Nicht-Entscheidungen
|
||||
|
||||
- **Kein WSDL-Codegen.** Die `RequestCertificate`-Surface ist klein genug,
|
||||
um per Hand mit `quick-xml` zu bauen. Eingespart: ein zerbrechlicher
|
||||
Codegen-Schritt.
|
||||
- **Kein DI-Framework.** `Arc<dyn Trait>` im `AppState` reicht.
|
||||
- **XML-DSig nicht in reinem Rust.** Wir wrappen das `xmlsec1`-CLI im
|
||||
`SoftHsmAdapter` (über Pipes), bis ein reifer Rust-Wrapper für `libxmlsec1`
|
||||
verfügbar ist. Dokumentiert in [`bsi-compliance.md`](bsi-compliance.md).
|
||||
@@ -0,0 +1,141 @@
|
||||
# BSI-Konformität
|
||||
|
||||
Mapping der Vorgaben aus den BSI-Richtlinien auf die Code-Stellen in diesem
|
||||
Projekt. Quelle aller Anforderungen: **BSI TR-03129-4**, **BSI TR-03109-1**
|
||||
und **SM-PKI Certificate Policy**.
|
||||
|
||||
## 1. TR-03129-4 — Schnittstelle zur Zertifizierungsstelle
|
||||
|
||||
### 1.1 Transportsicherheit (mTLS)
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| ------------------------------------------------------------------ | -------------------------------------- |
|
||||
| SOAP-Nachrichten enthalten **keine** Auth-Daten. | `builders/soap_req.rs` |
|
||||
| Autorisierung + Verschlüsselung **ausschließlich** via mTLS. | `adapters/sub_ca.rs` |
|
||||
| Client-Cert (EMT-Testzertifikat) konfiguriert in `reqwest`. | `SubCaSoapAdapter::new(...)` |
|
||||
| Server-Cert der CA gegen vertrauten CA-Trust-Store verifiziert. | `SubCaSoapAdapter::new(...)` |
|
||||
|
||||
Konkret in `reqwest`:
|
||||
|
||||
```rust
|
||||
reqwest::ClientBuilder::new()
|
||||
.use_rustls_tls()
|
||||
.identity(reqwest::Identity::from_pem(client_pem_bundle)?)
|
||||
.add_root_certificate(reqwest::Certificate::from_pem(ca_pem)?)
|
||||
.build()?
|
||||
```
|
||||
|
||||
### 1.2 WSDL-Konformität
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| -------------------------------------------------------- | ------------------------- |
|
||||
| Envelope folgt der offiziellen BSI-WSDL. | `builders/soap_req.rs` |
|
||||
| Operation `RequestCertificate` mit korrektem Namespace. | `SoapRequestBuilder::build_request_certificate` |
|
||||
| CSR wird Base64-kodiert im Feld `certReq` übertragen. | dito |
|
||||
|
||||
Bewusste Entscheidung: **kein WSDL-Codegen**. Die Surface ist klein,
|
||||
`quick-xml` reicht; Codegen-Crates für Rust sind unausgereift und brittle.
|
||||
|
||||
### 1.3 Asynchrone Callbacks (Polling-Verbot)
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| ------------------------------------------------------ | -------------------------------------------------- |
|
||||
| `callbackIndicator = callback_possible` im Request. | `SoapRequestBuilder::build_request_certificate` |
|
||||
| Eindeutige `messageID` pro Anfrage. | `SoapRequestBuilder::message_id` |
|
||||
| Synchroner Response enthält i.d.R. nur `returnCode`. | `SubCaSoapAdapter::request_certificate` |
|
||||
| Webhook nimmt das fertige Zertifikat entgegen. | Axum-Handler (folgt; `POST /pki/callback`) |
|
||||
| Zuordnung Callback → Request über `messageID`. | `StoragePort::save_pending_request` / Lookup |
|
||||
| Zertifikatskette aus `certificateSeq` extrahieren. | Axum-Handler / `HandleCaCallback` |
|
||||
|
||||
**Sicherheitspunkt:** `messageID` allein ist **kein** Trust-Boundary. Der
|
||||
Callback-Handler muss zusätzlich:
|
||||
|
||||
1. mTLS-Client-Cert der CA prüfen.
|
||||
2. SOAP-Signatur der Nachricht prüfen.
|
||||
|
||||
Erst dann darf in der DB nachgeschlagen werden.
|
||||
|
||||
### 1.4 Datenfelder
|
||||
|
||||
| Feld | Inhalt | Quelle |
|
||||
| ----------------- | ------------------------------------------ | ----------------------------------- |
|
||||
| `certReq` | Base64(CSR-DER) — vom HSM erzeugt. | `HsmPort::sign_csr` |
|
||||
| `messageID` | UUIDv4, persistiert in `pending_requests`. | `SoapRequestBuilder::message_id` |
|
||||
| `callbackIndicator` | `callback_possible` | `SoapRequestBuilder` |
|
||||
| `certificateSeq` | Antwort der CA via Callback. | Axum-Handler |
|
||||
|
||||
## 2. TR-03109-1 — Initial-Konfiguration
|
||||
|
||||
### 2.1 Dateiformat `iconfig.tar`
|
||||
|
||||
| Pflicht-Inhalt | Code-Stelle |
|
||||
| ----------------------------------------------- | ------------------------------------ |
|
||||
| `iconfig.xml` — Konfiguration. | `builders/iconfig.rs` |
|
||||
| `iconfig.sig` — XML-DSig über `iconfig.xml`. | `builders/iconfig.rs` + `HsmPort` |
|
||||
| Beide Dateien in einem Tar-Archiv verpackt. | `builders/iconfig.rs` (Tar-Stufe) |
|
||||
|
||||
### 2.2 Inhalt der `iconfig.xml`
|
||||
|
||||
Pflichtbestandteile:
|
||||
|
||||
- Admin-Zertifikate (öffentliche Teile).
|
||||
- CA-Zertifikatskette.
|
||||
- Netzwerkprofil (IP-Access für WAN).
|
||||
- Kommunikationsprofil (TLS-Admin für WAN-Schnittstelle).
|
||||
|
||||
Generierung im `InitialConfigBuilder` via `quick-xml`. Eingangsdaten aus der
|
||||
Domäne (`Gateway`) bzw. aus Konfig-Files.
|
||||
|
||||
### 2.3 Kryptografische Signatur `iconfig.sig`
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| -------------------------------------------------------------------- | ------------------------------------ |
|
||||
| Signatur mit `GWADM_SIG_PRV` (privater Signaturschlüssel des GWA). | `HsmPort::sign_xml` |
|
||||
| Schlüssel liegt im HSM; Zugriff via PKCS#11. | `adapters/hsm.rs` (`cryptoki`) |
|
||||
| Signatur über **kanonische** Bytes (XML-C14N). | `InitialConfigBuilder` |
|
||||
|
||||
**Implementierungs-Hinweis (XML-DSig):**
|
||||
|
||||
Reines Rust für XML-DSig ist nicht reif. Optionen:
|
||||
|
||||
1. `xmlsec1` CLI über `std::process::Command` aus dem Adapter aufrufen
|
||||
(pragmatischer Start).
|
||||
2. `libxmlsec1` über FFI binden.
|
||||
3. C14N + Signatur manuell via `cryptoki` zusammensetzen (fehleranfällig
|
||||
wegen exakter Canonicalization).
|
||||
|
||||
Wir starten mit Option 1 und kapseln das vollständig hinter `HsmPort::sign_xml`,
|
||||
damit ein späterer Wechsel keinen Domänen-Code anfasst.
|
||||
|
||||
## 3. SM-PKI Certificate Policy
|
||||
|
||||
### 3.1 Schlüsselspeicherung
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| ------------------------------------------------------------ | ------------------------- |
|
||||
| Alle Teilnehmer-Schlüssel in HSMs ≥ "Security Level 1". | `adapters/hsm.rs` |
|
||||
| SoftHSMv2 erfüllt SL1 **nur** für Entwicklung. | siehe Sicherheitshinweis |
|
||||
|
||||
In Produktion: zertifiziertes HSM (CC EAL4+) zwingend. Der `HsmPort` bleibt
|
||||
identisch — nur das Backend tauscht.
|
||||
|
||||
### 3.2 Zertifikatslaufzeiten und Rotation
|
||||
|
||||
| Anforderung | Code-Stelle |
|
||||
| -------------------------------------------------------- | ------------------------------------------ |
|
||||
| Endentitäten/Sub-CAs in der Regel alle 2 Jahre. | Datenmodell `Certificate.not_after` |
|
||||
| Erneuerung rechtzeitig vor Ablauf (Standard: 30 Tage). | `RenewExpiringCertificates::run(days=30)` |
|
||||
| Asynchroner `RequestCertificate`-Flow. | siehe §1.3 |
|
||||
| Testbare Zeitquelle. | `ClockPort` / `SystemClock` |
|
||||
|
||||
Das 30-Tage-Fenster ist Default, kein Gesetz — über Config einstellbar.
|
||||
|
||||
## 4. Offene Punkte
|
||||
|
||||
- Konkrete Werte der BSI-Namespace-URIs und Operationsnamen aus der aktuellen
|
||||
WSDL-Version müssen in `builders/soap_req.rs` als Konstanten gepflegt
|
||||
werden, sobald die WSDL aus dem BSI-Repo gezogen ist.
|
||||
- Schema-Validierung der `iconfig.xml` gegen das BSI-XSD ist offen; sinnvoll
|
||||
als zusätzlicher Adapter-Check vor dem Signieren.
|
||||
- mTLS-Server (Axum für Callback) muss Client-Cert-Pinning auf die CA-Cert
|
||||
durchsetzen — Konfiguration unter [`development.md`](development.md).
|
||||
@@ -0,0 +1,153 @@
|
||||
# Development Setup
|
||||
|
||||
Lokales Setup für die Lab-Umgebung. Ziel: Tool gegen einen SoftHSM2-Container
|
||||
und eine Test-Sub-CA betreiben, ohne echte SMGW-Hardware.
|
||||
|
||||
## Voraussetzungen
|
||||
|
||||
- Rust ≥ 1.80 (`rustup`)
|
||||
- Docker / Podman
|
||||
- `xmlsec1` CLI (für die XML-DSig in `iconfig.sig`)
|
||||
- OpenSSL CLI (für Testzertifikate)
|
||||
|
||||
Auf Debian/Ubuntu:
|
||||
|
||||
```bash
|
||||
sudo apt install xmlsec1 libxmlsec1-dev openssl pkg-config
|
||||
```
|
||||
|
||||
## Build & Run
|
||||
|
||||
```bash
|
||||
cargo check
|
||||
cargo run
|
||||
```
|
||||
|
||||
Logs via `RUST_LOG`:
|
||||
|
||||
```bash
|
||||
RUST_LOG=smgw_pki_automator=debug,info cargo run
|
||||
```
|
||||
|
||||
## SoftHSMv2 als Container
|
||||
|
||||
`SoftHSMv2` per Docker laufen lassen. Beispiel (Pseudo-Compose, anpassen an
|
||||
euer Setup):
|
||||
|
||||
```yaml
|
||||
services:
|
||||
softhsm:
|
||||
image: ghcr.io/example/softhsm2:latest
|
||||
volumes:
|
||||
- ./tokens:/var/lib/softhsm/tokens
|
||||
environment:
|
||||
SOFTHSM2_CONF: /etc/softhsm2.conf
|
||||
```
|
||||
|
||||
Token einmalig initialisieren:
|
||||
|
||||
```bash
|
||||
softhsm2-util --init-token --slot 0 \
|
||||
--label "smgw-lab" \
|
||||
--pin 1234 --so-pin 5678
|
||||
```
|
||||
|
||||
Im `app.rs`-Boot-Pfad wird der Adapter konstruiert mit:
|
||||
|
||||
```rust
|
||||
SoftHsmAdapter::new("/usr/lib/softhsm/libsofthsm2.so", "1234")
|
||||
```
|
||||
|
||||
Pfad zur PKCS#11-Lib hängt vom Container/OS ab.
|
||||
|
||||
## mTLS-Testzertifikate
|
||||
|
||||
Für die Kommunikation mit der Test-Sub-CA brauchen wir:
|
||||
|
||||
- **Client-Cert (EMT)** — wird im `reqwest`-Client als `Identity` geladen.
|
||||
- **Server-Cert der CA** — wird als Trust-Root im Client geladen.
|
||||
- **Eigenes Server-Cert** für den Axum-Callback-Endpunkt (mTLS-Server).
|
||||
- **CA-Trust** für eingehende CA-Calls, damit das Client-Cert der CA gegen
|
||||
diesen Trust verifiziert werden kann.
|
||||
|
||||
Test-PKI mit OpenSSL aufsetzen (vereinfacht):
|
||||
|
||||
```bash
|
||||
mkdir -p certs && cd certs
|
||||
|
||||
# Test-Root
|
||||
openssl req -x509 -newkey rsa:4096 -days 3650 -nodes \
|
||||
-keyout test-root.key -out test-root.crt \
|
||||
-subj "/CN=smgw-lab-root"
|
||||
|
||||
# Client (EMT)
|
||||
openssl req -newkey rsa:4096 -nodes -keyout emt.key -out emt.csr \
|
||||
-subj "/CN=emt-client"
|
||||
openssl x509 -req -in emt.csr -CA test-root.crt -CAkey test-root.key \
|
||||
-CAcreateserial -out emt.crt -days 825
|
||||
|
||||
# Server (CA-Mock + Axum-Callback)
|
||||
openssl req -newkey rsa:4096 -nodes -keyout server.key -out server.csr \
|
||||
-subj "/CN=ca.test.local"
|
||||
openssl x509 -req -in server.csr -CA test-root.crt -CAkey test-root.key \
|
||||
-CAcreateserial -out server.crt -days 825
|
||||
```
|
||||
|
||||
PEM-Bundle für `reqwest::Identity::from_pem`:
|
||||
|
||||
```bash
|
||||
cat emt.crt emt.key > emt-bundle.pem
|
||||
```
|
||||
|
||||
## Datenbank
|
||||
|
||||
SQLite, default `sqlite::memory:` im Stub-Boot. Für Persistenz Pfad setzen:
|
||||
|
||||
```bash
|
||||
DATABASE_URL=sqlite://./data/smgw.db cargo run
|
||||
```
|
||||
|
||||
Migrationen (sobald angelegt) via `sqlx`:
|
||||
|
||||
```bash
|
||||
cargo install sqlx-cli --no-default-features --features sqlite,rustls
|
||||
sqlx migrate run
|
||||
```
|
||||
|
||||
## SMTP
|
||||
|
||||
Für lokales Testing `MailHog` oder `Mailpit`:
|
||||
|
||||
```bash
|
||||
docker run -d -p 1025:1025 -p 8025:8025 axllent/mailpit
|
||||
```
|
||||
|
||||
`SmtpAdapter::new("localhost", 1025)`.
|
||||
|
||||
## Test-Sub-CA
|
||||
|
||||
Optionen:
|
||||
|
||||
1. **Mock-Server** mit `wiremock` für Integrationstests — antwortet auf
|
||||
`RequestCertificate` mit `ok_syntax` und triggert anschließend einen
|
||||
HTTP-Call gegen den eigenen Callback-Endpunkt. Kein BSI-Compliance-Ersatz,
|
||||
aber gut für CI.
|
||||
2. **Echte Test-Sub-CA**-Instanz (sofern verfügbar) — Adresse + Test-EMT-
|
||||
Zertifikate aus eurem Lab-Bestand einsetzen.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
cargo test # Unit + In-Memory-Integration
|
||||
cargo test --features it # Integration mit SoftHSM-Container (folgt)
|
||||
```
|
||||
|
||||
Konvention: Tests liegen neben dem Code (`#[cfg(test)] mod tests`). Adapter-
|
||||
Tests, die externe Dienste brauchen, hinter Cargo-Feature `it` gaten.
|
||||
|
||||
## Code-Style
|
||||
|
||||
- `cargo fmt` vor jedem Commit.
|
||||
- `cargo clippy --all-targets -- -D warnings` muss grün sein.
|
||||
- Keine `Result<_, String>` in Produktionscode. Strukturierte Fehler via
|
||||
`thiserror`.
|
||||
Reference in New Issue
Block a user