Skip to content

Commit

Permalink
feat(oidc): implement additional OIDC configuration settings (envoypr…
Browse files Browse the repository at this point in the history
…oxy#3514)

implement additional OIDC configuration settings

Signed-off-by: Jesse Haka <haka.jesse@gmail.com>
  • Loading branch information
zetaab authored Jun 3, 2024
1 parent 01dd7b1 commit 6e946db
Show file tree
Hide file tree
Showing 13 changed files with 119 additions and 32 deletions.
6 changes: 1 addition & 5 deletions api/v1alpha1/oidc_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,7 @@ type OIDC struct {
// via the Authorization header Bearer scheme to the upstream.
// If not specified, defaults to false.
// +optional
// +notImplementedHide
ForwardAccessToken *bool `json:"ForwardAccessToken,omitempty"`
ForwardAccessToken *bool `json:"forwardAccessToken,omitempty"`

// DefaultTokenTTL is the default lifetime of the id token and access token.
// Please note that Envoy will always use the expiry time from the response
Expand All @@ -76,7 +75,6 @@ type OIDC struct {
// OAuth flow will fail.
//
// +optional
// +notImplementedHide
DefaultTokenTTL *metav1.Duration `json:"defaultTokenTTL,omitempty"`

// RefreshToken indicates whether the Envoy should automatically refresh the
Expand All @@ -86,7 +84,6 @@ type OIDC struct {
//
// If not specified, defaults to false.
// +optional
// +notImplementedHide
RefreshToken *bool `json:"refreshToken,omitempty"`

// DefaultRefreshTokenTTL is the default lifetime of the refresh token.
Expand All @@ -96,7 +93,6 @@ type OIDC struct {
// If not specified, defaults to 604800s (one week).
// Note: this field is only applicable when the "refreshToken" field is set to true.
// +optional
// +notImplementedHide
DefaultRefreshTokenTTL *metav1.Duration `json:"defaultRefreshTokenTTL,omitempty"`
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -811,12 +811,6 @@ spec:
description: OIDC defines the configuration for the OpenID Connect
(OIDC) authentication.
properties:
ForwardAccessToken:
description: |-
ForwardAccessToken indicates whether the Envoy should forward the access token
via the Authorization header Bearer scheme to the upstream.
If not specified, defaults to false.
type: boolean
clientID:
description: |-
The client ID to be used in the OIDC
Expand Down Expand Up @@ -913,6 +907,12 @@ spec:
the authorization response must be set by the authorization server, or the
OAuth flow will fail.
type: string
forwardAccessToken:
description: |-
ForwardAccessToken indicates whether the Envoy should forward the access token
via the Authorization header Bearer scheme to the upstream.
If not specified, defaults to false.
type: boolean
logoutPath:
description: |-
The path to log a user out, clearing their credential cookies.
Expand Down
50 changes: 32 additions & 18 deletions internal/gatewayapi/securitypolicy.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,11 @@ import (
)

const (
defaultRedirectURL = "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback"
defaultRedirectPath = "/oauth2/callback"
defaultLogoutPath = "/logout"
defaultRedirectURL = "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback"
defaultRedirectPath = "/oauth2/callback"
defaultLogoutPath = "/logout"
defaultForwardAccessToken = false
defaultRefreshToken = false

// nolint: gosec
oidcHMACSecretName = "envoy-oidc-hmac"
Expand Down Expand Up @@ -587,9 +589,11 @@ func (t *Translator) buildOIDC(
scopes := appendOpenidScopeIfNotExist(oidc.Scopes)

var (
redirectURL = defaultRedirectURL
redirectPath = defaultRedirectPath
logoutPath = defaultLogoutPath
redirectURL = defaultRedirectURL
redirectPath = defaultRedirectPath
logoutPath = defaultLogoutPath
forwardAccessToken = defaultForwardAccessToken
refreshToken = defaultRefreshToken
)

if oidc.RedirectURL != nil {
Expand All @@ -603,6 +607,12 @@ func (t *Translator) buildOIDC(
if oidc.LogoutPath != nil {
logoutPath = *oidc.LogoutPath
}
if oidc.ForwardAccessToken != nil {
forwardAccessToken = *oidc.ForwardAccessToken
}
if oidc.RefreshToken != nil {
refreshToken = *oidc.RefreshToken
}

// Generate a unique cookie suffix for oauth filters.
// This is to avoid cookie name collision when multiple security policies are applied
Expand All @@ -624,18 +634,22 @@ func (t *Translator) buildOIDC(
}

return &ir.OIDC{
Name: irConfigName(policy),
Provider: *provider,
ClientID: oidc.ClientID,
ClientSecret: clientSecretBytes,
Scopes: scopes,
Resources: oidc.Resources,
RedirectURL: redirectURL,
RedirectPath: redirectPath,
LogoutPath: logoutPath,
CookieSuffix: suffix,
CookieNameOverrides: policy.Spec.OIDC.CookieNames,
HMACSecret: hmacData,
Name: irConfigName(policy),
Provider: *provider,
ClientID: oidc.ClientID,
ClientSecret: clientSecretBytes,
Scopes: scopes,
Resources: oidc.Resources,
RedirectURL: redirectURL,
RedirectPath: redirectPath,
LogoutPath: logoutPath,
ForwardAccessToken: forwardAccessToken,
DefaultTokenTTL: oidc.DefaultTokenTTL,
RefreshToken: refreshToken,
DefaultRefreshTokenTTL: oidc.DefaultRefreshTokenTTL,
CookieSuffix: suffix,
CookieNameOverrides: policy.Spec.OIDC.CookieNames,
HMACSecret: hmacData,
}, nil
}

Expand Down
8 changes: 8 additions & 0 deletions internal/gatewayapi/testdata/securitypolicy-with-oidc.in.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ securityPolicies:
name: "client1-secret"
redirectURL: "https://www.example.com/bar/oauth2/callback"
logoutPath: "/bar/logout"
forwardAccessToken: true
defaultTokenTTL: 30m
refreshToken: true
defaultRefreshTokenTTL: 24h
- apiVersion: gateway.envoyproxy.io/v1alpha1
kind: SecurityPolicy
metadata:
Expand All @@ -124,3 +128,7 @@ securityPolicies:
resources: ["api"]
redirectURL: "https://www.example.com/foo/oauth2/callback"
logoutPath: "/foo/logout"
forwardAccessToken: true
defaultTokenTTL: 1h
refreshToken: true
defaultRefreshTokenTTL: 48h
16 changes: 16 additions & 0 deletions internal/gatewayapi/testdata/securitypolicy-with-oidc.out.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,16 @@ securityPolicies:
group: null
kind: null
name: client2-secret
defaultRefreshTokenTTL: 48h0m0s
defaultTokenTTL: 1h0m0s
forwardAccessToken: true
logoutPath: /foo/logout
provider:
authorizationEndpoint: https://oauth.foo.com/oauth2/v2/auth
issuer: https://oauth.foo.com
tokenEndpoint: https://oauth.foo.com/token
redirectURL: https://www.example.com/foo/oauth2/callback
refreshToken: true
resources:
- api
scopes:
Expand Down Expand Up @@ -192,10 +196,14 @@ securityPolicies:
group: null
kind: null
name: client1-secret
defaultRefreshTokenTTL: 24h0m0s
defaultTokenTTL: 30m0s
forwardAccessToken: true
logoutPath: /bar/logout
provider:
issuer: https://accounts.google.com
redirectURL: https://www.example.com/bar/oauth2/callback
refreshToken: true
targetRef:
group: gateway.networking.k8s.io
kind: Gateway
Expand Down Expand Up @@ -257,6 +265,9 @@ xdsIR:
clientID: client2.oauth.foo.com
clientSecret: Y2xpZW50MTpzZWNyZXQK
cookieSuffix: 5f93c2e4
defaultRefreshTokenTTL: 48h0m0s
defaultTokenTTL: 1h0m0s
forwardAccessToken: true
hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY=
logoutPath: /foo/logout
name: securitypolicy/default/policy-for-http-route
Expand All @@ -265,6 +276,7 @@ xdsIR:
tokenEndpoint: https://oauth.foo.com/token
redirectPath: /foo/oauth2/callback
redirectURL: https://www.example.com/foo/oauth2/callback
refreshToken: true
resources:
- api
scopes:
Expand Down Expand Up @@ -292,6 +304,9 @@ xdsIR:
clientID: client1.apps.googleusercontent.com
clientSecret: Y2xpZW50MTpzZWNyZXQK
cookieSuffix: b0a1b740
defaultRefreshTokenTTL: 24h0m0s
defaultTokenTTL: 30m0s
forwardAccessToken: true
hmacSecret: qrOYACHXoe7UEDI/raOjNSx+Z9ufXSc/22C3T6X/zPY=
logoutPath: /bar/logout
name: securitypolicy/envoy-gateway/policy-for-gateway
Expand All @@ -300,5 +315,6 @@ xdsIR:
tokenEndpoint: https://oauth2.googleapis.com/token
redirectPath: /bar/oauth2/callback
redirectURL: https://www.example.com/bar/oauth2/callback
refreshToken: true
scopes:
- openid
14 changes: 14 additions & 0 deletions internal/ir/xds.go
Original file line number Diff line number Diff line change
Expand Up @@ -695,6 +695,20 @@ type OIDC struct {
// The path to log a user out, clearing their credential cookies.
LogoutPath string `json:"logoutPath,omitempty"`

// ForwardAccessToken indicates whether the Envoy should forward the access token
// via the Authorization header Bearer scheme to the upstream.
ForwardAccessToken bool `json:"forwardAccessToken,omitempty"`

// DefaultTokenTTL is the default lifetime of the id token and access token.
DefaultTokenTTL *metav1.Duration `json:"defaultTokenTTL,omitempty"`

// RefreshToken indicates whether the Envoy should automatically refresh the
// id token and access token when they expire.
RefreshToken bool `json:"refreshToken,omitempty"`

// DefaultRefreshTokenTTL is the default lifetime of the refresh token.
DefaultRefreshTokenTTL *metav1.Duration `json:"defaultRefreshTokenTTL,omitempty"`

// CookieSuffix will be added to the name of the cookies set by the oauth filter.
// Adding a suffix avoids multiple oauth filters from overwriting each other's cookies.
// These cookies are set by the oauth filter, including: AccessToken,
Expand Down
10 changes: 10 additions & 0 deletions internal/ir/zz_generated.deepcopy.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 15 additions & 1 deletion internal/xds/translator/oidc.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import (
hcmv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/filters/network/http_connection_manager/v3"
tlsv3 "github.com/envoyproxy/go-control-plane/envoy/extensions/transport_sockets/tls/v3"
matcherv3 "github.com/envoyproxy/go-control-plane/envoy/type/matcher/v3"
"github.com/golang/protobuf/ptypes/wrappers"
"google.golang.org/protobuf/types/known/anypb"
"google.golang.org/protobuf/types/known/durationpb"
"k8s.io/utils/ptr"
Expand Down Expand Up @@ -145,7 +146,8 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) {
},
},
},
ForwardBearerToken: true,
UseRefreshToken: &wrappers.BoolValue{Value: oidc.RefreshToken},
ForwardBearerToken: oidc.ForwardAccessToken,
Credentials: &oauth2v3.OAuth2Credentials{
ClientId: oidc.ClientID,
TokenSecret: &tlsv3.SdsSecretConfig{
Expand Down Expand Up @@ -173,6 +175,18 @@ func oauth2Config(oidc *ir.OIDC) (*oauth2v3.OAuth2, error) {
},
}

if oidc.DefaultTokenTTL != nil {
oauth2.Config.DefaultExpiresIn = &durationpb.Duration{
Seconds: int64(oidc.DefaultTokenTTL.Seconds()),
}
}

if oidc.DefaultRefreshTokenTTL != nil {
oauth2.Config.DefaultRefreshTokenExpiresIn = &durationpb.Duration{
Seconds: int64(oidc.DefaultRefreshTokenTTL.Seconds()),
}
}

if oidc.CookieNameOverrides != nil &&
oidc.CookieNameOverrides.AccessToken != nil {
oauth2.Config.Credentials.CookieNames.BearerToken = *oidc.CookieNameOverrides.AccessToken
Expand Down
4 changes: 4 additions & 0 deletions internal/xds/translator/testdata/in/xds-ir/oidc.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ http:
redirectPath: "/foo/oauth2/callback"
logoutPath: "/foo/logout"
cookieSuffix: 5F93C2E4
forwardAccessToken: true
defaultTokenTTL: 1h
refreshToken: true
defaultRefreshTokenTTL: 48h
- name: "second-route"
hostname: "*"
pathMatch:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -132,7 +132,6 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
forwardBearerToken: true
redirectPathMatcher:
path:
exact: /foo/oauth2/callback
Expand All @@ -144,6 +143,7 @@
cluster: oauth_foo_com_443
timeout: 10s
uri: https://oauth.foo.com/token
useRefreshToken: false
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
defaultExpiresIn: 3600s
defaultRefreshTokenExpiresIn: 172800s
forwardBearerToken: true
redirectPathMatcher:
path:
Expand All @@ -57,6 +59,7 @@
cluster: oauth_foo_com_443
timeout: 10s
uri: https://oauth.foo.com/token
useRefreshToken: true
- disabled: true
name: envoy.filters.http.oauth2/securitypolicy/default/policy-for-second-route
typedConfig:
Expand Down Expand Up @@ -86,7 +89,6 @@
sdsConfig:
ads: {}
resourceApiVersion: V3
forwardBearerToken: true
redirectPathMatcher:
path:
exact: /bar/oauth2/callback
Expand All @@ -100,6 +102,7 @@
cluster: oauth_bar_com_443
timeout: 10s
uri: https://oauth.bar.com/token
useRefreshToken: false
- name: envoy.filters.http.router
typedConfig:
'@type': type.googleapis.com/envoy.extensions.filters.http.router.v3.Router
Expand Down
4 changes: 4 additions & 0 deletions site/content/en/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -2390,6 +2390,10 @@ _Appears in:_
| `resources` | _string array_ | false | The OIDC resources to be used in the<br />[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). |
| `redirectURL` | _string_ | true | The redirect URL to be used in the OIDC<br />[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).<br />If not specified, uses the default redirect URI "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" |
| `logoutPath` | _string_ | true | The path to log a user out, clearing their credential cookies.<br /><br />If not specified, uses a default logout path "/logout" |
| `forwardAccessToken` | _boolean_ | false | ForwardAccessToken indicates whether the Envoy should forward the access token<br />via the Authorization header Bearer scheme to the upstream.<br />If not specified, defaults to false. |
| `defaultTokenTTL` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | false | DefaultTokenTTL is the default lifetime of the id token and access token.<br />Please note that Envoy will always use the expiry time from the response<br />of the authorization server if it is provided. This field is only used when<br />the expiry time is not provided by the authorization.<br /><br />If not specified, defaults to 0. In this case, the "expires_in" field in<br />the authorization response must be set by the authorization server, or the<br />OAuth flow will fail. |
| `refreshToken` | _boolean_ | false | RefreshToken indicates whether the Envoy should automatically refresh the<br />id token and access token when they expire.<br />When set to true, the Envoy will use the refresh token to get a new id token<br />and access token when they expire.<br /><br />If not specified, defaults to false. |
| `defaultRefreshTokenTTL` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | false | DefaultRefreshTokenTTL is the default lifetime of the refresh token.<br />This field is only used when the exp (expiration time) claim is omitted in<br />the refresh token or the refresh token is not JWT.<br /><br />If not specified, defaults to 604800s (one week).<br />Note: this field is only applicable when the "refreshToken" field is set to true. |


#### OIDCCookieNames
Expand Down
4 changes: 4 additions & 0 deletions site/content/zh/latest/api/extension_types.md
Original file line number Diff line number Diff line change
Expand Up @@ -2390,6 +2390,10 @@ _Appears in:_
| `resources` | _string array_ | false | The OIDC resources to be used in the<br />[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest). |
| `redirectURL` | _string_ | true | The redirect URL to be used in the OIDC<br />[Authentication Request](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest).<br />If not specified, uses the default redirect URI "%REQ(x-forwarded-proto)%://%REQ(:authority)%/oauth2/callback" |
| `logoutPath` | _string_ | true | The path to log a user out, clearing their credential cookies.<br /><br />If not specified, uses a default logout path "/logout" |
| `forwardAccessToken` | _boolean_ | false | ForwardAccessToken indicates whether the Envoy should forward the access token<br />via the Authorization header Bearer scheme to the upstream.<br />If not specified, defaults to false. |
| `defaultTokenTTL` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | false | DefaultTokenTTL is the default lifetime of the id token and access token.<br />Please note that Envoy will always use the expiry time from the response<br />of the authorization server if it is provided. This field is only used when<br />the expiry time is not provided by the authorization.<br /><br />If not specified, defaults to 0. In this case, the "expires_in" field in<br />the authorization response must be set by the authorization server, or the<br />OAuth flow will fail. |
| `refreshToken` | _boolean_ | false | RefreshToken indicates whether the Envoy should automatically refresh the<br />id token and access token when they expire.<br />When set to true, the Envoy will use the refresh token to get a new id token<br />and access token when they expire.<br /><br />If not specified, defaults to false. |
| `defaultRefreshTokenTTL` | _[Duration](https://kubernetes.io/docs/reference/generated/kubernetes-api/v1.29/#duration-v1-meta)_ | false | DefaultRefreshTokenTTL is the default lifetime of the refresh token.<br />This field is only used when the exp (expiration time) claim is omitted in<br />the refresh token or the refresh token is not JWT.<br /><br />If not specified, defaults to 604800s (one week).<br />Note: this field is only applicable when the "refreshToken" field is set to true. |


#### OIDCCookieNames
Expand Down

0 comments on commit 6e946db

Please sign in to comment.