Funktionen
Überblick über das, was das Gateway tatsächlich kann. Bedienung dazu: Admin-UI; Einbau auf der Kundenseite: Formular einbinden.
Mandantenfähigkeit
Abschnitt betitelt „Mandantenfähigkeit“Ein Mandant ist eine Kundenseite. Angelegt wird er im Admin-UI, alles Weitere hängt daran:
- Site-Key (
site_…) — die öffentliche Kennung im Client-JS. Kein Geheimnis, nur Zuordnung; die Sicherheit liegt komplett serverseitig. - Empfänger-Adresse — das Ziel der Formularmails. Kommt nie aus dem Formular, sondern immer aus der Mandanten-Konfiguration.
- Origin-Allowlist — von welchen Domains Absendungen angenommen werden. Serverseitig geprüft, nicht bloß per CORS.
- Eigene SMTP-Route oder geteilte Default-Route — jeder Mandant kann über den eigenen Mailserver versenden oder über den der Plattform.
- Kill-Switch — ein Klick, und der Mandant lehnt sofort alles ab.
Die Formulardaten jedes Mandanten liegen in einem eigenen ORM++-Tenant — Submissions und Nonces sind dadurch hart getrennt.
Kein Formular-Schema
Abschnitt betitelt „Kein Formular-Schema“Das Gateway kennt keine Feldliste. Jedes <input name="…"> wird
übertragen und landet im Mailtext; ein neues Feld braucht nur HTML, keine
Konfiguration im Gateway. Nur drei Feldnamen sind reserviert:
_gw_reply (wird Reply-To), _gw_form (Formular-Kennung) und _gw_hp
(Honeypot, setzt das Snippet selbst).
Details und Grenzen: Formular einbinden.
Schutz vor Spam und Missbrauch
Abschnitt betitelt „Schutz vor Spam und Missbrauch“Die Prüf-Pipeline arbeitet „billig zuerst” — was am wenigsten kostet, läuft zuerst, damit ein Angriff früh abbricht:
| # | Schicht | Wirkung |
|---|---|---|
| 1 | Rate-Limits (global, IP, Site-Key) | in-memory, vor allem anderen |
| 2 | Mandant aktiv? Origin erlaubt? | Kill-Switch und Allowlist, serverseitig |
| 3 | Challenge-Token | HMAC-signiert, TTL 10 min, an Site und Origin gebunden |
| 4 | Mindest-Ausfüllzeit | Formular in unter n Sekunden ausgefüllt = Bot |
| 5 | Honeypot | unsichtbares Feld; Treffer täuscht Erfolg vor |
| 6 | IP-/ASN-Reputation | Tor-Exits, iptoasn.com, eigene Blocklist |
| 6b | Herkunftssperre | Länder-/Regionsregel des Mandanten (IPv4 und IPv6) |
| 7 | Proof-of-Work | pro Mandant konfigurierbare Bit-Schwelle |
| 8 | Nonce | atomar eingelöst — ein Token gilt genau einmal (Replay-Schutz) |
| 9 | Eingabe-Härtung | Feld- und Längenlimits, CRLF-Schutz, Reply-To-Validierung |
| 10 | E-Mail-Blockmuster | Sperrmuster gegen die Antwortadresse |
| 11 | Tageskontingent | persistent, zählt wartende und gesendete Mails |
| 12 | Spam-Score | ab Schwelle Quarantäne statt Versand |
Wiederholt auffällige IPs kann das Gateway zusätzlich an eine CrowdSec LAPI melden — die vorgelagerte Firewall sperrt sie dann, bevor der nächste Request überhaupt ankommt (Sicherheit).
Nach außen bleibt das Gateway wortkarg: jede Ablehnung ist
400 {"ok":false,"reason":"rejected"}. Welche Schicht gegriffen hat,
steht nur im Log und im Admin — ein Spammer bekommt kein Orakel, an dem
er seine Versuche kalibrieren kann. Honeypot-Treffer und Quarantäne sehen
für den Client sogar wie Erfolg aus.
Alle Zahlenwerte (PoW-Bits, Spam-Schwelle, Tageskontingent,
Mindest-Ausfüllzeit) sind pro Mandant einstellbar und einzeln
abschaltbar (0).
Quarantäne statt Wegwerfen
Abschnitt betitelt „Quarantäne statt Wegwerfen“Was über der Spam-Schwelle liegt, wird nicht verworfen, sondern quarantänisiert. Im Mandanten-Detail lässt sich jede Absendung freigeben (geht dann in den Versand) oder verwerfen. Falsch positive Treffer kosten damit keine Nachricht.
Versand
Abschnitt betitelt „Versand“- SMTP-Routen mit STARTTLS (587), implizitem TLS (465) oder unverschlüsselt (nur für Relays auf derselben Maschine).
- Fester Absender pro Route — kein frei wählbares
Fromaus dem Formular. Das schützt die Reputation der Absenderdomain; die Antwortadresse des Einsenders reist alsReply-To. - Retry mit Backoff über eine Queue in der Datenbank; ein kurzzeitig nicht erreichbarer Mailserver verliert keine Nachricht.
- Test-Versand-Button im Admin prüft Verbindung und Login, bevor echte Formulare darauf laufen.
- Health-Anzeige je Route: letzter Fehler, letzter erfolgreicher Versand.
Eingangsbestätigung an den Einsender
Abschnitt betitelt „Eingangsbestätigung an den Einsender“Optional pro Mandant: Wer das Formular abschickt, bekommt eine kurze
Bestätigung an die Adresse, die er selbst eingetragen hat (Feld
_gw_reply). Den Text pflegt der Admin als Freitext — er steht so in
der Mail, wie er im Admin steht.
- Aus per Default. Ohne eingeschaltete Option und ohne Antwortadresse im Formular passiert nichts.
- Ohne die eingesendeten Daten. Die Bestätigung enthält bewusst nur den festen Text. Würden die Formularfelder mitgeschickt, ließe sich über das Formular beliebiger Text an eine beliebige fremde Adresse schicken — das Gateway wäre ein Verstärker.
- Erst nach bestandener Prüfung. Quarantänisierte Einsendungen lösen keine Bestätigung aus; erst die Freigabe im Admin tut es. Ein mutmaßlicher Spammer erfährt so auch nicht, dass seine Adresse zugestellt werden kann.
- Zählt auf das Tageskontingent, weil sie an eine vom Absender bestimmte Adresse geht — das begrenzt zugleich das Missbrauchspotenzial.
- Antworten erreichen den Seitenbetreiber:
Reply-Toder Bestätigung ist die Empfänger-Adresse des Mandanten.
Herkunftssperre (Geo)
Abschnitt betitelt „Herkunftssperre (Geo)“Pro Mandant lässt sich einschränken, aus welchen Ländern Formulare abgeschickt werden dürfen:
| Modus | Bedeutung |
|---|---|
off | keine Einschränkung (Standard) |
allow | nur die gelisteten Länder dürfen absenden |
deny | die gelisteten Länder dürfen nicht absenden |
Gelistet werden Ländercodes (DE, AT, US …) oder Gruppen:
DACH (DE/AT/CH), EU (die 27 Mitgliedstaaten), EEA (EU + IS/LI/NO),
EUROPE (geografisches Europa inkl. CH, GB, UA).
Die Zuordnung IP → Land kommt aus demselben iptoasn-Dump, der ohnehin täglich für die ASN-Prüfung geladen wird — kein zusätzlicher Dienst, keine Anfrage an Dritte pro Formular. IPv4 und IPv6 werden gleich behandelt; eine Sperre lässt sich also nicht durch einen IPv6-Anschluss umgehen.
Wichtig — unbekannte Herkunft blockt nicht. Lässt sich eine IP
keinem Land zuordnen (nicht geroutet, Dump noch nicht geladen oder
Download gescheitert), gilt sie als erlaubt — auch im allow-Modus.
Andernfalls würde ein gescheiterter Download eines Fremdservers das
Kontaktformular komplett stilllegen. Die übrigen Schichten (PoW,
Honeypot, Spam-Score, Quarantäne) greifen weiter.
Jeder Geo-Treffer erscheint als blocked-Submission im Admin (mit
Grund, z. B. „herkunftsland US gesperrt (modus allow)”) und zählt für
die Angriffserkennung mit. Hämmert dieselbe IP dauerhaft dagegen,
reißt sie die CrowdSec-Schwelle und wird von der vorgelagerten
Firewall gesperrt — bewusst mit niedrigem Gewicht, damit ein einzelner
Geschäftsreisender im Ausland nicht sofort in der Firewall landet.
Blocklist
Abschnitt betitelt „Blocklist“Eigene Sperrliste für IP, CIDR, ASN, E-Mail-Adressen und Länder/Regionen, jeweils mit Notiz. Einträge wirken sofort — sie werden in den Reputations-Speicher gespiegelt und greifen in Schicht 6 der Pipeline. Ein Länder-Eintrag hier gilt global für alle Mandanten; die mandantenspezifische Regel steht im Mandanten-Formular.
Admin-Bereich
Abschnitt betitelt „Admin-Bereich“Web-UI und API im selben Binary:
- Benutzer mit argon2id-Passwörtern und Rollen (RBAC, statischer Katalog im Code).
- API-Keys (
fgw_…) für Prozesse ohne Browser — mit Ablaufdatum, Widerruf,read-Scope und eigenem Rate-Limit. - Statistiken pro Mandant und Submission-Historie mit Statusfilter.
Details: Sicherheit.
Datenschutz
Abschnitt betitelt „Datenschutz“- Selbst gehostet — die Formulardaten verlassen die eigene Infrastruktur nicht.
- Client-IPs landen nur als gekürzter SHA-256-Hash in der Datenbank.
- Keine Cookies, kein Tracking im Client-Snippet, keine Drittressourcen.
- DSGVO-Export und -Purge pro Kundenseite über die ORM++-Tenant-Registry — Auskunft und Löschung sind eine Operation, kein SQL-Abenteuer.
- Audit-Log durch Event-Sourcing: jede Submission trägt ihre
Statushistorie (
received → scored → sent/quarantined → released/discarded).
Client-Snippet
Abschnitt betitelt „Client-Snippet“Dependency-freies Vanilla-JS, ~1,6 KB gzip, minifiziert vom Gateway
ausgeliefert. Holt die Challenge, löst den Proof-of-Work in einem Web
Worker (mit Fallback auf den Hauptthread), sendet ab und zeigt den Status
inline an. Mehrere Formulare pro Seite werden automatisch verdrahtet;
ohne JavaScript bleibt das <form> ein normales Formular.
Betrieb
Abschnitt betitelt „Betrieb“- Ein Binary — Go-Backend, Svelte-Admin-UI und Client-Snippet sind
einkompiliert. Eine Installation besteht aus Binary,
config.jsonund Datenbankdatei. - SQLite oder PostgreSQL, umgeschaltet durch eine Konfigurationszeile ohne Codeänderung.
- CGO-frei ⇒ Cross-Compiling für Linux (amd64/arm64), Windows und macOS ohne Toolchains.
- Docker-Image (Alpine, non-root, read-only, Healthcheck) und systemd-Betrieb sind dokumentiert.
- Graceful Shutdown bei SIGINT/SIGTERM, inklusive Hintergrund-Worker.
- Versions- und Build-Info über
-version,GET /api/v1/system/infound den Footer der Web-App.