diff --git a/cmd/webhook/main.go b/cmd/webhook/main.go
index fcb08c35ecd..006de160878 100644
--- a/cmd/webhook/main.go
+++ b/cmd/webhook/main.go
@@ -194,7 +194,7 @@ func NewConfigValidationController(ctx context.Context, _ configmap.Watcher) *co
func NewSinkBindingWebhook(opts ...psbinding.ReconcilerOption) injection.ControllerConstructor {
return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
- sbresolver := sinkbinding.WithContextFactory(ctx, func(types.NamespacedName) {})
+ withContext := sinkbinding.WithContextFactory(ctx, func(types.NamespacedName) {})
return psbinding.NewAdmissionController(ctx,
@@ -208,7 +208,7 @@ func NewSinkBindingWebhook(opts ...psbinding.ReconcilerOption) injection.Control
sinkbinding.ListAll,
// How to setup the context prior to invoking Do/Undo.
- sbresolver,
+ withContext,
opts...,
)
}
diff --git a/config/core/resources/containersource.yaml b/config/core/resources/containersource.yaml
index 50971891a1f..15a369ea509 100644
--- a/config/core/resources/containersource.yaml
+++ b/config/core/resources/containersource.yaml
@@ -143,6 +143,9 @@ spec:
sinkCACerts:
description: CACerts is the Certification Authority (CA) certificates in PEM format that the source trusts when sending events to the sink.
type: string
+ sinkAudience:
+ description: Audience is the OIDC audience of the sink.
+ type: string
additionalPrinterColumns:
- name: Sink
type: string
diff --git a/config/core/resources/sinkbindings.yaml b/config/core/resources/sinkbindings.yaml
index 5097493e7ef..a8d609604df 100644
--- a/config/core/resources/sinkbindings.yaml
+++ b/config/core/resources/sinkbindings.yaml
@@ -185,6 +185,12 @@ spec:
sinkCACerts:
description: CACerts is the Certification Authority (CA) certificates in PEM format that the source trusts when sending events to the sink.
type: string
+ sinkAudience:
+ description: Audience is the OIDC audience of the sink.
+ type: string
+ oidcTokenSecretName:
+ description: Name of the secret with the OIDC token for the sink.
+ type: string
additionalPrinterColumns:
- name: Sink
type: string
diff --git a/config/core/roles/webhook-clusterrole.yaml b/config/core/roles/webhook-clusterrole.yaml
index 91b6e330712..5fe8cf1ce88 100644
--- a/config/core/roles/webhook-clusterrole.yaml
+++ b/config/core/roles/webhook-clusterrole.yaml
@@ -109,12 +109,25 @@ rules:
- "create"
- "patch"
-# For the SinkBinding reconciler adding the OIDC identity service accounts
+ # For the SinkBinding reconciler adding the OIDC identity service accounts
- apiGroups:
- ""
resources:
- "serviceaccounts"
verbs: *everything
+ # For the SinkBinding reconciler creating the sinkbinding token secret
+ - apiGroups:
+ - ""
+ resources:
+ - "serviceaccounts/token"
+ verbs:
+ - "create"
+ - apiGroups:
+ - ""
+ resources:
+ - "secrets"
+ verbs: *everything
+
# Necessary for conversion webhook. These are copied from the serving
# TODO: Do we really need all these permissions?
- apiGroups: ["apiextensions.k8s.io"]
diff --git a/docs/eventing-api.md b/docs/eventing-api.md
index f8f61a64a2e..16a4343c97a 100644
--- a/docs/eventing-api.md
+++ b/docs/eventing-api.md
@@ -5958,6 +5958,18 @@ state.
Source.
+
+
+oidcTokenSecretName
+
+string
+
+ |
+
+ OIDCTokenSecretName is the name of the secret containing the token for
+this SinkBindings OIDC authentication
+ |
+
diff --git a/go.mod b/go.mod
index 868ad667d38..09214cc5cc8 100644
--- a/go.mod
+++ b/go.mod
@@ -9,6 +9,7 @@ require (
github.com/cloudevents/sdk-go/sql/v2 v2.13.0
github.com/cloudevents/sdk-go/v2 v2.13.0
github.com/coreos/go-oidc/v3 v3.6.0
+ github.com/go-jose/go-jose/v3 v3.0.0
github.com/golang/protobuf v1.5.3
github.com/google/go-cmp v0.6.0
github.com/google/gofuzz v1.2.0
@@ -71,7 +72,6 @@ require (
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
- github.com/go-jose/go-jose/v3 v3.0.0 // indirect
github.com/go-kit/log v0.2.1 // indirect
github.com/go-logfmt/logfmt v0.5.1 // indirect
github.com/go-logr/logr v1.2.4 // indirect
diff --git a/pkg/apis/sources/v1/sinkbinding_lifecycle.go b/pkg/apis/sources/v1/sinkbinding_lifecycle.go
index 5a8d1003554..45fe0725538 100644
--- a/pkg/apis/sources/v1/sinkbinding_lifecycle.go
+++ b/pkg/apis/sources/v1/sinkbinding_lifecycle.go
@@ -33,9 +33,14 @@ import (
"knative.dev/pkg/tracker"
)
+const (
+ oidcTokenVolumeName = "oidc-token"
+)
+
var sbCondSet = apis.NewLivingConditionSet(
SinkBindingConditionSinkProvided,
SinkBindingConditionOIDCIdentityCreated,
+ SinkBindingConditionOIDCTokenSecretCreated,
)
// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
@@ -90,6 +95,7 @@ func (sbs *SinkBindingStatus) MarkSink(addr *duckv1.Addressable) {
if addr != nil {
sbs.SinkURI = addr.URL
sbs.SinkCACerts = addr.CACerts
+ sbs.SinkAudience = addr.Audience
sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionSinkProvided)
} else {
sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionSinkProvided, "SinkEmpty", "Sink has resolved to empty.%s", "")
@@ -112,6 +118,22 @@ func (sbs *SinkBindingStatus) MarkOIDCIdentityCreatedUnknown(reason, messageForm
sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCIdentityCreated, reason, messageFormat, messageA...)
}
+func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceeded() {
+ sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionOIDCTokenSecretCreated)
+}
+
+func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceededWithReason(reason, messageFormat string, messageA ...interface{}) {
+ sbCondSet.Manage(sbs).MarkTrueWithReason(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
+}
+
+func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedFailed(reason, messageFormat string, messageA ...interface{}) {
+ sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
+}
+
+func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedUnknown(reason, messageFormat string, messageA ...interface{}) {
+ sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
+}
+
// Do implements psbinding.Bindable
func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) {
// First undo so that we can just unconditionally append below.
@@ -171,6 +193,38 @@ func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) {
Value: ceOverrides,
})
}
+
+ if sb.Status.OIDCTokenSecretName != nil {
+ ps.Spec.Template.Spec.Volumes = append(ps.Spec.Template.Spec.Volumes, corev1.Volume{
+ Name: oidcTokenVolumeName,
+ VolumeSource: corev1.VolumeSource{
+ Projected: &corev1.ProjectedVolumeSource{
+ Sources: []corev1.VolumeProjection{
+ {
+ Secret: &corev1.SecretProjection{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: *sb.Status.OIDCTokenSecretName,
+ },
+ },
+ },
+ },
+ },
+ },
+ })
+
+ for i := range spec.Containers {
+ spec.Containers[i].VolumeMounts = append(spec.Containers[i].VolumeMounts, corev1.VolumeMount{
+ Name: oidcTokenVolumeName,
+ MountPath: "/oidc",
+ })
+ }
+ for i := range spec.InitContainers {
+ spec.InitContainers[i].VolumeMounts = append(spec.InitContainers[i].VolumeMounts, corev1.VolumeMount{
+ Name: oidcTokenVolumeName,
+ MountPath: "/oidc",
+ })
+ }
+ }
}
func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
@@ -189,6 +243,17 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
}
}
spec.InitContainers[i].Env = env
+
+ if len(spec.InitContainers[i].VolumeMounts) > 0 {
+ volumeMounts := make([]corev1.VolumeMount, 0, len(spec.InitContainers[i].VolumeMounts))
+ for j, vol := range c.VolumeMounts {
+ if vol.Name == oidcTokenVolumeName {
+ continue
+ }
+ volumeMounts = append(volumeMounts, spec.InitContainers[i].VolumeMounts[j])
+ }
+ spec.InitContainers[i].VolumeMounts = volumeMounts
+ }
}
for i, c := range spec.Containers {
if len(c.Env) == 0 {
@@ -204,5 +269,27 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
}
}
spec.Containers[i].Env = env
+
+ if len(spec.Containers[i].VolumeMounts) > 0 {
+ volumeMounts := make([]corev1.VolumeMount, 0, len(spec.Containers[i].VolumeMounts))
+ for j, vol := range c.VolumeMounts {
+ if vol.Name == oidcTokenVolumeName {
+ continue
+ }
+ volumeMounts = append(volumeMounts, spec.Containers[i].VolumeMounts[j])
+ }
+ spec.Containers[i].VolumeMounts = volumeMounts
+ }
+ }
+
+ if len(spec.Volumes) > 0 {
+ volumes := make([]corev1.Volume, 0, len(spec.Volumes))
+ for i, vol := range spec.Volumes {
+ if vol.Name == oidcTokenVolumeName {
+ continue
+ }
+ volumes = append(volumes, spec.Volumes[i])
+ }
+ ps.Spec.Template.Spec.Volumes = volumes
}
}
diff --git a/pkg/apis/sources/v1/sinkbinding_lifecycle_test.go b/pkg/apis/sources/v1/sinkbinding_lifecycle_test.go
index 6795e4e84ec..9dcc80c250c 100644
--- a/pkg/apis/sources/v1/sinkbinding_lifecycle_test.go
+++ b/pkg/apis/sources/v1/sinkbinding_lifecycle_test.go
@@ -172,6 +172,7 @@ func TestSinkBindingStatusIsReady(t *testing.T) {
s.MarkSink(sink)
s.MarkBindingAvailable()
s.MarkOIDCIdentityCreatedSucceeded()
+ s.MarkOIDCTokenSecretCreatedSuccceeded()
return s
}(),
want: true,
@@ -183,6 +184,7 @@ func TestSinkBindingStatusIsReady(t *testing.T) {
s.MarkSink(sink)
s.MarkBindingAvailable()
s.MarkOIDCIdentityCreatedSucceeded()
+ s.MarkOIDCTokenSecretCreatedSuccceeded()
return s
}(),
want: true,
@@ -194,6 +196,7 @@ func TestSinkBindingStatusIsReady(t *testing.T) {
s.MarkSink(sink)
s.MarkBindingAvailable()
s.MarkOIDCIdentityCreatedSucceededWithReason("TheReason", "feature is disabled")
+ s.MarkOIDCTokenSecretCreatedSuccceeded()
return s
}(),
want: true,
@@ -205,6 +208,31 @@ func TestSinkBindingStatusIsReady(t *testing.T) {
s.MarkSink(sink)
s.MarkBindingAvailable()
s.MarkOIDCIdentityCreatedFailed("TheReason", "this is a message")
+ s.MarkOIDCTokenSecretCreatedSuccceeded()
+ return s
+ }(),
+ want: false,
+ }, {
+ name: "mark OIDC token secret created",
+ s: func() *SinkBindingStatus {
+ s := &SinkBindingStatus{}
+ s.InitializeConditions()
+ s.MarkSink(sink)
+ s.MarkBindingAvailable()
+ s.MarkOIDCIdentityCreatedSucceeded()
+ s.MarkOIDCTokenSecretCreatedSuccceeded()
+ return s
+ }(),
+ want: true,
+ }, {
+ name: "mark OIDC token secret failed",
+ s: func() *SinkBindingStatus {
+ s := &SinkBindingStatus{}
+ s.InitializeConditions()
+ s.MarkSink(sink)
+ s.MarkBindingAvailable()
+ s.MarkOIDCIdentityCreatedSucceeded()
+ s.MarkOIDCTokenSecretCreatedFailed("Some", "reason")
return s
}(),
want: false,
@@ -276,6 +304,11 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "K_CE_OVERRIDES",
Value: `{"extensions":{"foo":"bar"}}`,
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }, {
+ Name: oidcTokenVolumeName,
+ }},
}},
Containers: []corev1.Container{{
Name: "blah",
@@ -296,6 +329,11 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "K_CE_OVERRIDES",
Value: `{"extensions":{"foo":"bar"}}`,
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }, {
+ Name: oidcTokenVolumeName,
+ }},
}, {
Name: "sidecar",
Image: "busybox",
@@ -312,6 +350,16 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "K_CE_OVERRIDES",
Value: `{"extensions":{"foo":"bar"}}`,
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }, {
+ Name: oidcTokenVolumeName,
+ }},
+ }},
+ Volumes: []corev1.Volume{{
+ Name: "foo",
+ }, {
+ Name: oidcTokenVolumeName,
}},
},
},
@@ -331,6 +379,9 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "BAZ",
Value: "INGA",
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }},
}},
Containers: []corev1.Container{{
Name: "blah",
@@ -342,6 +393,9 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "BAZ",
Value: "INGA",
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }},
}, {
Name: "sidecar",
Image: "busybox",
@@ -349,6 +403,12 @@ func TestSinkBindingUndo(t *testing.T) {
Name: "BAZ",
Value: "INGA",
}},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: "foo",
+ }},
+ }},
+ Volumes: []corev1.Volume{{
+ Name: "foo",
}},
},
},
@@ -382,9 +442,11 @@ func TestSinkBindingDo(t *testing.T) {
overrides := duckv1.CloudEventOverrides{Extensions: map[string]string{"foo": "bar"}}
tests := []struct {
- name string
- in *duckv1.WithPod
- want *duckv1.WithPod
+ name string
+ in *duckv1.WithPod
+ sbStatus *SinkBindingStatus
+ want *duckv1.WithPod
+ ctx context.Context
}{{
name: "nothing to add",
in: &duckv1.WithPod{
@@ -567,22 +629,132 @@ func TestSinkBindingDo(t *testing.T) {
},
},
},
+ }, {
+ name: "adds OIDC token volume",
+ in: &duckv1.WithPod{
+ Spec: duckv1.WithPodSpec{
+ Template: duckv1.PodSpecable{
+ Spec: corev1.PodSpec{
+ InitContainers: []corev1.Container{{
+ Name: "init",
+ Image: "busybox",
+ Env: []corev1.EnvVar{{
+ Name: "K_SINK",
+ Value: destination.URI.String(),
+ }, {
+ Name: "K_CA_CERTS",
+ Value: caCert,
+ }, {
+ Name: "K_CE_OVERRIDES",
+ Value: `{"extensions":{"foo":"bar"}}`,
+ }},
+ }},
+ Containers: []corev1.Container{{
+ Name: "blah",
+ Image: "busybox",
+ Env: []corev1.EnvVar{{
+ Name: "K_SINK",
+ Value: destination.URI.String(),
+ }, {
+ Name: "K_CA_CERTS",
+ Value: caCert,
+ }, {
+ Name: "K_CE_OVERRIDES",
+ Value: `{"extensions":{"foo":"bar"}}`,
+ }},
+ }},
+ },
+ },
+ },
+ },
+ sbStatus: &SinkBindingStatus{
+ OIDCTokenSecretName: pointer.String("oidc-token"),
+ },
+ want: &duckv1.WithPod{
+ Spec: duckv1.WithPodSpec{
+ Template: duckv1.PodSpecable{
+ Spec: corev1.PodSpec{
+ InitContainers: []corev1.Container{{
+ Name: "init",
+ Image: "busybox",
+ Env: []corev1.EnvVar{{
+ Name: "K_SINK",
+ Value: destination.URI.String(),
+ }, {
+ Name: "K_CA_CERTS",
+ Value: caCert,
+ }, {
+ Name: "K_CE_OVERRIDES",
+ Value: `{"extensions":{"foo":"bar"}}`,
+ }},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: oidcTokenVolumeName,
+ MountPath: "/oidc",
+ }},
+ }},
+ Containers: []corev1.Container{{
+ Name: "blah",
+ Image: "busybox",
+ Env: []corev1.EnvVar{{
+ Name: "K_SINK",
+ Value: destination.URI.String(),
+ }, {
+ Name: "K_CA_CERTS",
+ Value: caCert,
+ }, {
+ Name: "K_CE_OVERRIDES",
+ Value: `{"extensions":{"foo":"bar"}}`,
+ }},
+ VolumeMounts: []corev1.VolumeMount{{
+ Name: oidcTokenVolumeName,
+ MountPath: "/oidc",
+ }},
+ }},
+ Volumes: []corev1.Volume{{
+ Name: oidcTokenVolumeName,
+ VolumeSource: corev1.VolumeSource{
+ Projected: &corev1.ProjectedVolumeSource{
+ Sources: []corev1.VolumeProjection{{
+ Secret: &corev1.SecretProjection{
+ LocalObjectReference: corev1.LocalObjectReference{
+ Name: "oidc-token",
+ },
+ },
+ }},
+ },
+ },
+ }},
+ },
+ },
+ },
+ },
}}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
got := test.in
- ctx, _ := fakedynamicclient.With(context.Background(), scheme.Scheme, got)
- ctx = addressable.WithDuck(ctx)
- r := resolver.NewURIResolverFromTracker(ctx, tracker.New(func(types.NamespacedName) {}, 0))
- ctx = WithURIResolver(context.Background(), r)
+ applicationContext, _ := fakedynamicclient.With(context.Background(), scheme.Scheme, got)
+ applicationContext = addressable.WithDuck(applicationContext)
+ r := resolver.NewURIResolverFromTracker(applicationContext, tracker.New(func(types.NamespacedName) {}, 0))
+
+ ctx := context.Background()
+ if test.ctx != nil {
+ ctx = test.ctx
+ }
+ ctx = WithURIResolver(ctx, r)
+
+ sb := &SinkBinding{
+ Spec: SinkBindingSpec{
+ SourceSpec: duckv1.SourceSpec{
+ Sink: destination,
+ CloudEventOverrides: &overrides,
+ }},
+ }
+
+ if test.sbStatus != nil {
+ sb.Status = *test.sbStatus
+ }
- sb := &SinkBinding{Spec: SinkBindingSpec{
- SourceSpec: duckv1.SourceSpec{
- Sink: destination,
- CloudEventOverrides: &overrides,
- },
- }}
sb.Do(ctx, got)
if !cmp.Equal(got, test.want) {
diff --git a/pkg/apis/sources/v1/sinkbinding_types.go b/pkg/apis/sources/v1/sinkbinding_types.go
index 512d687d31a..e8afbc1451d 100644
--- a/pkg/apis/sources/v1/sinkbinding_types.go
+++ b/pkg/apis/sources/v1/sinkbinding_types.go
@@ -81,6 +81,10 @@ const (
// SinkBindingConditionOIDCIdentityCreated is configured to indicate whether
// the OIDC identity has been created for the sink.
SinkBindingConditionOIDCIdentityCreated apis.ConditionType = "OIDCIdentityCreated"
+
+ // SinkBindingConditionOIDCTokenSecretCreated is configured to indicate whether
+ // the secret containing the OIDC token has been created for the sink.
+ SinkBindingConditionOIDCTokenSecretCreated apis.ConditionType = "OIDCTokenSecretCreated"
)
// SinkBindingStatus communicates the observed state of the SinkBinding (from the controller).
@@ -93,6 +97,10 @@ type SinkBindingStatus struct {
// * SinkURI - the current active sink URI that has been configured for the
// Source.
duckv1.SourceStatus `json:",inline"`
+
+ // OIDCTokenSecretName is the name of the secret containing the token for
+ // this SinkBindings OIDC authentication
+ OIDCTokenSecretName *string `json:"oidcTokenSecretName,omitempty"`
}
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
diff --git a/pkg/apis/sources/v1/zz_generated.deepcopy.go b/pkg/apis/sources/v1/zz_generated.deepcopy.go
index 551322eab40..6d175e3c960 100644
--- a/pkg/apis/sources/v1/zz_generated.deepcopy.go
+++ b/pkg/apis/sources/v1/zz_generated.deepcopy.go
@@ -454,6 +454,11 @@ func (in *SinkBindingSpec) DeepCopy() *SinkBindingSpec {
func (in *SinkBindingStatus) DeepCopyInto(out *SinkBindingStatus) {
*out = *in
in.SourceStatus.DeepCopyInto(&out.SourceStatus)
+ if in.OIDCTokenSecretName != nil {
+ in, out := &in.OIDCTokenSecretName, &out.OIDCTokenSecretName
+ *out = new(string)
+ **out = **in
+ }
return
}
diff --git a/pkg/auth/token_provider.go b/pkg/auth/token_provider.go
index d35a6e29f71..6fb115f9d4e 100644
--- a/pkg/auth/token_provider.go
+++ b/pkg/auth/token_provider.go
@@ -27,11 +27,13 @@ import (
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/cache"
"k8s.io/client-go/kubernetes"
+ "k8s.io/utils/pointer"
kubeclient "knative.dev/pkg/client/injection/kube/client"
"knative.dev/pkg/logging"
)
const (
+ TokenExpirationTime = time.Hour
expirationBufferTime = 5 * time.Minute
)
@@ -58,9 +60,16 @@ func (c *OIDCTokenProvider) GetJWT(serviceAccount types.NamespacedName, audience
}
// if not found in cache: request new token
+ return c.GetNewJWT(serviceAccount, audience)
+}
+
+// GetNewJWT returns a new JWT from the given service account for the given audience without using the token cache.
+func (c *OIDCTokenProvider) GetNewJWT(serviceAccount types.NamespacedName, audience string) (string, error) {
+ // request new token
tokenRequest := authv1.TokenRequest{
Spec: authv1.TokenRequestSpec{
- Audiences: []string{audience},
+ Audiences: []string{audience},
+ ExpirationSeconds: pointer.Int64(int64(TokenExpirationTime.Seconds())),
},
}
diff --git a/pkg/auth/utils.go b/pkg/auth/utils.go
index 0f52c34364f..81e7145a8f2 100644
--- a/pkg/auth/utils.go
+++ b/pkg/auth/utils.go
@@ -20,6 +20,9 @@ import (
"fmt"
"net/http"
"strings"
+ "time"
+
+ "github.com/go-jose/go-jose/v3/jwt"
)
const (
@@ -40,3 +43,22 @@ func GetJWTFromHeader(header http.Header) string {
func SetAuthHeader(jwt string, header http.Header) {
header.Set(AuthHeaderKey, fmt.Sprintf("Bearer %s", jwt))
}
+
+// GetJWTExpiry returns the expiry time of the token in UTC
+func GetJWTExpiry(token string) (time.Time, error) {
+ t, err := jwt.ParseSigned(token)
+ if err != nil {
+ return time.Time{}, err
+ }
+
+ var claims jwt.Claims
+ if err := t.UnsafeClaimsWithoutVerification(&claims); err != nil {
+ return time.Time{}, err
+ }
+
+ if claims.Expiry == nil {
+ return time.Time{}, fmt.Errorf("no expiry set in JWT")
+ }
+
+ return claims.Expiry.Time(), nil
+}
diff --git a/pkg/auth/utils_test.go b/pkg/auth/utils_test.go
new file mode 100644
index 00000000000..fa6eaf852b5
--- /dev/null
+++ b/pkg/auth/utils_test.go
@@ -0,0 +1,55 @@
+/*
+Copyright 2023 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package auth
+
+import (
+ "reflect"
+ "testing"
+ "time"
+)
+
+func TestGetJWTExpiry(t *testing.T) {
+ tests := []struct {
+ name string
+ token string
+ want time.Time
+ wantErr bool
+ }{
+ {
+ name: "Valid JWT",
+ token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteS1pc3N1ZXIiLCJpYXQiOjE1Nzc5MzA2NDUsImV4cCI6MTU3NzkzNDI0NSwiYXVkIjoibXktYXVkaWVuY2UiLCJzdWIiOiJzdWJqZWN0QGV4YW1wbGUuY29tIn0.Hl8n6Ipt0X0gI46QLPZtpESRtc7cQ75AqXNal0sQ2a4",
+ want: time.Date(2020, 1, 2, 3, 4, 5, 0, time.UTC),
+ wantErr: false,
+ }, {
+ name: "No valid JWT",
+ token: "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJteS1pc3N1ZXIiLCJpYXQiOjE1Nzc5MzA2NDUsImV4cCI6MTU3NzkzNDI0NSwiYXVkIjoibXktYXVkaWVuY2UiLCJzdWIiOiJzdWJqZWN0QGV4YW1wbGUuY29.Hl8n6Ipt0X0gI46QLPZtpESRtc7cQ75AqXNal0sQ2a4",
+ wantErr: true,
+ },
+ }
+ for _, tt := range tests {
+ t.Run(tt.name, func(t *testing.T) {
+ got, err := GetJWTExpiry(tt.token)
+ if (err != nil) != tt.wantErr {
+ t.Errorf("GetJWTExpiry() error = %v, wantErr %v", err, tt.wantErr)
+ return
+ }
+ if !reflect.DeepEqual(got.UTC(), tt.want.UTC()) {
+ t.Errorf("GetJWTExpiry() = %v, want %v", got.UTC(), tt.want.UTC())
+ }
+ })
+ }
+}
diff --git a/pkg/reconciler/sinkbinding/controller.go b/pkg/reconciler/sinkbinding/controller.go
index dab1680c477..2113fb2a07e 100644
--- a/pkg/reconciler/sinkbinding/controller.go
+++ b/pkg/reconciler/sinkbinding/controller.go
@@ -18,8 +18,7 @@ package sinkbinding
import (
"context"
- "errors"
- "fmt"
+ "time"
"knative.dev/eventing/pkg/auth"
sbinformer "knative.dev/eventing/pkg/client/injection/informers/sources/v1/sinkbinding"
@@ -32,17 +31,15 @@ import (
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/watch"
- "k8s.io/client-go/kubernetes"
"k8s.io/client-go/kubernetes/scheme"
typedcorev1 "k8s.io/client-go/kubernetes/typed/core/v1"
- corev1listers "k8s.io/client-go/listers/core/v1"
"k8s.io/client-go/tools/cache"
"k8s.io/client-go/tools/record"
"knative.dev/eventing/pkg/apis/feature"
v1 "knative.dev/eventing/pkg/apis/sources/v1"
"knative.dev/pkg/apis/duck"
- duckv1 "knative.dev/pkg/apis/duck/v1"
kubeclient "knative.dev/pkg/client/injection/kube/client"
+ secretinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/secret"
serviceaccountinformer "knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount"
"knative.dev/pkg/configmap"
"knative.dev/pkg/controller"
@@ -54,15 +51,14 @@ import (
const (
controllerAgentName = "sinkbinding-controller"
-)
-type SinkBindingSubResourcesReconciler struct {
- res *resolver.URIResolver
- tracker tracker.Interface
- serviceAccountLister corev1listers.ServiceAccountLister
- kubeclient kubernetes.Interface
- featureStore *feature.Store
-}
+ // resyncPeriod defines the period in which SinkBindings will be reenqued
+ // (e.g. to check the validity of their OIDC token secret)
+ resyncPeriod = auth.TokenExpirationTime / 2
+ // tokenExpiryBuffer defines an additional buffer for the expiry of OIDC
+ // token secrets
+ tokenExpiryBuffer = 5 * time.Minute
+)
// NewController returns a new SinkBinding reconciler.
func NewController(
@@ -76,6 +72,17 @@ func NewController(
psInformerFactory := podspecable.Get(ctx)
namespaceInformer := namespace.Get(ctx)
serviceaccountInformer := serviceaccountinformer.Get(ctx)
+ secretInformer := secretinformer.Get(ctx)
+
+ var globalResync func()
+ featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store"), func(name string, value interface{}) {
+ logger.Infof("feature config changed. name: %s, value: %v", name, value)
+
+ if globalResync != nil {
+ globalResync()
+ }
+ })
+ featureStore.WatchConfigs(cmw)
c := &psbinding.BaseReconciler{
LeaderAwareFuncs: reconciler.LeaderAwareFuncs{
@@ -106,11 +113,9 @@ func NewController(
Logger: logger,
})
- featureStore := feature.NewStore(logging.FromContext(ctx).Named("feature-config-store"), func(name string, value interface{}) {
+ globalResync = func() {
impl.GlobalResync(sbInformer.Informer())
- })
-
- featureStore.WatchConfigs(cmw)
+ }
sbInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
namespaceInformer.Informer().AddEventHandler(controller.HandleAll(impl.Enqueue))
@@ -121,7 +126,9 @@ func NewController(
tracker: impl.Tracker,
kubeclient: kubeclient.Get(ctx),
serviceAccountLister: serviceaccountInformer.Lister(),
+ secretLister: secretInformer.Lister(),
featureStore: featureStore,
+ tokenProvider: auth.NewOIDCTokenProvider(ctx),
}
c.WithContext = func(ctx context.Context, b psbinding.Bindable) (context.Context, error) {
@@ -141,9 +148,29 @@ func NewController(
Handler: controller.HandleAll(impl.EnqueueControllerOf),
})
+ // do a periodic reync of all sinkbindings to renew the token secrets eventually
+ go periodicResync(ctx, globalResync)
+
return impl
}
+func periodicResync(ctx context.Context, globalResyncFunc func()) {
+ ticker := time.NewTicker(resyncPeriod)
+ logger := logging.FromContext(ctx)
+
+ logger.Infof("Starting global resync of SinkBindings every %s", resyncPeriod)
+ for {
+ select {
+ case <-ticker.C:
+ logger.Debug("Triggering global resync of SinkBindings")
+ globalResyncFunc()
+ case <-ctx.Done():
+ logger.Debug("Context finished. Stopping periodic resync of SinkBindings")
+ return
+ }
+ }
+}
+
func ListAll(ctx context.Context, handler cache.ResourceEventHandler) psbinding.ListAll {
fbInformer := sbinformer.Get(ctx)
@@ -172,55 +199,6 @@ func WithContextFactory(ctx context.Context, handler func(types.NamespacedName))
}
}
-func (s *SinkBindingSubResourcesReconciler) Reconcile(ctx context.Context, b psbinding.Bindable) error {
- sb := b.(*v1.SinkBinding)
- if s.res == nil {
- err := errors.New("Resolver is nil")
- logging.FromContext(ctx).Errorf("%w", err)
- sb.Status.MarkBindingUnavailable("NoResolver", "No Resolver associated with context for sink")
- return err
- }
- if sb.Spec.Sink.Ref != nil {
- s.tracker.TrackReference(tracker.Reference{
- APIVersion: sb.Spec.Sink.Ref.APIVersion,
- Kind: sb.Spec.Sink.Ref.Kind,
- Namespace: sb.Spec.Sink.Ref.Namespace,
- Name: sb.Spec.Sink.Ref.Name,
- }, b)
- }
-
- featureFlags := s.featureStore.Load()
- if featureFlags.IsOIDCAuthentication() {
- saName := auth.GetOIDCServiceAccountNameForResource(v1.SchemeGroupVersion.WithKind("SinkBinding"), sb.ObjectMeta)
- sb.Status.Auth = &duckv1.AuthStatus{
- ServiceAccountName: &saName,
- }
-
- if err := auth.EnsureOIDCServiceAccountExistsForResource(ctx, s.serviceAccountLister, s.kubeclient, v1.SchemeGroupVersion.WithKind("SinkBinding"), sb.ObjectMeta); err != nil {
- sb.Status.MarkOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", "%v", err)
- return err
- }
- sb.Status.MarkOIDCIdentityCreatedSucceeded()
- } else {
- sb.Status.Auth = nil
- sb.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "")
- }
-
- addr, err := s.res.AddressableFromDestinationV1(ctx, sb.Spec.Sink, sb)
- if err != nil {
- logging.FromContext(ctx).Errorf("Failed to get Addressable from Destination: %w", err)
- sb.Status.MarkBindingUnavailable("NoAddressable", "Addressable could not be extracted from destination")
- return err
- }
- sb.Status.MarkSink(addr)
- return nil
-}
-
-// I'm just here so I won't get fined
-func (*SinkBindingSubResourcesReconciler) ReconcileDeletion(ctx context.Context, b psbinding.Bindable) error {
- return nil
-}
-
func createRecorder(ctx context.Context, agentName string) record.EventRecorder {
logger := logging.FromContext(ctx)
diff --git a/pkg/reconciler/sinkbinding/sinkbinding.go b/pkg/reconciler/sinkbinding/sinkbinding.go
new file mode 100644
index 00000000000..43645cf625c
--- /dev/null
+++ b/pkg/reconciler/sinkbinding/sinkbinding.go
@@ -0,0 +1,220 @@
+/*
+Copyright 2020 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package sinkbinding
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "time"
+
+ "knative.dev/eventing/pkg/auth"
+ "knative.dev/pkg/kmeta"
+ "knative.dev/pkg/resolver"
+
+ corev1 "k8s.io/api/core/v1"
+ apierrs "k8s.io/apimachinery/pkg/api/errors"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/types"
+ applyconfigurationcorev1 "k8s.io/client-go/applyconfigurations/core/v1"
+ applyconfigurationmetav1 "k8s.io/client-go/applyconfigurations/meta/v1"
+ "k8s.io/client-go/kubernetes"
+ corev1listers "k8s.io/client-go/listers/core/v1"
+ "k8s.io/utils/pointer"
+ "knative.dev/eventing/pkg/apis/feature"
+ v1 "knative.dev/eventing/pkg/apis/sources/v1"
+ duckv1 "knative.dev/pkg/apis/duck/v1"
+ "knative.dev/pkg/logging"
+ "knative.dev/pkg/tracker"
+ "knative.dev/pkg/webhook/psbinding"
+)
+
+type SinkBindingSubResourcesReconciler struct {
+ res *resolver.URIResolver
+ tracker tracker.Interface
+ serviceAccountLister corev1listers.ServiceAccountLister
+ secretLister corev1listers.SecretLister
+ kubeclient kubernetes.Interface
+ featureStore *feature.Store
+ tokenProvider *auth.OIDCTokenProvider
+}
+
+func (s *SinkBindingSubResourcesReconciler) Reconcile(ctx context.Context, b psbinding.Bindable) error {
+ sb := b.(*v1.SinkBinding)
+ if s.res == nil {
+ err := errors.New("Resolver is nil")
+ logging.FromContext(ctx).Errorf("%w", err)
+ sb.Status.MarkBindingUnavailable("NoResolver", "No Resolver associated with context for sink")
+ return err
+ }
+ if sb.Spec.Sink.Ref != nil {
+ s.tracker.TrackReference(tracker.Reference{
+ APIVersion: sb.Spec.Sink.Ref.APIVersion,
+ Kind: sb.Spec.Sink.Ref.Kind,
+ Namespace: sb.Spec.Sink.Ref.Namespace,
+ Name: sb.Spec.Sink.Ref.Name,
+ }, b)
+ }
+
+ addr, err := s.res.AddressableFromDestinationV1(ctx, sb.Spec.Sink, sb)
+ if err != nil {
+ logging.FromContext(ctx).Errorf("Failed to get Addressable from Destination: %w", err)
+ sb.Status.MarkBindingUnavailable("NoAddressable", "Addressable could not be extracted from destination")
+ return err
+ }
+ sb.Status.MarkSink(addr)
+
+ featureFlags := s.featureStore.Load()
+ if featureFlags.IsOIDCAuthentication() {
+ saName := auth.GetOIDCServiceAccountNameForResource(v1.SchemeGroupVersion.WithKind("SinkBinding"), sb.ObjectMeta)
+ sb.Status.Auth = &duckv1.AuthStatus{
+ ServiceAccountName: &saName,
+ }
+
+ if err := auth.EnsureOIDCServiceAccountExistsForResource(ctx, s.serviceAccountLister, s.kubeclient, v1.SchemeGroupVersion.WithKind("SinkBinding"), sb.ObjectMeta); err != nil {
+ sb.Status.MarkOIDCIdentityCreatedFailed("Unable to resolve service account for OIDC authentication", "%v", err)
+ return err
+ }
+ sb.Status.MarkOIDCIdentityCreatedSucceeded()
+
+ err := s.reconcileOIDCTokenSecret(ctx, sb)
+ if err != nil {
+ sb.Status.MarkOIDCTokenSecretCreatedFailed("Unable to reconcile OIDC token secret", "%v", err)
+ return err
+ }
+ sb.Status.MarkOIDCTokenSecretCreatedSuccceeded()
+
+ } else {
+ sb.Status.Auth = nil
+ sb.Status.MarkOIDCIdentityCreatedSucceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "")
+ sb.Status.MarkOIDCTokenSecretCreatedSuccceededWithReason(fmt.Sprintf("%s feature disabled", feature.OIDCAuthentication), "")
+
+ if err := s.removeOIDCTokenSecretEventually(ctx, sb); err != nil {
+ return err
+ }
+ sb.Status.OIDCTokenSecretName = nil
+ }
+
+ return nil
+}
+
+// I'm just here so I won't get fined
+func (*SinkBindingSubResourcesReconciler) ReconcileDeletion(ctx context.Context, b psbinding.Bindable) error {
+ return nil
+}
+
+func (s *SinkBindingSubResourcesReconciler) reconcileOIDCTokenSecret(ctx context.Context, sb *v1.SinkBinding) error {
+ logger := logging.FromContext(ctx)
+ secretName := s.oidcTokenSecretName(sb)
+
+ if sb.Status.SinkAudience == nil {
+ return fmt.Errorf("sinkAudience must be set on %s/%s to generate a OIDC token secret", sb.Name, sb.Namespace)
+ }
+
+ secret, err := s.secretLister.Secrets(sb.Namespace).Get(secretName)
+ if err != nil {
+ if apierrs.IsNotFound(err) {
+ // create new secret
+ logger.Debugf("No OIDC token secret found for %s/%s sinkbinding. Will create a new secret", sb.Name, sb.Namespace)
+
+ return s.renewOIDCTokenSecret(ctx, sb)
+ }
+
+ return fmt.Errorf("could not check if secret %q exists already: %w", secretName, err)
+ }
+
+ // check if token needs to be renewed
+ expiry, err := auth.GetJWTExpiry(string(secret.Data["token"]))
+ if err != nil {
+ logger.Warnf("Could not get expiry date of OIDC token secret: %s. Will renew token.", err)
+
+ return s.renewOIDCTokenSecret(ctx, sb)
+ }
+
+ resyncAndBufferDuration := resyncPeriod + tokenExpiryBuffer
+ if expiry.After(time.Now().Add(resyncAndBufferDuration)) {
+ logger.Debugf("OIDC token secret for %s/%s sinkbinding still valid for > %s (expires %s). Will not update secret", sb.Name, sb.Namespace, resyncAndBufferDuration, expiry)
+ // token is still valid for resync period + buffer --> we're fine
+
+ return nil
+ }
+
+ logger.Debugf("OIDC token secret for %s/%s sinkbinding is valid for less than %s (expires %s). Will update secret", sb.Name, sb.Namespace, resyncAndBufferDuration, expiry)
+
+ return s.renewOIDCTokenSecret(ctx, sb)
+}
+
+func (s *SinkBindingSubResourcesReconciler) renewOIDCTokenSecret(ctx context.Context, sb *v1.SinkBinding) error {
+ logger := logging.FromContext(ctx)
+ secretName := s.oidcTokenSecretName(sb)
+
+ token, err := s.tokenProvider.GetNewJWT(types.NamespacedName{
+ Namespace: sb.Namespace,
+ Name: *sb.Status.Auth.ServiceAccountName,
+ }, *sb.Status.SinkAudience)
+
+ if err != nil {
+ return fmt.Errorf("could not create token for SinkBinding %s/%s: %w", sb.Name, sb.Namespace, err)
+ }
+
+ apiVersion := fmt.Sprintf("%s/%s", v1.SchemeGroupVersion.Group, v1.SchemeGroupVersion.Version)
+ applyConfig := new(applyconfigurationcorev1.SecretApplyConfiguration).
+ WithName(secretName).
+ WithNamespace(sb.Namespace).
+ WithType(corev1.SecretTypeOpaque).
+ WithKind("Secret").
+ WithAPIVersion("v1").
+ WithOwnerReferences(&applyconfigurationmetav1.OwnerReferenceApplyConfiguration{
+ APIVersion: &apiVersion,
+ Kind: pointer.String("SinkBinding"),
+ Name: &sb.Name,
+ UID: &sb.UID,
+ Controller: pointer.Bool(true),
+ BlockOwnerDeletion: pointer.Bool(false),
+ }).
+ WithStringData(map[string]string{
+ "token": token,
+ })
+
+ _, err = s.kubeclient.CoreV1().Secrets(sb.Namespace).Apply(ctx, applyConfig, metav1.ApplyOptions{FieldManager: controllerAgentName})
+ if err != nil {
+ return fmt.Errorf("could not create or update OIDC token secret for SinkBinding %s/%s: %w", sb.Name, sb.Namespace, err)
+ }
+
+ logger.Debugf("Created/Updated OIDC token secret for %s/%s sinkbinding with new token.", sb.Name, sb.Namespace)
+
+ sb.Status.OIDCTokenSecretName = &secretName
+
+ return nil
+}
+
+func (s *SinkBindingSubResourcesReconciler) oidcTokenSecretName(sb *v1.SinkBinding) string {
+ return kmeta.ChildName(sb.Name, "-oidc-token")
+}
+
+func (s *SinkBindingSubResourcesReconciler) removeOIDCTokenSecretEventually(ctx context.Context, sb *v1.SinkBinding) error {
+ if sb.Status.OIDCTokenSecretName == nil {
+ return nil
+ }
+
+ _, err := s.secretLister.Secrets(sb.Namespace).Get(*sb.Status.OIDCTokenSecretName)
+ if apierrs.IsNotFound(err) {
+ return nil
+ }
+
+ return s.kubeclient.CoreV1().Secrets(sb.Namespace).Delete(ctx, *sb.Status.OIDCTokenSecretName, metav1.DeleteOptions{})
+}
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go b/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go
new file mode 100644
index 00000000000..7df270cc39d
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/builder.go
@@ -0,0 +1,334 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "bytes"
+ "reflect"
+
+ "github.com/go-jose/go-jose/v3/json"
+
+ "github.com/go-jose/go-jose/v3"
+)
+
+// Builder is a utility for making JSON Web Tokens. Calls can be chained, and
+// errors are accumulated until the final call to CompactSerialize/FullSerialize.
+type Builder interface {
+ // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
+ // into single JSON object. If you are passing private claims, make sure to set
+ // struct field tags to specify the name for the JSON key to be used when
+ // serializing.
+ Claims(i interface{}) Builder
+ // Token builds a JSONWebToken from provided data.
+ Token() (*JSONWebToken, error)
+ // FullSerialize serializes a token using the JWS/JWE JSON Serialization format.
+ FullSerialize() (string, error)
+ // CompactSerialize serializes a token using the compact serialization format.
+ CompactSerialize() (string, error)
+}
+
+// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
+// Calls can be chained, and errors are accumulated until final call to
+// CompactSerialize/FullSerialize.
+type NestedBuilder interface {
+ // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
+ // into single JSON object. If you are passing private claims, make sure to set
+ // struct field tags to specify the name for the JSON key to be used when
+ // serializing.
+ Claims(i interface{}) NestedBuilder
+ // Token builds a NestedJSONWebToken from provided data.
+ Token() (*NestedJSONWebToken, error)
+ // FullSerialize serializes a token using the JSON Serialization format.
+ FullSerialize() (string, error)
+ // CompactSerialize serializes a token using the compact serialization format.
+ CompactSerialize() (string, error)
+}
+
+type builder struct {
+ payload map[string]interface{}
+ err error
+}
+
+type signedBuilder struct {
+ builder
+ sig jose.Signer
+}
+
+type encryptedBuilder struct {
+ builder
+ enc jose.Encrypter
+}
+
+type nestedBuilder struct {
+ builder
+ sig jose.Signer
+ enc jose.Encrypter
+}
+
+// Signed creates builder for signed tokens.
+func Signed(sig jose.Signer) Builder {
+ return &signedBuilder{
+ sig: sig,
+ }
+}
+
+// Encrypted creates builder for encrypted tokens.
+func Encrypted(enc jose.Encrypter) Builder {
+ return &encryptedBuilder{
+ enc: enc,
+ }
+}
+
+// SignedAndEncrypted creates builder for signed-then-encrypted tokens.
+// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.
+func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder {
+ if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" {
+ return &nestedBuilder{
+ builder: builder{
+ err: ErrInvalidContentType,
+ },
+ }
+ }
+ return &nestedBuilder{
+ sig: sig,
+ enc: enc,
+ }
+}
+
+func (b builder) claims(i interface{}) builder {
+ if b.err != nil {
+ return b
+ }
+
+ m, ok := i.(map[string]interface{})
+ switch {
+ case ok:
+ return b.merge(m)
+ case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct:
+ m, err := normalize(i)
+ if err != nil {
+ return builder{
+ err: err,
+ }
+ }
+ return b.merge(m)
+ default:
+ return builder{
+ err: ErrInvalidClaims,
+ }
+ }
+}
+
+func normalize(i interface{}) (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+
+ raw, err := json.Marshal(i)
+ if err != nil {
+ return nil, err
+ }
+
+ d := json.NewDecoder(bytes.NewReader(raw))
+ d.SetNumberType(json.UnmarshalJSONNumber)
+
+ if err := d.Decode(&m); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+func (b *builder) merge(m map[string]interface{}) builder {
+ p := make(map[string]interface{})
+ for k, v := range b.payload {
+ p[k] = v
+ }
+ for k, v := range m {
+ p[k] = v
+ }
+
+ return builder{
+ payload: p,
+ }
+}
+
+func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) {
+ return &JSONWebToken{
+ payload: p,
+ Headers: h,
+ }, nil
+}
+
+func (b *signedBuilder) Claims(i interface{}) Builder {
+ return &signedBuilder{
+ builder: b.builder.claims(i),
+ sig: b.sig,
+ }
+}
+
+func (b *signedBuilder) Token() (*JSONWebToken, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return nil, err
+ }
+
+ h := make([]jose.Header, len(sig.Signatures))
+ for i, v := range sig.Signatures {
+ h[i] = v.Header
+ }
+
+ return b.builder.token(sig.Verify, h)
+}
+
+func (b *signedBuilder) CompactSerialize() (string, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return "", err
+ }
+
+ return sig.CompactSerialize()
+}
+
+func (b *signedBuilder) FullSerialize() (string, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return "", err
+ }
+
+ return sig.FullSerialize(), nil
+}
+
+func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ return b.sig.Sign(p)
+}
+
+func (b *encryptedBuilder) Claims(i interface{}) Builder {
+ return &encryptedBuilder{
+ builder: b.builder.claims(i),
+ enc: b.enc,
+ }
+}
+
+func (b *encryptedBuilder) CompactSerialize() (string, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.CompactSerialize()
+}
+
+func (b *encryptedBuilder) FullSerialize() (string, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.FullSerialize(), nil
+}
+
+func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return nil, err
+ }
+
+ return b.builder.token(enc.Decrypt, []jose.Header{enc.Header})
+}
+
+func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ return b.enc.Encrypt(p)
+}
+
+func (b *nestedBuilder) Claims(i interface{}) NestedBuilder {
+ return &nestedBuilder{
+ builder: b.builder.claims(i),
+ sig: b.sig,
+ enc: b.enc,
+ }
+}
+
+func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return nil, err
+ }
+
+ return &NestedJSONWebToken{
+ enc: enc,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
+
+func (b *nestedBuilder) CompactSerialize() (string, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.CompactSerialize()
+}
+
+func (b *nestedBuilder) FullSerialize() (string, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.FullSerialize(), nil
+}
+
+func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ sig, err := b.sig.Sign(p)
+ if err != nil {
+ return nil, err
+ }
+
+ p2, err := sig.CompactSerialize()
+ if err != nil {
+ return nil, err
+ }
+
+ return b.enc.Encrypt([]byte(p2))
+}
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go b/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go
new file mode 100644
index 00000000000..286be1d2fe9
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/claims.go
@@ -0,0 +1,130 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "strconv"
+ "time"
+
+ "github.com/go-jose/go-jose/v3/json"
+)
+
+// Claims represents public claim values (as specified in RFC 7519).
+type Claims struct {
+ Issuer string `json:"iss,omitempty"`
+ Subject string `json:"sub,omitempty"`
+ Audience Audience `json:"aud,omitempty"`
+ Expiry *NumericDate `json:"exp,omitempty"`
+ NotBefore *NumericDate `json:"nbf,omitempty"`
+ IssuedAt *NumericDate `json:"iat,omitempty"`
+ ID string `json:"jti,omitempty"`
+}
+
+// NumericDate represents date and time as the number of seconds since the
+// epoch, ignoring leap seconds. Non-integer values can be represented
+// in the serialized format, but we round to the nearest second.
+// See RFC7519 Section 2: https://tools.ietf.org/html/rfc7519#section-2
+type NumericDate int64
+
+// NewNumericDate constructs NumericDate from time.Time value.
+func NewNumericDate(t time.Time) *NumericDate {
+ if t.IsZero() {
+ return nil
+ }
+
+ // While RFC 7519 technically states that NumericDate values may be
+ // non-integer values, we don't bother serializing timestamps in
+ // claims with sub-second accurancy and just round to the nearest
+ // second instead. Not convined sub-second accuracy is useful here.
+ out := NumericDate(t.Unix())
+ return &out
+}
+
+// MarshalJSON serializes the given NumericDate into its JSON representation.
+func (n NumericDate) MarshalJSON() ([]byte, error) {
+ return []byte(strconv.FormatInt(int64(n), 10)), nil
+}
+
+// UnmarshalJSON reads a date from its JSON representation.
+func (n *NumericDate) UnmarshalJSON(b []byte) error {
+ s := string(b)
+
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return ErrUnmarshalNumericDate
+ }
+
+ *n = NumericDate(f)
+ return nil
+}
+
+// Time returns time.Time representation of NumericDate.
+func (n *NumericDate) Time() time.Time {
+ if n == nil {
+ return time.Time{}
+ }
+ return time.Unix(int64(*n), 0)
+}
+
+// Audience represents the recipients that the token is intended for.
+type Audience []string
+
+// UnmarshalJSON reads an audience from its JSON representation.
+func (s *Audience) UnmarshalJSON(b []byte) error {
+ var v interface{}
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ switch v := v.(type) {
+ case string:
+ *s = []string{v}
+ case []interface{}:
+ a := make([]string, len(v))
+ for i, e := range v {
+ s, ok := e.(string)
+ if !ok {
+ return ErrUnmarshalAudience
+ }
+ a[i] = s
+ }
+ *s = a
+ default:
+ return ErrUnmarshalAudience
+ }
+
+ return nil
+}
+
+// MarshalJSON converts audience to json representation.
+func (s Audience) MarshalJSON() ([]byte, error) {
+ if len(s) == 1 {
+ return json.Marshal(s[0])
+ }
+ return json.Marshal([]string(s))
+}
+
+//Contains checks whether a given string is included in the Audience
+func (s Audience) Contains(v string) bool {
+ for _, a := range s {
+ if a == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go b/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go
new file mode 100644
index 00000000000..4cf97b54e78
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/doc.go
@@ -0,0 +1,22 @@
+/*-
+ * Copyright 2017 Square Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+
+Package jwt provides an implementation of the JSON Web Token standard.
+
+*/
+package jwt
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go b/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go
new file mode 100644
index 00000000000..27388e5449a
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/errors.go
@@ -0,0 +1,53 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import "errors"
+
+// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled.
+var ErrUnmarshalAudience = errors.New("go-jose/go-jose/jwt: expected string or array value to unmarshal to Audience")
+
+// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled.
+var ErrUnmarshalNumericDate = errors.New("go-jose/go-jose/jwt: expected number value to unmarshal NumericDate")
+
+// ErrInvalidClaims indicates that given claims have invalid type.
+var ErrInvalidClaims = errors.New("go-jose/go-jose/jwt: expected claims to be value convertible into JSON object")
+
+// ErrInvalidIssuer indicates invalid iss claim.
+var ErrInvalidIssuer = errors.New("go-jose/go-jose/jwt: validation failed, invalid issuer claim (iss)")
+
+// ErrInvalidSubject indicates invalid sub claim.
+var ErrInvalidSubject = errors.New("go-jose/go-jose/jwt: validation failed, invalid subject claim (sub)")
+
+// ErrInvalidAudience indicated invalid aud claim.
+var ErrInvalidAudience = errors.New("go-jose/go-jose/jwt: validation failed, invalid audience claim (aud)")
+
+// ErrInvalidID indicates invalid jti claim.
+var ErrInvalidID = errors.New("go-jose/go-jose/jwt: validation failed, invalid ID claim (jti)")
+
+// ErrNotValidYet indicates that token is used before time indicated in nbf claim.
+var ErrNotValidYet = errors.New("go-jose/go-jose/jwt: validation failed, token not valid yet (nbf)")
+
+// ErrExpired indicates that token is used after expiry time indicated in exp claim.
+var ErrExpired = errors.New("go-jose/go-jose/jwt: validation failed, token is expired (exp)")
+
+// ErrIssuedInTheFuture indicates that the iat field is in the future.
+var ErrIssuedInTheFuture = errors.New("go-jose/go-jose/jwt: validation field, token issued in the future (iat)")
+
+// ErrInvalidContentType indicates that token requires JWT cty header.
+var ErrInvalidContentType = errors.New("go-jose/go-jose/jwt: expected content type to be JWT (cty header)")
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go b/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go
new file mode 100644
index 00000000000..8553fc50b08
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/jwt.go
@@ -0,0 +1,133 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "fmt"
+ "strings"
+
+ jose "github.com/go-jose/go-jose/v3"
+ "github.com/go-jose/go-jose/v3/json"
+)
+
+// JSONWebToken represents a JSON Web Token (as specified in RFC7519).
+type JSONWebToken struct {
+ payload func(k interface{}) ([]byte, error)
+ unverifiedPayload func() []byte
+ Headers []jose.Header
+}
+
+type NestedJSONWebToken struct {
+ enc *jose.JSONWebEncryption
+ Headers []jose.Header
+}
+
+// Claims deserializes a JSONWebToken into dest using the provided key.
+func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error {
+ b, err := t.payload(key)
+ if err != nil {
+ return err
+ }
+
+ for _, d := range dest {
+ if err := json.Unmarshal(b, d); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+// UnsafeClaimsWithoutVerification deserializes the claims of a
+// JSONWebToken into the dests. For signed JWTs, the claims are not
+// verified. This function won't work for encrypted JWTs.
+func (t *JSONWebToken) UnsafeClaimsWithoutVerification(dest ...interface{}) error {
+ if t.unverifiedPayload == nil {
+ return fmt.Errorf("go-jose/go-jose: Cannot get unverified claims")
+ }
+ claims := t.unverifiedPayload()
+ for _, d := range dest {
+ if err := json.Unmarshal(claims, d); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) {
+ b, err := t.enc.Decrypt(decryptionKey)
+ if err != nil {
+ return nil, err
+ }
+
+ sig, err := ParseSigned(string(b))
+ if err != nil {
+ return nil, err
+ }
+
+ return sig, nil
+}
+
+// ParseSigned parses token from JWS form.
+func ParseSigned(s string) (*JSONWebToken, error) {
+ sig, err := jose.ParseSigned(s)
+ if err != nil {
+ return nil, err
+ }
+ headers := make([]jose.Header, len(sig.Signatures))
+ for i, signature := range sig.Signatures {
+ headers[i] = signature.Header
+ }
+
+ return &JSONWebToken{
+ payload: sig.Verify,
+ unverifiedPayload: sig.UnsafePayloadWithoutVerification,
+ Headers: headers,
+ }, nil
+}
+
+// ParseEncrypted parses token from JWE form.
+func ParseEncrypted(s string) (*JSONWebToken, error) {
+ enc, err := jose.ParseEncrypted(s)
+ if err != nil {
+ return nil, err
+ }
+
+ return &JSONWebToken{
+ payload: enc.Decrypt,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
+
+// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.
+func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) {
+ enc, err := jose.ParseEncrypted(s)
+ if err != nil {
+ return nil, err
+ }
+
+ contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string)
+ if strings.ToUpper(contentType) != "JWT" {
+ return nil, ErrInvalidContentType
+ }
+
+ return &NestedJSONWebToken{
+ enc: enc,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
diff --git a/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go b/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go
new file mode 100644
index 00000000000..09d8541f4ce
--- /dev/null
+++ b/vendor/github.com/go-jose/go-jose/v3/jwt/validation.go
@@ -0,0 +1,120 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import "time"
+
+const (
+ // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims.
+ DefaultLeeway = 1.0 * time.Minute
+)
+
+// Expected defines values used for protected claims validation.
+// If field has zero value then validation is skipped, with the exception of
+// Time, where the zero value means "now." To skip validating them, set the
+// corresponding field in the Claims struct to nil.
+type Expected struct {
+ // Issuer matches the "iss" claim exactly.
+ Issuer string
+ // Subject matches the "sub" claim exactly.
+ Subject string
+ // Audience matches the values in "aud" claim, regardless of their order.
+ Audience Audience
+ // ID matches the "jti" claim exactly.
+ ID string
+ // Time matches the "exp", "nbf" and "iat" claims with leeway.
+ Time time.Time
+}
+
+// WithTime copies expectations with new time.
+func (e Expected) WithTime(t time.Time) Expected {
+ e.Time = t
+ return e
+}
+
+// Validate checks claims in a token against expected values.
+// A default leeway value of one minute is used to compare time values.
+//
+// The default leeway will cause the token to be deemed valid until one
+// minute after the expiration time. If you're a server application that
+// wants to give an extra minute to client tokens, use this
+// function. If you're a client application wondering if the server
+// will accept your token, use ValidateWithLeeway with a leeway <=0,
+// otherwise this function might make you think a token is valid when
+// it is not.
+func (c Claims) Validate(e Expected) error {
+ return c.ValidateWithLeeway(e, DefaultLeeway)
+}
+
+// ValidateWithLeeway checks claims in a token against expected values. A
+// custom leeway may be specified for comparing time values. You may pass a
+// zero value to check time values with no leeway, but you should note that
+// numeric date values are rounded to the nearest second and sub-second
+// precision is not supported.
+//
+// The leeway gives some extra time to the token from the server's
+// point of view. That is, if the token is expired, ValidateWithLeeway
+// will still accept the token for 'leeway' amount of time. This fails
+// if you're using this function to check if a server will accept your
+// token, because it will think the token is valid even after it
+// expires. So if you're a client validating if the token is valid to
+// be submitted to a server, use leeway <=0, if you're a server
+// validation a token, use leeway >=0.
+func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error {
+ if e.Issuer != "" && e.Issuer != c.Issuer {
+ return ErrInvalidIssuer
+ }
+
+ if e.Subject != "" && e.Subject != c.Subject {
+ return ErrInvalidSubject
+ }
+
+ if e.ID != "" && e.ID != c.ID {
+ return ErrInvalidID
+ }
+
+ if len(e.Audience) != 0 {
+ for _, v := range e.Audience {
+ if !c.Audience.Contains(v) {
+ return ErrInvalidAudience
+ }
+ }
+ }
+
+ // validate using the e.Time, or time.Now if not provided
+ validationTime := e.Time
+ if validationTime.IsZero() {
+ validationTime = time.Now()
+ }
+
+ if c.NotBefore != nil && validationTime.Add(leeway).Before(c.NotBefore.Time()) {
+ return ErrNotValidYet
+ }
+
+ if c.Expiry != nil && validationTime.Add(-leeway).After(c.Expiry.Time()) {
+ return ErrExpired
+ }
+
+ // IssuedAt is optional but cannot be in the future. This is not required by the RFC, but
+ // something is misconfigured if this happens and we should not trust it.
+ if c.IssuedAt != nil && validationTime.Add(leeway).Before(c.IssuedAt.Time()) {
+ return ErrIssuedInTheFuture
+ }
+
+ return nil
+}
diff --git a/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/secret.go b/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/secret.go
new file mode 100644
index 00000000000..22ddeb56426
--- /dev/null
+++ b/vendor/knative.dev/pkg/client/injection/kube/informers/core/v1/secret/secret.go
@@ -0,0 +1,52 @@
+/*
+Copyright 2022 The Knative Authors
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by injection-gen. DO NOT EDIT.
+
+package secret
+
+import (
+ context "context"
+
+ v1 "k8s.io/client-go/informers/core/v1"
+ factory "knative.dev/pkg/client/injection/kube/informers/factory"
+ controller "knative.dev/pkg/controller"
+ injection "knative.dev/pkg/injection"
+ logging "knative.dev/pkg/logging"
+)
+
+func init() {
+ injection.Default.RegisterInformer(withInformer)
+}
+
+// Key is used for associating the Informer inside the context.Context.
+type Key struct{}
+
+func withInformer(ctx context.Context) (context.Context, controller.Informer) {
+ f := factory.Get(ctx)
+ inf := f.Core().V1().Secrets()
+ return context.WithValue(ctx, Key{}, inf), inf.Informer()
+}
+
+// Get extracts the typed informer from the context.
+func Get(ctx context.Context) v1.SecretInformer {
+ untyped := ctx.Value(Key{})
+ if untyped == nil {
+ logging.FromContext(ctx).Panic(
+ "Unable to fetch k8s.io/client-go/informers/core/v1.SecretInformer from context.")
+ }
+ return untyped.(v1.SecretInformer)
+}
diff --git a/vendor/modules.txt b/vendor/modules.txt
index b4a5382e50b..217cdf53599 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -114,6 +114,7 @@ github.com/evanphx/json-patch/v5
github.com/go-jose/go-jose/v3
github.com/go-jose/go-jose/v3/cipher
github.com/go-jose/go-jose/v3/json
+github.com/go-jose/go-jose/v3/jwt
# github.com/go-kit/log v0.2.1
## explicit; go 1.17
github.com/go-kit/log
@@ -1256,6 +1257,7 @@ knative.dev/pkg/client/injection/kube/informers/core/v1/endpoints/fake
knative.dev/pkg/client/injection/kube/informers/core/v1/namespace
knative.dev/pkg/client/injection/kube/informers/core/v1/namespace/fake
knative.dev/pkg/client/injection/kube/informers/core/v1/pod
+knative.dev/pkg/client/injection/kube/informers/core/v1/secret
knative.dev/pkg/client/injection/kube/informers/core/v1/service
knative.dev/pkg/client/injection/kube/informers/core/v1/service/fake
knative.dev/pkg/client/injection/kube/informers/core/v1/serviceaccount