Service Account Token Volume Projection

特性介绍

社区实现的pod 通过 volume 方式引用service account 的 token,支持audience和token有效期设置。在1.11引入(Alpha),在1.12为Beta。

使用

ServiceAccountTokenVolumeProjection 在 k8s 1.11版本需要手动设置 TokenRequestProjection 这个feature gate 为 true 来启用此功能,在1.12版本 TokenRequestProjection 默认为true

kubelet可以将service account token挂载到pod中。并且可以指定token的一些期望的属性,例如audience 和expirationSeconds。这些属性在default service account token下无法配置。一旦Pod和ServiceAccount被删除,service account token会跟着变成invalid

可以在pod的yaml中定义使用:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
kind: Pod
apiVersion: v1
metadata:
name: nginx
spec:
containers:
- image: nginx
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/tokens
name: vault-token
serviceAccountName: acct
volumes:
- name: vault-token
projected:
sources:
- serviceAccountToken: #名为serviceAccountToken的一种ProjectedVolume
path: vault-token
expirationSeconds: 7200 #指定token过期时间为2小时
audience: vault # identifier: vault
  • audience字段

    包含token的目标受众(audience)。token的接收者必须使用token的audience中指定的标识符(identifier)来标识自己,否则接收者应该拒绝该token。此参数为可选,默认为api server的identifier(api)

  • expirationSeconds字段

    指定service account token的有效时间。默认1小时,最小需要10分钟

  • path字段

    指定projected volume的挂载点的相对路径

创建pod之后,kubelet会替Pod请求和储存这个token,让pod在可配置的文件路径下访问到此token,并且一旦达到配置的到期时间,就会刷新token。

一旦token达到总TTL的80%,或者token已经超过24小时,kubelet 将主动更新token(rotate token)

kubelet实现

pkg\kubelet\kubelet.go

1
2
3
4
5
6
7
8
func NewMainKubelet(...)(*Kubelet, error) {
...
tokenManager := token.NewManager(kubeDeps.KubeClient)
...
klet.volumePluginMgr, err =
NewInitializedVolumePluginMgr(klet, secretManager, configMapManager, tokenManager, kubeDeps.VolumePlugins, kubeDeps.DynamicPluginProber)
...
}

pkg\kubelet\volume_host.go

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
func NewInitializedVolumePluginMgr(
kubelet *Kubelet,
secretManager secret.Manager,
configMapManager configmap.Manager,
tokenManager *token.Manager,
plugins []volume.VolumePlugin,
prober volume.DynamicPluginProber) (*volume.VolumePluginMgr, error) {
...
kvh := &kubeletVolumeHost{
kubelet: kubelet,
volumePluginMgr: volume.VolumePluginMgr{},
secretManager: secretManager,
configMapManager: configMapManager,
tokenManager: tokenManager,
mountPodManager: mountPodManager,
}
...

}

func (kvh *kubeletVolumeHost) GetServiceAccountTokenFunc() func(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
return kvh.tokenManager.GetServiceAccountToken
}

pkg\kubelet\token\token_manager.go

GetServiceAccountToken 从 cache 或者 TokenRequest API 里获取service account token

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
func (m *Manager) GetServiceAccountToken(namespace, name string, tr *authenticationv1.TokenRequest) (*authenticationv1.TokenRequest, error) {
//解析TokenRequest,返回name/namespace/tr.Spec.Audiences/expirationSeconds/boundObjectRef 格式的 key
key := keyFunc(name, namespace, tr)

//从cache中获取tokenRequests
ctr, ok := m.get(key)

//判断token是否需要刷新(一旦token达到总TTL的80%,或者token已经超过24小时),不需要刷新直接返回
if ok && !m.requiresRefresh(ctr) {
return ctr, nil
}

//创建新的token
//getToken: c.CoreV1().ServiceAccounts(namespace).CreateToken(name, tr)
tr, err := m.getToken(name, namespace, tr)
if err != nil {
switch {
case !ok:
return nil, fmt.Errorf("failed to fetch token: %v", err)
case m.expired(ctr):
return nil, fmt.Errorf("token %s expired and refresh failed: %v", key, err)
default:
klog.Errorf("couldn't update token %s: %v", key, err)
return ctr, nil
}
}

m.set(key, tr)
return tr, nil
}

相关内容

  • projected

    projected volume 映射多个存在的volume 资源到同一目录。

    所有资源要求必须和pod在同一个namespace。

    当前有四种volume资源可以被映射(projected):

    • secret
    • downwardAPI
    • configMap
    • serviceAccountToken

本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!