K8S初级入门系列之十一-安全

news2024/11/26 18:37:37

一、前言

    安全是K8S重要的特性,在K8S初级入门系列之四-Namespace/ConfigMap/Secret章节,我们已经已经了解了Namespace,Secret与安全相关的知识。本篇将梳理K8S在安全方面的策略。主要包括两个方面,API安全访问策略以及Pod安全策略。

二、用户/用户组

在介绍安全前,我们先了解下用户和用户组的概念。

 1、用户

在K8S中,用户分为两种:

  • 真实的用户,即User,如K8S的管理员,开发者等。
  • Pod的账号,即Service Account,是给运行在Pod里面的进程提供必要的身份证明,通过其来限制Pod的访问权限。

       这里重点看下Service Account,每个命名空间(namespace)都有一个默认的Service Account。如果Pod不指定ServiceAccount,则使用该空间中默认的。

[root@k8s-master ~]# kubectl get sa
NAME                     SECRETS   AGE
default                  1         203d

(1)自定义ServiceAccount

当然,我们也可以自定义一个ServiceAccount,其yaml如下:

[root@k8s-master yaml]# cat my-serviceaccount.yaml 
apiVersion: v1
kind: ServiceAccount
metadata:
  name: my-servicesaccount
  namespace: default

创建完成后,查看详情

[root@k8s-master yaml]# kubectl describe sa my-servicesaccount
Name:                my-servicesaccount
Namespace:           default
Labels:              <none>
Annotations:         <none>
Image pull secrets:  <none>
Mountable secrets:   my-servicesaccount-token-cz8kp
Tokens:              my-servicesaccount-token-cz8kp
Events:              <none>

   可以看到 mountable secret和tokens都关联了一个名为my-servicesaccount-token-cz8kp的secret,我们查看其secret的详情

[root@k8s-master yaml]# kubectl describe secret my-servicesaccount-token-cz8kp
Name:         my-servicesaccount-token-cz8kp
Namespace:    default
Labels:       <none>
Annotations:  kubernetes.io/service-account.name: my-servicesaccount
              kubernetes.io/service-account.uid: 1f493157-13e7-4d43-9f0b-b8779dfe84ae

Type:  kubernetes.io/service-account-token

Data
====
ca.crt:     1099 bytes
namespace:  7 bytes
token:      eyJhbGciOiJSUzI1NiIsImtpZCI6ImpoSnJ...

      其data包含ca.crt,namespace,token密钥三部分,其中ca.crt即颁发的CA证书,namespace即命名空间,token文件中存放的密钥。这三部分的用途我们待会讲到。

(2)将ServiceAccount分配给Pod

   创建Pod,将上面的serviceAccount分配给Pod,其yaml内容如下:

[root@k8s-master yaml]# cat pod-read.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: pod-read
spec:
  serviceAccount: my-servicesaccount
  containers:
  - name: netshoot
    image: nicolaka/netshoot
    imagePullPolicy: IfNotPresent
    command: ["sleep","3600"]

         该Pod中定义serviceAccount属性,并设置为刚创建的my-servicesaccount。我们进入该pod,查看下/var/run/secrets/kubernetes.io/serviceaccount目录。

[root@k8s-master ~]#  kubectl exec -it pod-read sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
~ # cd /var/run/secrets/kubernetes.io/serviceaccount
/run/secrets/kubernetes.io/serviceaccount # ls
ca.crt     namespace  token
/run/secrets/kubernetes.io/serviceaccount # cat token
eyJhbGciOiJSUzI1NiIsImtpZCI6ImpoSnJ

      该目录下,包含了my-servicesaccount的secret的三个文件。实际上,ServiceAcccount分配给Pod,就是将serviceAccount的secret作为volume挂载到pod的的固定目录下(即/var/run/secrets/kubernetes.io/serviceaccount ),后续Pod将通过这三个文件协同完成身份验证。

2、用户组

     多个用户可以属于一个或者多个用户组,用户组可以一次给多个用户赋予权限。K8S系统内置了一些组,如:

  • system:unauthenticated组用于所有认证插件都不会认证客户端身份的请求。
  • system:authenticated组会自动分配给一个成功通过认证的用户。
  • system:serviceaccounts组包含所有在系统中的 ServiceAccount
  • system:serviceaccounts:<namespace>组包含了所有在特定命名空间中的ServiceAccount。

三、API  Server安全访问策略

      在K8S初级入门系列之一-概述章节的K8S架构中,我们形象的比喻过,API Server就像是办事大厅,对外对内提供统一的访问接口,所以API Server访问安全策略至关重要。先来看下官方上的图。

     当访问API Server时,需要经过三层关卡,分别是认证(Authentication),鉴权(Authorization)和准入控制(Admission Control)。

1、认证

      认证就是识别用户的身份,对于Pod来说,在访问API Server时,携带其Service Account的token密钥(前面介绍的),由API Service进行认证,这种方式类似JWT token验证。过程如下:

 (1)、Pod关联ServiceAccount,并挂载了ServiceAccount的secret。

