# 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 ` | **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.