Skip to content

Commit 4e0df8c

Browse files
feat(docs): Add HAProxy Configurations to Docs (#1424)
* Add HAProxy docs * Add changes to Changelog * Add CodeBlock import to haproxy.mdc * Fix typos * Add exceptions to spelling
1 parent c34ec67 commit 4e0df8c

10 files changed

Lines changed: 225 additions & 0 deletions

File tree

.github/actions/spelling/expect.txt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ FCr
119119
fcrdns
120120
fediverse
121121
ffprobe
122+
fhdr
122123
financials
123124
finfos
124125
Firecrawl
@@ -156,6 +157,7 @@ grw
156157
gzw
157158
Hashcash
158159
hashrate
160+
hdr
159161
headermap
160162
healthcheck
161163
healthz
@@ -165,6 +167,7 @@ Hetzner
165167
hmc
166168
homelab
167169
hostable
170+
HSTS
168171
htmlc
169172
htmx
170173
httpdebug
@@ -331,6 +334,8 @@ stackoverflow
331334
startprecmd
332335
stoppostcmd
333336
storetest
337+
srcip
338+
strcmp
334339
subgrid
335340
subr
336341
subrequest
@@ -355,6 +360,7 @@ Timpibot
355360
TLog
356361
traefik
357362
trunc
363+
txn
358364
uberspace
359365
Unbreak
360366
unbreakdocker

docs/docs/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1616
- Fix honeypot and imprint links missing `BASE_PREFIX` when deployed behind a path prefix ([#1402](https://github.com/TecharoHQ/anubis/issues/1402))
1717
- Add ANEXIA Sponsor logo to docs ([#1409](https://github.com/TecharoHQ/anubis/pull/1409))
1818
- Improve idle performance in memory storage
19+
- Add HAProxy Configurations to Docs ([#1424](https://github.com/TecharoHQ/anubis/pull/1424))
1920

2021
<!-- This changes the project to: -->
2122

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
# HAProxy
2+
3+
import CodeBlock from "@theme/CodeBlock";
4+
5+
To use Anubis with HAProxy, you have two variants:
6+
- simple - stick Anubis between HAProxy and your application backend (simple)
7+
- perfect if you only have a single application in general
8+
- advanced - force Anubis challenge by default and route to the application backend by HAProxy if the challenge is correct
9+
- useful for complex setups
10+
- routing can be done in HAProxy
11+
- define ACLs in HAProxy for domains, paths etc which are required/excluded regarding Anubis
12+
- HAProxy 3.0 recommended
13+
14+
## Simple Variant
15+
16+
```mermaid
17+
---
18+
title: HAProxy with simple config
19+
---
20+
flowchart LR
21+
T(User Traffic)
22+
HAProxy(HAProxy Port 80/443)
23+
Anubis
24+
Application
25+
26+
T --> HAProxy
27+
HAProxy --> Anubis
28+
Anubis --> |Happy Traffic| Application
29+
```
30+
31+
Your Anubis env file configuration may look like this:
32+
33+
import simpleAnubis from "!!raw-loader!./haproxy/simple-config.env";
34+
35+
<CodeBlock language="bash">{simpleAnubis}</CodeBlock>
36+
37+
The important part is that `TARGET` points to your actual application and if Anubis and HAProxy are on the same machine, a UNIX socket can be used.
38+
39+
Your frontend and backend configuration of HAProxy may look like the following:
40+
41+
import simpleHAProxy from "!!raw-loader!./haproxy/simple-haproxy.cfg";
42+
43+
<CodeBlock language="bash">{simpleHAProxy}</CodeBlock>
44+
45+
This simply enables SSL offloading, sets some useful and required headers and routes to Anubis directly.
46+
47+
## Advanced Variant
48+
49+
Due to the fact that HAProxy can decode JWT, we are able to verify the Anubis token directly in HAProxy and route the traffic to the specific backends ourselves.
50+
51+
In this example are three applications behind one HAProxy frontend. Only App1 and App2 are secured via Anubis; App3 is open for everyone. The path `/excluded/path` can also be accessed by anyone.
52+
53+
```mermaid
54+
---
55+
title: HAProxy with advanced config
56+
---
57+
58+
flowchart LR
59+
T(User Traffic)
60+
HAProxy(HAProxy Port 80/443)
61+
B1(App1)
62+
B2(App2)
63+
B3(App3)
64+
Anubis
65+
66+
T --> HAProxy
67+
HAProxy --> |Traffic for App1 and App2 without valid challenge| Anubis
68+
HAProxy --> |app1.example.com | B1
69+
HAProxy --> |app2.example.com| B2
70+
HAProxy --> |app3.example.com| B3
71+
```
72+
73+
:::note
74+
75+
For an improved JWT decoding performance, it's recommended to use HAProxy version 3.0 or above.
76+
77+
:::
78+
79+
Your Anubis env file configuration may look like this:
80+
81+
import advancedAnubis from "!!raw-loader!./haproxy/advanced-config.env";
82+
83+
<CodeBlock language="bash">{advancedAnubis}</CodeBlock>
84+
85+
It's important to use `HS512_SECRET` which HAProxy understands. Please replace `<SECRET-HERE>` with your own secret string (alphanumerical string with 128 characters recommended).
86+
87+
You can set Anubis to force a challenge for every request using the following policy file:
88+
89+
import advancedAnubisPolicy from "!!raw-loader!./haproxy/advanced-config-policy.yml";
90+
91+
<CodeBlock language="yaml">{advancedAnubisPolicy}</CodeBlock>
92+
93+
The HAProxy config file may look like this:
94+
95+
import advancedHAProxy from "!!raw-loader!./haproxy/advanced-haproxy.cfg";
96+
97+
<CodeBlock language="haproxy">{advancedHAProxy}</CodeBlock>
98+
99+
Please replace `<SECRET-HERE>` with the same secret from the Anubis config.
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
# /etc/anubis/challenge-any.yml
2+
3+
bots:
4+
- name: any
5+
action: CHALLENGE
6+
user_agent_regex: .*
7+
8+
status_codes:
9+
CHALLENGE: 403
10+
DENY: 403
11+
12+
thresholds: []
13+
14+
dnsbl: false
15+
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
# /etc/anubis/default.env
2+
3+
BIND=/run/anubis/default.sock
4+
BIND_NETWORK=unix
5+
DIFFICULTY=4
6+
METRICS_BIND=:9090
7+
# target is irrelevant here, backend routing happens in HAProxy
8+
TARGET=http://0.0.0.0
9+
HS512_SECRET=<SECRET-HERE>
10+
COOKIE_DYNAMIC_DOMAIN=True
11+
POLICY_FNAME=/etc/anubis/challenge-any.yml
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
# /etc/haproxy/haproxy.cfg
2+
3+
frontend FE-multiple-applications
4+
mode http
5+
bind :80
6+
# ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
7+
bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
8+
9+
# set X-Real-IP header required for Anubis
10+
http-request set-header X-Real-IP "%[src]"
11+
12+
# redirect HTTP to HTTPS
13+
http-request redirect scheme https code 301 unless { ssl_fc }
14+
# add HSTS header
15+
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
16+
17+
# only force Anubis challenge for app1 and app2
18+
acl acl_anubis_required hdr(host) -i "app1.example.com"
19+
acl acl_anubis_required hdr(host) -i "app2.example.com"
20+
21+
# exclude Anubis for a specific path
22+
acl acl_anubis_ignore path /excluded/path
23+
24+
# use Anubis if auth cookie not found
25+
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ req.cook(techaro.lol-anubis-auth) -m found }
26+
27+
# get payload of the JWT such as algorithm, expire time, restrictions
28+
http-request set-var(txn.anubis_jwt_alg) req.cook(techaro.lol-anubis-auth),jwt_header_query('$.alg') if acl_anubis_required !acl_anubis_ignore
29+
http-request set-var(txn.anubis_jwt_exp) cook(techaro.lol-anubis-auth),jwt_payload_query('$.exp','int') if acl_anubis_required !acl_anubis_ignore
30+
http-request set-var(txn.anubis_jwt_res) cook(techaro.lol-anubis-auth),jwt_payload_query('$.restriction') if acl_anubis_required !acl_anubis_ignore
31+
http-request set-var(txn.srcip) req.fhdr(X-Real-IP) if acl_anubis_required !acl_anubis_ignore
32+
http-request set-var(txn.now) date() if acl_anubis_required !acl_anubis_ignore
33+
34+
# use Anubis if JWT has wrong algorithm, is expired, restrictions don't match or isn't signed with the correct key
35+
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.anubis_jwt_alg) -m str HS512 }
36+
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore { var(txn.anubis_jwt_exp),sub(txn.now) -m int lt 0 }
37+
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ var(txn.srcip),digest(sha256),hex,lower,strcmp(txn.anubis_jwt_res) eq 0 }
38+
use_backend BE-anubis if acl_anubis_required !acl_anubis_ignore !{ cook(techaro.lol-anubis-auth),jwt_verify(txn.anubis_jwt_alg,"<SECRET-HERE>") -m int 1 }
39+
40+
# custom routing in HAProxy
41+
use_backend BE-app1 if { hdr(host) -i "app1.example.com" }
42+
use_backend BE-app2 if { hdr(host) -i "app2.example.com" }
43+
use_backend BE-app3 if { hdr(host) -i "app3.example.com" }
44+
45+
backend BE-app1
46+
mode http
47+
server app1-server 127.0.0.1:3000
48+
49+
backend BE-app2
50+
mode http
51+
server app2-server 127.0.0.1:4000
52+
53+
backend BE-app3
54+
mode http
55+
server app3-server 127.0.0.1:5000
56+
57+
BE-anubis
58+
mode http
59+
server anubis /run/anubis/default.sock
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
# /etc/anubis/default.env
2+
3+
BIND=/run/anubis/default.sock
4+
BIND_NETWORK=unix
5+
SOCKET_MODE=0666
6+
DIFFICULTY=4
7+
METRICS_BIND=:9090
8+
COOKIE_DYNAMIC_DOMAIN=true
9+
# address and port of the actual application
10+
TARGET=http://localhost:3000
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# /etc/haproxy/haproxy.cfg
2+
3+
frontend FE-application
4+
mode http
5+
bind :80
6+
# ssl offloading on port 443 using a certificate from /etc/haproxy/ssl/ directory
7+
bind :443 ssl crt /etc/haproxy/ssl/ alpn h2,http/1.1 ssl-min-ver TLSv1.2 no-tls-tickets
8+
9+
# set X-Real-IP header required for Anubis
10+
http-request set-header X-Real-IP "%[src]"
11+
12+
# redirect HTTP to HTTPS
13+
http-request redirect scheme https code 301 unless { ssl_fc }
14+
# add HSTS header
15+
http-response set-header Strict-Transport-Security "max-age=31536000; includeSubDomains; preload"
16+
17+
# route to Anubis backend by default
18+
default_backend BE-anubis-application
19+
20+
BE-anubis-application
21+
mode http
22+
server anubis /run/anubis/default.sock

docs/docs/admin/installation.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -203,6 +203,7 @@ To get Anubis filtering your traffic, you need to make sure it's added to your H
203203
- [Kubernetes](./environments/kubernetes.mdx)
204204
- [Nginx](./environments/nginx.mdx)
205205
- [Traefik](./environments/traefik.mdx)
206+
- [HAProxy](./environments/haproxy.mdx)
206207

207208
:::note
208209

docs/docs/admin/native-install.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,3 +143,4 @@ For more details on particular reverse proxies, see here:
143143

144144
- [Apache](./environments/apache.mdx)
145145
- [Nginx](./environments/nginx.mdx)
146+
- [HAProxy](./environments/haproxy.mdx)

0 commit comments

Comments
 (0)