权限认证之ServiceAccount

2022-06-13 15:07:55 浏览数 (1)

号里很多瓜友问我,Kubernetes中的应用程序是如何访问kubernetes中的资源对象的?

我想了下这个问题,觉得可以先从"SA"谈起。

权限管理是Kubernetes和OpenShift的核心功能之一。Kubenetes本身提供了一系列的安全机制,比如认证(Authentication)、授权(Authorization)、准入控制(Admission Control)认证(Authentication)、授权(Authorization)、准入控制(Admission Control)。但是它本身并不管理用户的信息,用户的身份信息管理是以插件形式存在的,比如管理员可以配置HTpasswd、KeyStone、LDAP、GitHub等登录验证方式。自然人用户可以获取相应的Token来去访问API Server. 那么在系统中运行的程序进程该如何访问API Server呢?

注:在Kubernetes/OpenShift云计算系统中,所有的用户(自然人用户,程序用户)都需要和API Server进行交互,只有认证、授权、准入控制通过后,这些用户才能使用相应的资源(API)。API Server类似门禁一样保证了后端etcd中的数据安全。

为了解决此问题,Kubernetes 引入了ServiceAccount(SA)的概念。我们以OpenShift为例。

注:Pod是Kubernetes/OpenShift系统中最小基本单元,它里面可包含多个容器服务。

SA以Namespace(命名空间)划分,我们新建一个Namespace,叫做test,如下:

代码语言:javascript复制
$ oc new-project test
$ oc get sa
NAME       SECRETS   AGE
builder    2         3m20s
default    2         3m20s
deployer   2         3m20s

注:在OpenShift中,oc = kubectl

可以看到有三个SA被默认创建了,那它们分别起什么作用呢?

先来查看下它们被赋予的权限:

代码语言:javascript复制
$ oc get rolebinding
NAME                    ROLE                               AGE
admin                   Cl
usterRole/admin                  15m
system:deployers        ClusterRole/system:deployer        15m
system:image-builders   ClusterRole/system:image-builder   15m
system:image-pullers    ClusterRole/system:image-puller    15m

可以看到,SA builder 被赋予了system:image-builder 的权限。我们再去看下system:image-builder可以做什么。

代码语言:javascript复制
$ oc describe rolebinding system:image-builders 
Name:         system:image-builders
Labels:       <none>
Annotations:  openshift.io/description:
                Allows builds in this namespace to push images to this namespace.  It is auto-managed by a controller; remove subjects to disable.
Role:
  Kind:  ClusterRole
  Name:  system:image-builder
Subjects:
  Kind            Name     Namespace
  ----            ----     ---------
  ServiceAccount  builder  test

可以看到,system:image-builder 可以做一些imagestream的创建、更新等操作。

代码语言:javascript复制
$ oc describe clusterrole system:image-builder
Name:         system:image-builder
Labels:       rbac.authorization.k8s.io/aggregate-to-admin=true
              rbac.authorization.k8s.io/aggregate-to-edit=true
Annotations:  openshift.io/description:
                Grants the right to build, push and pull images from within a project.  Used primarily with service accounts for builds.
              rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources                               Non-Resource URLs  Resource Names  Verbs
  ---------                               -----------------  --------------  -----
  imagestreams                            []                 []              [create]
  imagestreams.image.openshift.io         []                 []              [create]
  imagestreams/layers                     []                 []              [get update]
  imagestreams.image.openshift.io/layers  []                 []              [get update]
  builds                                  []                 []              [get]
  builds.build.openshift.io               []                 []              [get]
  builds/details                          []                 []              [update]
  builds.build.openshift.io/details       []                 []              [update]

类似地,SA deploer 可做一些pod部署的操作。

代码语言:javascript复制
$ oc describe rolebinding system:deployers
Name:         system:deployers
Labels:       <none>
Annotations:  openshift.io/description:
                Allows deploymentconfigs in this namespace to rollout pods in this namespace.  It is auto-managed by a controller; remove subjects to disa...
Role:
  Kind:  ClusterRole
  Name:  system:deployer
Subjects:
  Kind            Name      Namespace
  ----            ----      ---------
  ServiceAccount  deployer  test

$ oc describe clusterrole system:deployer
Name:         system:deployer
Labels:       <none>
Annotations:  openshift.io/description: Grants the right to deploy within a project.  Used primarily with service accounts for automated deployments.
              rbac.authorization.kubernetes.io/autoupdate: true
PolicyRule:
  Resources                           Non-Resource URLs  Resource Names  Verbs
  ---------                           -----------------  --------------  -----
  pods                                []                 []              [create get list watch]
  events                              []                 []              [create list]
  imagestreamtags                     []                 []              [create update]
  imagetags                           []                 []              [create update]
  imagestreamtags.image.openshift.io  []                 []              [create update]
  imagetags.image.openshift.io        []                 []              [create update]
  replicationcontrollers              []                 []              [delete get list update watch]
  replicationcontrollers/scale        []                 []              [get update]
  pods/log                            []                 []              [get]

简单概括如下:

ServiceAccount

作用

builder

构建Pod, 镜像流

deployer

部署Pod

default

Pod中默认使用该SA

我们知道要访问API Server,是需要证书、Token的,那SA是如何获取这些证书的呢?

我们看一下这个Pod中默认使用的SA: default.

代码语言:javascript复制
$ oc describe sa default
Name:                default
Namespace:           test
Labels:              <none>
Annotations:         <none>
Image pull secrets:  default-dockercfg-7n87s
Mountable secrets:   default-dockercfg-7n87s
                     default-token-n9f6q
Tokens:              default-token-m29km
                     default-token-n9f6q
Events:              <none>

