Skip to content

Commit ed1a74a

Browse files
cseufertfrancislavoie
authored andcommitted
Support for trusting unix socket X-Forwarded-* headers
Added a new server configuration option `trusted_proxies_unix` to support trusting connections to a `bind unix://path.sock`. This also works with strict mode enabled `trusted_proxies_strict`. This allows for seamless setup of `(tcp:443) -> caddy -> (unix socket) -> caddy -> php_fastcgi` to have the correct remote address available. Fixes #7263
1 parent 10ac7da commit ed1a74a

File tree

3 files changed

+60
-0
lines changed

3 files changed

+60
-0
lines changed

caddyconfig/httpcaddyfile/serveroptions.go

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ type serverOptions struct {
5151
StrictSNIHost *bool
5252
TrustedProxiesRaw json.RawMessage
5353
TrustedProxiesStrict int
54+
TrustedProxiesUnix int
5455
ClientIPHeaders []string
5556
ShouldLogCredentials bool
5657
Metrics *caddyhttp.Metrics
@@ -251,6 +252,12 @@ func unmarshalCaddyfileServerOptions(d *caddyfile.Dispenser) (any, error) {
251252
}
252253
serverOpts.TrustedProxiesStrict = 1
253254

255+
case "trusted_proxies_unix":
256+
if d.NextArg() {
257+
return nil, d.ArgErr()
258+
}
259+
serverOpts.TrustedProxiesUnix = 1
260+
254261
case "client_ip_headers":
255262
headers := d.RemainingArgs()
256263
for _, header := range headers {
@@ -342,6 +349,7 @@ func applyServerOptions(
342349
server.TrustedProxiesRaw = opts.TrustedProxiesRaw
343350
server.ClientIPHeaders = opts.ClientIPHeaders
344351
server.TrustedProxiesStrict = opts.TrustedProxiesStrict
352+
server.TrustedProxiesUnix = opts.TrustedProxiesUnix
345353
server.Metrics = opts.Metrics
346354
if opts.ShouldLogCredentials {
347355
if server.Logs == nil {

modules/caddyhttp/server.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,14 @@ type Server struct {
202202
// This option is disabled by default.
203203
TrustedProxiesStrict int `json:"trusted_proxies_strict,omitempty"`
204204

205+
// If greater than zero, enables trusting socket connections
206+
// (e.g. Unix domain sockets) as coming from a trusted
207+
// proxy.
208+
//
209+
// This option is disabled by default.
210+
211+
TrustedProxiesUnix int `json:"trusted_proxies_unix,omitempty"`
212+
205213
// Enables access logging and configures how access logs are handled
206214
// in this server. To minimally enable access logs, simply set this
207215
// to a non-null, empty struct.
@@ -941,6 +949,17 @@ func determineTrustedProxy(r *http.Request, s *Server) (bool, string) {
941949
return false, ""
942950
}
943951

952+
if s.TrustedProxiesUnix > 0 && r.RemoteAddr == "@" {
953+
if s.TrustedProxiesStrict > 0 {
954+
ipRanges := []netip.Prefix{}
955+
if s.trustedProxies != nil {
956+
ipRanges = s.trustedProxies.GetIPRanges(r)
957+
}
958+
return true, strictUntrustedClientIp(r, s.ClientIPHeaders, ipRanges, "@")
959+
} else {
960+
return true, trustedRealClientIP(r, s.ClientIPHeaders, "@")
961+
}
962+
}
944963
// Parse the remote IP, ignore the error as non-fatal,
945964
// but the remote IP is required to continue, so we
946965
// just return early. This should probably never happen

modules/caddyhttp/server_test.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -297,6 +297,39 @@ func TestServer_DetermineTrustedProxy_TrustedLoopback(t *testing.T) {
297297
assert.Equal(t, clientIP, "31.40.0.10")
298298
}
299299

300+
func TestServer_DetermineTrustedProxy_UnixSocket(t *testing.T) {
301+
server := &Server{
302+
ClientIPHeaders: []string{"X-Forwarded-For"},
303+
TrustedProxiesUnix: 1,
304+
}
305+
306+
req := httptest.NewRequest("GET", "/", nil)
307+
req.RemoteAddr = "@"
308+
req.Header.Set("X-Forwarded-For", "2.2.2.2, 3.3.3.3")
309+
310+
trusted, clientIP := determineTrustedProxy(req, server)
311+
312+
assert.True(t, trusted)
313+
assert.Equal(t, "2.2.2.2", clientIP)
314+
}
315+
316+
func TestServer_DetermineTrustedProxy_UnixSocketStrict(t *testing.T) {
317+
server := &Server{
318+
ClientIPHeaders: []string{"X-Forwarded-For"},
319+
TrustedProxiesUnix: 1,
320+
TrustedProxiesStrict: 1,
321+
}
322+
323+
req := httptest.NewRequest("GET", "/", nil)
324+
req.RemoteAddr = "@"
325+
req.Header.Set("X-Forwarded-For", "2.2.2.2, 3.3.3.3")
326+
327+
trusted, clientIP := determineTrustedProxy(req, server)
328+
329+
assert.True(t, trusted)
330+
assert.Equal(t, "3.3.3.3", clientIP)
331+
}
332+
300333
func TestServer_DetermineTrustedProxy_UntrustedPrefix(t *testing.T) {
301334
loopbackPrefix, _ := netip.ParsePrefix("127.0.0.1/8")
302335

0 commit comments

Comments
 (0)