From dcf78f7f3d38561c77139de50afdb8180e696ec5 Mon Sep 17 00:00:00 2001 From: Ralf Haferkamp Date: Tue, 29 Apr 2025 12:20:26 +0200 Subject: [PATCH] proxy(router): Allow to set some outgoing headers This introduces the "additional_headers", "remote_user_header" and "skip_x_access_token" config keys to allow configuring routes to external services that require addtional headers to be set. "remote_user_header": defines the name of a Header that will carry the userid of the authenticated user on the outgoing request. "additional_headers": defines a list of header names and values that will be added to outgoing requests on matching routes. "skip_x_access_token": when set to true the reva access token will not be added to the outgoing request. Needed for #206 --- services/proxy/pkg/config/config.go | 9 ++++-- .../proxy/pkg/middleware/account_resolver.go | 9 +++++- .../pkg/middleware/account_resolver_test.go | 2 ++ services/proxy/pkg/router/router.go | 29 +++++++++++++++---- 4 files changed, 40 insertions(+), 9 deletions(-) diff --git a/services/proxy/pkg/config/config.go b/services/proxy/pkg/config/config.go index da25558a20..8d0314aacd 100644 --- a/services/proxy/pkg/config/config.go +++ b/services/proxy/pkg/config/config.go @@ -64,9 +64,12 @@ type Route struct { // Backend is a static URL to forward the request to Backend string `yaml:"backend,omitempty"` // Service name to look up in the registry - Service string `yaml:"service,omitempty"` - ApacheVHost bool `yaml:"apache_vhost,omitempty"` - Unprotected bool `yaml:"unprotected,omitempty"` + Service string `yaml:"service,omitempty"` + ApacheVHost bool `yaml:"apache_vhost,omitempty"` + Unprotected bool `yaml:"unprotected,omitempty"` + AdditionalHeaders map[string]string `yaml:"additional_headers,omitempty"` + RemoteUserHeader string `yaml:"remote_user_header,omitempty"` + SkipXAccessToken bool `yaml:"skip_x_access_token"` } // RouteType defines the type of route diff --git a/services/proxy/pkg/middleware/account_resolver.go b/services/proxy/pkg/middleware/account_resolver.go index b8155ad446..17364ef3dd 100644 --- a/services/proxy/pkg/middleware/account_resolver.go +++ b/services/proxy/pkg/middleware/account_resolver.go @@ -7,6 +7,7 @@ import ( "time" "github.com/jellydator/ttlcache/v3" + "github.com/opencloud-eu/opencloud/services/proxy/pkg/router" "github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend" "github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles" @@ -209,7 +210,13 @@ func (m accountResolver) ServeHTTP(w http.ResponseWriter, req *http.Request) { } } - req.Header.Set(revactx.TokenHeader, token) + ri := router.ContextRoutingInfo(ctx) + if ri.RemoteUserHeader() != "" { + req.Header.Set(ri.RemoteUserHeader(), user.GetId().GetOpaqueId()) + } + if !ri.SkipXAccessToken() { + req.Header.Set(revactx.TokenHeader, token) + } m.next.ServeHTTP(w, req) } diff --git a/services/proxy/pkg/middleware/account_resolver_test.go b/services/proxy/pkg/middleware/account_resolver_test.go index 0dec8eb836..e2aa7457a4 100644 --- a/services/proxy/pkg/middleware/account_resolver_test.go +++ b/services/proxy/pkg/middleware/account_resolver_test.go @@ -9,6 +9,7 @@ import ( userv1beta1 "github.com/cs3org/go-cs3apis/cs3/identity/user/v1beta1" "github.com/opencloud-eu/opencloud/pkg/log" "github.com/opencloud-eu/opencloud/pkg/oidc" + "github.com/opencloud-eu/opencloud/services/proxy/pkg/router" "github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend" "github.com/opencloud-eu/opencloud/services/proxy/pkg/user/backend/mocks" userRoleMocks "github.com/opencloud-eu/opencloud/services/proxy/pkg/userroles/mocks" @@ -206,6 +207,7 @@ func mockRequest(claims map[string]interface{}) (*http.Request, *httptest.Respon } ctx := oidc.NewContext(context.Background(), claims) + ctx = router.SetRoutingInfo(ctx, router.RoutingInfo{}) req := httptest.NewRequest("GET", "http://example.com/foo", nil).WithContext(ctx) rw := httptest.NewRecorder() diff --git a/services/proxy/pkg/router/router.go b/services/proxy/pkg/router/router.go index 35ec9d11fe..36096e3962 100644 --- a/services/proxy/pkg/router/router.go +++ b/services/proxy/pkg/router/router.go @@ -86,9 +86,11 @@ func New(serviceSelector selector.Selector, policySelectorCfg *config.PolicySele // RoutingInfo contains the proxy rewrite hook and some information about the route. type RoutingInfo struct { - rewrite func(*httputil.ProxyRequest) - endpoint string - unprotected bool + rewrite func(*httputil.ProxyRequest) + endpoint string + unprotected bool + remoteUserHeader string + skipXAccessToken bool } // Rewrite returns the proxy rewrite hook. @@ -101,6 +103,17 @@ func (r RoutingInfo) IsRouteUnprotected() bool { return r.unprotected } +// RemoteUserHeader returns the name of Header for setting the remote user value +func (r RoutingInfo) RemoteUserHeader() string { + return r.remoteUserHeader +} + +// SkipXAccessToken return true if the reva access token should not be added to the +// outgoing request +func (r RoutingInfo) SkipXAccessToken() bool { + return r.skipXAccessToken +} + // Router handles the routing of HTTP requests according to the given policies. type Router struct { logger log.Logger @@ -126,8 +139,10 @@ func (rt Router) addHost(policy string, target *url.URL, route config.Route) { } rt.rewriters[policy][routeType][route.Method] = append(rt.rewriters[policy][routeType][route.Method], RoutingInfo{ - endpoint: route.Endpoint, - unprotected: route.Unprotected, + endpoint: route.Endpoint, + unprotected: route.Unprotected, + remoteUserHeader: route.RemoteUserHeader, + skipXAccessToken: route.SkipXAccessToken, rewrite: func(req *httputil.ProxyRequest) { if route.Service != "" { // select next node @@ -161,6 +176,10 @@ func (rt Router) addHost(policy string, target *url.URL, route config.Route) { req.Out.Host = target.Host } + for k, v := range route.AdditionalHeaders { + req.Out.Header.Set(k, v) + } + req.Out.URL.Path = singleJoiningSlash(target.Path, req.Out.URL.Path) if targetQuery == "" || req.Out.URL.RawQuery == "" { req.Out.URL.RawQuery = targetQuery + req.Out.URL.RawQuery