查看下这个default-dockercfg-7n87s 中都有些什么。

代码语言:javascript复制
$ oc describe secret default-dockercfg-7n87s
Name:         default-dockercfg-7n87s
Namespace:    test
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: default
              kubernetes.io/service-account.uid: 6942fa8f-3ddf-4b6a-8bde-5a50e647f7cf
              openshift.io/token-secret.name: default-token-m29km
              openshift.io/token-secret.value:
                eyJhbGciOiJSUzI1NiIsImtpZCI6IkpseHB5c0tySnRtdXZjSFlvckNPTlVUZXMyaFFmM3F1cUtZdFNMQ3Nza2sifQ.eyJpc3MiOiJrdWJlcm5ldGVzL3NlcnZpY2VhY2NvdW50Iiw...

Type:  kubernetes.io/dockercfg

Data
====
.dockercfg:  6723 bytes

使用get 指令获取详细的数据信息:

代码语言:javascript复制
$ oc get  secret default-dockercfg-7n87s -o yaml
apiVersion: v1
data:
  .dockercfg: eyIxNzIuMzAuNTkuMTA1OjUwMDAiOnsidXNlcm5hbWUiOiJzZX...
kind: Secret
metadata:
...

把上面data中的数据用base64 解析出来,得到如下结果:

代码语言:javascript复制
{
    "172.30.59.105:5000":{
        "username":"serviceaccount",
        "password":"eyJhbGciOiJSUzI1NiIsImtp...",
        "email":"serviceaccount@example.org",
        "auth":"c2VydmljZWFjY291bnQ6ZXlKaGJHY2lPaUpTVX..."
    },
    "image-registry.openshift-image-registry.svc.cluster.local:5000":{
        "username":"serviceaccount",
        "password":"eyJhbGciOiJSUzI1NiIsImtp...",
        "email":"serviceaccount@example.org",
        "auth":"c2VydmljZWFjY291bnQ...“
    },
    "image-registry.openshift-image-registry.svc:5000":{
        "username":"serviceaccount",
        "password":"eyJhbGciOiJSUzI1...",
        "email":"serviceaccount@example.org",
        "auth":"c2VydmljZWFjY291bnQ6ZX..."
    }
}

可以看到,这个secret(default-dockercfg-7n87s)存储了访问内部镜像仓库的auth 信息。

代码语言:javascript复制
$ oc get svc -n openshift-image-registry 
NAME                      TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)     AGE
image-registry            ClusterIP   172.30.59.105   <none>        5000/TCP    31h
image-registry-operator   ClusterIP   None            <none>        60000/TCP   31h

既然上面的secret 是用来拉取镜像的,那么另一个应该就是来访问API Server的吧,我们来验证下:

代码语言:javascript复制
$ oc get secret default-token-n9f6q -o yaml
apiVersion: v1
data:
  ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FU...
  namespace: dGVzdA==
  service-ca.crt: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0t...
  token: ZXlKaGJHY2lPaUpTVXpJMU5...
metadata:
  annotations:
...

$ echo -n "dGVzdA=="|base64 -D
test

可以看到该secret中包含了 API Server的CA公钥证书,所在的namespace,访问服务所需的证书,身份验证的Token信息。

我们部署一个pod来看下,它是如何被使用的。

代码语言:javascript复制
$ cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: myapp-pod
  labels:
    app: myapp
spec:
  containers:
  - name: myapp-container
    image: busybox
    command: ['sh', '-c', 'echo Hello OpenShift! && sleep 3600']
    
$ oc create -f pod.yaml 
pod/myapp-pod created

mac:~ jianzhang$ oc get pods
NAME        READY   STATUS    RESTARTS   AGE
myapp-pod   1/1     Running   0          2m32s

可以看到该Pod默认使用了default SA,并挂载default-token-n9f6q 到容器内部的 /var/run/secrets/kubernetes.io/serviceaccount目录下。该Pod中的应用程序可以读取这个挂载的Token 来访问API Server了!

代码语言:javascript复制
mac:~ jianzhang$ oc get pods myapp-pod -o yaml
apiVersion: v1
kind: Pod
metadata:
... 
  name: myapp-pod
  namespace: test
...
spec:
  containers:
  - command:
    - sh
    - -c
    - echo Hello OpenShift! && sleep 3600
    image: busybox
    imagePullPolicy: Always
    name: myapp-container
    resources: {}
    securityContext:
      capabilities:
        drop:
        - MKNOD
    terminationMessagePath: /dev/termination-log
    terminationMessagePolicy: File
    volumeMounts:
    - mountPath: /var/run/secrets/kubernetes.io/serviceaccount
      name: default-token-n9f6q
      readOnly: true
  dnsPolicy: ClusterFirst
  enableServiceLinks: true
  imagePullSecrets:
  - name: default-dockercfg-7n87s
  nodeName: ...
  serviceAccount: default
  serviceAccountName: default
  terminationGracePeriodSeconds: 30
  tolerations:
 ...

最后,可以用下面的curl指令去试下看看会返回什么。祝大家5.20快乐!

代码语言:javascript复制
$ oc rsh myapp-pod 
/ # ls
bin   dev   etc   home  proc  root  run   sys   tmp   usr   var
/ # ls /var/run/secrets/kubernetes.io/serviceaccount
ca.crt          namespace       service-ca.crt  token
/ # TOKEN="$(cat /var/run/secrets/kubernetes.io/serviceaccount/token)"
/ # curl -H "Authorization: Bearer $TOKEN" --cacert /var/run/secrets/kubernetes.io/serviceaccount/ca.crt "https://openshift.default.svc.cluster.local/oapi/v1/users/-" 

0 人点赞