浅谈serviceaccout在认证和鉴权中的作用
kube-system命名空间
在k8s中,每个命名空间下,都会有一个default serviceAccount,这是ServiceAccount准入控制器所设置的,kube-system命名空间也不例外;并且如果一个 Pod 没有声明 serviceAccountName,Kubernetes 会自动将 Namespace 下名叫 default 的默认 ServiceAccount分配给这个 Pod,但在这种情况下,这个默认 ServiceAccount 并没有关联任何 Role。
而且我们知道,ServiceAccount为了集群内的应用和apiserver交互而生的,它充当了账号的功能。再结合RABC权限管理,就能够控制一个账号的访问权限了。
那么诸如controller-manager、kube-scheduler、etcd是否会持有default serviceAccount,从而和apiserver进行认证和授权呢?如果是这样的,是怎样进行认证和授权的呢?
另外延伸一下普通命名空间下的serviceaccount是否也遵循同样的认证和授权规则呢?
带着上面的问题,我们来到了kube-system命名空间下。在查看了kubeadm方式安装的k8s,像controller-manager、kube-scheduler的POD yaml定义中,spec字段中并没有绑定serviceAccount,这是为什么呢?
是因为这些组件比较的特殊,它们是静态POD,而静态 Pod 的 spec 不能引用其他的 API 对象(例如: ServiceAccount、 ConfigMap、 Secret 等)。
那么问题随之而来,它们使用什么样的账号和apiserver进行认证呢?
我们在查看/etc/kubernetes/pki(有些时候,证书会以base64编码到/etc/kubernetes/*conf文件中)下,可以看到有很多的证书文件,正是使用了这些证书文件来通过kubernetes认证检查的。有关证书认证的细节,可以查看《kubernetes权威指南-6.1.1 HTTPS证书认证》。
接着再考虑鉴权问题。kubernetes中为核心组件定义了各种角色和角色绑定,如角色system:kube-controller-manager,角色绑定system:kube-controller-manager,并且角色绑定了所关联的用户正是证书中所定义的CN字段值。
如,在/etc/kubernetes/controller-manager.conf中,指定了base64所编码的证书,解码后得到Subject: CN=system:kube-controller-manager
而查看角色绑定,system:kube-controller-manager其中所绑定的用户正是证书中cn字段的值
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2022-09-26T09:31:13Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:kube-controller-manager
resourceVersion: "141"
uid: 7f80fca5-8a95-42fa-877a-1f51bbaaa70c
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:kube-controller-manager
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: User
name: system:kube-controller-manager
其他一些核心组件,如etcd、kube-scheduler的认证和鉴权也是如此。
普通命名空间
我们将视角转到普通命名空间下,观察它的认证和授权。
首先创建一个POD:
kubectl run nginx --image=nginx
然后查看该POD的YAML定义,发现spec.serviceAccountName为default
$ kubectl get pod/nginx -oyaml
apiVersion: v1
kind: Pod
metadata:
creationTimestamp: "2022-11-15T04:01:58Z"
labels:
run: nginx
name: nginx
namespace: default
resourceVersion: "5892284"
uid: ab98dbe1-9ed7-4e46-aae8-ca02ec68204e
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
resources: {}
terminationMessagePath: /dev/termination-log
terminationMessagePolicy: File
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-l8prv
readOnly: true
dnsPolicy: ClusterFirst
enableServiceLinks: true
serviceAccount: default
serviceAccountName: default
另外ServiceAccout,会被Projected到容器的/run/secrets/kubernetes.io/serviceaccount下
...
spec:
containers:
- image: nginx
imagePullPolicy: Always
name: nginx
volumeMounts:
- mountPath: /var/run/secrets/kubernetes.io/serviceaccount
name: kube-api-access-xs8cp
readOnly: true
...
volumes:
- name: kube-api-access-xs8cp
projected:
defaultMode: 420
sources:
- serviceAccountToken:
expirationSeconds: 3607
path: token
- configMap:
items:
- key: ca.crt
path: ca.crt
name: kube-root-ca.crt
- downwardAPI:
items:
- fieldRef:
apiVersion: v1
fieldPath: metadata.namespace
path: namespace
到容器内部查看Secret信息:
root@nginx:/run/secrets/kubernetes.io/serviceaccount# ls -l
total 0
lrwxrwxrwx 1 root root 13 Nov 10 04:53 ca.crt -> ..data/ca.crt
lrwxrwxrwx 1 root root 16 Nov 10 04:53 namespace -> ..data/namespace
lrwxrwxrwx 1 root root 12 Nov 10 04:53 token -> ..data/token
root@nginx:/run/secrets/kubernetes.io/serviceaccount#
ca.crt
eyJhbGciOiJSUzI1NiIsImtpZCI6ImlSbjhteGZ0MlRlUV9yUjlUbk1Na0VMZngxODhBTlg0MTZtT1lJWFhUREkifQ.eyJhdWQiOlsiaHR0cHM6Ly9rdWJlcm5ldGVzLmRlZmF1bHQuc3ZjLmNsdXN0ZXIubG9jYWwiXSwiZXhwIjoxNjk5NjY1MTY5LCJpYXQiOjE2NjgxMjkxNjksImlzcyI6Imh0dHBzOi8va3ViZXJuZXRlcy5kZWZhdWx0LnN2Yy5jbHVzdGVyLmxvY2FsIiwia3ViZXJuZXRlcy5pbyI6eyJuYW1lc3BhY2UiOiJkZWZhdWx0IiwicG9kIjp7Im5hbWUiOiJuZ2lueCIsInVpZCI6ImU1YjkzODFjLWExY2EtNDYzMi1hYmIwLWFmYmM5Y2I1NmUwYiJ9LCJzZXJ2aWNlYWNjb3VudCI6eyJuYW1lIjoiZGVmYXVsdCIsInVpZCI6IjJmNTE2ODVjLTM0MjUtNDNjOS04OTFlLWJhYWRlNzg0MzFjMCJ9LCJ3YXJuYWZ0ZXIiOjE2NjgxMzI3NzZ9LCJuYmYiOjE2NjgxMjkxNjksInN1YiI6InN5c3RlbTpzZXJ2aWNlYWNjb3VudDpkZWZhdWx0OmRlZmF1bHQifQ.h7SNcZTAz9rn2jQah3A37HRwrp2aKDn4rdEttBPGQY_ZQW61dN09UluObH6p3SJxxabp5Diw1--VaeP5EEved3rLw6YwUQg3rxDnoA1Xuy5tfRPJXmMlW6_dxfSEEJ6tTMvx0UgLTOIyikYZ3gj6bdz6f7PJC-hfPTcnqPkkkQA7w1h0h1w6Aj5vgy3EUVGXEK8xQynZDNUS9J-TRIh_Lptepl5AzQeoRM8qgFT4U6MvM0Cl4gbPR7gXy1LkOomQJFsQWEBJ5yV3-e90tY5s4JZPYPkDS0YiEufcBg1rrsP42WYOLVMPKkhrStSMRygL4IpfIO0Diflb9_Zf3oP2Fw
token
-----BEGIN CERTIFICATE-----
MIIDBjCCAe6gAwIBAgIBATANBgkqhkiG9w0BAQsFADAVMRMwEQYDVQQDEwptaW5p
a3ViZUNBMB4XDTIyMTEwNzEyMDQ1OFoXDTMyMTEwNTEyMDQ1OFowFTETMBEGA1UE
AxMKbWluaWt1YmVDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAL/s
NbBzIflL83iGloIh3MhfKW05H4HSh+bYFUho5Kc6emNcca14h/A+l8NZw2vjH1GV
uIQZ6vu29n7pCESB1rc01oHOBx4xfrOUsnD7CDT9fe5CUmx6MmYKfWoExWxn1Mpc
Wrxk3kPVikOoDoleGc3syEVJatbXte0db6lxMA2BpZqFrztypVRNaYx4P6guR0Kn
HhDv+JIfzkcOOGAsZlwwc9jdP+dQj3t+2SBjM84m07bVJIzdr7ChNlkP07VP1dAp
LBGZqvKuy0gtbsn2W+nfF/DXscG0ZSiF4Vp5p/QmbIlJ8Ks6KtGovj24BWkNJSOQ
Ix1VnCgKfwQsrnZiCqkCAwEAAaNhMF8wDgYDVR0PAQH/BAQDAgKkMB0GA1UdJQQW
MBQGCCsGAQUFBwMCBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQW
BBSX+0UH4kjXeUl9X6xaXYmWRimADjANBgkqhkiG9w0BAQsFAAOCAQEAN2XYgE5C
2s84uix5Qe9NKC9t9SVcnlnf4ZS4EBARH567gYSfxdh1q9DiCJCnP4xtd0noKpPP
e+FeBxkDBrtfBbytIsC5Lq8n+97iwEvdonVXOMJUFZiUC8XtrCmD9f7xCU+iUEva
GmYXP6hygWive1hcMZqHTVzhdOPSTEuPOLAyGiNJVpPbEZqkjUU443VgWpL0CtR6
7BQeluxTSJq5qJCaCetq7gy4RRaphAcBPKeRIxT8sLOrBUzsiQ1tT1BHHBQ0Jfpt
Kb5sX5+rXbQyWPepRNhcSjuNqTx+MukkkIk1rr0PTDM0XtN2Z3iQAX1OhnPIwwRm
dge/2sobKt5XzQ==
-----END CERTIFICATE-----
namespace
default
我们尝试使用该serviceaccount访问集群中的资源
$ kubectl exec -it nginx -- sh
# Point to the internal API server hostname
APISERVER=https://kubernetes.default.svc
# Path to ServiceAccount token
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
# Read this Pod's namespace
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
# Read the ServiceAccount bearer token
TOKEN=$(cat ${SERVICEACCOUNT}/token)
# Reference the internal certificate authority (CA)
CACERT=${SERVICEACCOUNT}/ca.crt
# Explore the API with TOKEN
$ curl --cacert ${CACERT} --header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/api
{
"kind": "APIVersions",
"versions": [
"v1"
],
"serverAddressByClientCIDRs": [
{
"clientCIDR": "0.0.0.0/0",
"serverAddress": "192.168.0.41:6443"
}
]
}
$ curl --cacert ${CACERT} \
--header "Authorization: Bearer ${TOKEN}" -X GET ${APISERVER}/versions
{
"kind": "Status",
"apiVersion": "v1",
"metadata": {
},
"status": "Failure",
"message": "forbidden: User \"system:serviceaccount:default:default\" cannot get path \"/versions\"",
"reason": "Forbidden",
"details": {
},
"code": 403
}
发现虽然通过了认证检查,但是没有通过鉴权检查(403),表明我们没有权限来操作集群中的资源。
能够看到这个default serviceaccout权限太小了,默认情况下它继承了system:serviceaccounts的权限,只能操作一些非资源类型,如果提升权限,就只能创建对应角色并绑定它了。
system:serviceaccounts组的角色和角色绑定,
角色绑定:system:service-account-issuer-discovery
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2022-09-26T09:31:13Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:service-account-issuer-discovery
resourceVersion: "146"
uid: d6ce5a27-41ae-4e26-a860-6c8a1008b41e
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: system:service-account-issuer-discovery
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:serviceaccounts
角色内容:service-account-issuer-discovery
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
annotations:
rbac.authorization.kubernetes.io/autoupdate: "true"
creationTimestamp: "2022-09-26T09:31:13Z"
labels:
kubernetes.io/bootstrapping: rbac-defaults
name: system:service-account-issuer-discovery
resourceVersion: "101"
uid: a301c134-7d65-45d6-8890-1c13311a3fc9
rules:
- nonResourceURLs:
- /.well-known/openid-configuration
- /openid/v1/jwks
verbs:
- get
如果您觉得阅读本文对您有帮助,请点一下“推荐”按钮,您的“推荐”将是我最大的写作动力!欢迎各位转载,但是未经作者本人同意,转载文章之后必须在文章页面明显位置给出作者和原文连接,否则保留追究法律责任的权利。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理
· 单元测试从入门到精通
· 上周热点回顾(3.3-3.9)
· Vue3状态管理终极指南:Pinia保姆级教程