diff --git a/pkg/service/debug/service.go b/pkg/service/debug/service.go index 026f2e90ec..8fda90584d 100644 --- a/pkg/service/debug/service.go +++ b/pkg/service/debug/service.go @@ -73,7 +73,7 @@ func NewService(opts ...Option) *http.Server { return baseCtx }, Handler: alice.New( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, chimiddleware.RequestID, middleware.NoCache, middleware.Cors( diff --git a/services/idp/pkg/server/http/server.go b/services/idp/pkg/server/http/server.go index 4a74785943..e500c632bc 100644 --- a/services/idp/pkg/server/http/server.go +++ b/services/idp/pkg/server/http/server.go @@ -57,7 +57,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Logger(options.Logger), svc.Config(options.Config), svc.Middleware( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, chimiddleware.RequestID, middleware.TraceContext, middleware.NoCache, diff --git a/services/invitations/pkg/server/http/server.go b/services/invitations/pkg/server/http/server.go index cc60b27747..78dfdce3e9 100644 --- a/services/invitations/pkg/server/http/server.go +++ b/services/invitations/pkg/server/http/server.go @@ -43,7 +43,7 @@ func Server(opts ...Option) (ohttp.Service, error) { mux := chi.NewMux() - mux.Use(chimiddleware.RealIP) + mux.Use(chimiddleware.ClientIPFromRemoteAddr) mux.Use(chimiddleware.RequestID) mux.Use(middleware.TraceContext) mux.Use(middleware.NoCache) diff --git a/services/ocs/pkg/server/http/server.go b/services/ocs/pkg/server/http/server.go index 74c42b43c4..6dc39e235f 100644 --- a/services/ocs/pkg/server/http/server.go +++ b/services/ocs/pkg/server/http/server.go @@ -53,7 +53,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Logger(options.Logger), svc.Config(options.Config), svc.Middleware( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, chimiddleware.RequestID, middleware.NoCache, middleware.Cors( diff --git a/services/proxy/pkg/command/server.go b/services/proxy/pkg/command/server.go index 4beba8f79f..6b0cf7b5f3 100644 --- a/services/proxy/pkg/command/server.go +++ b/services/proxy/pkg/command/server.go @@ -390,7 +390,8 @@ func loadMiddlewares(logger log.Logger, cfg *config.Config, } return alice.New( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, + chimiddleware.ClientIPFromXFF(), chimiddleware.RequestID, // 1. Logging & Tracing first diff --git a/services/proxy/pkg/middleware/accesslog.go b/services/proxy/pkg/middleware/accesslog.go index 23c58f4d24..316271d4f9 100644 --- a/services/proxy/pkg/middleware/accesslog.go +++ b/services/proxy/pkg/middleware/accesslog.go @@ -25,7 +25,7 @@ func AccessLog(logger log.Logger) func(http.Handler) http.Handler { Str("proto", r.Proto). Str(log.RequestIDString, requestID). Str("traceid", spanContext.TraceID().String()). - Str("remote-addr", r.RemoteAddr). + Str("remote-addr", middleware.GetClientIP(r.Context())). Str("method", r.Method). Int("status", wrap.Status()). Str("path", r.URL.Path). diff --git a/services/proxy/pkg/middleware/clientip_chain_test.go b/services/proxy/pkg/middleware/clientip_chain_test.go new file mode 100644 index 0000000000..1ac562ebd9 --- /dev/null +++ b/services/proxy/pkg/middleware/clientip_chain_test.go @@ -0,0 +1,69 @@ +package middleware_test + +import ( + "fmt" + "net/http" + "net/http/httptest" + "testing" + + chimiddleware "github.com/go-chi/chi/v5/middleware" + + "gotest.tools/v3/assert" +) + +// TestClientIPChain verifies the order in which the chi ClientIP middlewares +// are chained for the proxy: ClientIPFromRemoteAddr runs first and provides the +// fallback, ClientIPFromXFF overwrites it with the rightmost X-Forwarded-For +// value when the header is present. This mirrors the alice chain configured in +// services/proxy/pkg/command/server.go. +func TestClientIPChain(t *testing.T) { + handler := chimiddleware.ClientIPFromRemoteAddr(chimiddleware.ClientIPFromXFF()(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + _, _ = fmt.Fprint(w, chimiddleware.GetClientIP(r.Context())) + }))) + + tests := []struct { + name string + remoteAddr string + forwarded []string + expected string + }{ + { + name: "reverse proxy sets X-Forwarded-For", + remoteAddr: "192.0.2.1:1234", + forwarded: []string{"198.51.100.10"}, + expected: "198.51.100.10", + }, + { + name: "rightmost forwarded entry wins", + remoteAddr: "192.0.2.1:1234", + forwarded: []string{"198.51.100.10, 203.0.113.5"}, + expected: "203.0.113.5", + }, + { + name: "direct connection falls back to remote addr", + remoteAddr: "192.0.2.1:1234", + expected: "192.0.2.1", + }, + { + name: "unparseable forwarded falls back to remote addr", + remoteAddr: "192.0.2.1:1234", + forwarded: []string{"not-an-ip"}, + expected: "192.0.2.1", + }, + } + + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + rec := httptest.NewRecorder() + req := httptest.NewRequest(http.MethodGet, "/", nil) + req.RemoteAddr = tt.remoteAddr + for _, f := range tt.forwarded { + req.Header.Add("X-Forwarded-For", f) + } + + handler.ServeHTTP(rec, req) + + assert.Equal(t, rec.Body.String(), tt.expected) + }) + } +} diff --git a/services/proxy/pkg/middleware/context_logger.go b/services/proxy/pkg/middleware/context_logger.go index 5f8fd326d8..b323d0a111 100644 --- a/services/proxy/pkg/middleware/context_logger.go +++ b/services/proxy/pkg/middleware/context_logger.go @@ -3,6 +3,7 @@ package middleware import ( "net/http" + "github.com/go-chi/chi/v5/middleware" "github.com/opencloud-eu/opencloud/pkg/log" ) @@ -12,7 +13,7 @@ func ContextLogger(logger log.Logger) func(http.Handler) http.Handler { return func(next http.Handler) http.Handler { return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { ctx := logger.With(). - Str("remoteAddr", r.RemoteAddr). + Str("remoteAddr", middleware.GetClientIP(r.Context())). Str(log.RequestIDString, r.Header.Get("X-Request-ID")). Str("proto", r.Proto). Str("method", r.Method). diff --git a/services/proxy/pkg/middleware/oidc_auth.go b/services/proxy/pkg/middleware/oidc_auth.go index 17f5fc7871..87b6168a44 100644 --- a/services/proxy/pkg/middleware/oidc_auth.go +++ b/services/proxy/pkg/middleware/oidc_auth.go @@ -15,6 +15,7 @@ import ( "golang.org/x/crypto/sha3" "golang.org/x/oauth2" + "github.com/go-chi/chi/v5/middleware" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/oidc" "github.com/opencloud-eu/opencloud/services/proxy/pkg/config" @@ -235,7 +236,7 @@ func (m *OIDCAuthenticator) Authenticate(r *http.Request) AuthenticationResult { Str("authenticator", "oidc"). Str("path", r.URL.Path). Str("user_agent", r.UserAgent()). - Str("client.address", r.Header.Get("X-Forwarded-For")). + Str("client.address", middleware.GetClientIP(r.Context())). Str("network.peer.address", host). Str("network.peer.port", port). Msg("failed to authenticate the request") diff --git a/services/settings/pkg/server/http/server.go b/services/settings/pkg/server/http/server.go index 6c5b85c115..0b2156ae88 100644 --- a/services/settings/pkg/server/http/server.go +++ b/services/settings/pkg/server/http/server.go @@ -42,7 +42,7 @@ func Server(opts ...Option) (ohttp.Service, error) { mux := chi.NewMux() - mux.Use(chimiddleware.RealIP) + mux.Use(chimiddleware.ClientIPFromRemoteAddr) mux.Use(chimiddleware.RequestID) mux.Use(middleware.NoCache) mux.Use(middleware.Cors( diff --git a/services/thumbnails/pkg/server/http/server.go b/services/thumbnails/pkg/server/http/server.go index b75249df8f..e49753ebc8 100644 --- a/services/thumbnails/pkg/server/http/server.go +++ b/services/thumbnails/pkg/server/http/server.go @@ -37,7 +37,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Logger(options.Logger), svc.Config(options.Config), svc.Middleware( - middleware.RealIP, + middleware.ClientIPFromRemoteAddr, middleware.RequestID, opencloudmiddleware.Cors( cors.Logger(options.Logger), diff --git a/services/web/pkg/server/http/server.go b/services/web/pkg/server/http/server.go index 0b9621c798..cd43f95073 100644 --- a/services/web/pkg/server/http/server.go +++ b/services/web/pkg/server/http/server.go @@ -118,7 +118,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Config(options.Config), svc.GatewaySelector(gatewaySelector), svc.Middleware( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, chimiddleware.RequestID, chimiddleware.Compress(5), middleware.NoCache, diff --git a/services/webdav/pkg/server/http/server.go b/services/webdav/pkg/server/http/server.go index 4e7c794f24..30d53be1be 100644 --- a/services/webdav/pkg/server/http/server.go +++ b/services/webdav/pkg/server/http/server.go @@ -38,7 +38,7 @@ func Server(opts ...Option) (http.Service, error) { svc.Logger(options.Logger), svc.Config(options.Config), svc.Middleware( - chimiddleware.RealIP, + chimiddleware.ClientIPFromRemoteAddr, chimiddleware.RequestID, middleware.NoCache, middleware.Cors( diff --git a/services/webfinger/pkg/server/http/server.go b/services/webfinger/pkg/server/http/server.go index dbe91b0ca4..bd9677d185 100644 --- a/services/webfinger/pkg/server/http/server.go +++ b/services/webfinger/pkg/server/http/server.go @@ -45,7 +45,7 @@ func Server(opts ...Option) (ohttp.Service, error) { mux := chi.NewMux() - mux.Use(chimiddleware.RealIP) + mux.Use(chimiddleware.ClientIPFromRemoteAddr) mux.Use(chimiddleware.RequestID) mux.Use(middleware.TraceContext) mux.Use(middleware.NoCache)