Skip to content

Commit 67f382d

Browse files
authored
Provide volume with OIDC token in SinkBinding (#7444)
* 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
1 parent 3ec99b4 commit 67f382d

File tree

23 files changed

+1520
-84
lines changed

23 files changed

+1520
-84
lines changed

cmd/webhook/main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@ func NewConfigValidationController(ctx context.Context, _ configmap.Watcher) *co
194194

195195
func NewSinkBindingWebhook(opts ...psbinding.ReconcilerOption) injection.ControllerConstructor {
196196
return func(ctx context.Context, cmw configmap.Watcher) *controller.Impl {
197-
sbresolver := sinkbinding.WithContextFactory(ctx, func(types.NamespacedName) {})
197+
withContext := sinkbinding.WithContextFactory(ctx, func(types.NamespacedName) {})
198198

199199
return psbinding.NewAdmissionController(ctx,
200200

@@ -208,7 +208,7 @@ func NewSinkBindingWebhook(opts ...psbinding.ReconcilerOption) injection.Control
208208
sinkbinding.ListAll,
209209

210210
// How to setup the context prior to invoking Do/Undo.
211-
sbresolver,
211+
withContext,
212212
opts...,
213213
)
214214
}

config/core/resources/containersource.yaml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,9 @@ spec:
143143
sinkCACerts:
144144
description: CACerts is the Certification Authority (CA) certificates in PEM format that the source trusts when sending events to the sink.
145145
type: string
146+
sinkAudience:
147+
description: Audience is the OIDC audience of the sink.
148+
type: string
146149
additionalPrinterColumns:
147150
- name: Sink
148151
type: string

config/core/resources/sinkbindings.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -185,6 +185,12 @@ spec:
185185
sinkCACerts:
186186
description: CACerts is the Certification Authority (CA) certificates in PEM format that the source trusts when sending events to the sink.
187187
type: string
188+
sinkAudience:
189+
description: Audience is the OIDC audience of the sink.
190+
type: string
191+
oidcTokenSecretName:
192+
description: Name of the secret with the OIDC token for the sink.
193+
type: string
188194
additionalPrinterColumns:
189195
- name: Sink
190196
type: string

config/core/roles/webhook-clusterrole.yaml

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,12 +109,25 @@ rules:
109109
- "create"
110110
- "patch"
111111

112-
# For the SinkBinding reconciler adding the OIDC identity service accounts
112+
# For the SinkBinding reconciler adding the OIDC identity service accounts
113113
- apiGroups:
114114
- ""
115115
resources:
116116
- "serviceaccounts"
117117
verbs: *everything
118+
# For the SinkBinding reconciler creating the sinkbinding token secret
119+
- apiGroups:
120+
- ""
121+
resources:
122+
- "serviceaccounts/token"
123+
verbs:
124+
- "create"
125+
- apiGroups:
126+
- ""
127+
resources:
128+
- "secrets"
129+
verbs: *everything
130+
118131
# Necessary for conversion webhook. These are copied from the serving
119132
# TODO: Do we really need all these permissions?
120133
- apiGroups: ["apiextensions.k8s.io"]

docs/eventing-api.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5958,6 +5958,18 @@ state.
59585958
Source.</p>
59595959
</td>
59605960
</tr>
5961+
<tr>
5962+
<td>
5963+
<code>oidcTokenSecretName</code><br/>
5964+
<em>
5965+
string
5966+
</em>
5967+
</td>
5968+
<td>
5969+
<p>OIDCTokenSecretName is the name of the secret containing the token for
5970+
this SinkBindings OIDC authentication</p>
5971+
</td>
5972+
</tr>
59615973
</tbody>
59625974
</table>
59635975
<hr/>

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ require (
99
github.com/cloudevents/sdk-go/sql/v2 v2.13.0
1010
github.com/cloudevents/sdk-go/v2 v2.13.0
1111
github.com/coreos/go-oidc/v3 v3.6.0
12+
github.com/go-jose/go-jose/v3 v3.0.0
1213
github.com/golang/protobuf v1.5.3
1314
github.com/google/go-cmp v0.6.0
1415
github.com/google/gofuzz v1.2.0
@@ -71,7 +72,6 @@ require (
7172
github.com/emicklei/go-restful/v3 v3.9.0 // indirect
7273
github.com/evanphx/json-patch v4.12.0+incompatible // indirect
7374
github.com/evanphx/json-patch/v5 v5.7.0 // indirect
74-
github.com/go-jose/go-jose/v3 v3.0.0 // indirect
7575
github.com/go-kit/log v0.2.1 // indirect
7676
github.com/go-logfmt/logfmt v0.5.1 // indirect
7777
github.com/go-logr/logr v1.2.4 // indirect

pkg/apis/sources/v1/sinkbinding_lifecycle.go

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,14 @@ import (
3333
"knative.dev/pkg/tracker"
3434
)
3535

36+
const (
37+
oidcTokenVolumeName = "oidc-token"
38+
)
39+
3640
var sbCondSet = apis.NewLivingConditionSet(
3741
SinkBindingConditionSinkProvided,
3842
SinkBindingConditionOIDCIdentityCreated,
43+
SinkBindingConditionOIDCTokenSecretCreated,
3944
)
4045

4146
// GetConditionSet retrieves the condition set for this resource. Implements the KRShaped interface.
@@ -90,6 +95,7 @@ func (sbs *SinkBindingStatus) MarkSink(addr *duckv1.Addressable) {
9095
if addr != nil {
9196
sbs.SinkURI = addr.URL
9297
sbs.SinkCACerts = addr.CACerts
98+
sbs.SinkAudience = addr.Audience
9399
sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionSinkProvided)
94100
} else {
95101
sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionSinkProvided, "SinkEmpty", "Sink has resolved to empty.%s", "")
@@ -112,6 +118,22 @@ func (sbs *SinkBindingStatus) MarkOIDCIdentityCreatedUnknown(reason, messageForm
112118
sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCIdentityCreated, reason, messageFormat, messageA...)
113119
}
114120

121+
func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceeded() {
122+
sbCondSet.Manage(sbs).MarkTrue(SinkBindingConditionOIDCTokenSecretCreated)
123+
}
124+
125+
func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedSuccceededWithReason(reason, messageFormat string, messageA ...interface{}) {
126+
sbCondSet.Manage(sbs).MarkTrueWithReason(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
127+
}
128+
129+
func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedFailed(reason, messageFormat string, messageA ...interface{}) {
130+
sbCondSet.Manage(sbs).MarkFalse(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
131+
}
132+
133+
func (sbs *SinkBindingStatus) MarkOIDCTokenSecretCreatedUnknown(reason, messageFormat string, messageA ...interface{}) {
134+
sbCondSet.Manage(sbs).MarkUnknown(SinkBindingConditionOIDCTokenSecretCreated, reason, messageFormat, messageA...)
135+
}
136+
115137
// Do implements psbinding.Bindable
116138
func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) {
117139
// First undo so that we can just unconditionally append below.
@@ -171,6 +193,38 @@ func (sb *SinkBinding) Do(ctx context.Context, ps *duckv1.WithPod) {
171193
Value: ceOverrides,
172194
})
173195
}
196+
197+
if sb.Status.OIDCTokenSecretName != nil {
198+
ps.Spec.Template.Spec.Volumes = append(ps.Spec.Template.Spec.Volumes, corev1.Volume{
199+
Name: oidcTokenVolumeName,
200+
VolumeSource: corev1.VolumeSource{
201+
Projected: &corev1.ProjectedVolumeSource{
202+
Sources: []corev1.VolumeProjection{
203+
{
204+
Secret: &corev1.SecretProjection{
205+
LocalObjectReference: corev1.LocalObjectReference{
206+
Name: *sb.Status.OIDCTokenSecretName,
207+
},
208+
},
209+
},
210+
},
211+
},
212+
},
213+
})
214+
215+
for i := range spec.Containers {
216+
spec.Containers[i].VolumeMounts = append(spec.Containers[i].VolumeMounts, corev1.VolumeMount{
217+
Name: oidcTokenVolumeName,
218+
MountPath: "/oidc",
219+
})
220+
}
221+
for i := range spec.InitContainers {
222+
spec.InitContainers[i].VolumeMounts = append(spec.InitContainers[i].VolumeMounts, corev1.VolumeMount{
223+
Name: oidcTokenVolumeName,
224+
MountPath: "/oidc",
225+
})
226+
}
227+
}
174228
}
175229

176230
func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
@@ -189,6 +243,17 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
189243
}
190244
}
191245
spec.InitContainers[i].Env = env
246+
247+
if len(spec.InitContainers[i].VolumeMounts) > 0 {
248+
volumeMounts := make([]corev1.VolumeMount, 0, len(spec.InitContainers[i].VolumeMounts))
249+
for j, vol := range c.VolumeMounts {
250+
if vol.Name == oidcTokenVolumeName {
251+
continue
252+
}
253+
volumeMounts = append(volumeMounts, spec.InitContainers[i].VolumeMounts[j])
254+
}
255+
spec.InitContainers[i].VolumeMounts = volumeMounts
256+
}
192257
}
193258
for i, c := range spec.Containers {
194259
if len(c.Env) == 0 {
@@ -204,5 +269,27 @@ func (sb *SinkBinding) Undo(ctx context.Context, ps *duckv1.WithPod) {
204269
}
205270
}
206271
spec.Containers[i].Env = env
272+
273+
if len(spec.Containers[i].VolumeMounts) > 0 {
274+
volumeMounts := make([]corev1.VolumeMount, 0, len(spec.Containers[i].VolumeMounts))
275+
for j, vol := range c.VolumeMounts {
276+
if vol.Name == oidcTokenVolumeName {
277+
continue
278+
}
279+
volumeMounts = append(volumeMounts, spec.Containers[i].VolumeMounts[j])
280+
}
281+
spec.Containers[i].VolumeMounts = volumeMounts
282+
}
283+
}
284+
285+
if len(spec.Volumes) > 0 {
286+
volumes := make([]corev1.Volume, 0, len(spec.Volumes))
287+
for i, vol := range spec.Volumes {
288+
if vol.Name == oidcTokenVolumeName {
289+
continue
290+
}
291+
volumes = append(volumes, spec.Volumes[i])
292+
}
293+
ps.Spec.Template.Spec.Volumes = volumes
207294
}
208295
}

0 commit comments

Comments
 (0)