Skip to content

Commit af83259

Browse files
authored
Merge pull request kubernetes#2663 from zshihang/bound
BoundServiceAccountTokenVolume going GA
2 parents 1ffcd8c + 40d2bb2 commit af83259

File tree

5 files changed

+232
-83
lines changed

5 files changed

+232
-83
lines changed
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
kep-number: 1205
22
beta:
33
approver: "@deads2k"
4+
stable:
5+
approver: "@deads2k"

keps/sig-auth/1205-bound-service-account-tokens/README.md

Lines changed: 169 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,29 @@
33
## Table Of Contents
44

55
<!-- toc -->
6-
76
- [Summary](#summary)
87
- [Background](#background)
98
- [Motivation](#motivation)
109
- [Design Details](#design-details)
11-
- [Token attenuations](#token-attenuations)
12-
- [Audience binding](#audience-binding)
13-
- [Time binding](#time-binding)
14-
- [Object binding](#object-binding)
15-
- [API Changes](#api-changes)
16-
- [Add <code>tokenrequests.authentication.k8s.io</code>](#add-)
17-
- [Modify <code>tokenreviews.authentication.k8s.io</code>](#modify-)
18-
- [Example Flow](#example-flow)
19-
- [Service Account Authenticator Modification](#service-account-authenticator-modification)
20-
- [ACLs for TokenRequest](#acls-for-tokenrequest)
10+
- [TokenRequest](#tokenrequest)
11+
- [Token Attenuations](#token-attenuations)
12+
- [Audience binding](#audience-binding)
13+
- [Time Binding](#time-binding)
14+
- [Object Binding](#object-binding)
15+
- [API Changes](#api-changes)
16+
- [Add <code>tokenrequests.authentication.k8s.io</code>](#add-)
17+
- [Modify <code>tokenreviews.authentication.k8s.io</code>](#modify-)
18+
- [Example Flow](#example-flow)
19+
- [Service Account Authenticator Modification](#service-account-authenticator-modification)
20+
- [ACLs for TokenRequest](#acls-for-tokenrequest)
21+
- [TokenRequestProjection](#tokenrequestprojection)
22+
- [API Change](#api-change)
23+
- [File Permission](#file-permission)
24+
- [Proposed Heuristics](#proposed-heuristics)
25+
- [Alternatives Considered](#alternatives-considered)
2126
- [ServiceAccount Admission Controller Migration](#serviceaccount-admission-controller-migration)
2227
- [Prerequisites](#prerequisites)
23-
- [Safe rollout of time-bound token](#safe-rollout-of-time-bound-token)
28+
- [Safe Rollout of Time-bound Token](#safe-rollout-of-time-bound-token)
2429
- [Test Plan](#test-plan)
2530
- [TokenRequest/TokenRequestProjection](#tokenrequesttokenrequestprojection)
2631
- [RootCAConfigMap](#rootcaconfigmap)
@@ -40,12 +45,15 @@
4045
- [Dependencies](#dependencies)
4146
- [Scalability](#scalability)
4247
- [Troubleshooting](#troubleshooting)
43-
<!-- /toc -->
48+
<!-- /toc -->
4449

4550
## Summary
4651

4752
This KEP describes an API that would allow workloads running on Kubernetes to
48-
request JSON Web Tokens that are audience, time and eventually key bound.
53+
request JSON Web Tokens that are audience, time and eventually key bound. In
54+
addition, this KEP introduces a new mechanism of distribution with support for
55+
bound service account tokens and explores how to migrate from the existing
56+
mechanism backwards compatibly.
4957

5058
## Background
5159

@@ -74,14 +82,16 @@ requirements.
7482

7583
## Design Details
7684

85+
### TokenRequest
86+
7787
Infrastructure to support on demand token requests will be implemented in the
7888
core apiserver. Once this API exists, a client of the apiserver will request an
7989
attenuated token for its own use. The API will enforce required attenuations,
8090
e.g. audience and time binding.
8191

82-
### Token attenuations
92+
#### Token Attenuations
8393

84-
#### Audience binding
94+
##### Audience binding
8595

8696
Tokens issued from this API will be audience bound. Audience of requested tokens
8797
will be bound by the `aud` claim. The `aud` claim is an array of strings
@@ -90,7 +100,7 @@ recipient of a token is responsible for verifying that it identifies as one of
90100
the values in the audience claim, and should otherwise reject the token. The
91101
TokenReview API will support this validation.
92102

93-
#### Time binding
103+
##### Time Binding
94104

95105
Tokens issued from this API will be time bound. Time validity of these tokens
96106
will be claimed in the following fields:
@@ -108,7 +118,7 @@ for expiring tokens. During the migration off of the old service account tokens,
108118
clients of this API may request tokens that are valid for many years. These
109119
tokens will be drop in replacements for the current service account tokens.
110120

111-
#### Object binding
121+
##### Object Binding
112122

113123
Tokens issued from this API may be bound to a Kubernetes object in the same
114124
namespace as the service account. The name, group, version, kind and uid of the
@@ -123,9 +133,9 @@ kinds that will be supported are:
123133

124134
The TokenRequest API will validate this binding.
125135

126-
### API Changes
136+
#### API Changes
127137

128-
#### Add `tokenrequests.authentication.k8s.io`
138+
##### Add `tokenrequests.authentication.k8s.io`
129139

130140
We will add an imperative API (a la TokenReview) to the `authentication.k8s.io`
131141
API group:
@@ -180,7 +190,7 @@ This API will be exposed as a subresource under a serviceaccount object. A
180190
requestor for a token for a specific service account will `POST` a
181191
`TokenRequest` to the `/token` subresource of that serviceaccount object.
182192

183-
#### Modify `tokenreviews.authentication.k8s.io`
193+
##### Modify `tokenreviews.authentication.k8s.io`
184194

185195
The TokenReview API will be extended to support passing an additional audience
186196
field which the service account authenticator will validate.
@@ -194,7 +204,7 @@ type TokenReviewSpec struct {
194204
}
195205
```
196206

197-
#### Example Flow
207+
##### Example Flow
198208

199209
```
200210
> POST /apis/v1/namespaces/default/serviceaccounts/default/token
@@ -259,18 +269,147 @@ The token payload will be:
259269
}
260270
```
261271

262-
### Service Account Authenticator Modification
272+
#### Service Account Authenticator Modification
263273

264274
The service account token authenticator will be extended to support validation
265275
of time and audience binding claims.
266276

267-
### ACLs for TokenRequest
277+
#### ACLs for TokenRequest
268278

269279
The NodeAuthorizer will allow the kubelet to use its credentials to request a
270280
service account token on behalf of pods running on that node. The
271281
NodeRestriction admission controller will require that these tokens are pod
272282
bound.
273283

284+
### TokenRequestProjection
285+
286+
A ServiceAccountToken volume projection that maintains a service account token
287+
requested by the node from the TokenRequest API.
288+
289+
#### API Change
290+
291+
A new volume projection will be implemented with an API that closely matches the
292+
TokenRequest API.
293+
294+
```go
295+
type ProjectedVolumeSource struct {
296+
Sources []VolumeProjection
297+
DefaultMode *int32
298+
}
299+
300+
type VolumeProjection struct {
301+
Secret *SecretProjection
302+
DownwardAPI *DownwardAPIProjection
303+
ConfigMap *ConfigMapProjection
304+
ServiceAccountToken *ServiceAccountTokenProjection
305+
}
306+
307+
// ServiceAccountTokenProjection represents a projected service account token
308+
// volume. This projection can be used to insert a service account token into
309+
// the pods runtime filesystem for use against APIs (Kubernetes API Server or
310+
// otherwise).
311+
type ServiceAccountTokenProjection struct {
312+
// Audience is the intended audience of the token. A recipient of a token
313+
// must identify itself with an identifier specified in the audience of the
314+
// token, and otherwise should reject the token. The audience defaults to the
315+
// identifier of the apiserver.
316+
Audience string
317+
// ExpirationSeconds is the requested duration of validity of the service
318+
// account token. As the token approaches expiration, the kubelet volume
319+
// plugin will proactively rotate the service account token. The kubelet will
320+
// start trying to rotate the token if the token is older than 80 percent of
321+
// its time to live or if the token is older than 24 hours.Defaults to 1 hour
322+
// and must be at least 10 minutes.
323+
ExpirationSeconds int64
324+
// Path is the relative path of the file to project the token into.
325+
Path string
326+
}
327+
```
328+
329+
A volume plugin implemented in the kubelet will project a service account token
330+
sourced from the TokenRequest API into volumes created from
331+
ProjectedVolumeSources. As the token approaches expiration, the kubelet volume
332+
plugin will proactively rotate the service account token. The kubelet will start
333+
trying to rotate the token if the token is older than 80 percent of its time to
334+
live or if the token is older than 24 hours.
335+
336+
To replace the current service account token secrets, we also need to inject the
337+
clusters CA certificate bundle. We will deploy it as a configmap per-namespace
338+
and reference it using a ConfigMapProjection.
339+
340+
A projected volume source that is equivalent to the current service account
341+
secret:
342+
343+
```yaml
344+
- name: kube-api-access-xxxxx
345+
projected:
346+
defaultMode: 420 # 0644
347+
sources:
348+
- serviceAccountToken:
349+
expirationSeconds: 3600
350+
path: token
351+
- configMap:
352+
items:
353+
- key: ca.crt
354+
path: ca.crt
355+
name: kube-root-ca.crt
356+
- downwardAPI:
357+
items:
358+
- fieldRef:
359+
apiVersion: v1
360+
fieldPath: metadata.namespace
361+
path: namespace
362+
```
363+
364+
#### File Permission
365+
366+
The secret projections are currently written with world readable (0644,
367+
effectively 444) file permissions. Given that file permissions are one of the
368+
oldest and most hardened isolation mechanisms on unix, this is not ideal.
369+
We would like to opportunistically restrict permissions for projected service
370+
account tokens as long we can show that they won’t break users if we are to
371+
migrate away from secrets to distribute service account credentials.
372+
373+
##### Proposed Heuristics
374+
375+
- _Case 1_: The pod has an fsGroup set. We can set the file permission on the
376+
token file to 0600 and let the fsGroup mechanism work as designed. It will
377+
set the permissions to 0640, chown the token file to the fsGroup and start
378+
the containers with a supplemental group that grants them access to the
379+
token file. This works today.
380+
- _Case 2_: The pod’s containers declare the same runAsUser for all containers
381+
(ephemeral containers are excluded) in the pod. We chown the token file to
382+
the pod’s runAsUser to grant the containers access to the token. All
383+
containers must have UID either specified in container security context or
384+
inherited from pod security context. Preferred UIDs in container images are
385+
ignored.
386+
- _Fallback_: We set the file permissions to world readable (0644) to match
387+
the behavior of secrets.
388+
389+
This gives users that run as non-root greater isolation between users without
390+
breaking existing applications. We also may consider adding more cases in the
391+
future as long as we can ensure that they won’t break users.
392+
393+
##### Alternatives Considered
394+
395+
- We can create a volume for each UserID and set the owner to be that UserID
396+
with mode 0400. If user doesn't specify runAsUser, fetching UserID in image
397+
requires a re-design of kubelet regarding volume mounts and image pulling.
398+
This has significant implementation complexity because:
399+
- We would have to reorder container creation to introspect images (that
400+
might declare USER or GROUP directives) to pass this information to the
401+
projected volume mounter.
402+
- Further, images are mutable so these directives may change over the
403+
lifetime of the pod.
404+
- Volumes are shared between all pods that mount them today. Mapping a
405+
single logical volume in a pod spec to distinct mount points is likely a
406+
significant architectural change.
407+
- We pick a random group and set fsGroup on all pods in the service account
408+
admission controller. It’s unclear how we would do this without conflicting
409+
with usage of groups and potentially compromising security.
410+
- We set token files to be world readable always. Problems with this are
411+
discussed above.
412+
274413
### ServiceAccount Admission Controller Migration
275414
276415
#### Prerequisites
@@ -324,7 +463,7 @@ If anything goes wrong, please file a bug and CC @kubernetes/sig-auth-bugs. More
324463
contact information
325464
[here](https://github.com/kubernetes/community/tree/master/sig-auth#contact).
326465

327-
#### Safe rollout of time-bound token
466+
#### Safe Rollout of Time-bound Token
328467

329468
Legacy service account tokens distributed via secrets are not time-bound. Many
330469
client libraries have come to depend on this behavior. After time-bound service
@@ -439,7 +578,8 @@ are properly reloading tokens by:
439578
https://github.com/kubernetes/kubernetes/issues/68164
440579
- [x] Pods running as non root may not access the service account token.
441580
- Fixed in https://github.com/kubernetes/kubernetes/pull/89193
442-
- [ ] Dynamic clientbuilder does not invalidate token.
581+
- [x] Dynamic clientbuilder does not invalidate token.
582+
- Fixed in https://github.com/kubernetes/kubernetes/pull/99324
443583

444584
* [x] Tests passing
445585

@@ -452,9 +592,10 @@ are properly reloading tokens by:
452592

453593
##### Beta -> GA Graduation
454594

455-
- [ ] Allow kube-apiserver to recognize multiple issuers to enable non
595+
- [x] Allow kube-apiserver to recognize multiple issuers to enable non
456596
disruptive issuer change.
457-
- [ ] New `ServiceAccount` admission controller work as intended in Beta
597+
- Fixed in https://github.com/kubernetes/kubernetes/pull/101155
598+
- [x] New `ServiceAccount` admission controller work as intended in Beta
458599
for >= 1 minor release without significant issues.
459600

460601
## Production Readiness Review Questionnaire

keps/sig-auth/1205-bound-service-account-tokens/kep.yaml

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,22 @@ authors:
66
owning-sig: sig-auth
77
participating-sigs:
88
- sig-auth
9+
- sig-storage
910
reviewers:
1011
- "@liggitt"
1112
approvers:
1213
- "@liggitt"
1314
creation-date: 2019-08-06
14-
last-updated: 2021-02-08
15-
status: implemented
16-
stage: beta
17-
latest-milestone: "v1.21"
15+
last-updated: 2021-04-29
16+
status: implementable
17+
replaces:
18+
- "/keps/sig-storage/2451-service-account-token-volumes"
19+
stage: stable
20+
latest-milestone: "v1.22"
1821
milestone:
1922
alpha: "v1.13"
2023
beta: "v1.21"
24+
stable: "v1.22"
2125
feature-gates:
2226
- name: TokenRequest
2327
components:
@@ -35,3 +39,4 @@ feature-gates:
3539
- kube-apiserver
3640
metrics:
3741
- serviceaccount_stale_tokens_total
42+
- serviceaccount_valid_tokens_total

0 commit comments

Comments
 (0)