git.iko.soy/typeable/oauth2-proxy
on master help sign in
fix: handle Unix socket RemoteAddr in IP resolution (#3374)
a4d8903H1net
CHANGELOG.md+2
@@ -14,6 +14,8 @@
14# V7.15.0
14# V7.15.0
15
15
16- [#3382](https://github.com/oauth2-proxy/oauth2-proxy/pull/3382) chore(deps): update gomod and golangci/golangci-lint to v2.11.4 (@tuunit)
16- [#3382](https://github.com/oauth2-proxy/oauth2-proxy/pull/3382) chore(deps): update gomod and golangci/golangci-lint to v2.11.4 (@tuunit)
17+- [#3374](https://github.com/oauth2-proxy/oauth2-proxy/pull/3374) fix: handle Unix socket RemoteAddr in IP resolution (@H1net)
18+
17
19
18## Release Highlights
20## Release Highlights
19
21
docs/docs/configuration/systemd_socket.md+8
@@ -40,4 +40,12 @@ the listener it created onto the process, starting with file descriptor 3.
40 --client-secret=...
40 --client-secret=...
41```
41```
42
42
43+## Trusted IPs
44+
45+When listening on a Unix socket, Go sets `http.Request.RemoteAddr` to `"@"` instead of the usual `"host:port"` format. This means there is no client IP available from the connection itself.
46+
47+As a result, `--trusted-ip` entries cannot match against the direct connection address for Unix socket listeners. Requests arriving over a Unix socket will never be considered "trusted" based on their `RemoteAddr`. IP-based trust decisions will still work if a trusted reverse proxy sets `X-Forwarded-For` or `X-Real-IP` headers and `--reverse-proxy=true` is configured.
48+
49+## TLS
50+
43Currently TLS is not supported (but it's doable).
51Currently TLS is not supported (but it's doable).
docs/versioned_docs/version-7.15.x/configuration/systemd_socket.md+8
@@ -40,4 +40,12 @@ the listener it created onto the process, starting with file descriptor 3.
40 --client-secret=...
40 --client-secret=...
41```
41```
42
42
43+## Trusted IPs
44+
45+When listening on a Unix socket, Go sets `http.Request.RemoteAddr` to `"@"` instead of the usual `"host:port"` format. This means there is no client IP available from the connection itself.
46+
47+As a result, `--trusted-ip` entries cannot match against the direct connection address for Unix socket listeners. Requests arriving over a Unix socket will never be considered "trusted" based on their `RemoteAddr`. IP-based trust decisions will still work if a trusted reverse proxy sets `X-Forwarded-For` or `X-Real-IP` headers and `--reverse-proxy=true` is configured.
48+
49+## TLS
50+
43Currently TLS is not supported (but it's doable).
51Currently TLS is not supported (but it's doable).
oauthproxy.go+1−3
@@ -613,9 +613,7 @@ func (p *OAuthProxy) isAPIPath(req *http.Request) bool {
613
613
614// isTrustedIP is used to check if a request comes from a trusted client IP address.
614// isTrustedIP is used to check if a request comes from a trusted client IP address.
615func (p *OAuthProxy) isTrustedIP(req *http.Request) bool {
615func (p *OAuthProxy) isTrustedIP(req *http.Request) bool {
616− // RemoteAddr @ means unix socket
617− // https://github.com/golang/go/blob/0fa53e41f122b1661d0678a6d36d71b7b5ad031d/src/syscall/syscall_linux.go#L506-L511
618− if p.trustedIPs == nil && req.RemoteAddr != "@" {
616+ if p.trustedIPs == nil {
619 return false
617 return false
620 }
618 }
621
619
oauthproxy_test.go+26
@@ -2150,6 +2150,32 @@ func TestTrustedIPs(t *testing.T) {
2150 }(),
2150 }(),
2151 expectTrusted: false,
2151 expectTrusted: false,
2152 },
2152 },
2153+ // Check Unix socket with no trusted IPs configured does not error.
2154+ {
2155+ name: "UnixSocketWithoutTrustedIPs",
2156+ trustedIPs: nil,
2157+ reverseProxy: false,
2158+ realClientIPHeader: "X-Real-IP",
2159+ req: func() *http.Request {
2160+ req, _ := http.NewRequest("GET", "/", nil)
2161+ req.RemoteAddr = "@"
2162+ return req
2163+ }(),
2164+ expectTrusted: false,
2165+ },
2166+ // Check Unix socket with trusted IPs configured returns false (no IP to match).
2167+ {
2168+ name: "UnixSocketWithTrustedIPs",
2169+ trustedIPs: []string{"127.0.0.1"},
2170+ reverseProxy: false,
2171+ realClientIPHeader: "X-Real-IP",
2172+ req: func() *http.Request {
2173+ req, _ := http.NewRequest("GET", "/", nil)
2174+ req.RemoteAddr = "@"
2175+ return req
2176+ }(),
2177+ expectTrusted: false,
2178+ },
2153 // Check using req.RemoteAddr (Options.ReverseProxy == false).
2179 // Check using req.RemoteAddr (Options.ReverseProxy == false).
2154 {
2180 {
2155 name: "WithRemoteAddr",
2181 name: "WithRemoteAddr",
pkg/ip/realclientip.go+7−1
@@ -73,6 +73,12 @@ func GetClientIP(p ipapi.RealClientIPParser, req *http.Request) (net.IP, error)
73
73
74// getRemoteIP obtains the IP of the low-level connected network host
74// getRemoteIP obtains the IP of the low-level connected network host
75func getRemoteIP(req *http.Request) (net.IP, error) {
75func getRemoteIP(req *http.Request) (net.IP, error) {
76+ // Unix domain sockets set RemoteAddr to "@" which has no meaningful IP.
77+ // https://github.com/golang/go/blob/0fa53e41f122b1661d0678a6d36d71b7b5ad031d/src/syscall/syscall_linux.go#L506-L511
78+ if req.RemoteAddr == "@" {
79+ return nil, nil
80+ }
81+
76 //revive:disable:indent-error-flow
82 //revive:disable:indent-error-flow
77 if ipStr, _, err := net.SplitHostPort(req.RemoteAddr); err != nil {
83 if ipStr, _, err := net.SplitHostPort(req.RemoteAddr); err != nil {
78 return nil, fmt.Errorf("unable to get ip and port from http.RemoteAddr (%s)", req.RemoteAddr)
84 return nil, fmt.Errorf("unable to get ip and port from http.RemoteAddr (%s)", req.RemoteAddr)
@@ -94,7 +100,7 @@ func GetClientString(p ipapi.RealClientIPParser, req *http.Request, full bool) (
94 }
100 }
95
101
96 var remoteIPStr string
102 var remoteIPStr string
97− if remoteIP, err := getRemoteIP(req); err == nil {
103+ if remoteIP, err := getRemoteIP(req); err == nil && remoteIP != nil {
98 remoteIPStr = remoteIP.String()
104 remoteIPStr = remoteIP.String()
99 }
105 }
100
106
pkg/ip/realclientip_test.go+4
@@ -112,6 +112,8 @@ func TestGetRemoteIP(t *testing.T) {
112 errString string
112 errString string
113 expectedIP net.IP
113 expectedIP net.IP
114 }{
114 }{
115+ // Unix domain sockets set RemoteAddr to "@"
116+ {"@", "", nil},
115 {"", "unable to get ip and port from http.RemoteAddr ()", nil},
117 {"", "unable to get ip and port from http.RemoteAddr ()", nil},
116 {"nil", "unable to get ip and port from http.RemoteAddr (nil)", nil},
118 {"nil", "unable to get ip and port from http.RemoteAddr (nil)", nil},
117 {"235.28.129.186", "unable to get ip and port from http.RemoteAddr (235.28.129.186)", nil},
119 {"235.28.129.186", "unable to get ip and port from http.RemoteAddr (235.28.129.186)", nil},
@@ -155,6 +157,8 @@ func TestGetClientString(t *testing.T) {
155 }{
157 }{
156 // Should fail quietly, only printing warnings to the log
158 // Should fail quietly, only printing warnings to the log
157 {nil, "", "", "", ""},
159 {nil, "", "", "", ""},
160+ // Unix domain socket — no IP available
161+ {nil, "@", "", "", ""},
158 {p, "127.0.0.1:11950", "", "127.0.0.1", "127.0.0.1"},
162 {p, "127.0.0.1:11950", "", "127.0.0.1", "127.0.0.1"},
159 {p, "[::1]:28660", "99.103.56.12", "99.103.56.12", "::1 (99.103.56.12)"},
163 {p, "[::1]:28660", "99.103.56.12", "99.103.56.12", "::1 (99.103.56.12)"},
160 {nil, "10.254.244.165:62750", "", "10.254.244.165", "10.254.244.165"},
164 {nil, "10.254.244.165:62750", "", "10.254.244.165", "10.254.244.165"},