(2)、通过HTTPS方式与API Server建立连接后,会用Pod里CA证书(文件名为ca.crt)验证API Server发来的证书,验证是否为CA证书签名的合法证书。

(3)、API Server收到Token后,采用自身私钥对Token进行合法性验证。

       整个认证过程,需要用到上面介绍的var/run/secrets/kubernetes.io/serviceaccount下三个文件。我们来实验下,从Pod内部,通过curl访问Api Server的接口。其命令如下:

# 指向内部 API 服务器的主机名,一般为kubernetes.default.svc,也可以通过env查看
APISERVER=https://kubernetes.default.svc

# 服务账号令牌的路径
SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount

# 读取 Pod 的名字空间
NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)

# 读取服务账号的持有者令牌
TOKEN=$(cat ${SERVICEACCOUNT}/token)

# 引用内部证书机构(CA)
CACERT=${SERVICEACCOUNT}/ca.crt

# 使用令牌访问 API curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -s  ${APISERVER}/api

进入pod内部,输入上述指令

[root@k8s-master ~]# kubectl exec -it pod-read sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
~ # APISERVER=https://kubernetes.default.svc
~ # SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
~ # NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
~ # TOKEN=$(cat ${SERVICEACCOUNT}/token)
~ # CACERT=${SERVICEACCOUNT}/ca.crt
~ # curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -s  ${APISERVER}/api
{
  "kind": "APIVersions",
  "versions": [
    "v1"
  ],
  "serverAddressByClientCIDRs": [
    {
      "clientCIDR": "0.0.0.0/0",
      "serverAddress": "192.168.16.4:6443"
    }
  ]
}

可以看下,HTTPS携带相关认证信息,Api认证成功后,正确的返回了Api的相关信息。

2、鉴权

      认证是对用户合法性的认证,但用户合法,不代表可以做任何操作,而鉴权是对调用的API是否合法进行鉴权,授予用户不同的访问权限。先看一个例子,我们进入上述Pod,访问下pod列表。

 #   curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -X GET  ${APISERVER}/api/v1/namespaces/default/pods
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "default \"pods\" is forbidden: User \"system:serviceaccount:default:default\" cannot get resource \"default\" in API group \"\" at the cluster scope",
  "reason": "Forbidden",
  "details": {
    "name": "pods",
    "kind": "default"
  },
  "code": 403

       鉴权未通过被拒绝了,说明该Pod使用的ServiceAccount是没有访问该资源权限的。

      下面我们来进行授权访问,授权的方式主要包括ABAC(基于属性授权),RBAC(基于角色授权),Webhook(外部REST服务对用户授权)等,其中RBAC是最主要,也是API Server默认的方式,我们来重点介绍,其模型如下:

  •   资源权限,表示对何种对象,有什么的操作权限,对象包括核心资源,如Pod,Deployment,job等,也包括非资源端点,如"/healthz"等。
  •  Role(角色),是资源权限的集合。
  • RoleBinding(角色绑定),将Role授予给ServiceAccount,User,Group等。

接下来,我们通过案例,实现对于Pod列表的访问。

(1)Role(角色)

首先创建一个Role的yaml文件,内容如下:

[root@k8s-master yaml]# cat pod-read-role.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""] # "" 标明 core API 组
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

在rules可以定义多组资源权限,每组包含三个属性:

  • apiGoups,即资源对象所在的api组,由于pods是核心对象,所以为"",比如对象为jobs,那么该值就是batch。
  • resources,资源对象组,可以是pods,deploymenets,jobs等等
  • verbs,对资源对象的操作权限组,包括get,list,watch,create,delete等。

     该Role命名为pod-reader,并申明对于Pod对象具备get,watch,list相关权限(注意,不具备删除,创建权限)。执行文件,创建role对象,查看状态。

[root@k8s-master yaml]# kubectl get role
NAME                                    CREATED AT
pod-reader                              2023-05-28T09:24:06Z

(2)RoleBinding

       接下来创建RoleBinding,将上面的Role(pod-reader)与ServiceAccount(my-serviceaccount)绑定。其yaml内容如下:

[root@k8s-master yaml]# cat pod-reader-rolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: pod-reader-rolebinding
  namespace: default
subjects:
# 这里可以指定多个主体,User,ServiceAccount,Group
- kind: ServiceAccount
  name: my-servicesaccount
  namespace: default 
  apiGroup: ""
roleRef:
  # "roleRef" 指定与某 Role 绑定关系
  kind: Role       
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

rolebinding包含两个属性。

  • subjects,即绑定的用户主体,可以是User,ServiceAccount,Group,能同时绑定多个不同的主体。
  • roleRef,即用户主体待授予的角色。

将my-servicesaccount账号与pod-reader角色绑定,执行文件,创建rolebinding,查看状态

