From cf1aa2cad068e3fddd4a1b38f2c09443b62232c2 Mon Sep 17 00:00:00 2001 From: Zeck Date: Wed, 27 Nov 2024 08:48:37 -0600 Subject: [PATCH] oidc ignore client/audience support --- .chloggen/oidc-ignore-audience.yaml | 27 +++++++ extension/oidcauthextension/README.md | 1 + extension/oidcauthextension/config.go | 8 +- extension/oidcauthextension/extension.go | 1 + extension/oidcauthextension/extension_test.go | 78 +++++++++++++++++++ 5 files changed, 113 insertions(+), 2 deletions(-) create mode 100644 .chloggen/oidc-ignore-audience.yaml diff --git a/.chloggen/oidc-ignore-audience.yaml b/.chloggen/oidc-ignore-audience.yaml new file mode 100644 index 000000000000..df09a8cd055f --- /dev/null +++ b/.chloggen/oidc-ignore-audience.yaml @@ -0,0 +1,27 @@ +# Use this changelog template to create an entry for release notes. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: enhancement + +# The name of the component, or a single word describing the area of concern, (e.g. filelogreceiver) +component: oidcauthextension + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: Add ignore_audience config option for ignoring oidc audience + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +issues: [36568] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: go-oidc SkipClientIDCheck is set based on config ignore_audience + +# If your change doesn't affect end users or the exported elements of any package, +# you should instead start your pull request title with [chore] or use the "Skip Changelog" label. +# Optional: The change log or logs in which this entry should be included. +# e.g. '[user]' or '[user, api]' +# Include 'user' if the change is relevant to end users. +# Include 'api' if there is a change to a library API. +# Default: '[user]' +change_logs: [user] diff --git a/extension/oidcauthextension/README.md b/extension/oidcauthextension/README.md index 5a24f22b65ff..7407f27914a1 100644 --- a/extension/oidcauthextension/README.md +++ b/extension/oidcauthextension/README.md @@ -23,6 +23,7 @@ extensions: issuer_url: http://localhost:8080/auth/realms/opentelemetry issuer_ca_path: /etc/pki/tls/cert.pem audience: account + ignore_audience: # false if unspecified username_claim: email receivers: diff --git a/extension/oidcauthextension/config.go b/extension/oidcauthextension/config.go index 327baea5365f..5df328e4f51d 100644 --- a/extension/oidcauthextension/config.go +++ b/extension/oidcauthextension/config.go @@ -15,9 +15,13 @@ type Config struct { // Audience of the token, used during the verification. // For example: "https://accounts.google.com" or "https://login.salesforce.com". - // Required. + // Required unless IgnoreAudience is true. Audience string `mapstructure:"audience"` + // When true, this skips validating the audience field. + // Optional. + IgnoreAudience bool `mapstructure:"ignore_audience"` + // The local path for the issuer CA's TLS server cert. // Optional. IssuerCAPath string `mapstructure:"issuer_ca_path"` @@ -32,7 +36,7 @@ type Config struct { } func (c *Config) Validate() error { - if c.Audience == "" { + if c.Audience == "" && !c.IgnoreAudience { return errNoAudienceProvided } if c.IssuerURL == "" { diff --git a/extension/oidcauthextension/extension.go b/extension/oidcauthextension/extension.go index c79e3231ef3a..db6c8b1ba3f1 100644 --- a/extension/oidcauthextension/extension.go +++ b/extension/oidcauthextension/extension.go @@ -68,6 +68,7 @@ func (e *oidcExtension) start(ctx context.Context, _ component.Host) error { } e.verifier = e.provider.Verifier(&oidc.Config{ ClientID: e.cfg.Audience, + SkipClientIDCheck: e.cfg.IgnoreAudience, }) return nil } diff --git a/extension/oidcauthextension/extension_test.go b/extension/oidcauthextension/extension_test.go index 92b72d15fa14..bc3f4c82d321 100644 --- a/extension/oidcauthextension/extension_test.go +++ b/extension/oidcauthextension/extension_test.go @@ -69,6 +69,70 @@ func TestOIDCAuthenticationSucceeded(t *testing.T) { // TODO(jpkroehling): assert that the authentication routine set the subject/membership to the resource } +func TestOIDCAuthenticationSucceededIgnoreAudienceMismatch(t *testing.T) { + // prepare + oidcServer, err := newOIDCServer() + require.NoError(t, err) + oidcServer.Start() + defer oidcServer.Close() + + config := &Config{ + IssuerURL: oidcServer.URL, + Audience: "unit-test", + IgnoreAudience: true, + } + p := newExtension(config, zap.NewNop()) + + err = p.Start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + + payload, _ := json.Marshal(map[string]any{ + "iss": oidcServer.URL, + "aud": "not-unit-test", + "exp": time.Now().Add(time.Minute).Unix(), + }) + token, err := oidcServer.token(payload) + require.NoError(t, err) + + // test + ctx, err := p.Authenticate(context.Background(), map[string][]string{"authorization": {fmt.Sprintf("Bearer %s", token)}}) + + // verify + assert.NoError(t, err) + assert.NotNil(t, ctx) +} + +func TestOIDCAuthenticationFailAudienceMismatch(t *testing.T) { + // prepare + oidcServer, err := newOIDCServer() + require.NoError(t, err) + oidcServer.Start() + defer oidcServer.Close() + + config := &Config{ + IssuerURL: oidcServer.URL, + Audience: "unit-test", + } + p := newExtension(config, zap.NewNop()) + + err = p.Start(context.Background(), componenttest.NewNopHost()) + require.NoError(t, err) + + payload, _ := json.Marshal(map[string]any{ + "iss": oidcServer.URL, + "aud": "not-unit-test", + "exp": time.Now().Add(time.Minute).Unix(), + }) + token, err := oidcServer.token(payload) + require.NoError(t, err) + + // test + _, err = p.Authenticate(context.Background(), map[string][]string{"authorization": {fmt.Sprintf("Bearer %s", token)}}) + + // verify + assert.Error(t, err) +} + func TestOIDCProviderForConfigWithTLS(t *testing.T) { // prepare the CA cert for the TLS handler cert := x509.Certificate{ @@ -423,6 +487,20 @@ func TestMissingClient(t *testing.T) { assert.Equal(t, errNoAudienceProvided, err) } +func TestIgnoreMissingClient(t *testing.T) { + // prepare + config := &Config{ + IssuerURL: "http://example.com/", + IgnoreAudience: true, + } + + // test + err := config.Validate() + + // verify + assert.Nil(t, err) +} + func TestMissingIssuerURL(t *testing.T) { // prepare config := &Config{