proxy fixes

ddb8532 · gmorell · 2026-05-21 05:13

9 files +31 -10

Files changed

modified deploy/argo/neiam-ircd/app.yml
+9 −3
@@ -94,7 +94,10 @@ spec:
94 94 initialDelaySeconds: 10
95 95 periodSeconds: 5
96 96 livenessProbe:
97 − tcpSocket: { port: irc }
97 + # Use the HTTP port — same BEAM serves it, no PROXY required.
98 + # A bare TCP connect to 6667 would now fail because the
99 + # listener expects a PROXY header on every connection.
100 + httpGet: { path: /, port: http }
98 101 initialDelaySeconds: 30
99 102 periodSeconds: 15
100 103
@@ -214,7 +217,10 @@ spec:
214 217 services:
215 218 - name: ircd-irc
216 219 port: 6697
217 − proxyProtocol:
218 − version: 2
220 + # No proxyProtocol here — when Traefik does TLS passthrough,
221 + # the PROXY v2 header would land before the ClientHello and
222 + # confuse :ssl in the pod. TLS users currently appear as
223 + # Traefik's IP; switching to TLS-termination-at-Traefik with
224 + # PROXY would fix it but loses per-handshake cert refresh.
219 225 tls:
220 226 passthrough: true
modified lib/oh_my_ex_oh_ircd/irc/connection.ex
+16 −6
@@ -116,20 +116,30 @@ defmodule OhMyExOhIrcd.IRC.Connection do
116 116 end
117 117
118 118 defp handle_proxy_header(socket, state) do
119 + require Logger
120 +
119 121 case OhMyExOhIrcd.IRC.Proxy.read_header(socket, 5_000) do
120 122 {:ok, real_ip, leftover} ->
121 123 {:continue, %{state | host: real_ip, buffer: leftover}}
122 124
123 125 :no_header ->
124 − # No header arrived. With proxy_protocol enabled this is most
125 − # likely a direct (non-proxied) connection — common during health
126 − # checks. Fail closed so we don't surface a misleading peer IP.
127 − require Logger
128 − Logger.warning("PROXY protocol enabled but first bytes weren't a header; dropping")
126 + # Direct TCP connect with no PROXY header — likely a port scan
127 + # or a k8s probe that bypassed the proxy. Drop quietly.
128 + {:close, state}
129 +
130 + {:error, :closed} ->
131 + # Peer closed before sending anything; expected churn from
132 + # health checks and load-balancer probes.
133 + {:close, state}
134 +
135 + {:error, :timeout} ->
136 + # Peer connected but sent nothing within the deadline. Same as :closed —
137 + # uninteresting, just drop.
129 138 {:close, state}
130 139
131 140 {:error, reason} ->
132 − require Logger
141 + # Truncated / malformed header — worth surfacing because it
142 + # usually means our proxy and listener disagree on the version.
133 143 Logger.warning("PROXY header parse failed: #{inspect(reason)}; dropping")
134 144 {:close, state}
135 145 end
modified lib/oh_my_ex_oh_ircd/irc/tls_listener.ex
+6 −1
@@ -71,7 +71,12 @@ defmodule OhMyExOhIrcd.IRC.TLSListener do
71 71 ping_timeout_ms: Keyword.get(listener_cfg, :ping_timeout_ms, 60_000),
72 72 flood_threshold_seconds: Keyword.get(listener_cfg, :flood_threshold_seconds, 10),
73 73 flood_penalty_default: Keyword.get(listener_cfg, :flood_penalty_default, 1.0),
74 − proxy_protocol: Keyword.get(listener_cfg, :proxy_protocol, false)
74 + # PROXY protocol is plain-port-only for now. On the TLS port
75 + # Traefik passthrough would put the v2 header before the
76 + # ClientHello, which `:ssl` can't skip. Read this listener's
77 + # own `:proxy_protocol` (default false) instead of inheriting
78 + # from the plain listener config.
79 + proxy_protocol: Keyword.get(cfg, :proxy_protocol, false)
75 80 }
76 81 )
77 82 end

Parents: e3999f1