[root@k8s-master yaml]# kubectl get rolebinding
NAME                                    ROLE                                         AGE
pod-reader-rolebinding                  Role/pod-reader                              10h

此时,我们进入Pod,再看下能否获取pod列表。

[root@k8s-master yaml]# kubectl exec -it pod-read sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
~ # APISERVER=https://kubernetes.default.svc
~ # SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
~ # NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
~ # TOKEN=$(cat ${SERVICEACCOUNT}/token)
~ # CACERT=${SERVICEACCOUNT}/ca.crt
~ # curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -X GET  ${APISERVER}/api/v1/namespaces/default/pods
{
   [
      "metadata": {
        "name": "taint-pod",
        "namespace": "default",
        "uid": "12603b58-86b9-4e95-a6d5-b5d8c86a5e9c",
        "resourceVersion": "4812227",
    ...
   ]
   ...
}

可以看到,能正确的获取了。我们尝试删除其中一个pod

# curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -X DELETE  ${APISERVER}/api/v1/namespaces/default/pods/busybox-pod
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "pods \"busybox-pod\" is forbidden: User \"system:serviceaccount:default:my-servicesaccount\" cannot delete resource \"pods\" in API group \"\" in the namespace \"default\"",
  "reason": "Forbidden",
  "details": {
    "name": "busybox-pod",
    "kind": "pods"
  },
  "code": 403
}

可以看到,由于没有授予删除相关的权限,删除指令被拒绝。

(3)ClusterRole和ClusterRoleBinding

      由于NameSpace的隔离,对于某个空间的ServiceAccount是无法访问其他空间的资源对象的。如下图所示,default空间的my-serviceaccount账号是无法访问dev空间的Pod资源列表。

      在实际工程中,又需要访问集群中的其他空间的资源,为了解决这一问题,K8S提供了ClusterRole和ClusterBinding。如下图所示:

       ClusterRole和ClusterRoleBinding组合与Role以及RoleBinding组合的功能类似,只是它们属于整个集群,不属于具体某个命名空间,所以它们可以访问集群中任何命名空间的资源。接下来,我们来实现下这个例子。

       首先创建一个NameSpace和属于该NameSpace的Pod,我们使用K8S初级入门系列之四-Namespace/ConfigMap/Secret中创建好的命名空间dev,以及ns-pod的Pod,可以查看下pod状态。

[root@k8s-master yaml]# kubectl get pod -n dev
NAME     READY   STATUS    RESTARTS   AGE
ns-pod   1/1     Running   0          8s

       在没有使用ClusterRole和ClusterRoleBinding之前,我们看下能否从default空间中访问dev空间的资源。

