-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathhaproxy.cfg
More file actions
146 lines (134 loc) · 7.53 KB
/
Copy pathhaproxy.cfg
File metadata and controls
146 lines (134 loc) · 7.53 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
# Minimal recommended HAProxy configuration for reverse-http workers.
#
# Tested against HAProxy >= 3.4-dev10. Older versions are missing the
# mux-h2 fix in commit a0541f5d21 ("ignore conn->owner when deciding if a
# connection is dead") — without it, reverse HTTP/2 tunnels are torn down
# after each completed stream at low load, producing intermittent 503s.
# See https://github.com/haproxy/haproxy/commit/a0541f5d21 for details.
global
log stdout format raw daemon
# The `attach-srv` action and the `rhttp@` server protocol are still
# gated behind this in current HAProxy; without it the config is
# rejected.
expose-experimental-directives
# Refuse TLS handshakes below 1.2 on every `bind` in this config. TLS
# 1.0/1.1 are deprecated (RFC 8996) and broadly disabled in clients;
# pinning the floor explicitly avoids surprises if a future HAProxy
# default changes or a downgrade attack is attempted.
ssl-default-bind-options ssl-min-ver TLSv1.2
defaults
mode http
log global
option httplog
option dontlognull
# Long client timeout is critical for SSE: each long-lived response
# stays open for the full duration, only data flow keeps it alive.
timeout client 30m
timeout server 30m
timeout connect 5s
timeout http-request 10s
# `tunnel` covers connections after a successful CONNECT or upgrade —
# not normally needed for reverse-http, but harmless and useful if you
# add WebSocket later.
timeout tunnel 2h
# Errorfiles are optional but strongly recommended: HAProxy's stock
# 502/503/504 pages are terse. Point this at your friendlier templates
# and you'll get them for any HAProxy-originated 5xx (e.g. when all
# reverse workers have disconnected).
http-errors my-errors
errorfile 503 /etc/haproxy/errors/503.http
# ─────────────────────────────────────────────────────────────────────
# 1. Reverse-tunnel ingress.
#
# Workers (running this Go library) dial this listener over TLS with
# ALPN `h2`. After the TLS handshake, the `tcp-request session
# attach-srv` action re-targets the established connection to the
# back-end below — the connection becomes a backend `server` slot that
# HAProxy can multiplex public requests onto.
# ─────────────────────────────────────────────────────────────────────
frontend reverse-in
# Public listener for reverse workers. mTLS is mandatory — workers
# present a client cert signed by `ca-file`, and `verify required`
# tells HAProxy to drop the TLS handshake if the cert doesn't chain
# to that CA. The library refuses to start without a worker cert, so
# the two sides enforce each other:
#
# /etc/haproxy/server.pem this HAProxy's server cert+key
# (combined PEM: cert followed by key)
# /etc/haproxy/workers-ca.crt CA that signed every legitimate
# worker's client cert (the
# workers-ca.crt produced by
# examples/gen-certs.sh)
#
# `idle-ping 1m` keeps the connection alive across NATs that drop
# idle TCP.
bind 0.0.0.0:8443 ssl crt /etc/haproxy/server.pem \
ca-file /etc/haproxy/workers-ca.crt verify required \
alpn h2 idle-ping 1m
# Workers identify themselves via their certificate's CN (or any
# other certificate attribute — SAN, OU, etc.). Match here and
# route matching ones into the backend below; reject anything else.
#
# The CN below MUST match the CN passed to gen-certs.sh when minting
# the worker cert (default: `rhttp-worker`). Change both sides in
# lockstep — a mismatch makes every reverse handshake hit the
# `reject` rule below, producing silent 503s on the public frontend.
acl is_worker ssl_c_s_dn(CN) -m str rhttp-worker
tcp-request session attach-srv app/workers if is_worker
tcp-request session reject unless is_worker
# ─────────────────────────────────────────────────────────────────────
# 2. Public-facing frontend.
#
# This is where end users hit. Requests are routed to the `app` backend,
# which multiplexes them onto whatever reverse tunnels are currently
# connected.
# ─────────────────────────────────────────────────────────────────────
frontend public
bind 0.0.0.0:443 ssl crt /etc/haproxy/public.pem alpn h2,http/1.1
# Optional: also serve cleartext for local-network access.
# bind 0.0.0.0:80
# TODO: add per-source rate limits before exposing publicly. The reverse
# tunnel pool has a fixed capacity (one H2 conn per worker); the public
# listener can accept much more than the backend can deliver, so an
# unthrottled client can saturate the pool with cheap requests.
#
# Minimal example — track per-IP request rate, deny over threshold:
#
# stick-table type ip size 100k expire 30s store http_req_rate(10s),conn_rate(10s)
# http-request track-sc0 src
# http-request deny deny_status 429 if { sc_http_req_rate(0) gt 100 }
# tcp-request connection reject if { sc_conn_rate(0) gt 50 }
#
# Tune numbers to your traffic. Consider also: `timeout http-keep-alive`,
# request-body size caps (`http-request deny if { req.body_size gt … }`),
# and a separate stick-table for auth-failed sources.
default_backend app
# ─────────────────────────────────────────────────────────────────────
# 3. The reverse-tunnel backend.
#
# These four lines are the ones every reverse-http deployment must get
# right; the rest of the config is conventional HAProxy.
# ─────────────────────────────────────────────────────────────────────
backend app
# `always` is what unlocks H2 stream multiplexing across the workers
# on the same backend. Without it the default `safe` mode treats the
# first request on each conn as private, severely undercutting the
# pool capacity on tight bursts.
http-reuse always
# Transparently retry any retryable failure (including the brief
# idle-pool race that produces SC-- on burst). 3 is HAProxy's default;
# 5 gives more headroom without measurable downside.
retries 5
retry-on all-retryable-errors
# Custom errorfile set defined above. Any HAProxy-originated 5xx in
# this backend (e.g. "no worker connected") will use the friendly
# 503 page.
errorfiles my-errors
# The `rhttp@` pseudo-address tells HAProxy this server is fed by
# incoming reverse connections (attached via `attach-srv` in the
# `reverse-in` frontend) rather than dialed actively.
#
# `idle-ping 20s` sends an H2 PING every 20s on each tunnel — both
# keeps NATs from dropping the connection and gives liveness
# feedback. The library's own PING-liveness watchdog runs on top.
server workers rhttp@ idle-ping 20s