-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhaproxy.cfg
More file actions
386 lines (354 loc) · 17.5 KB
/
Copy pathhaproxy.cfg
File metadata and controls
386 lines (354 loc) · 17.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
global
log stdout format raw local0 info
maxconn 100000
ssl-default-bind-ciphers ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305
ssl-default-bind-ciphersuites TLS_AES_128_GCM_SHA256:TLS_AES_256_GCM_SHA384:TLS_CHACHA20_POLY1305_SHA256
ssl-default-bind-options ssl-min-ver TLSv1.2 no-tls-tickets
defaults
log global
mode http
option httplog
option dontlognull
option forwardfor except 127.0.0.0/8
option http-server-close
timeout connect 5s
timeout client 60s
timeout server 60s
timeout tunnel 3600s
# -----------------------------------------------------------------------------
# HTTP → HTTPS (redirection + challenge ACME Let's Encrypt)
# -----------------------------------------------------------------------------
frontend http
bind *:80
mode http
acl letsencrypt path_beg /.well-known/acme-challenge/
http-request redirect scheme https code 301 unless letsencrypt
use_backend letsencrypt-backend if letsencrypt
# -----------------------------------------------------------------------------
# HTTPS — point d'entrée principal
# Les domaines sont injectés au démarrage via envsubst (haproxy-entrypoint.sh)
# -----------------------------------------------------------------------------
frontend https
bind *:443 ssl crt /certs/
mode http
# Protection Slowloris — abandon si la requête n'est pas reçue en entier en 10s
timeout http-request 10s
# Bloquer les méthodes HTTP dangereuses (TRACE, DEBUG, CONNECT)
acl bad_method method TRACE DEBUG CONNECT
http-request deny if bad_method
# Restreindre les méthodes WebDAV aux seuls chemins API Nextcloud (remote.php, ocs…)
# OPTIONS non bloqué : nécessaire pour les preflight CORS de Collabora / Whiteboard
# Requêtes S3 (AWS4-HMAC-SHA256) exemptées — elles passent par le backend rustfs-s3api
acl is_api_path path_beg /remote.php /public.php /ocs /apps/whiteboard /s3-console /rustfs
acl is_s3_api req.hdr(authorization) -m beg "AWS4-HMAC-SHA256"
http-request deny if { method PUT DELETE MKCOL COPY MOVE LOCK UNLOCK PROPFIND PROPPATCH } !is_api_path !is_s3_api
# Bloquer les user-agents connus pour être malveillants
acl bad_ua hdr_sub(user-agent) -i sqlmap nikto nmap masscan zgrab curl/7.2 python-requests/2.0
http-request deny deny_status 403 if bad_ua
# Bloquer les paths de scan courants (bots, exploits WordPress/PHP)
acl is_scan_path path_beg /wp-admin /wp-login /.git /.env /phpmyadmin /cgi-bin /vendor /backup
acl is_scan_path path /xmlrpc.php /wp-login.php /.DS_Store /config.php /shell.php
http-request deny deny_status 403 if is_scan_path
# Silence les health checks fréquents pour réduire le bruit de log (~80% des entrées)
acl is_hc_path path /status.php /robots.txt /favicon.ico
http-request set-log-level silent if is_hc_path
# En-têtes de sécurité HTTP communs à tous les backends
# X-Frame-Options est volontairement absent ici : Collabora et Whiteboard ont besoin
# d'être chargés en iframe depuis Nextcloud (domaines différents). Il est appliqué
# uniquement sur le backend nextcloud ci-dessous.
http-response set-header X-Content-Type-Options "nosniff"
http-response set-header X-XSS-Protection "1; mode=block"
http-response set-header Referrer-Policy "strict-origin-when-cross-origin"
http-response set-header Strict-Transport-Security "max-age=63072000; includeSubDomains; preload"
http-response set-header Permissions-Policy "camera=(self), microphone=(self), geolocation=(self), payment=()"
http-response set-header X-Download-Options "noopen"
http-response set-header X-Permitted-Cross-Domain-Policies "none"
http-response del-header Server
http-response del-header X-Powered-By
http-request add-header X-Forwarded-Proto https
http-request add-header X-Real-IP %[src]
# Statistiques HAProxy sur /stats (HTTPS, authentifié)
# BEGIN_STATS
stats enable
stats uri /stats
stats refresh 30s
stats auth admin:${HAPROXY_STATS_PASSWORD}
stats hide-version
stats show-legends
stats show-node
# END_STATS
# Console RustFS built-in — entry point /s3-console, served at /rustfs/console/
# BEGIN_S3_CONSOLE
acl is_rustfs_console path_beg /rustfs
# Fix RustFS beta bug: logout emits a relative redirect, doubling the base path
http-request redirect location /rustfs/console/auth/login/ code 302 if { path_beg /rustfs/console/rustfs/console/ }
# Friendly redirect: /s3-console → /rustfs/console/ (RustFS native path)
http-request redirect location /rustfs/console/ code 301 if { path_beg /s3-console }
use_backend s3-console if is_rustfs_console
# END_S3_CONSOLE
# Redirections Nextcloud .well-known
acl url_discovery path /.well-known/caldav /.well-known/carddav
http-request redirect location /remote.php/dav/ code 301 if url_discovery
# ACL par sous-domaine (valeurs injectées depuis .env via envsubst)
acl is_nextcloud hdr(host) -i ${NEXTCLOUD_DOMAIN}
acl is_collabora hdr(host) -i ${COLLABORA_DOMAIN}
acl is_whiteboard hdr(host) -i ${WHITEBOARD_DOMAIN}
acl is_talk hdr(host) -i ${TALK_DOMAIN}
# Mémoriser en phase requête (path_end + hdr valides ici) pour exploitation en réponse
http-request set-var(txn.collab_js) str(1) if is_collabora { path_end .js }
# Corriger le Content-Type des .js Collabora servis en text/plain (var + res.hdr valides ici)
http-response set-header Content-Type "application/javascript; charset=utf-8" if { var(txn.collab_js) -m str 1 } { res.hdr(Content-Type) -m beg "text/plain" }
# S3 API — requêtes signées AWS4 (console RustFS browser-side) → port 9000
# Le browser fait des appels S3 directs pour s'authentifier et lister les buckets.
# L'Authorization: AWS4-HMAC-SHA256 distingue ces requêtes des requêtes Nextcloud.
use_backend rustfs-s3api if is_s3_api
# Client Push (notify_push) — must be before general nextcloud rule
acl is_push path_beg /push
use_backend notify-push if is_nextcloud is_push
use_backend nextcloud if is_nextcloud
use_backend coolwsd if is_collabora
use_backend whiteboard if is_whiteboard
use_backend signaling if is_talk
default_backend nextcloud
# -----------------------------------------------------------------------------
# Backend ACME (Let's Encrypt via nginx-acme)
# -----------------------------------------------------------------------------
backend letsencrypt-backend
server nginx-acme nginx-acme:80
# -----------------------------------------------------------------------------
# Résolution DNS interne Docker
# -----------------------------------------------------------------------------
resolvers docker
nameserver dns1 127.0.0.11:53
resolve_retries 3
timeout resolve 1s
timeout retry 1s
hold valid 10s
# -----------------------------------------------------------------------------
# Backend Nextcloud
# -----------------------------------------------------------------------------
backend nextcloud
balance roundrobin
option http-keep-alive
option httpchk
option redispatch
retries 3
http-check send meth GET uri /status.php ver HTTP/1.1 hdr Host ${NEXTCLOUD_DOMAIN}
http-check expect string installed
cookie SRVNAME insert indirect preserve nocache httponly secure
timeout connect 10s
timeout server 60s
default-server inter 5s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_NEXTCLOUD
server nginx-next-01 nginx-next-01:80 check cookie NC01
server nginx-next-02 nginx-next-02:80 check cookie NC02
server nginx-next-03 nginx-next-03:80 check cookie NC03
# END_SERVERS_NEXTCLOUD
# X-Frame-Options uniquement sur Nextcloud (Collabora/Whiteboard ont besoin d'iframes cross-origin)
http-response set-header X-Frame-Options "SAMEORIGIN"
# Étendre le CSP pour autoriser les connexions WebSocket vers Whiteboard et Collabora
# Nextcloud génère connect-src 'self' — on y ajoute les domaines externes nécessaires
http-response replace-header Content-Security-Policy "connect-src ([^;]*)" "connect-src \1 https://${WHITEBOARD_DOMAIN} wss://${WHITEBOARD_DOMAIN} https://${COLLABORA_DOMAIN} wss://${COLLABORA_DOMAIN} https://${TALK_DOMAIN} wss://${TALK_DOMAIN}"
# Nextcloud 33 utilise des nonces — 'unsafe-inline' est ignoré par les navigateurs modernes
# quand un nonce est présent. On le supprime de script-src uniquement ([^;]* s'arrête au ;)
# Capture group \1 reconstruit le reste de script-src sans 'unsafe-inline'.
http-response replace-header Content-Security-Policy "script-src([^;]*) 'unsafe-inline'" "script-src\1"
# -----------------------------------------------------------------------------
# Backend Collabora Online (CODE)
# -----------------------------------------------------------------------------
backend coolwsd
mode http
balance leastconn
hash-type consistent
timeout tunnel 3600s
stick-table type string len 100 size 1m expire 30m
stick on url_param(WOPISrc) if { url_param(WOPISrc) -m found }
stick on url_param(doc_id) if { url_param(doc_id) -m found }
option httpchk
http-check send meth GET uri /hosting/discovery ver HTTP/1.1 hdr Host ${COLLABORA_DOMAIN}
http-check expect status 200
default-server inter 10s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
http-request deny deny_status 403 if { path_beg /browser/dist/admin }
# BEGIN_SERVERS_COLLABORA
server collabora-node1 collabora-node1:9980 check
server collabora-node2 collabora-node2:9980 check
server collabora-node3 collabora-node3:9980 check
# END_SERVERS_COLLABORA
# -----------------------------------------------------------------------------
# Backend Whiteboard
# -----------------------------------------------------------------------------
backend whiteboard
mode http
balance roundrobin
no option http-server-close
no option redispatch
retries 0
timeout connect 5s
timeout server 60s
timeout tunnel 3600s
cookie WBC insert indirect nocache
default-server inter 10s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_WHITEBOARD
server whiteboard-node1 whiteboard-node1:3002 check cookie WB1
server whiteboard-node2 whiteboard-node2:3002 check cookie WB2
# END_SERVERS_WHITEBOARD
# -----------------------------------------------------------------------------
# Load Balancing MariaDB Galera (TCP interne — non exposé à l'extérieur)
# Prérequis : l'utilisateur haproxy_check est créé par mariadb/init.sql
# -----------------------------------------------------------------------------
listen galera
bind *:3306
mode tcp
option tcplog
option tcpka
option mysql-check user haproxy_check
balance leastconn
default-server inter 5s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_GALERA
server mariadb-node1 mariadb-node1:3306 check
server mariadb-node2 mariadb-node2:3306 check
server mariadb-node3 mariadb-node3:3306 check
# END_SERVERS_GALERA
# -----------------------------------------------------------------------------
# Load Balancing RustFS S3 (TCP interne — non exposé à l'extérieur)
# -----------------------------------------------------------------------------
listen rustfs
bind *:9000
mode tcp
option tcplog
balance leastconn
default-server inter 2s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_RUSTFS
server rustfs-node1 rustfs-node1:9000 check
server rustfs-node2 rustfs-node2:9000 check
server rustfs-node3 rustfs-node3:9000 check
server rustfs-node4 rustfs-node4:9000 check
# END_SERVERS_RUSTFS
# -----------------------------------------------------------------------------
# Monitoring Redis Cluster (TCP — visible dans les stats, trafic non proxifié)
# Nextcloud se connecte directement aux nodes ; ce bloc sert uniquement à la
# supervision de l'état des nodes depuis la page /stats
# -----------------------------------------------------------------------------
listen redis-cluster
bind *:6380
mode tcp
option tcplog
option tcpka
balance leastconn
default-server inter 5s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_REDIS
server redis-node1 redis-node1:6379 check
server redis-node2 redis-node2:6379 check
server redis-node3 redis-node3:6379 check
server redis-node4 redis-node4:6379 check
server redis-node5 redis-node5:6379 check
server redis-node6 redis-node6:6379 check
# END_SERVERS_REDIS
# -----------------------------------------------------------------------------
# Monitoring nœuds FPM Nextcloud (TCP — visible dans les stats, trafic non proxifié)
# HAProxy vérifie le port 9000 de chaque container FPM directement
# -----------------------------------------------------------------------------
listen nextcloud-fpm
bind *:9001
mode tcp
option tcplog
option tcpka
balance leastconn
default-server inter 5s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_NEXTCLOUD_FPM
server app-next-01 app-next-01:9000 check
server app-next-02 app-next-02:9000 check
server app-next-03 app-next-03:9000 check
# END_SERVERS_NEXTCLOUD_FPM
# BEGIN_TALK_BACKEND
# -----------------------------------------------------------------------------
# Backend Talk Signaling (nextcloud-spreed-signaling)
# Long-lived WebSocket connections — no http-server-close, tunnel timeout 1h
# -----------------------------------------------------------------------------
backend signaling
mode http
balance leastconn
no option http-server-close
no option redispatch
retries 0
timeout connect 5s
timeout server 60s
timeout tunnel 3600s
option httpchk
http-check send meth GET uri /api/v1/welcome ver HTTP/1.0
http-check expect status 200
default-server inter 5s fastinter 1s downinter 2s rise 2 fall 2 resolvers docker init-addr libc,none on-marked-down shutdown-sessions
# BEGIN_SERVERS_SIGNALING
server spreed-signaling-01 spreed-signaling-01:8080 check
server spreed-signaling-02 spreed-signaling-02:8080 check
# END_SERVERS_SIGNALING
# -----------------------------------------------------------------------------
# Backend NATS cluster — TCP healthcheck only (talk-net, visible in /stats)
# -----------------------------------------------------------------------------
backend nats-cluster
mode tcp
option tcp-check
default-server inter 5s fastinter 1s downinter 2s rise 2 fall 2 resolvers docker init-addr libc,none
server nats-01 nats-01:4222 check
server nats-02 nats-02:4222 check
server nats-03 nats-03:4222 check
# END_TALK_BACKEND
# -----------------------------------------------------------------------------
# Backend Client Push (notify_push) — real-time sync notifications
# -----------------------------------------------------------------------------
backend notify-push
mode http
balance roundrobin
timeout server 300s
default-server inter 30s fastinter 1s rise 2 fall 3 resolvers docker init-addr libc,none
server notify-push notify-push:7867 check
# BEGIN_S3_CONSOLE_BACKEND
# -----------------------------------------------------------------------------
# RustFS built-in web console (port 9001).
# RustFS console serves at /rustfs/console/ — no path stripping needed.
# HAProxy routes all /rustfs/* requests (console + assets + API) to port 9001.
# Entry point /s3-console is redirected to /rustfs/console/ in the frontend.
# -----------------------------------------------------------------------------
backend s3-console
balance leastconn
timeout connect 5s
timeout server 60s
timeout tunnel 3600s
default-server inter 15s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# Strip browser-cached Basic auth on GET requests only — the browser forwards
# Nextcloud WebDAV credentials to all same-domain requests causing RustFS to
# return "invalid header: authorization". POST/API calls (login, JWT) are preserved.
http-request del-header Authorization if { method GET } { req.hdr(authorization) -m beg "Basic " }
# Sticky session — console has login state; routing to a different node mid-session
# causes auth loops. Cookie pinned to the node that handled the login.
cookie RUSTFS_CONSOLE insert indirect nocache httponly secure
# BEGIN_SERVERS_RUSTFS_CONSOLE
server rustfs-node1 rustfs-node1:9001 check cookie RC1
server rustfs-node2 rustfs-node2:9001 check cookie RC2
server rustfs-node3 rustfs-node3:9001 check cookie RC3
server rustfs-node4 rustfs-node4:9001 check cookie RC4
# END_SERVERS_RUSTFS_CONSOLE
# END_S3_CONSOLE_BACKEND
# -----------------------------------------------------------------------------
# Backend RustFS S3 API — requêtes signées AWS4 depuis le browser (console)
# Le browser-side JS de la console envoie des requêtes S3 signées (GET /, etc.)
# vers le domaine public. Ce backend les route vers le port 9000 des nodes RustFS.
# Le is_s3_api ACL identifie ces requêtes via l'en-tête Authorization: AWS4-HMAC-SHA256.
# -----------------------------------------------------------------------------
backend rustfs-s3api
mode http
balance leastconn
timeout connect 5s
timeout server 60s
option http-keep-alive
option httpchk
http-check send meth GET uri /health
http-check expect status 200
default-server inter 10s fastinter 1s rise 2 fall 2 resolvers docker init-addr libc,none
# BEGIN_SERVERS_RUSTFS_S3API
server rustfs-node1 rustfs-node1:9000 check
server rustfs-node2 rustfs-node2:9000 check
server rustfs-node3 rustfs-node3:9000 check
server rustfs-node4 rustfs-node4:9000 check
http-response return status 302 hdr Location /rustfs/console/auth/login/ if { status 400 401 403 }
# END_SERVERS_RUSTFS_S3API