Files
Clark Boylan d39fd825c9 Use Haproxy PROXY Protocol with Gitea
Haproxy supports the PROXY protocol which sends useful proxy information
to the backends even though we're not terminating tls on the haproxy
load balancer. Hopefully this will give us better x forwarded for
information which can be useful for debugging.

Rebase onto the current Anubis-era gitea vhost (which proxies to Anubis
at http://[::1]:10000/ rather than directly to gitea:3000) and except
IPv6 localhost (::1) in addition to 127.0.0.1 from the PROXY protocol
requirement. The functional tests connect to the backend directly, e.g.
`git clone https://localhost:3081`, where localhost resolves to ::1;
without that exception mod_remoteip's RemoteIPProxyProtocol drops those
header-less connections and system-config-run-gitea fails. The haproxy
load balancer is deliberately not excepted so mod_remoteip can still
recover the real client address for proxied traffic; its health checks
work because send-proxy also prepends the PROXY header to httpchk
connections.

Bind the balance_git_https listener as separate native IPv4 (0.0.0.0:443)
and IPv6 (:::443 v6only) sockets rather than a single dual-stack :::443.
On a dual-stack socket the kernel presents IPv4 peers as IPv4-mapped IPv6
(::ffff:A.B.C.D), so send-proxy emits `PROXY TCP6 ::ffff:...`, which
released Apache mod_remoteip rejects with AH03500 (its TCP6 address
charset has no '.'), aborting the connection -- including the gitea-lb
testinfra requests to 127.0.0.1 and every real IPv4 client once PROXY
protocol is enabled. Native sockets make haproxy emit `PROXY TCP4
A.B.C.D` for v4 clients, which mod_remoteip accepts; IPv6 clients still
get a native TCP6 header.

Set the backend proxy_string to 'send-proxy' only, not 'ssl send-proxy'.
The frontend bind has no 'ssl'/'crt', so in mode tcp haproxy relays the
byte stream and TLS is end-to-end between the client and gitea's Apache.
Adding 'ssl' to the server line makes haproxy open a second TLS session
to the backend and write the client's already-encrypted bytes into it;
Apache then decrypts the outer layer and reads the client's ClientHello
as a plaintext request, answering in cleartext to a client waiting for a
ServerHello. That surfaced as curl exit 35 in testinfra with nothing at
all in gitea's ssl error log. The existing check-ssl is unaffected: it
governs the health check, not the data path.

Do not emit RemoteIPTrustedProxy for the load balancers. That directive
only gates the header-based path (RemoteIPHeader, e.g. X-Forwarded-For),
and no RemoteIPHeader is set here, so the lines were inert. Access to
port 3081 is already restricted to the gitea-lb group by
iptables_extra_allowed_groups, and RemoteIPProxyProtocolExceptions is
what scopes PROXY header trust. Leaving them in would suggest the client
address depends on them, which it does not.

Change-Id: Ic223a7723babd10f0e7f010925462594c384f0de
Signed-off-by: Anil Belur <abelur@linuxfoundation.org>
2026-07-28 21:40:22 +10:00
..