Skip to content

Commit

Permalink
Provide volume with OIDC token in SinkBinding (#7444)
Browse files Browse the repository at this point in the history
* Split Sinkbinding controller setup and reconciler

* Provide volume with OIDC token in SinkBinding

* Fix init container volume mounts

* Add unit test

* Add expiry annotation in token secret and update only if close to expiry

* Update tokenProvider GetJWT to return token expiry too

* Add owner reference to secret

* Add periodic resync

* Use secret lister to reduce API server calls to reconcile token secret

* Request a new JWT (without using cache)

* Add token expiry buffer as a constant

* Revert "Update tokenProvider GetJWT to return token expiry too"

This reverts commit 1f87397.

* Get expiry from token itself

* Use explicit token expiration duration

* Update secret generation on update

* Renew token if issues with parsing

* Simplify token reconcilation a bit

* Create token secret name via kmeta.ChildName function

* Make sinkbinding_lifecycle independent from feature config

* Remove token secret when oidc feature disabled
  • Loading branch information
creydr authored Nov 20, 2023
1 parent 3ec99b4 commit 67f382d
Show file tree
Hide file tree
Showing 23 changed files with 1,520 additions and 84 deletions.
4 changes: 2 additions & 2 deletions cmd/webhook/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,

Expand All @@ -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...,
)
}
Expand Down
3 changes: 3 additions & 0 deletions config/core/resources/containersource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 6 additions & 0 deletions config/core/resources/sinkbindings.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
15 changes: 14 additions & 1 deletion config/core/roles/webhook-clusterrole.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]
Expand Down
12 changes: 12 additions & 0 deletions docs/eventing-api.md
Original file line number Diff line number Diff line change
Expand Up @@ -5958,6 +5958,18 @@ state.
Source.</p>
</td>
</tr>
<tr>
<td>
<code>oidcTokenSecretName</code><br/>
<em>
string
</em>
</td>
<td>
<p>OIDCTokenSecretName is the name of the secret containing the token for
this SinkBindings OIDC authentication</p>
</td>
</tr>
</tbody>
</table>
<hr/>
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down
87 changes: 87 additions & 0 deletions pkg/apis/sources/v1/sinkbinding_lifecycle.go
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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", "")
Expand All @@ -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.
Expand Down Expand Up @@ -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) {
Expand All @@ -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 {
Expand All @@ -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
}
}
Loading

0 comments on commit 67f382d

Please sign in to comment.