浅谈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 对象(例如: ServiceAccountConfigMapSecret 等)

 

那么问题随之而来,它们使用什么样的账号和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

posted @ 2022-11-15 12:31  cosmoswong  阅读(792)  评论(0编辑  收藏  举报