feat(opnsense,certmatch): read-only Export-Client mit Streaming und CN-Zuordnung
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01NBHF4R9EAejDJUMdwr6C68
This commit is contained in:
parent
99ee8758cc
commit
e49882b8a8
9 changed files with 1280 additions and 0 deletions
102
docs/opnsense-api.md
Normal file
102
docs/opnsense-api.md
Normal file
|
|
@ -0,0 +1,102 @@
|
|||
# OPNsense-API: genutzte Endpunkte und Annahmen
|
||||
|
||||
Das Portal spricht ausschließlich das Plugin **`os-openvpn-client-export`** an
|
||||
und ausschließlich lesend.
|
||||
|
||||
> **Diese Annahmen sind mit `go test -tags integration ./internal/opnsense/`
|
||||
> gegen eine echte Instanz zu bestätigen, bevor v1 ausgeliefert wird.**
|
||||
> Die exakten Feldnamen und Antwortformen wechseln zwischen OPNsense-Versionen;
|
||||
> der Client ist deshalb bewusst tolerant gebaut.
|
||||
|
||||
## Genutzte Endpunkte
|
||||
|
||||
| Methode | Pfad | Zweck |
|
||||
|---|---|---|
|
||||
| `GET` | `/api/openvpn/export/providers` | Liste der exportierbaren VPN-Instanzen |
|
||||
| `GET` | `/api/openvpn/export/accounts/{vpnid}` | Zertifikate einer Instanz |
|
||||
| `GET` | `/api/openvpn/export/download/{vpnid}/{format}/{certref}` | Konfiguration herunterladen |
|
||||
|
||||
Alle drei sind `GET`. Das Portal ruft **keinen** schreibenden Endpunkt auf und
|
||||
übergibt **keine** Exportoptionen — die auf der Firewall hinterlegten
|
||||
Einstellungen sind die Quelle der Wahrheit.
|
||||
|
||||
## Authentifizierung
|
||||
|
||||
HTTP Basic Auth mit API-Key als Benutzername und API-Secret als Passwort.
|
||||
Der API-Benutzer braucht ausschließlich das Privileg
|
||||
**„VPN: OpenVPN Client Export"** — niemals einen Admin-Key.
|
||||
|
||||
Bei ungültigem Key antwortet OPNsense je nach Version mit `401` oder mit
|
||||
`200` und einer HTML-Loginseite. Der Client behandelt beides als
|
||||
`ErrUnauthorized`; die HTML-Erkennung prüft sowohl den `Content-Type` als
|
||||
auch ein führendes `<` im Body.
|
||||
|
||||
## Akzeptierte Feldnamen
|
||||
|
||||
Der Client akzeptiert mehrere Schreibweisen und nimmt den ersten
|
||||
nicht-leeren Treffer (`internal/opnsense/types.go`, `rawAccount`):
|
||||
|
||||
| Bedeutung | Akzeptierte JSON-Felder |
|
||||
|---|---|
|
||||
| Common Name | `commonName`, `common_name` |
|
||||
| Beschreibung | `description`, `descr` |
|
||||
| Ablaufdatum | `validTo`, `valid_to`, `validto` |
|
||||
| Revoziert | `isRevoked`, `revoked`, `is_revoked` |
|
||||
| VPN-ID (Provider) | `vpnid`, sonst der Map-Schlüssel |
|
||||
| Instanzname | `name`, `description`, sonst `VPN <id>` |
|
||||
|
||||
**Revoziert** gilt, sobald *eines* der drei Felder wahr meldet — im Zweifel
|
||||
restriktiv. Als „wahr" zählen `true`, `1`, `"1"`, `"true"`, `"yes"`, `"on"`.
|
||||
|
||||
## Akzeptierte Datumsformate
|
||||
|
||||
`time.RFC3339`, `2006-01-02T15:04:05`, `2006-01-02 15:04:05`, `2006-01-02`,
|
||||
`Jan _2 15:04:05 2006 MST` (OpenSSL-Stil), `060102150405Z` (ASN.1 UTCTime)
|
||||
sowie Unix-Zeitstempel als Zahl.
|
||||
|
||||
Ein **nicht auswertbares oder fehlendes** Ablaufdatum führt *nicht* zum
|
||||
Ausschluss des Zertifikats — die Revocation-Prüfung bleibt maßgeblich, und ein
|
||||
unbekanntes Datum darf keine gültige Konfiguration blockieren. Der
|
||||
Integrationstest `TestIntegrationAccountsExpiryIsParsed` schlägt fehl, wenn
|
||||
*kein* Zertifikat ein auswertbares Datum liefert; das ist das Signal, die
|
||||
Layout-Liste zu ergänzen.
|
||||
|
||||
## Antwortformen des Downloads
|
||||
|
||||
Zwei Varianten sind implementiert und werden am `Content-Type` unterschieden:
|
||||
|
||||
1. **Rohdaten** (`application/octet-stream` o. ä.) — wird direkt
|
||||
durchgestreamt, nichts wird gepuffert oder auf Platte geschrieben.
|
||||
Der Dateiname kommt aus `Content-Disposition`.
|
||||
2. **Base64 in JSON** (`application/json`) — Feld `content` oder `data`,
|
||||
optional `filename`. Lässt sich der Inhalt nicht Base64-dekodieren, wird
|
||||
er als Klartext behandelt.
|
||||
|
||||
## Unterstützte Exportformate
|
||||
|
||||
Das Portal bietet genau zwei Formate an: `ovpn` (inline `.ovpn`) und
|
||||
`viscosity`. Weitere Formate sind bewusst nicht wählbar.
|
||||
|
||||
## Mindestversion
|
||||
|
||||
**Beim ersten erfolgreichen Integrationstestlauf hier eintragen:**
|
||||
|
||||
- OPNsense: _(zu ermitteln)_
|
||||
- `os-openvpn-client-export`: _(zu ermitteln)_
|
||||
|
||||
## Integrationstests ausführen
|
||||
|
||||
```bash
|
||||
export OPNSENSE_URL="https://fw01.firma.local"
|
||||
export OPNSENSE_KEY="..."
|
||||
export OPNSENSE_SECRET="..."
|
||||
export OPNSENSE_CA="/pfad/zur/firma-ca.pem" # optional
|
||||
export OPNSENSE_REVOKED_CN="testuser-revoked" # optional, für den Revocation-Test
|
||||
export OPNSENSE_VPNID="1" # optional, für den Export-Test
|
||||
export OPNSENSE_CERT_REF="abc123" # optional, für den Export-Test
|
||||
|
||||
go test -tags integration ./internal/opnsense/ -v
|
||||
```
|
||||
|
||||
Ohne gesetzte Variablen überspringen sich die Tests selbst; sie laufen
|
||||
deshalb nie versehentlich in der normalen Testsuite mit.
|
||||
Loading…
Add table
Add a link
Reference in a new issue