Skip to content

Commit 24269fc

Browse files
committed
native oauth2 per-route config
Signed-off-by: Huabing (Robin) Zhao <zhaohuabing@gmail.com>
1 parent c7e21fa commit 24269fc

20 files changed

Lines changed: 385 additions & 403 deletions

examples/extension-server/go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ go 1.26.1
44

55
require (
66
github.com/envoyproxy/gateway v1.3.1
7-
github.com/envoyproxy/go-control-plane v0.14.0
7+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14
88
github.com/envoyproxy/go-control-plane/envoy v1.37.1-0.20260304210048-a81710db7097
99
github.com/urfave/cli/v2 v2.27.7
1010
google.golang.org/grpc v1.80.0

examples/extension-server/go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
2222
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
2323
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
2424
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
25-
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
26-
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
25+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14 h1:7g8SJv4OrVcLT4yfkzIbsTcwLBwyLu8gKb/yCf3Loxk=
26+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14/go.mod h1:18SVzvkoF8AL2O7baVikhojMZ+7rFPh3o8tOOsBVyok=
2727
github.com/envoyproxy/go-control-plane/envoy v1.37.1-0.20260304210048-a81710db7097 h1:Ou9X6qsPOiDOsQgaboj3jlCE5ZYngdYeSVDKBcT95QE=
2828
github.com/envoyproxy/go-control-plane/envoy v1.37.1-0.20260304210048-a81710db7097/go.mod h1:237/ZQHepDd4v5BjpRNFI2mMG7WEBd+mQnt8jwbqrnk=
2929
github.com/envoyproxy/protoc-gen-validate v1.3.3 h1:MVQghNeW+LZcmXe7SY1V36Z+WFMDjpqGAGacLe2T0ds=

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ require (
1111
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc
1212
github.com/docker/cli v29.4.0+incompatible
1313
github.com/dominikbraun/graph v0.23.0
14-
github.com/envoyproxy/go-control-plane v0.14.0
14+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14
1515
github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260115164926-066cbd5b3989
1616
github.com/envoyproxy/go-control-plane/envoy v1.37.1-0.20260304210048-a81710db7097
1717
github.com/envoyproxy/go-control-plane/ratelimit v0.1.1-0.20260115164926-066cbd5b3989

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,8 +140,8 @@ github.com/ebitengine/purego v0.10.0 h1:QIw4xfpWT6GWTzaW5XEKy3HXoqrJGx1ijYHzTF0/
140140
github.com/ebitengine/purego v0.10.0/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ=
141141
github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes=
142142
github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
143-
github.com/envoyproxy/go-control-plane v0.14.0 h1:hbG2kr4RuFj222B6+7T83thSPqLjwBIfQawTkC++2HA=
144-
github.com/envoyproxy/go-control-plane v0.14.0/go.mod h1:NcS5X47pLl/hfqxU70yPwL9ZMkUlwlKxtAohpi2wBEU=
143+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14 h1:7g8SJv4OrVcLT4yfkzIbsTcwLBwyLu8gKb/yCf3Loxk=
144+
github.com/envoyproxy/go-control-plane v0.14.1-0.20260409050421-3f47accd6e14/go.mod h1:18SVzvkoF8AL2O7baVikhojMZ+7rFPh3o8tOOsBVyok=
145145
github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260115164926-066cbd5b3989 h1:KTd1TJym7dgV1L1XlxXeJNct7rJI3xTV+iuArq40wm0=
146146
github.com/envoyproxy/go-control-plane/contrib v1.36.1-0.20260115164926-066cbd5b3989/go.mod h1:+fG/snSdlOxU+5RWuuKSYxF9zusT3Duy1MDbETA44Bo=
147147
github.com/envoyproxy/go-control-plane/envoy v1.37.1-0.20260304210048-a81710db7097 h1:Ou9X6qsPOiDOsQgaboj3jlCE5ZYngdYeSVDKBcT95QE=

internal/xds/translator/oidc.go

Lines changed: 24 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -36,11 +36,7 @@ var _ httpFilter = &oidc{}
3636

3737
// patchHCM builds and appends the oauth2 Filters to the HTTP Connection Manager
3838
// if applicable, and it does not already exist.
39-
// Note: this method creates an oauth2 filter for each route that contains an OIDC config.
40-
// the filter is disabled by default. It is enabled on the route level.
4139
func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListener) error {
42-
var errs error
43-
4440
if mgr == nil {
4541
return errors.New("hcm is nil")
4642
}
@@ -49,55 +45,42 @@ func (*oidc) patchHCM(mgr *hcmv3.HttpConnectionManager, irListener *ir.HTTPListe
4945
return errors.New("ir listener is nil")
5046
}
5147

48+
if hcmContainsFilter(mgr, string(egv1a1.EnvoyFilterOAuth2)) {
49+
return nil
50+
}
51+
5252
for _, route := range irListener.Routes {
5353
if !routeContainsOIDC(route) {
5454
continue
5555
}
5656

57-
// Only generates one OAuth2 Envoy filter for each unique name.
58-
// For example, if there are two routes under the same gateway with the
59-
// same OAuth2 config, only one OAuth2 filter will be generated.
60-
if hcmContainsFilter(mgr, oauth2FilterName(route.Security.OIDC)) {
61-
continue
62-
}
63-
64-
filter, err := buildHCMOAuth2Filter(route.Security)
57+
filter, err := buildHCMOAuth2Filter()
6558
if err != nil {
66-
errs = errors.Join(errs, err)
67-
continue
59+
return err
6860
}
69-
7061
mgr.HttpFilters = append(mgr.HttpFilters, filter)
62+
return nil
7163
}
7264

73-
return errs
65+
return nil
7466
}
7567

76-
// buildHCMOAuth2Filter returns an OAuth2 HTTP filter from the provided IR HTTPRoute.
77-
func buildHCMOAuth2Filter(securityFeatures *ir.SecurityFeatures) (*hcmv3.HttpFilter, error) {
78-
oauth2Proto, err := oauth2Config(securityFeatures)
79-
if err != nil {
80-
return nil, err
81-
}
82-
68+
// buildHCMOAuth2Filter returns the listener-level OAuth2 HTTP filter.
69+
func buildHCMOAuth2Filter() (*hcmv3.HttpFilter, error) {
70+
oauth2Proto := &oauth2v3.OAuth2{}
8371
OAuth2Any, err := proto.ToAnyWithValidation(oauth2Proto)
8472
if err != nil {
8573
return nil, err
8674
}
8775

8876
return &hcmv3.HttpFilter{
89-
Name: oauth2FilterName(securityFeatures.OIDC),
90-
Disabled: true,
77+
Name: string(egv1a1.EnvoyFilterOAuth2),
9178
ConfigType: &hcmv3.HttpFilter_TypedConfig{
9279
TypedConfig: OAuth2Any,
9380
},
9481
}, nil
9582
}
9683

97-
func oauth2FilterName(oidc *ir.OIDC) string {
98-
return perRouteFilterName(egv1a1.EnvoyFilterOAuth2, oidc.Name)
99-
}
100-
10184
func oauth2Config(securityFeatures *ir.SecurityFeatures) (*oauth2v3.OAuth2, error) {
10285
var (
10386
tokenEndpointCluster string
@@ -586,11 +569,19 @@ func (*oidc) patchRoute(route *routev3.Route, irRoute *ir.HTTPRoute, _ *ir.HTTPL
586569
if irRoute.Security == nil || irRoute.Security.OIDC == nil {
587570
return nil
588571
}
589-
filterName := oauth2FilterName(irRoute.Security.OIDC)
590-
if err := enableFilterOnRoute(route, filterName, &routev3.FilterConfig{
591-
Config: &anypb.Any{},
592-
}); err != nil {
572+
oauth2Proto, err := oauth2Config(irRoute.Security)
573+
if err != nil {
593574
return err
594575
}
576+
if route.TypedPerFilterConfig == nil {
577+
route.TypedPerFilterConfig = make(map[string]*anypb.Any)
578+
}
579+
580+
oauth2Any, err := proto.ToAnyWithValidation(oauth2Proto)
581+
if err != nil {
582+
return err
583+
}
584+
585+
route.TypedPerFilterConfig[string(egv1a1.EnvoyFilterOAuth2)] = oauth2Any
595586
return nil
596587
}

internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.listeners.yaml

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -39,50 +39,9 @@
3939
'@type': type.googleapis.com/envoy.extensions.filters.http.basic_auth.v3.BasicAuth
4040
users:
4141
inlineBytes: dXNlcjE6e1NIQX10RVNzQm1FL3lOWTNsYjZhMEw2dlZRRVpOcXc9CnVzZXIyOntTSEF9RUo5TFBGRFhzTjl5blNtYnh2anA3NUJtbHg4PQo=
42-
- disabled: true
43-
name: envoy.filters.http.oauth2/securitypolicy/default/policy-for-gateway-2
42+
- name: envoy.filters.http.oauth2
4443
typedConfig:
4544
'@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
46-
config:
47-
authScopes:
48-
- openid
49-
- email
50-
- profile
51-
authType: BASIC_AUTH
52-
authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth
53-
credentials:
54-
clientId: client.oauth.foo.com
55-
cookieNames:
56-
bearerToken: AccessToken-5F93C2E4
57-
idToken: IdToken-5F93C2E4
58-
oauthExpires: OauthExpires-5F93C2E4
59-
oauthHmac: OauthHMAC-5F93C2E4
60-
oauthNonce: OauthNonce-5F93C2E4
61-
refreshToken: RefreshToken-5F93C2E4
62-
hmacSecret:
63-
name: oauth2/hmac_secret/securitypolicy/default/policy-for-gateway-2
64-
sdsConfig:
65-
ads: {}
66-
resourceApiVersion: V3
67-
tokenSecret:
68-
name: oauth2/client_secret/securitypolicy/default/policy-for-gateway-2
69-
sdsConfig:
70-
ads: {}
71-
resourceApiVersion: V3
72-
preserveAuthorizationHeader: true
73-
redirectPathMatcher:
74-
path:
75-
exact: /foo/oauth2/callback
76-
redirectUri: https://www.example.com/foo/oauth2/callback
77-
signoutPath:
78-
path:
79-
exact: /foo/logout
80-
statPrefix: securitypolicy/default/policy-for-gateway-2
81-
tokenEndpoint:
82-
cluster: oauth_foo_com_443
83-
timeout: 10s
84-
uri: https://oauth.foo.com/token
85-
useRefreshToken: true
8645
- name: envoy.filters.http.router
8746
typedConfig:
8847
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router

internal/xds/translator/testdata/out/xds-ir/multiple-listeners-same-port-with-different-filters.routes.yaml

Lines changed: 42 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,45 @@
4040
upgradeConfigs:
4141
- upgradeType: websocket
4242
typedPerFilterConfig:
43-
envoy.filters.http.oauth2/securitypolicy/default/policy-for-gateway-2:
44-
'@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
45-
config: {}
43+
envoy.filters.http.oauth2:
44+
'@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
45+
config:
46+
authScopes:
47+
- openid
48+
- email
49+
- profile
50+
authType: BASIC_AUTH
51+
authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth
52+
credentials:
53+
clientId: client.oauth.foo.com
54+
cookieNames:
55+
bearerToken: AccessToken-5F93C2E4
56+
idToken: IdToken-5F93C2E4
57+
oauthExpires: OauthExpires-5F93C2E4
58+
oauthHmac: OauthHMAC-5F93C2E4
59+
oauthNonce: OauthNonce-5F93C2E4
60+
refreshToken: RefreshToken-5F93C2E4
61+
hmacSecret:
62+
name: oauth2/hmac_secret/securitypolicy/default/policy-for-gateway-2
63+
sdsConfig:
64+
ads: {}
65+
resourceApiVersion: V3
66+
tokenSecret:
67+
name: oauth2/client_secret/securitypolicy/default/policy-for-gateway-2
68+
sdsConfig:
69+
ads: {}
70+
resourceApiVersion: V3
71+
preserveAuthorizationHeader: true
72+
redirectPathMatcher:
73+
path:
74+
exact: /foo/oauth2/callback
75+
redirectUri: https://www.example.com/foo/oauth2/callback
76+
signoutPath:
77+
path:
78+
exact: /foo/logout
79+
statPrefix: securitypolicy/default/policy-for-gateway-2
80+
tokenEndpoint:
81+
cluster: oauth_foo_com_443
82+
timeout: 10s
83+
uri: https://oauth.foo.com/token
84+
useRefreshToken: true

internal/xds/translator/testdata/out/xds-ir/oidc-and-jwt-with-passthrough.listeners.yaml

Lines changed: 1 addition & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -14,56 +14,9 @@
1414
initialStreamWindowSize: 65536
1515
maxConcurrentStreams: 100
1616
httpFilters:
17-
- disabled: true
18-
name: envoy.filters.http.oauth2/securitypolicy/envoy-gateway/security-policy-1
17+
- name: envoy.filters.http.oauth2
1918
typedConfig:
2019
'@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
21-
config:
22-
authScopes:
23-
- openid
24-
authType: BASIC_AUTH
25-
authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth
26-
credentials:
27-
clientId: client.oauth.foo.com
28-
cookieNames:
29-
bearerToken: AccessToken-b0a1b740
30-
idToken: IdToken-b0a1b740
31-
oauthExpires: OauthExpires-b0a1b740
32-
oauthHmac: OauthHMAC-b0a1b740
33-
oauthNonce: OauthNonce-b0a1b740
34-
refreshToken: RefreshToken-b0a1b740
35-
hmacSecret:
36-
name: oauth2/hmac_secret/securitypolicy/envoy-gateway/security-policy-1
37-
sdsConfig:
38-
ads: {}
39-
resourceApiVersion: V3
40-
tokenSecret:
41-
name: oauth2/client_secret/securitypolicy/envoy-gateway/security-policy-1
42-
sdsConfig:
43-
ads: {}
44-
resourceApiVersion: V3
45-
passThroughMatcher:
46-
- name: Authorization
47-
stringMatch:
48-
prefix: 'Bearer '
49-
- name: MyHeaderPrefixed
50-
stringMatch:
51-
prefix: MyPrefix
52-
- name: MyHeaderNoPrefix
53-
preserveAuthorizationHeader: true
54-
redirectPathMatcher:
55-
path:
56-
exact: /oauth2/callback
57-
redirectUri: https://www.example.com/oauth2/callback
58-
signoutPath:
59-
path:
60-
exact: /logout
61-
statPrefix: securitypolicy/envoy-gateway/security-policy-1
62-
tokenEndpoint:
63-
cluster: oauth_foo_com_443
64-
timeout: 10s
65-
uri: https://oauth.foo.com/token
66-
useRefreshToken: true
6720
- name: envoy.filters.http.jwt_authn
6821
typedConfig:
6922
'@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication

internal/xds/translator/testdata/out/xds-ir/oidc-and-jwt-with-passthrough.routes.yaml

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,51 @@
1616
envoy.filters.http.jwt_authn:
1717
'@type': type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.PerRouteConfig
1818
requirementName: httproute/default/httproute-1/rule/0/match/0/www_example_com
19-
envoy.filters.http.oauth2/securitypolicy/envoy-gateway/security-policy-1:
20-
'@type': type.googleapis.com/envoy.config.route.v3.FilterConfig
21-
config: {}
19+
envoy.filters.http.oauth2:
20+
'@type': type.googleapis.com/envoy.extensions.filters.http.oauth2.v3.OAuth2
21+
config:
22+
authScopes:
23+
- openid
24+
authType: BASIC_AUTH
25+
authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth
26+
credentials:
27+
clientId: client.oauth.foo.com
28+
cookieNames:
29+
bearerToken: AccessToken-b0a1b740
30+
idToken: IdToken-b0a1b740
31+
oauthExpires: OauthExpires-b0a1b740
32+
oauthHmac: OauthHMAC-b0a1b740
33+
oauthNonce: OauthNonce-b0a1b740
34+
refreshToken: RefreshToken-b0a1b740
35+
hmacSecret:
36+
name: oauth2/hmac_secret/securitypolicy/envoy-gateway/security-policy-1
37+
sdsConfig:
38+
ads: {}
39+
resourceApiVersion: V3
40+
tokenSecret:
41+
name: oauth2/client_secret/securitypolicy/envoy-gateway/security-policy-1
42+
sdsConfig:
43+
ads: {}
44+
resourceApiVersion: V3
45+
passThroughMatcher:
46+
- name: Authorization
47+
stringMatch:
48+
prefix: 'Bearer '
49+
- name: MyHeaderPrefixed
50+
stringMatch:
51+
prefix: MyPrefix
52+
- name: MyHeaderNoPrefix
53+
preserveAuthorizationHeader: true
54+
redirectPathMatcher:
55+
path:
56+
exact: /oauth2/callback
57+
redirectUri: https://www.example.com/oauth2/callback
58+
signoutPath:
59+
path:
60+
exact: /logout
61+
statPrefix: securitypolicy/envoy-gateway/security-policy-1
62+
tokenEndpoint:
63+
cluster: oauth_foo_com_443
64+
timeout: 10s
65+
uri: https://oauth.foo.com/token
66+
useRefreshToken: true

0 commit comments

Comments
 (0)