[root@k8s-master yaml]# kubectl exec -it pod-read sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
~ # APISERVER=https://kubernetes.default.svc
~ # SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
~ # NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
~ # TOKEN=$(cat ${SERVICEACCOUNT}/token)
~ # CACERT=${SERVICEACCOUNT}/ca.crt
~ # curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -X GET  ${APISERVER}/api/v1/namespaces/dev/pods
{
  "kind": "Status",
  "apiVersion": "v1",
  "metadata": {},
  "status": "Failure",
  "message": "pods is forbidden: User \"system:serviceaccount:default:my-servicesaccount\" cannot list resource \"pods\" in API group \"\" in the namespace \"dev\"",
  "reason": "Forbidden",
  "details": {
    "kind": "pods"
  },
  "code": 403

      访问被拒绝了,无法访问到其他命名空间pod。接下来,创建ClusterRole,命名为pod-reader-clusterrole

[root@k8s-master yaml]# cat pod-read-clusterrole.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: pod-reader-clusterrole
rules:
- apiGroups: [""] # "" 标明 core API 组
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

       与Role比较,ClusterRole没有NameSpace属性。执行该文件,创建完成后我们可以看下。

[root@k8s-master yaml]# kubectl get clusterrole
NAME                                                                   CREATED AT
admin                                                                  2022-11-04T15:44:14Z
calico-kube-controllers                                                2022-11-05T03:09:48Z
calico-node                                                            2022-11-05T03:09:48Z
cluster-admin                                                          2022-11-04T15:44:14Z
edit                                                                   2022-11-04T15:44:14Z
ingress-nginx                                                          2023-04-02T10:47:54Z
ingress-nginx-admission                                                2023-04-02T10:47:54Z
kubeadm:get-nodes                                                      2022-11-04T15:44:15Z
kubernetes-dashboard                                                   2022-11-05T09:53:47Z
pod-reader-clusterrole                                                 2023-05-30T15:36:51Z
system:aggregate-to-admin                                              2022-11-04T15:44:14Z
system:aggregate-to-edit                                               2022-11-04T15:44:14Z
...

      除了创建的pod-reader-clusterrole外,还可以看到大量的系统预置的ClusterRole。继续创建ClusterRoleBinding,命名为pod-reader-clusterrolebinding。

[root@k8s-master yaml]# cat pod-reader-clusterrolebinding.yaml 
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: pod-reader-clusterrolebinding
subjects:
# 这里可以指定多个主体,User,ServiceAccount,Group
- kind: ServiceAccount
  name: my-servicesaccount
  namespace: default 
  apiGroup: ""
roleRef:
  # "roleRef" 指定与某 Role 绑定关系
  kind: ClusterRole       
  name: pod-reader-clusterrole
  apiGroup: rbac.authorization.k8s.io

      将my-serviceaccount账号与新创建的pod-reader-clusterrole角色绑定。

[root@k8s-master yaml]# kubectl get clusterrolebinding
NAME                                                   ROLE                                                                               AGE
...
pod-reader-clusterrolebinding                          ClusterRole/pod-reader-clusterrole                                                 16s
system:controller:attachdetach-controller              ClusterRole/system:controller:attachdetach-controller                              209d
system:controller:certificate-controller               ClusterRole/system:controller:certificate-controller                               209d                         
....

      同样,除了我们创建的pod-reader-clusterrolebinding外,也存在大量的系统预置ClusterRoleBinding对象。再次进入default空间pod,访问dev空间的pod列表

[root@k8s-master yaml]# kubectl exec -it pod-read sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
~ # APISERVER=https://kubernetes.default.svc
~ # SERVICEACCOUNT=/var/run/secrets/kubernetes.io/serviceaccount
~ # NAMESPACE=$(cat ${SERVICEACCOUNT}/namespace)
~ # TOKEN=$(cat ${SERVICEACCOUNT}/token)
~ # CACERT=${SERVICEACCOUNT}/ca.crt
~ # curl --cacert ${CACERT} -H "Authorization: Bearer $TOKEN" -X GET  ${APISERVER}/api/v1/namespaces/dev/pods
{
  "kind": "PodList",
  "apiVersion": "v1",
  "metadata": {
    "resourceVersion": "31415719"
  },
  "items": [
    {
      "metadata": {
        "name": "ns-pod",
        "namespace": "dev",
        "uid": "31bc247f-6c5a-496a-8805-2631f03e7df2",
        "resourceVersion": "31324908",
        "creationTimestamp": "2023-06-02T01:26:27Z",
        "labels": {
          "app": "nginx-pod"
        },
...

      此时可以正确访问pod列表了。

3、准入

       突破了认证和鉴权两层关卡后,对于API的请求还需要通过"准入"这道关卡,K8S配备了一个准入控制器的插件列表,发送给API Server的任何请求都需要通过列表中每个准入控制器的检查,检查通不过,则拒绝调用请求。

    准入控制器 是一段代码,它会在请求通过认证和鉴权之后、对象被持久化之前拦截到达 API 服务器的请求,准入控制器又可以分为验证(Validating)和变更(Mutating),变更(mutating)控制器可以根据被其接受的请求更改相关对象,Validating 控制器不会。如果任何一个阶段中的任何控制器拒绝了请求,则会立即拒绝整个请求,并将错误返回给最终的用户。

   K8S定义了多个准入控制器,可以查看官方文档准入控制器参考 | Kubernetes,用户也可以自定义扩展插件。通过如下的命令可以查看K8S默认开启的准入控制器。

[root@k8s-master ~]# kubectl exec -it kube-apiserver-k8s-master -n kube-system -- kube-apiserver -h | grep enable-admission-plugins
      .....
      --enable-admission-plugins strings       admission plugins that should be enabled in addition to default enabled ones (NamespaceLifecycle, LimitRanger, ServiceAccount, TaintNodesByCondition, PodSecurity, .....

     稍后我们专门分析其中的PodSecurity准入控制器。

四、Pod安全策略

       Pod除了对于API Server访问需要控制,Pod自身的安全策略也非常重要,因为Pod加载的容器镜像是由开发者,甚至是三方提供,如果不在Pod层进行安全的控制,这些镜像运行的代码就有可能利用漏洞实现非法的操作。

        有些同学可能会问,Pod中的容器本来就是和宿主隔离的,就算有影响,也只会影响该容器的环境,其实不然,在前面的章节,我们了解到容器的目录是可以挂在到宿主节点上的,网络也可以直接使用宿主的,如果此时容器拥有root权限,就会随意篡改宿主节点的目录,或者利用宿主机网络进行恶意访问其他主机,从而影响集群安全。

      为了解决这些安全问题,K8S对于Pod提供一系列的安全策略方案。

1、安全上下文配置

        安全上下文即配置securityContext属性,Pod和Container上都可以配置该属性,两者的策略项既有重复的,也有不同的,对于重复的部分,Container策略会覆盖Pod上的。我们先来看下Pod上的配置。

(1)Pod配置

        我们先看下没有配置securityContext属性时,Pod都有哪些权限。创建security-context-demo.yaml文件

[root@k8s-master yaml]# cat security-context-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo

执行文件,Pod创建完成后,进入容器内部,看下其默认的进程用户,组,以及文件目录属组。

[root@k8s-master yaml]# kubectl exec -it security-context-demo sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # id
uid=0(root) gid=0(root) groups=0(root),10(wheel)
/ # cd /data
/data # ls -l
total 4
drwxrwxrwx    2 root     root          4096 Jun  4 05:58 demo

       可以看到uid,gid,以及文件目录的属主都是root,为了安全起见,我们认为该Pod下的容器不需要root权限,那么就可以通过Pod级别的securityContext属性配置。下面我们修改yaml文件。

[root@k8s-master yaml]# cat security-context-demo.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
    runAsGroup: 3000
    fsGroup: 2000
  volumes:
  ...

securityContext属性中有三项内容:

  • runAsUser,指定容器中运行进程的用户(用户 ID
  • runAsGroup,指定容器中运行组(组 ID
  • fsGroup,文件属主组(组ID)

再进入容器看下

[root@k8s-master yaml]# kubectl exec -it security-context-demo sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ $ id
uid=1000 gid=3000 groups=2000,3000
/ $ cd /data
/data $ ls -l
total 4
drwxrwsrwx    2 root     2000          4096 Jun  4 06:30 demo

       可以看到已经按照Pod的设置进行了更变。除了以上的三个配置项,Pod还可以设置其他的,具体参考:Kubernetes API Reference Docs

(2)容器配置

        容器中也可以配置securityContext属性,首先我们看下runAsUser,runAsGroup,fsGroup属性。我们再来创建一个pod yaml

[root@k8s-master yaml]# cat security-context-demo1.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo
spec:
  securityContext:
    runAsUser: 1000
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      runAsUser: 2000

      在container中,增加了securityContext属性,并配置了runAsUser了,其值和Pod的不同。我们进入Pod内部看下。

[root@k8s-master yaml]#  kubectl exec -it security-context-demo1 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ $ id
uid=2000 gid=0(root) groups=0(root)

       可以看到,Container定义的uid=2000覆盖了Pod定义的uid=1000。

        默认的情况下,Pod是无法使用宿主内核的功能(容器镜像没有内核),比如修改网络接口,修改系统时间等等,如果获取这些权限,就需要申请授权特权模式。

      我们先来看下在没有授权特权模式下,Pod操作内核指令的情况。 创建一个新的Pod,其yaml内容如下:

[root@k8s-master yaml]# cat security-context-demo2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo2
spec:
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo

     进入Pod,访问/dev目录下的设备列表,以及修改网络接口

[root@k8s-master yaml]# kubectl exec -it security-context-demo2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # cd /dev
/dev # ls
core             full             null             pts              shm              stdin            termination-log  urandom
fd               mqueue           ptmx             random           stderr           stdout           tty              zero
/dev # ip link add dummy0 type dummy
ip: RTNETLINK answers: Operation not permitted

      可以看到修改网络接口指令被拒绝了。dev目录下展示内容待会进行比较。接下来,我们修改yaml内容,增加特权配置( privileged: true)。

[root@k8s-master yaml]# cat security-context-demo2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo2
spec:
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
      privileged: true

 再次进入Pod,执行相关指令

[root@k8s-master yaml]# kubectl exec -it security-context-demo2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # cd /dev
/dev # ls
autofs           input            raw              tty12            tty26            tty4             tty53            ttyS0            vcs3             vhci
bsg              kmsg             rtc0             tty13            tty27            tty40            tty54            ttyS1            vcs4             vhost-net
bus              loop-control     sg0              tty14            tty28            tty41            tty55            ttyS2            vcs5             vhost-vsock
core             mapper           shm              tty15            tty29            tty42            tty56            ttyS3            vcs6             watchdog
cpu              mcelog           snapshot         tty16            tty3             ....
/dev # ip link add dummy0 type dummy
/dev # 

       可以看到,/dev下面显示多个特权设备 ,也正确的执行了网络接口修改指令。

       特权虽然能支持内核操作,但是其权限粒度较大,从Linux内核2.2开始,引入了 Capabilities 机制来对 内核操作进行了更加细粒度的控制,可以实现按需授权,我们以修改网络接口为例。修改yaml文件如下:

[root@k8s-master yaml]# cat security-context-demo2.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: security-context-demo2
spec:
  volumes:
  - name: sec-ctx-vol
    emptyDir: {}
  containers:
  - name: sec-ctx-demo
    image: busybox
    command: [ "sh", "-c", "sleep 1h" ]
    volumeMounts:
    - name: sec-ctx-vol
      mountPath: /data/demo
    securityContext:
       capabilities:
        add: # 添加
        - NET_ADMIN
        drop:  # 删除
        - KILL

      删除了privileged: true配置,增加了capabilities的列表配置,这里仅需要网络权限,配置NET_ADMIN即可。

[root@k8s-master yaml]# kubectl exec -it security-context-demo2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # ip link add dummy0 type dummy

    可以正确的修改了网络接口配置。

     容器的securityContext还支持seLinuxOptions,allowPrivilegeEscalation等其他配置项。完整的配置项参见:SecurityContext - Kubernetes指南

2、Pod准入策略

      Pod一般都是有应用开发者配置和创建的,上面的securityContext配置是否都被允许的呢?比如说容器设置了特权模式,拥有了完整的操作内核的能力,集群管理员如何进行统一管控?这里就要用到上面介绍的Pod准入策略,其作用是,在Pod创建,进行准入校验。

      在V1.21版本前,通过PodSecurityPolicy统一配置Pod的安全策略,该方式在V1.25中废弃,代替的是PodSecurity准入控制器。 PodSecurity定义了三种不同的策略,其限制程度逐级提升。

  • Privileged,不受限制的策略,提供最大可能范围的权限许可。通常针对由特权较高、受信任的用户所管理的系统级或基础设施级负载。
  • Baseline,限制性最弱的策略,禁止已知的策略提升。允许使用默认的(规定最少)Pod 配置。这种策略是最常见的,针对的是应用运维人员和非关键性应用的开发人员。其策略内容主要有,禁止有特权容器,禁止打破网络隔离等。
  • Restricted,限制性非常强的策略,遵循当前的保护 Pod 的最佳实践。这类策略会牺牲一些兼容性,主要针对运维人员和安全性很重要的应用的开发人员,以及不太被信任的用户。其策略内容主要有,要求容器以非 root 用户运行, 容器组必须弃用 ALL capabilities ,并且只允许添加 NET_BIND_SERVICE 能力等。

     目前PodSecurity仅限于这三种策略,且无法进行扩展的。这些策略需要应用于命名空间,实现对命名空间下所有Pod的约束,在命名空间中也可以配置三种模式。

  • enforce,策略违例会导致 Pod 被拒绝
  • audit,策略违例会触发审计日志中记录新事件时添加审计注解;但是 Pod 仍是被接受的。
  • warn,策略违例会触发用户可见的警告信息,但是 Pod 仍是被接受的。

      这三种模式配合上述的策略使用,命名空间可以配置多种策略。下面我们来看下案例。创建一个命名空间,设定安全策略。

[root@k8s-master yaml]# cat pod-level-ns.yaml 
apiVersion: v1
kind: Namespace
metadata:
  name: podlevel
  labels: 
    #强制执行baseline安全标准,执行拒绝。适用于最新的k8s版本
    pod-security.kubernetes.io/enforce: baseline
    pod-security.kubernetes.io/enforce-version: latest
    #对restricted的Pod安全标准执行警告(warn)和审核(audit),适用于最新的k8s版本
    pod-security.kubernetes.io/warn: restricted
    pod-security.kubernetes.io/warn-version: latest
    pod-security.kubernetes.io/audit: restricted
    pod-security.kubernetes.io/audit-version: latest

在命名空间中增加了labels属性配置,下面是对其设置的解释。

# 设定模式及安全标准策略等级
# MODE必须是 `enforce`, `audit`或`warn`其中之一。
# LEVEL必须是`privileged`, `baseline`或 `restricted`其中之一
pod-security.kubernetes.io/<MODE>: <LEVEL>

# 此选项是非必填的,用来锁定使用哪个版本的的安全标准
# MODE必须是 `enforce`, `audit`或`warn`其中之一。
# VERSION必须是一个有效的kubernetes minor version(例如v1.23),或者 `latest`
pod-security.kubernetes.io/<MODE>-version: <VERSION>

     本例中我们设置了三种模式,对于enforce,配置了baseline策略,也就是违反baseline策略的Pod一律拒绝执行;对于warn和audit,配置了restricted策略,也就是违反restricted策略的Pod进行警告和审核。

    接下来,我们创建一个违反restricted策略的Pod。由于它的条件比较苛刻,默认的配置也无法校验通过。

[root@k8s-master yaml]# cat level-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: level-pod
  namespace: podlevel
spec:
  containers:
    - image: nginx
      name: nginx
      ports:
        - containerPort: 80

   执行该文件,创建Pod

[root@k8s-master yaml]# kubectl apply -f level-pod.yaml 
Warning: would violate PodSecurity "restricted:latest": allowPrivilegeEscalation != false (container "nginx" must set securityContext.allowPrivilegeEscalation=false), unrestricted capabilities (container "nginx" must set securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container "nginx" must set securityContext.runAsNonRoot=true), seccompProfile (pod or container "nginx" must set securityContext.seccompProfile.type to "RuntimeDefault" or "Localhost")
pod/level-pod created
[root@k8s-master yaml]# kubectl get pod -n podlevel
NAME        READY   STATUS    RESTARTS   AGE
level-pod   1/1     Running   0          78s

    可以看到,违反了restricted策略,对用户进行了告警提示,但是Pod最终还是创建成功的。我们继续修改上面的Pod,让其违反baseline策略,baseline策略不允许有特权容器存在。

[root@k8s-master yaml]# cat level-pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: level-pod
  namespace: podlevel
spec:
  containers:
    - image: nginx
      name: nginx
      ports:
        - containerPort: 80
      securityContext:
       allowPrivilegeEscalation: true
       privileged: true
       capabilities:
         drop:
         - ALL

执行该文件,可以看到,拒绝执行Pod创建。

[root@k8s-master yaml]# kubectl apply -f level-pod.yaml 
Error from server (Forbidden): error when creating "level-pod.yaml": pods "level-pod" is forbidden: violates PodSecurity "baseline:latest": privileged (container "nginx" must not set securityContext.privileged=true)

    综上所述,Pod安全上下文配置实现了对于容器镜像的约束,Pod准入策略,实现了Pod的约束。逐级构建了安全防护网,实现Pod安全。

五、总结

    本篇我们介绍了K8S的安全相关内容,主要从API安全访问策略以及Pod安全策略两个方面。

   API安全访问策略,主要是对API Server的访问安全控制,其设置了认证,鉴权,准入三道关卡。认证是针对用户的身份合法性验证,鉴权是对访问的API接口合法性验证,准入是通过一系列的准入控制器进行准入检查。只有经过了这三层关卡,才能访问API Server的资源。

   Pod安全策略,主要介绍了安全上下文配置和Pod的准入检查。安全上下文配置是在Pod或者Container中设置securityContext属性;Pod的准入检查通过在命名空间下配置不同的策略,实现Pod创建时准入检查。

 附:

K8S初级入门系列之一-概述

K8S初级入门系列之二-集群搭建

K8S初级入门系列之三-Pod的基本概念和操作

K8S初级入门系列之四-Namespace/ConfigMap/Secret

K8S初级入门系列之五-Pod的高级特性

K8S初级入门系列之六-控制器(RC/RS/Deployment)

K8S初级入门系列之七-控制器(Job/CronJob/Daemonset)

K8S初级入门系列之八-网络

K8S初级入门系列之九-共享存储

K8S初级入门系列之十-控制器(StatefulSet)

K8S初级入门系列之十一-安全

K8S初级入门系列之十二-计算资源管理

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/778370.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

C++ 之命名空间namespace【详解】

文章目录 一&#xff0c;命名空间出现的意义二&#xff0c;命名空间的定义命名空间里面可以包含变量&#xff0c;函数&#xff0c;类型&#xff1a;命名空间不可以定义在局部作用域&#xff1a;命名空间可以嵌套&#xff1a; 三&#xff0c;同一个工程中允许存在多个相同名称的…

vue-cli项目中,使用webpack-bundle-analyzer进行模块分析,查看各个模块的体积,方便后期代码优化

一、安装 npm install --save-dev webpack-bundle-analyzer 二、在vue.config.js中配置 const BundleAnalyzerPlugin require(webpack-bundle-analyzer).BundleAnalyzerPlugin plugins: [new BundleAnalyzerPlugin({analyzerMode: server,analyzerHost: 127.0.0.1,analyze…

微信小程序原生上传图片和预览+云函数上传

1.前台页面 1.1wxml问阿金 <!-- 说明一个上传页面的按钮 --> <button type"primary" bindtap"uploadPage">上传页面展示</button> <!-- 声明一个上传服务器的按钮 --> <button type"warn" bindtap"uploadSeve…

【广州华锐互动】列车人员疏散VR虚拟演练系统

随着科技的不断发展&#xff0c;虚拟现实(VR)技术已经逐渐应用于各个领域。在火车站安全方面&#xff0c;为了提高旅客的安全意识和应对突发事件的能力&#xff0c;列车人员疏散VR虚拟演练系统应运而生。 列车人员疏散VR虚拟演练系统是一种基于虚拟现实技术的教育培训系统&…

进程信号的理解

进程信号 1. 信号的概念2. 信号的产生3. 信号的保存1. 信号其他相关常见概念2. 在内核中的表示3.信号集操作函数 4. 信号的处理&#xff08;捕捉&#xff09; 1. 信号的概念 信号的一生&#xff0c;进程信号从产生到被处理所经历的过程一共分成了三步&#xff1a;信号产生、信…

【C++】STL---list基本用法介绍

个人主页&#xff1a;平行线也会相交&#x1f4aa; 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 平行线也会相交 原创 收录于专栏【C之路】&#x1f48c; 本专栏旨在记录C的学习路线&#xff0c;望对大家有所帮助&#x1f647;‍ 希望我们一起努力、成长&…

文件批量智能归类

在日常工作中我们会经常碰到同一个文件夹里面多个文件有视频&#xff0c;图片&#xff0c;或视频标题名称不一样&#xff0c;图片名称不一样&#xff0c;整个文件夹看下来很混乱&#xff0c;需要找一个文件工花费很长时间去找&#xff0c;一个一个用眼睛去看&#xff0c;看久眼…

Linux开发工具使用

Linux开发工具使用 vim1.vim的基本概念2.vim三种模式的切换3.底行模式的基础操作4.命令模式下的基础操作5.vim的配置 yum1.yum的概念2.yum的基础操作 gcc/g1.gcc/g的概念2.一个C/C程序形成的过程3.gcc/g基本使用 make和makefile1.基础概念2.makefile【1】生成【2】清理 调试器g…

2023国际高校数学建模竞赛B题三星堆文物原创论文讲解

大家好呀&#xff0c;从昨天发布赛题一直到现在&#xff0c;总算完成了国际高校数学建模竞赛B题完整的成品论文。 本论文可以保证原创&#xff0c;保证高质量。绝不是随便引用一大堆模型和代码复制粘贴进来完全没有应用糊弄人的垃圾半成品论文。 B题论文共28页&#xff0c;一些…

docker php 容器安装redis和mongodb扩展

一、背景 很多项目(几乎所有)都有用到redis和mongodb来存储数据&#xff0c;php没有自带这些扩展&#xff0c;需要手动安装 二、PHP redis扩展安装步骤 这里以php8.2版本容器为例&#xff0c;以下命令中‘php82’均为容器名称&#xff0c;需要更换为你自己的实际名称&#x…

JZ31 栈的压入、弹出序列-C++

题目来源&#xff1a;牛客网 题目描述&#xff1a; 输入两个整数序列&#xff0c;第一个序列表示栈的压入顺序&#xff0c;请判断第二个序列是否可能为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序&#xff0c;序列4,5,3,2,1是该压栈序列…

List如何正确删除元素

public static void main(String[] args) {List<Integer> list Lists.newArrayList(1, 2, 3, 4, 5);list.forEach(item -> {if (item 3) {list.remove(3);}});} 使用foreach删除集合元素的时候&#xff0c;有可能会报错&#xff0c;报错信息如下&#xff1a; 这是因…

【docker】docker

目录 一、docker概念二、docker安装(centos7)三、docker架构3.1 镜像image3.2 容器container 四、配置docker镜像加速器五、docker命令5.1 docker服务命令5.2 docker镜像命令5.3 docker容器命令 六、docker容器的数据卷6.1 容器卷概念及作用6.2 配置数据卷6.3 挂载示例6.4 数据…

【Linux】多线程概念理论

目录 1 什么是线程&#xff1f; 2 线程的优点 3 线程的缺点 4 线程异常 5 线程用途 6 Linux线程和进程对比 1 什么是线程&#xff1f; 在一个程序里的一个执行路线就叫做线程&#xff08;thread&#xff09;。更准确的定义是&#xff1a;线程是“一个进程内部的控制序列…

【ARMv8 SIMD和浮点指令编程】NEON 移位指令——左右移位之术

NEON 移位指令主要涉及逻辑移位、算术移位两大类,同时下面还介绍了两个移位插入指令。 一、逻辑移位 1.1 SHL 左移(立即数)。该指令从向量中读取每个值,将每个结果左移一个立即值,将最终结果写入向量,并将向量写入目标 SIMD&FP 寄存器。 标量 SHL <V><d…

hive之存储优化

从这里开始就是hive调优阶段&#xff0c;怎么让hive跑的更快。 分区表和分桶表都是从存储方向进行优化。 目录 分区表&#xff1a; 概念&#xff1a; 代码&#xff1a; load填充数据&#xff1a; insertselect填充数据&#xff1a; 需求&#xff1a; 分区表基本操作 (一)…

vue3自定义日历

原理 现在的日历分为两种开头&#xff1a; 1. 日, 一, 二, 三, 四, 五, 六 2. 一, 二, 三, 四, 五, 六, 日一行7个日期&#xff0c;一共6行 其实不管哪种都一样&#xff0c;首先要确定第一行1号在哪个位置。 如果说是 日, 一, 二, 三, 四, 五, 六&#xff0c;那么getDay()是几…

了解Unity编辑器之组件篇UI(一)

UI组件&#xff1a;提供了用户交互&#xff0c;信息展示&#xff0c;用户导航等功能 一、Button&#xff1a;用于响应用户的点击事件 1.Interactable&#xff08;可交互&#xff09;&#xff1a;该属性控制按钮是否可以与用户交互&#xff0c;如果禁用则按钮无法被点击。可以通…

为什么TM服务器要安装php~

"想像力比知识更重要。因为知识是有限的&#xff0c;而想像力是无限&#xff0c;它包含了一切&#xff0c;推动着进步&#xff0c;是人类进化的源泉。 -- 爱因斯坦 为什么服务器要安装php~ 服务器为什么安装PHP (2023年) 导读&#xff1a;今天来给各位分享关于服务器为什么…

List有值二次转换给其他对象报null

List<PlatformUsersData> listData platformUsersMapper.selectPlatformUserDataById(data); users.setPlatformUsersData(listData);为什么listData 有值&#xff0c;users.getPlatformUsersData&#xff08;&#xff09;仍然为空在这段代码中&#xff0c;我们假设listD…