K8S初级入门系列之十一-安全
一、前言
安全是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-servicesaccountnamespace: 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-servicesaccountkubernetes.io/service-account.uid: 1f493157-13e7-4d43-9f0b-b8779dfe84aeType: kubernetes.io/service-account-tokenData
====
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-servicesaccountcontainers:- name: netshootimage: nicolaka/netshootimagePullPolicy: IfNotPresentcommand: ["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: defaultname: 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-rolebindingnamespace: default
subjects:
# 这里可以指定多个主体,User,ServiceAccount,Group
- kind: ServiceAccountname: my-servicesaccountnamespace: default apiGroup: ""
roleRef:# "roleRef" 指定与某 Role 绑定关系kind: Role name: pod-readerapiGroup: 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: ServiceAccountname: my-servicesaccountnamespace: default apiGroup: ""
roleRef:# "roleRef" 指定与某 Role 绑定关系kind: ClusterRole name: pod-reader-clusterroleapiGroup: 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-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /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: 1000runAsGroup: 3000fsGroup: 2000volumes:...
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: 1000volumes:- name: sec-ctx-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /data/demosecurityContext: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-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /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-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /data/demosecurityContext: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-volemptyDir: {}containers:- name: sec-ctx-demoimage: busyboxcommand: [ "sh", "-c", "sleep 1h" ]volumeMounts:- name: sec-ctx-volmountPath: /data/demosecurityContext:capabilities:add: # 添加- NET_ADMINdrop: # 删除- 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: podlevellabels: #强制执行baseline安全标准,执行拒绝。适用于最新的k8s版本pod-security.kubernetes.io/enforce: baselinepod-security.kubernetes.io/enforce-version: latest#对restricted的Pod安全标准执行警告(warn)和审核(audit),适用于最新的k8s版本pod-security.kubernetes.io/warn: restrictedpod-security.kubernetes.io/warn-version: latestpod-security.kubernetes.io/audit: restrictedpod-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-podnamespace: podlevel
spec:containers:- image: nginxname: nginxports:- 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-podnamespace: podlevel
spec:containers:- image: nginxname: nginxports:- containerPort: 80securityContext:allowPrivilegeEscalation: trueprivileged: truecapabilities: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初级入门系列之十二-计算资源管理
相关文章:

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

【雕爷学编程】MicroPython动手做(02)——尝试搭建K210开发板的IDE环境6
#尝试搭建K210的Micropython开发环境(Win10) #实验程序之六:测试Microphone阵列算法 #尝试搭建K210的Micropython开发环境(Win10) #实验程序之六:测试Microphone阵列算法from Maix import MIC_ARRAY as mi…...

“深入解析Spring Boot:从入门到精通“
标题:深入解析Spring Boot:从入门到精通 摘要:本文深入解析了Spring Boot框架,从入门到精通,包括核心概念、特性、使用方法和示例代码。通过阅读本文,读者将对Spring Boot有一个全面的了解,并可…...

[自然语言处理] 自然语言处理库spaCy使用指北
spaCy是一个基于Python编写的开源自然语言处理库。基于自然处理领域的最新研究,spaCy提供了一系列高效且易用的工具,用于文本预处理、文本解析、命名实体识别、词性标注、句法分析和文本分类等任务。 spaCy的官方仓库地址为:spaCy-github。本…...

【新日语(2)】第6課 拓哉もさしみを食べたがってします
第6課 拓哉もさしみを食べたがっています 注释: 食べたがっています:食べ+たが+ています、想要吃。たがっています:たがる+ています、想要。 练习A 一、 例句 わたしは、明日、デパートへ行きます。 …...

uni-app 经验分享,从入门到离职(一)——初始 uni-app,快速上手(文末送书福利1.0)
文章目录 📋前言🎯什么是 uni-app🎯创建第一个 uni-app 项目🧩前期工作🧩创建项目(熟悉默认项目、结构)🧩运行项目 📝最后🎯文末送书🔥参与方式 &…...

Python爬虫实例之淘宝商品页面爬取(api接口)
可以使用Python中的requests和BeautifulSoup库来进行网页爬取和数据提取。以下是一个简单的示例: import requests from bs4 import BeautifulSoupdef get_product_data(url):# 发送GET请求,获取网页内容headers {User-Agent: Mozilla/5.0 (Windows NT…...

并发编程 | CompletionService - 如何优雅地处理批量异步任务
引言 上一篇文章中,我们详细地介绍了 CompletableFuture,它是一种强大的并发工具,能帮助我们以声明式的方式处理异步任务。虽然 CompletableFuture 很强大,但它并不总是最适合所有场景的解决方案。 在这篇文章中,我们…...

医学案例|ROC曲线之面积对比
一、案例介绍 为评价CT和CT增强对肝癌的诊断效果,共检查了32例患者,每例患者分别用两种方法检查,由医生盲态按4个等级诊断,最后经手术病理检查确诊其中有16例患有肝癌,评价CT个CT增强对肝癌是有有诊断效果并且试着比较…...

Kotlin线程的基本用法
线程的基本用法 新建一个类继承自Thread,然后重写父类的run()方法 class MyThread : Thread() {override fun run() {// 编写具体的逻辑} }// 使用 MyThread().start()实现Runnable接口 class MyThread : Runnable {override fun run() {// 编写具体的逻辑} }// …...

2.03 PageHelper分页工具
步骤1:在application.yml中添加分页配置 # 分页插件配置 pagehelper:helperDialect: mysqlsupportMethodsArguments: true步骤2:在顶级工程pom文件下引入分页插件依赖 <!--5.PageHelper --> <dependency><groupId>com.github.pagehe…...

VUE中使用ElementUI组件的单选按钮el-radio-button实现第二点击时取消选择的功能
页面样式为: html 代码为: 日志等级: <el-radio-group v-model"logLevel"><el-radio-button label"DEBUG" click.native.prevent"changeLogLevel(DEBUG)">DEBUG</el-radio-button><el-r…...

瓴羊Quick BI:可视化大屏界面设计满足企业个性需求
大数据技术成为现阶段企业缩短与竞争对手之间差距的重要抓手,依托以瓴羊Quick BI为代表的工具开展内部数据处理分析工作,也成为诸多企业持续获取竞争优势的必由之路。早年间国内企业倾向于使用进口BI工具,但随着瓴羊Quick BI等一众国内数据处…...

617. 合并二叉树
题目 题解一:递归 /*** 递归* param root1* param root2* return*/public TreeNode mergeTrees(TreeNode root1, TreeNode root2) {//结束条件if (root1 null) {return root2;} //结束条件if (root2 null) {return root1;}//两节点数值相加TreeNode me…...

【T1】存货成本异常、数量为零金额不为零的处理方法。
【问题描述】 使用T1飞跃专业版的过程中, 由于业务问题或者是操作问题, 经常会遇到某个商品成本异常不准确, 或者是遇到数量为0金额不为0的情况,需要将其成本调为0。 但是T1软件没有出入库调整单,并且结账无法针对数量…...

EtherNet IP转PROFINET网关连接西门子与欧姆龙方法
本文主要介绍了捷米特JM-PN-EIP(EtherNet/IP转PROFINET)网关西门子200智能PLC(PROFINET)和欧姆龙系统EtherNet/IP通信的配置过程。 1, 将 EDS 文件复制到欧姆龙软件的对应文件夹下 2, 首先添加捷米特JM-PN-EIP网关的全局变量&…...

低代码开发重要工具:jvs-flow(流程引擎)审批功能配置说明
流程引擎场景介绍 流程引擎基于一组节点与执行界面,通过人机交互的形式自动地执行和协调各个任务和活动。它可以实现任务的分配、协作、路由和跟踪。通过流程引擎,组织能够实现业务流程的优化、标准化和自动化,提高工作效率和质量。 在企业…...

[SQL挖掘机] - GROUP BY语句
介绍: group by 是 sql 中用于对结果集进行分组的关键字。通过使用 group by,可以根据一个或多个列的值将结果集中的行分组,并对每个分组应用某种聚合函数(如 count、sum、avg 等)以生成汇总信息。这样可以方便地对数据进行分类、…...

【ubuntu|内核】ubuntu 22.04修改内核为指定版本
every blog every motto: You can do more than you think. https://blog.csdn.net/weixin_39190382?typeblog 0. 前言 ubuntu 22.04 安装指定内核 1. 正文 查看已安装的内核镜像 dpkg --get-selections | grep linux-image1.1 安装指定版本的内核 安装镜像 sudo apt-g…...

Carla教程一:动力学模型到LQR
Carla教程一、动力学模型到LQR 从运动学模型和动力学模型到LQR 模型就是可以描述车辆运动规律的模型。车辆建模都是基于自行车模型的设定,也就是将四个轮子抽象为自行车一样的两个轮子来建模。 1、运动学模型 运动学模型是基于几何关系分析出来的,一般适用于低俗情况下,…...

IDE/mingw下动态库(.dll和.a文件)的生成和部署使用(对比MSVC下.dll和.lib)
文章目录 概述问题的产生基于mingw的DLL动态库基于mingw的EXE可执行程序Makefile文件中使用Qt库的\*.a文件mingw下的*.a 文件 和 *.dll 到底谁起作用小插曲 mingw 生成的 \*.a文件到底是什么为啥mingw的dll可用以编译链接过程转换为lib引导文件 概述 本文介绍了 QtCreator mi…...

点击加号添加新的输入框
实现如上图的效果 html部分: <el-form-item class"forminput" v-for"(item,index) in formdata.description" :key"index" :label"描述(index1)" prop"description"><el-input v-model"formdata…...

SQL AND OR 运算符
AND & OR 运算符用于基于一个以上的条件对记录进行过滤。 如果第一个条件和第二个条件都成立,则 AND 运算符显示一条记录。 如果第一个条件和第二个条件中只要有一个成立,则 OR 运算符显示一条记录。 下面是选自 "students" 表的数据&a…...

6、C++内存模型
原文: https://my.oschina.net/u/2516597/blog/805489 背景 C11开始支持多线程,其中提供了原子类型atomic, 和atomic关系比较密切的是memory_order,所有的内存模型都是指atomic类型 enum memory_order {memory_order_relaxed,memory_order…...

上海市青少年算法2023年1月月赛(丙组)
上海市青少年算法2023年1月月赛(丙组)T1 实验日志 题目描述 小爱正在完成一个物理实验,为期n天,其中第i天,小爱会记录ai条实验数据在实验日志中。 已知小爱的实验日志每一页最多纪录m条数据,每天做完实验后他都会将日志合上,第二天,他便从第一页开始依次翻页,直到找到…...

移动开发之Wifi列表获取功能
一、场景 业务需要通过App给设备配置无线网络连接,所以需要App获取附近的WiFi列表,并进行网络连接验证。 二、安卓端实现 1、阅读谷歌官网文档,关于Wifi 接口使用 https://developer.android.com/guide/topics/connectivity/wifi-scan?hl…...

MyBatisPlus - 实体类 的 常用注解
TableName(“表名”) 假设 表名是 book,实体类类名是 Book MyBatisPlus会进行自动映射 但如果 表名是 tab_book,实体类类名是 Book 那么MyBatisPlus就无法进行自动映射,需要我们使用 TableName注解 去指定实体类对应的表 如下 TableNa…...

vue3+ts+elementui-plus二次封装树形表格实现不同层级展开收起的功能
一、TableTreeLevel组件 <template><div classmain><div class"btns"><el-button type"primary" click"expandLevel(1)">展开一级</el-button><el-button type"primary" click"expandLevel(2…...

Qt之切换语言的方法(传统数组法与Qt语言家)
http://t.csdn.cn/BVigB 传统数组法: 定义一个字符串二维数组, QString weekStr[2][7] {"星期一","星期二","星期三","星期四","星期五","星期六","星期日",\ "Monday&…...

qt root start faild
深入解析chown -r root:root命令_笔记大全_设计学院 ffmpeg第五弹:QtSDLffmpeg视频播放演示_txp玩Linux的博客-CSDN博客...