mirror of
https://github.com/pomerium/pomerium.git
synced 2025-08-06 10:21:05 +02:00
proxy: implement pass-through for authenticate backend (#1870)
* proxy: implement pass-through for authenticate backend * address comments
This commit is contained in:
parent
4bf5179bb6
commit
963399b53d
2 changed files with 153 additions and 69 deletions
|
@ -49,51 +49,60 @@ func (srv *Server) buildGRPCRoutes() ([]*envoy_config_route_v3.Route, error) {
|
||||||
|
|
||||||
func (srv *Server) buildPomeriumHTTPRoutes(options *config.Options, domain string) ([]*envoy_config_route_v3.Route, error) {
|
func (srv *Server) buildPomeriumHTTPRoutes(options *config.Options, domain string) ([]*envoy_config_route_v3.Route, error) {
|
||||||
var routes []*envoy_config_route_v3.Route
|
var routes []*envoy_config_route_v3.Route
|
||||||
// enable ext_authz
|
|
||||||
r, err := srv.buildControlPlanePathRoute("/.pomerium/jwt", true)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
|
|
||||||
// disable ext_authz and passthrough to proxy handlers
|
// if this is the pomerium proxy in front of the the authenticate service, don't add
|
||||||
r, err = srv.buildControlPlanePathRoute("/ping", false)
|
// these routes since they will be handled by authenticate
|
||||||
|
isFrontingAuthenticate, err := isProxyFrontingAuthenticate(options, domain)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
routes = append(routes, r)
|
if !isFrontingAuthenticate {
|
||||||
r, err = srv.buildControlPlanePathRoute("/healthz", false)
|
// enable ext_authz
|
||||||
if err != nil {
|
r, err := srv.buildControlPlanePathRoute("/.pomerium/jwt", true)
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
r, err = srv.buildControlPlanePathRoute("/.pomerium", false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
r, err = srv.buildControlPlanePrefixRoute("/.pomerium/", false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
r, err = srv.buildControlPlanePathRoute("/.well-known/pomerium", false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
r, err = srv.buildControlPlanePrefixRoute("/.well-known/pomerium/", false)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
routes = append(routes, r)
|
|
||||||
// per #837, only add robots.txt if there are no unauthenticated routes
|
|
||||||
if !hasPublicPolicyMatchingURL(options, url.URL{Scheme: "https", Host: domain, Path: "/robots.txt"}) {
|
|
||||||
r, err := srv.buildControlPlanePathRoute("/robots.txt", false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
routes = append(routes, r)
|
routes = append(routes, r)
|
||||||
|
|
||||||
|
// disable ext_authz and passthrough to proxy handlers
|
||||||
|
r, err = srv.buildControlPlanePathRoute("/ping", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
r, err = srv.buildControlPlanePathRoute("/healthz", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
r, err = srv.buildControlPlanePathRoute("/.pomerium", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
r, err = srv.buildControlPlanePrefixRoute("/.pomerium/", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
r, err = srv.buildControlPlanePathRoute("/.well-known/pomerium", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
r, err = srv.buildControlPlanePrefixRoute("/.well-known/pomerium/", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
// per #837, only add robots.txt if there are no unauthenticated routes
|
||||||
|
if !hasPublicPolicyMatchingURL(options, url.URL{Scheme: "https", Host: domain, Path: "/robots.txt"}) {
|
||||||
|
r, err := srv.buildControlPlanePathRoute("/robots.txt", false)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
routes = append(routes, r)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// if we're handling authentication, add the oauth2 callback url
|
// if we're handling authentication, add the oauth2 callback url
|
||||||
authenticateURL, err := options.GetAuthenticateURL()
|
authenticateURL, err := options.GetAuthenticateURL()
|
||||||
|
@ -260,37 +269,12 @@ func (srv *Server) buildPolicyRoutes(options *config.Options, domain string) ([]
|
||||||
}
|
}
|
||||||
|
|
||||||
match := mkRouteMatch(&policy)
|
match := mkRouteMatch(&policy)
|
||||||
requestHeadersToAdd := toEnvoyHeaders(policy.SetRequestHeaders)
|
|
||||||
requestHeadersToRemove := getRequestHeadersToRemove(options, &policy)
|
|
||||||
|
|
||||||
envoyRoute := &envoy_config_route_v3.Route{
|
envoyRoute := &envoy_config_route_v3.Route{
|
||||||
Name: fmt.Sprintf("policy-%d", i),
|
Name: fmt.Sprintf("policy-%d", i),
|
||||||
Match: match,
|
Match: match,
|
||||||
Metadata: &envoy_config_core_v3.Metadata{
|
Metadata: &envoy_config_core_v3.Metadata{},
|
||||||
FilterMetadata: map[string]*structpb.Struct{
|
RequestHeadersToAdd: toEnvoyHeaders(policy.SetRequestHeaders),
|
||||||
"envoy.filters.http.lua": {
|
RequestHeadersToRemove: getRequestHeadersToRemove(options, &policy),
|
||||||
Fields: map[string]*structpb.Value{
|
|
||||||
"remove_pomerium_cookie": {
|
|
||||||
Kind: &structpb.Value_StringValue{
|
|
||||||
StringValue: options.CookieName,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"remove_pomerium_authorization": {
|
|
||||||
Kind: &structpb.Value_BoolValue{
|
|
||||||
BoolValue: true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
"remove_impersonate_headers": {
|
|
||||||
Kind: &structpb.Value_BoolValue{
|
|
||||||
BoolValue: policy.KubernetesServiceAccountTokenFile != "" || policy.KubernetesServiceAccountToken != "",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
RequestHeadersToAdd: requestHeadersToAdd,
|
|
||||||
RequestHeadersToRemove: requestHeadersToRemove,
|
|
||||||
}
|
}
|
||||||
if policy.Redirect != nil {
|
if policy.Redirect != nil {
|
||||||
action, err := srv.buildPolicyRouteRedirectAction(policy.Redirect)
|
action, err := srv.buildPolicyRouteRedirectAction(policy.Redirect)
|
||||||
|
@ -306,6 +290,39 @@ func (srv *Server) buildPolicyRoutes(options *config.Options, domain string) ([]
|
||||||
envoyRoute.Action = &envoy_config_route_v3.Route_Route{Route: action}
|
envoyRoute.Action = &envoy_config_route_v3.Route_Route{Route: action}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// disable authentication entirely when the proxy is fronting authenticate
|
||||||
|
isFrontingAuthenticate, err := isProxyFrontingAuthenticate(options, domain)
|
||||||
|
if err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
if isFrontingAuthenticate {
|
||||||
|
envoyRoute.TypedPerFilterConfig = map[string]*any.Any{
|
||||||
|
"envoy.filters.http.ext_authz": disableExtAuthz,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
envoyRoute.Metadata.FilterMetadata = map[string]*structpb.Struct{
|
||||||
|
"envoy.filters.http.lua": {
|
||||||
|
Fields: map[string]*structpb.Value{
|
||||||
|
"remove_pomerium_cookie": {
|
||||||
|
Kind: &structpb.Value_StringValue{
|
||||||
|
StringValue: options.CookieName,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"remove_pomerium_authorization": {
|
||||||
|
Kind: &structpb.Value_BoolValue{
|
||||||
|
BoolValue: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"remove_impersonate_headers": {
|
||||||
|
Kind: &structpb.Value_BoolValue{
|
||||||
|
BoolValue: policy.KubernetesServiceAccountTokenFile != "" || policy.KubernetesServiceAccountToken != "",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
routes = append(routes, envoyRoute)
|
routes = append(routes, envoyRoute)
|
||||||
}
|
}
|
||||||
return routes, nil
|
return routes, nil
|
||||||
|
@ -529,3 +546,16 @@ func hasPublicPolicyMatchingURL(options *config.Options, requestURL url.URL) boo
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func isProxyFrontingAuthenticate(options *config.Options, domain string) (bool, error) {
|
||||||
|
authenticateURL, err := options.GetAuthenticateURL()
|
||||||
|
if err != nil {
|
||||||
|
return false, err
|
||||||
|
}
|
||||||
|
|
||||||
|
if !config.IsAuthenticate(options.Services) && hostMatchesDomain(authenticateURL, domain) {
|
||||||
|
return true, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return false, nil
|
||||||
|
}
|
||||||
|
|
|
@ -99,6 +99,16 @@ func Test_buildPomeriumHTTPRoutes(t *testing.T) {
|
||||||
`+routeString("path", "/oauth2/callback", false)+`
|
`+routeString("path", "/oauth2/callback", false)+`
|
||||||
]`, routes)
|
]`, routes)
|
||||||
})
|
})
|
||||||
|
t.Run("proxy fronting authenticate", func(t *testing.T) {
|
||||||
|
options := &config.Options{
|
||||||
|
Services: "proxy",
|
||||||
|
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
|
||||||
|
AuthenticateCallbackPath: "/oauth2/callback",
|
||||||
|
}
|
||||||
|
routes, err := srv.buildPomeriumHTTPRoutes(options, "authenticate.example.com")
|
||||||
|
require.NoError(t, err)
|
||||||
|
testutil.AssertProtoJSONEqual(t, "null", routes)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("with robots", func(t *testing.T) {
|
t.Run("with robots", func(t *testing.T) {
|
||||||
options := &config.Options{
|
options := &config.Options{
|
||||||
|
@ -484,6 +494,50 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
]
|
]
|
||||||
`, routes)
|
`, routes)
|
||||||
|
|
||||||
|
t.Run("fronting-authenticate", func(t *testing.T) {
|
||||||
|
routes, err := srv.buildPolicyRoutes(&config.Options{
|
||||||
|
AuthenticateURL: mustParseURL(t, "https://authenticate.example.com"),
|
||||||
|
Services: "proxy",
|
||||||
|
CookieName: "pomerium",
|
||||||
|
DefaultUpstreamTimeout: time.Second * 3,
|
||||||
|
Policies: []config.Policy{
|
||||||
|
{
|
||||||
|
Source: &config.StringURL{URL: mustParseURL(t, "https://authenticate.example.com")},
|
||||||
|
PassIdentityHeaders: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}, "authenticate.example.com")
|
||||||
|
require.NoError(t, err)
|
||||||
|
|
||||||
|
testutil.AssertProtoJSONEqual(t, `
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name": "policy-0",
|
||||||
|
"match": {
|
||||||
|
"prefix": "/"
|
||||||
|
},
|
||||||
|
"metadata": {
|
||||||
|
},
|
||||||
|
"route": {
|
||||||
|
"autoHostRewrite": true,
|
||||||
|
"cluster": "policy-9",
|
||||||
|
"timeout": "3s",
|
||||||
|
"upgradeConfigs": [
|
||||||
|
{ "enabled": false, "upgradeType": "websocket"},
|
||||||
|
{ "enabled": false, "upgradeType": "spdy/3.1"}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"typedPerFilterConfig": {
|
||||||
|
"envoy.filters.http.ext_authz": {
|
||||||
|
"@type": "type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute",
|
||||||
|
"disabled": true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
`, routes)
|
||||||
|
})
|
||||||
|
|
||||||
t.Run("tcp", func(t *testing.T) {
|
t.Run("tcp", func(t *testing.T) {
|
||||||
routes, err := srv.buildPolicyRoutes(&config.Options{
|
routes, err := srv.buildPolicyRoutes(&config.Options{
|
||||||
CookieName: "pomerium",
|
CookieName: "pomerium",
|
||||||
|
@ -520,7 +574,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
"route": {
|
"route": {
|
||||||
"autoHostRewrite": true,
|
"autoHostRewrite": true,
|
||||||
"cluster": "policy-9",
|
"cluster": "policy-10",
|
||||||
"idleTimeout": "0s",
|
"idleTimeout": "0s",
|
||||||
"timeout": "0s",
|
"timeout": "0s",
|
||||||
"upgradeConfigs": [
|
"upgradeConfigs": [
|
||||||
|
@ -546,7 +600,7 @@ func Test_buildPolicyRoutes(t *testing.T) {
|
||||||
},
|
},
|
||||||
"route": {
|
"route": {
|
||||||
"autoHostRewrite": true,
|
"autoHostRewrite": true,
|
||||||
"cluster": "policy-10",
|
"cluster": "policy-11",
|
||||||
"idleTimeout": "0s",
|
"idleTimeout": "0s",
|
||||||
"timeout": "10s",
|
"timeout": "10s",
|
||||||
"upgradeConfigs": [
|
"upgradeConfigs": [
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue