You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
`cookie-guard-spoa` is an HAProxy SPOE (Stream Processing Offload Engine) agent that issues and validates HMAC‑signed cookies.
4
4
5
-
It ships with a privacy‑friendly browser challenge powered by ALTCHA and dedicated endpoints built into the agent. HAProxy serves a small HTML page and the agent verifies the puzzle solution, issuing the `hb_v2` cookie on success. Only clients presenting a valid cookie reach your backend.
5
+
It ships with two first-party protections enabled by default:
6
+
7
+
-**ALTCHA** – a lightweight, open-source puzzle that proves the visitor can execute JavaScript, persist cookies, and solve a human-friendly challenge before the origin ever sees the request.
8
+
-**BotD (FingerprintJS)** – a local copy of the BotD detector that fingerprints the browser for automation traits (headless Chrome, Selenium drivers, emulators) and reports the verdict back to Cookie Guard, allowing HAProxy or downstream SPOEs to block, throttle, or log suspect sessions.
9
+
10
+
HAProxy serves a small HTML page that embeds both protections. The agent verifies the ALTCHA solution, ingests the BotD verdict, and issues the `hb_v2` cookie on success. Only clients presenting a valid cookie reach your backend unless you explicitly disable the challenge or BotD via CLI flags.
6
11
7
12
Learn more about ALTCHA:
8
13
@@ -16,14 +21,14 @@ Learn more about ALTCHA:
16
21
17
22
## Overview
18
23
19
-
The agent offloads cookie lifecycle management from HAProxy:
24
+
Cookie Guard inserts an inline checkpoint between HAProxy and your origin that:
20
25
21
-
1.Generates short-lived, signed cookies derived from the client IP and User-Agent.
22
-
2.Exposes helper endpoints that HAProxy can embed in a challenge page.
23
-
3. Validates cookies on subsequent requestsand reports the outcome back to HAProxy via SPOE frames.
24
-
4.Enables HAProxy to allow, rate-limit, or block requests that fail the validation.
26
+
1.**Challenges new sessions** – serves the bundled ALTCHA puzzle and locally hosted BotD detector so only browsers that can execute JavaScript, persist cookies, and pass automation fingerprinting obtain an `hb_v2` cookie.
27
+
2.**Issues and tracks tokens** – mints short-lived, HMAC-signed cookies bound to the client IP and (optionally) User-Agent, then caches recent BotD verdicts for the same tuple.
28
+
3.**Validates on subsequent requests** – verifies hb_v2 on every request via SPOE and reuses the cached BotD verdict so downstream policies can treat “good”, “suspect”, or “bad” sessions differently.
29
+
4.**Feeds HAProxy/SPOE peers** – exposes fresh transaction variables (`cookieguard.valid`, `cookieguard.botd_kind`, `cookieguard.session_hmac`, etc.) that HAProxy, Decision-SPOA, or other agents can use to block, rate-limit, or log.
25
30
26
-
This setup filters out most headless bots, generic scanners, or curl-based tooling that cannot execute JavaScript or persist cookies.
31
+
Because the HTML and JavaScript are served from your own HAProxy backend, no third-party calls or trackers are involved. The combination of ALTCHA (prove you are interactive) and BotD (fingerprint automation) removes most headless browsers, cURL scripts, and basic scrapers before they ever see your real site.
27
32
28
33
---
29
34
@@ -101,6 +106,7 @@ Additionally, packages include the challenge pages and ALTCHA assets under `/etc
101
106
102
107
-`/etc/haproxy/altcha_challenge.html.lf`.
103
108
- ALTCHA JS is installed under `/etc/haproxy/assets/altcha/<version>/altcha.min.js[.lf]` with `/etc/haproxy/assets/altcha/active` symlink updated to the packaged version.
109
+
- BotD JS is installed under `/etc/haproxy/assets/botd/<version>/botd.esm.js[.lf]` with `/etc/haproxy/assets/botd/active` baked into the package so the challenge page can import `/assets/botd/active/botd.esm.js` immediately.
104
110
105
111
After installation, adjust `/etc/cookie-guard-spoa/secret.key` or edit the systemd unit as needed, then `systemctl restart cookie-guard-spoa`.
106
112
@@ -123,61 +129,118 @@ After editing, run `systemctl restart cookie-guard-spoa`.
server spoa1 127.0.0.1:9903 check inter 2s fall 2 rise 1
162
+
```
155
163
156
-
3.**Example application backend**
164
+
### Reference frontend/backends
157
165
158
-
```haproxy
159
-
backend be_app
160
-
option http-buffer-request
166
+
Below is a compact `public_www` setup that wires Cookie Guard alone in front of a single backend. Swap the binds/hosts for your environment and layer in additional SPOEs (Decision, Coraza, etc.) later once the basic flow works.
Backends reuse the same Cookie Guard SPOE engine. The snippet below illustrates challenge orchestration plus silent token issuance when Decision (or another policy component) is not involved yet. Feel free to inline your own exemption ACLs.
http-request add-header X-Forwarded-Proto https if { ssl_fc }
219
+
option forwarded
220
+
option forwardfor
221
+
server app1 127.0.0.1:8080 check
222
+
```
169
223
170
-
http-request set-spoe-group cookie_guard issue-token if chal_target !cookie_ok
224
+
Cookie Guard’s HTTP listener serves the ALTCHA HTML, ALTCHA JS, BotD bundle, and `/botd-report`. Route traffic there using:
171
225
172
-
server app1 127.0.0.1:8080 check
173
-
```
226
+
```haproxy
227
+
backend cookie_guard_http_backend
228
+
mode http
229
+
option forwarded
230
+
option forwardfor
231
+
http-request set-header X-Forwarded-For %[src]
232
+
server spoa_http 127.0.0.1:9904 check
233
+
```
174
234
175
-
When HAProxy runs the `verify-token` message, the agent populates the following transaction-scoped variables (prefixed via `option var-prefix cookieguard`):
235
+
### What HAProxy gets back
176
236
177
-
-`txn.cookieguard.valid`: `"1"` when the hb_v2 cookie validates, otherwise `"0"`.
178
-
-`txn.cookieguard.age_seconds`: age of the accepted cookie (stringified integer seconds).
179
-
-`txn.cookieguard.session_hmac`: HMAC handle derived from the cookie value for downstream session tracking (empty when invalid or missing).
180
-
-`txn.cookieguard.challenge_level`: textual label for the challenge that produced the cookie (currently `"altcha"` for hb_v2).
237
+
When HAProxy runs the `verify-token` message, the agent populates transaction-scoped variables (prefixed by `option var-prefix cookieguard`):
238
+
239
+
-`txn.cookieguard.valid`: `"1"` when the hb_v2 cookie validates, otherwise `"0"`.
240
+
-`txn.cookieguard.age_seconds`: age of the accepted cookie.
241
+
-`txn.cookieguard.session_hmac`: deterministic handle for downstream correlation.
242
+
-`txn.cookieguard.challenge_level`: label for the challenge that produced the cookie (`"altcha"` today).
@@ -205,6 +268,7 @@ With `option var-prefix cookieguard`, HAProxy sees the following variables under
205
268
-`age_seconds` (stringified integer, always set): age of the accepted cookie. Remains "0" for invalid/missing cookies. You can rate-limit or log based on freshness.
206
269
-`session_hmac` (hex string, optional): deterministic HMAC derived from the hb_v2 payload. Decision-SPOA uses this value as `cookieguard_session` to correlate sessions without exposing the token itself. Empty when validation fails.
207
270
-`challenge_level` (string, optional): label describing how the cookie originated. Currently always `"altcha"` when verification succeeds; keep space for future challenge types.
271
+
-`botd_verdict`/`botd_kind`/`botd_confidence`/`botd_request_id` (strings, optional): populated when a recent BotD report exists for the same client IP + UA hash. `botd_tool` remains as a backward-compatible alias of `botd_kind`. These let [decision-spoa](https://github.com/artefactual-labs/decision-spoa) or native HAProxy ACLs act on BotD detections without re-running the script.
208
272
209
273
By design, `verify-token` always resets every output to a safe default before attempting validation so stale data never leaks between transactions.
210
274
@@ -251,7 +315,32 @@ By design, `verify-token` always resets every output to a safe default before at
251
315
- The agent also serves the page at `/altcha` from `-altcha-page` (default `/etc/haproxy/altcha_challenge.html.lf`).
252
316
- Packages enable`-cookie-secure` by default so `hb_v2` ships with the `Secure` attribute. Comment it in`/etc/default/cookie-guard-spoa`if you must disable it.
253
317
254
-
318
+
5. **BotD verdict ingestion (optional)**
319
+
320
+
When `-botd` is enabled (default), the metrics listener exposes `POST /botd-report`. The shipped challenge page loads FingerprintJS BotD in the browser, detects automation, and POSTs the verdict before ALTCHA begins. The payload includes `verdict`, `botKind` (only set when automation is detected), `confidence`, `requestId`, and `ua_hash`. The agent caches each verdict for`-botd-ttl` (default `5m`) keyed by client IP and UA hash, exposes it via SPOE transaction variables (`botd_verdict`, `botd_kind`, `botd_confidence`, `botd_request_id`;`botd_tool` remains as an alias), and emits Prometheus metrics.
321
+
322
+
- `botd_confidence` mirrors Fingerprint’s 0–1 confidence score (the bundled OSS detector reports `0`for “no automation observed” and `1`for confirmed bots; the hosted SaaS may emit fractional probabilities).
323
+
- `botd_request_id` surfaces Fingerprint’s request identifier when present, which is useful forcorrelating detectionsin their dashboards/logs. Browsers that run entirely locally usually leave it empty.
324
+
325
+
- Route `/botd-report` to the same backend that serves `/altcha*` so the agent receives reports.
326
+
- Serve the bundled JS from `/assets/botd/active/botd.esm.js`; packages install it under `/etc/haproxy/assets/botd`, and `make botd-assets && sudo make install-botd-assets` refreshes the version.
327
+
- Enable or disable the endpoint with `-botd`;set cache capacity with `-botd-cache-max` (use `0` to disable storage).
- `cookie_guard_botd_cache_entries` shows live cache cardinality.
332
+
- `cookie_guard_botd_cache_evictions_total` increments when entries expire or capacity forces eviction.
333
+
334
+
Downstream policy engines (e.g., [decision-spoa](https://github.com/artefactual-labs/decision-spoa)) can read the new SPOE variables to make the final allow/challenge/block decision without changing cookie-guard’s core logic. Cookie Guard focuses on proving “is this a real, interactive browser?” while Decision consumes the resulting `cookieguard.*` and `botd_*` variables (plus GeoIP/session context) to apply richer rules—together they form a layered defense that challenges unknown traffic, fingerprints automation, and then enforces nuanced policies.
335
+
336
+
## Optional integration with decision-spoa
337
+
338
+
[decision-spoa](https://github.com/artefactual-labs/decision-spoa) is Artefactual’s policy SPOE for HAProxy. Pairing it with Cookie Guard combines:
- **Decision** – GeoIP lookups, session-rate tracking, JA3/UA heuristics, and a rule engine that consumes `cookieguard.*` / `botd_*` variables to choose block/allow/challenge routes.
342
+
343
+
Together they deliver a layered defense: Cookie Guard proves the visitor is an interactive browser and fingerprints automation; Decision ingests those signals plus its own telemetry to decide whether to serve the origin, throttle, or escalate.
255
344
256
345
6. **Frontend**
257
346
@@ -333,6 +422,17 @@ Versioning policy and updates:
333
422
systemctl reload haproxy
334
423
```
335
424
425
+
### Update BotD
426
+
427
+
- JS asset: pinned under `web/assets/botd/<version>` with `web/assets/botd/active` symlinked to the active version. To bump:
428
+
```bash
429
+
echo vX.Y.Z > web/assets/botd/VERSION
430
+
make botd-assets
431
+
sudo make install-botd-assets
432
+
systemctl reload haproxy
433
+
```
434
+
- Browser challenge: `web/altcha_challenge.html.lf` imports `/assets/botd/active/botd.esm.js`. Ensure HAProxy routes that path (and `/botd-report`) to the Cookie Guard HTTP listener so the new version is served immediately.
0 commit comments