K8s进阶6——pod安全上下文、Linux Capabilities、OPA Gatekeeper、gvisor_ ...

打印 上一主题 下一主题

主题 888|帖子 888|积分 2664

pod安全上下文配置参数释义spec.securityContextPod级别的安全上下文,对内部所有容器均有效。spec.securityContext.runAsUser < integer >以指定的用户身份运行容器进程,默认由镜像中的USER指定。spec.securityContext.runAsGroup < integer >以指定的用户组运行容器进程,默认利用的组随容器运行时。spec.securityContext.fsGroup < integer >数据卷挂载后的目录文件设置为该组。spec.securityContext.runAsNonRoot < boolean >是否以非root身份运行。spec.securityContext.seLinuxOptions < Object >SELinux的相关配置。spec.securityContext.sysctls < lObject >应用到当前Pod的名称空间级别的sysctl参数设置列表。spec.containers. securityContext容器级别的安全上下文,仅生效于当前容器。spec.containers. securityContext.runAsUser < integer >以指定的用户身份运行容器进程。spec.containers. securityContext.runAsGroup < integer >以指定的用户组运行容器进程。spec.containers. securityContext.runAsNonRoot < boolean >是否以非root身份运行。spec.containers. securityContext.allowPrivilegeEscalation < boolean >是否允许特权升级。spec.containers. securityContext.capabilities < Object >于当前容器上添加 (add) 删除 (drop)的内核能力。spec.containers. securityContext.add < [ ]string >添加由列表界说的各内核能力。spec.containers. securityContext.drop< [ ]string >移除由列表界说的各内核能力。spec.containers. securityContext.privileged < boolean >是否运举动特权容器。spec.containers. securityContext.readOnlyRootFilesystem < boolean >是否将根文件系统设置为只读模式spec.containers. securityContext.selinuxOptions < opect >SeLinux的相关配置。 1.2 案例1

   配景
  

  • 容器中的应用步伐默认以root账号运行的,这个root与宿主机root用户权限是一样的,拥有大部分对Linux内核的系统调用权限,不安全,所以我们应该将容器里的步伐以普通用户运行,减少应用步伐对权限的利用。
  需求:
  

  • 设置容器以普通用户运行。
  实现思绪:
  

  • Dockerfile里利用USER指定运行用户。
  • K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID。
  1.2.1 dockerfile方式

1.预备步伐源码。我这里是利用python写的一个脚本,渲染了一个index.html的模板首页,端口是8080。
  1. [root@k8s-master1 flask-demo]# cat Dockerfile
  2. FROM python                          ##基于python基础镜像构建。
  3. RUN mkdir /data/www -p        
  4. COPY . /data/www              ##将当前目录下的文件目录拷贝到/data/www目录。
  5. RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/  ##安装flask,是Python 的一个web框架
  6. WORKDIR /data/www               ##定义默认目录。
  7. CMD python qingjun.py                ##使用python启动这个man.py脚本。
  8. ##使用python写了一个web的demo。
  9. [root@k8s-master1 flask-demo]# cat qingjun.py
  10. from flask import Flask,render_template
  11. app = Flask(__name__)
  12. @app.route('/')
  13. def index():
  14.     return render_template("index.html")
  15. if __name__ == "__main__":
  16.     app.run(host="0.0.0.0",port=8080)
  17. [root@k8s-master1 flask-demo]# cat templates/index.html
  18. <!DOCTYPE html>
  19. <html lang="en">
  20. <head>
  21.     <meta charset="UTF-8">
  22.     <title>首页</title>
  23. </head>
  24. <body>
  25. <h1>你好  qingjun!</h1>
  26. </body>
  27. </html>
复制代码
2.构建镜像。
  1. [root@k8s-master1 flask-demo]# docker build -t flask-demo:v1 .
复制代码

3.利用构建镜像运行容器qingjun,此时可以查察到容器运行的进程是以root用户举行的。
  1. [root@k8s-master1 flask-demo]# docker run -d --name=qingjun -p 80:8080 flask-demo:v1
复制代码


4.访问web页面。

5.此时我们修改dockerfile,指定利用普通用户启用。
  1. [root@k8s-master1 flask-demo]# cat Dockerfile
  2. FROM python
  3. RUN useradd qingjun    ##创建普通用户qingjun。
  4. RUN mkdir -p  /data/www
  5. COPY . /data/www  
  6. RUN chown -R qingjun /data     #数据目录修改权限,不然还是root用户。
  7. RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
  8. WORKDIR /data/www
  9. USER qingjun       ##指定使用什么用户启用python程序。
  10. CMD python qingjun.py
复制代码
6.再次构建镜像。
  1. [root@k8s-master1 flask-demo]# docker build -t  flask-demo:v2 .
复制代码
7.新镜像运行容器baimu,此时可以查察到容器运行的进程是以普通用户pyuser举行的。
  1. [root@k8s-master1 flask-demo]# docker run -d --name=baimu -p 81:8080 flask-demo:v2
复制代码


8.访问网页。

1.2.2 pod安全上下文方式

   deployment.yaml指定示例:
  1. spec:
  2. securityContext:
  3.    runAsUser: 1000          ##镜像里必须有这个用户UID。
  4.    fsGroup: 1000            ##数据卷挂载后的目录属组设置为该组。
  5. containers:
  6. - image: 192.168.130.152/qingjun/flask-demo:v3
  7.    name: web
  8.    securityContext:
  9.      allowPrivilegeEscalation: false     ##不允许提权。
复制代码
1.利用dockerfile构建一个镜像,创建一个普通用户qingjun用户,但不指定qingjun用户启用步伐。
  1. [root@k8s-master1 ~]# cat dockerfile
  2. FROM python
  3. RUN mkdir /data/www -p
  4. RUN useradd qingjun
  5. COPY . /data/www
  6. RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
  7. WORKDIR /data/www
  8. CMD python qingjun.py
  9. ##构建镜像。
  10. [root@k8s-master1 ~]# docker build -t dockerfile flask-demo:v3 ./
  11. ##将进项推送到harbor仓库的qingjun库里。
  12. [root@k8s-master1 docker]# docker tag flask-demo:v3 192.168.130.152/qingjun/flask-demo:v3
  13. [root@k8s-master1 docker]# docker push 192.168.130.152/qingjun/flask-demo:v3
复制代码

2.利用刚创建的镜像运行容器,进入容器看到步伐是用root用户运行的。
  1. [root@k8s-master1 docker]# docker run -d --name=k8s_qingjun 192.168.130.152/qingjun/flask-demo:v3
  2. [root@k8s-master1 docker]# docker exec -it k8s_qingjun /bin/bash
复制代码

2.创建pod,利用安全上下文指定普通用户id。
  1. [root@k8s-master1 flask-demo]# cat deploy.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   labels:
  6.     app: qingjun
  7.   name: qingjun
  8. spec:
  9.   replicas: 1
  10.   selector:
  11.     matchLabels:
  12.       app: qingjun
  13.   template:
  14.     metadata:
  15.       labels:
  16.         app: qingjun
  17.     spec:
  18.       containers:
  19.       - image: 192.168.130.152/qingjun/flask-demo:v3
  20.         name: flask-demo
  21.         securityContext:     ##指定普通用户id。
  22.           runAsUser: 1000    ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。
  23. [root@k8s-master1 flask-demo]# kubectl  apply  -f deploy.yaml
复制代码
3.进入pod容器查察,是以普通用户id为1000的qingjun用户启用步伐。

4.若构建镜像时没有提前创建普通用户,则在pod.yaml里指定安全上下文创建的容器步伐里的普通用户id就是从1000开始。
  1. ##构建镜像没有提前创建普通用户。
  2. [root@k8s-master1 ~]# cat dockerfile
  3. FROM python
  4. RUN mkdir /data/www -p
  5. COPY . /data/www
  6. RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
  7. WORKDIR /data/www
  8. CMD python qingjun.py
  9. [root@k8s-master1 ~]# docker build -t flask-demo:v4 .
  10. ##新镜像推送到镜像仓库。
  11. [root@k8s-master1 ~]# docker tag flask-demo:v4 192.168.130.152/qingjun/flask-demo:v4
  12. [root@k8s-master1 ~]# docker push 192.168.130.152/qingjun/flask-demo:v4
  13. ##使用新镜像运行一个容器,进入容器查看是否是默认的root用户启动程序的。
  14. [root@k8s-master1 ~]# docker run -d --name=qingjun1 192.168.130.152/qingjun/flask-demo:v4
  15. [root@k8s-master1 ~]# docker exec -it qingjun1 /bin/bash
  16. ##创建一个pod,使用安全上下文指定普通用户id启动。
  17. [root@k8s-master1 ~]# cat deploy2.yaml
  18. apiVersion: apps/v1
  19. kind: Deployment
  20. metadata:
  21.   labels:
  22.     app: qingjun1
  23.   name: qingjun1
  24. spec:
  25.   replicas: 1
  26.   selector:
  27.     matchLabels:
  28.       app: qingjun1
  29.   template:
  30.     metadata:
  31.       labels:
  32.         app: qingjun1
  33.     spec:
  34.       containers:
  35.       - image: 192.168.130.152/qingjun/flask-demo:v4
  36.         name: flask-demo1
  37.         securityContext:     ##指定普通用户id。
  38.           runAsUser: 1000    ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。
  39. ##导入yaml,进入pod容器查看是否是用户id为1000的普通用户启动的容器。
  40. [root@k8s-master1 ~]# kubectl  apply -f deploy2.yaml
复制代码

1.3 案例2

   配景:
  

  • 容器中有些应用步伐可能需要访问宿主机装备、修改内核等需求,默认情况下,容器没有这个能力,这时可以思量给容器设置特权模式。
  需求:
  

  • 避免利用特权容器。
  启用特权模式:
  

  • docker方式:容器时指定–privileged参数,体现该容器内的步伐是以root用户启动,跟在宿主机上用root用户启动一个进程完全没有区别,都可以调用内核的所有参数。
  • K8s方式:在pod安全上下文设置参数
  1. containers:
  2. - image: 192.168.130.152/qingjun/flask-demo:v3
  3.   name: web
  4.   securityContext:
  5.     privileged: true
复制代码
留意事项:
  

  • 启用特权模式就意味着,你要为容器提供了访问Linux内核的所有能力,这是很伤害的,为了减少系统调用的供给,可以利用Capabilities为容器赋予仅所需的能力。
  1.在deployment.yaml文件中添加此参数,重新构建pod容器就会生效。

1.4 Linux Capabilities方案

   基本了解:
  

  • Capabilities 的出现就是增补直接在deployment.yaml中指定特权从而导致权限过大的弊端。
  • 它是一个内核级别的权限调用解决方案,它允许对内核调用权限举行更细粒度的控制,而不是简单地以 root 身份能力授权,但比之前讲的Seccomp限制的容器进程系统调用更大抵一些。
  • Capabilities 包罗更改文件权限、控制网络子系统和执行系统管理等功能。在pod安全上下文中添加或删除Capabilities,可以做到容器精致化权限控制。
  留意事项:
  

  • Linux 权能常数界说的情势为 CAP_XXX。但是 container清单中列举权能时,需要将权能名称中的 CAP_ 部分去掉。
  • 比方,要添加 CAP_SYS_TIME, 可在权能列表中添加 SYS_TIME。
  1.查察linux Capability权能。
  1. [root@k8s-master1 ~]# yum install man-pages
  2. [root@k8s-master1 ~]# man capabilities
复制代码

capability权能名称释义CAP_AUDIT_CONTROL启用和禁用内核审计;改变审计过滤规则;检索审计状态和过滤规则CAP_AUDIT_READ允许通过 multicast netlink 套接字读取审计日志CAP_AUDIT_WRITE将记录写入内核审计日志CAP_BLOCK_SUSPEND利用可以制止系统挂起的特性CAP_CHOWN修改文件所有者的权限CAP_DAC_OVERRIDE忽略文件的 DAC 访问限制CAP_DAC_READ_SEARCH忽略文件读及目录搜索的 DAC 访问限制CAP_FOWNER忽略文件属主 ID 必须和进程用户 ID 相匹配的限制CAP_FSETID允许设置文件的 setuid 位CAP_IPC_LOCK允许锁定共享内存片段CAP_IPC_OWNER忽略 IPC 所有权检查CAP_KILL允许对不属于本身的进程发送信号CAP_LEASE允许修改文件锁的 FL_LEASE 标志CAP_LINUX_IMMUTABLE允许修改文件的 IMMUTABLE 和 APPEND 属性标志CAP_MAC_ADMIN允许 MAC 配置或状态更改CAP_MAC_OVERRIDE覆盖 MAC(Mandatory Access Control)CAP_MKNOD允许利用 mknod() 系统调用CAP_NET_ADMIN允许执行网络管理任务CAP_NET_BIND_SERVICE允许绑定到小于 1024 的端口CAP_NET_BROADCAST允许网络广播和多播访问CAP_NET_RAW允许利用原始套接字CAP_SETGID允许改变进程的 GIDCAP_SETFCAP允许为文件设置任意的 capabilitiesCAP_SETPCAP参考 capabilities man pageCAP_SETUID允许改变进程的 UIDCAP_SYS_ADMIN允许执行系统管理任务,如加载或卸载文件系统、设置磁盘配额等CAP_SYS_BOOT允许重新启动系统CAP_SYS_CHROOT允许利用 chroot() 系统调用CAP_SYS_MODULE允许插入和删除内核模块CAP_SYS_NICE允许提升优先级及设置其他进程的优先级CAP_SYS_PACCT允许执行进程的 BSD 式审计CAP_SYS_PTRACE允许跟踪任何进程CAP_SYS_RAWIO允许直接访问 /devport、/dev/mem、/dev/kmem 及原始块装备CAP_SYS_RESOURCE忽略资源限制CAP_SYS_TIME允许改变系统时钟CAP_SYS_TTY_CONFIG允许配置 TTY 装备CAP_SYSLOG允许利用 syslog() 系统调用CAP_WAKE_ALARM允许触发一些能唤醒系统的东西(比如 CLOCK_BOOTTIME_ALARM 计时器) 案例1

   

  • 需求:添加指定权限。容器默认没有挂载文件系统能力,添加SYS_ADMIN权能增加这个功能。
  1.查察当前pod容器里具备哪些内核调用权限。
  1. [root@k8s-master1 flask-demo]# kubectl  run centos --image=centos -- sleep 24h
  2. [root@k8s-master1 flask-demo]# kubectl  exec -it centos -- bash
  3. [root@centos /]# capsh --print    ##查看权能。
复制代码

2.查察特权用户具备的权限。

3.查察普通用户当前权限。

4.创建一个测试容器,进入容器查察没有挂载权限,因为是普通用户。
  1. [root@k8s-master1 flask-demo]# kubectl run bs --image=busybox -- sleep 24h
复制代码


5.创建pod时指定挂载权限,进入容器测试可以挂载目录。
  1. [root@k8s-master1 flask-demo]# cat pod.yaml
  2. apiVersion: v1
  3. kind: Pod
  4. metadata:
  5.   name: cap-pod
  6. spec:
  7.   containers:
  8.   - image: busybox
  9.     name: test
  10.     command:
  11.     - sleep
  12.     - 24h
  13.     securityContext:
  14.       capabilities:
  15.         add: ["SYS_ADMIN"]      ##添加挂载权限。
  16. [root@k8s-master1 flask-demo]# kubectl  apply -f pod.yaml
复制代码


案例2

   

  • 需求:只读容器文件系统。只读挂载容器文件系统,防止恶意二进制文件创建。
  1.容器默认的普通用户是可读可写。

2.deployment.yaml中指定只读参数。
  1. [root@k8s-master1 flask-demo]# cat deploy.yaml
  2. apiVersion: apps/v1
  3. kind: Deployment
  4. metadata:
  5.   labels:
  6.     app: qingjun
  7.   name: qingjun
  8. spec:
  9.   replicas: 1
  10.   selector:
  11.     matchLabels:
  12.       app: qingjun
  13.   template:
  14.     metadata:
  15.       labels:
  16.         app: qingjun
  17.     spec:
  18.       containers:
  19.       - image: 192.168.130.152/qingjun/flask-demo:v4
  20.         name: flask-demo
  21.         securityContext:
  22.           runAsUser: 1000
  23.           readOnlyRootFilesystem: true    ##添加此行。
  24.          
  25. [root@k8s-master1 flask-demo]# kubectl  apply -f deploy.yaml
复制代码
3.进入容器查察,没有写权限,只能读。

二、pod安全策略

2.1 PSP(已废弃)

   概念:
  

  • PodSecurityPolicy,简称PSP,是K8s中Pod部署时重要的安全校验本领,能够有效地约束应用运行时举动安全。
  • 利用PSP对象界说一组Pod在运行时必须遵循的条件及相关字段的默认值,只有Pod满意这些条件才会被K8s接受。
  • Pod安全策略实现为一个准入控制器,默认没有启用,当启用后会强制实施Pod安全策略,没有满意的Pod将无法创建。因此,发起在启用PSP之前先添加策略并对其授权。
  启用Pod安全策略:
  

  • kube-apiserver.yaml配置文件添加准入控制器。
  • 重启kubelet,systemctl restart kubelet

  玩法思绪:
  

  • 创建SA服务账号。
  • 将SA绑定到系统内置Role edit。
  • 创建利用PSP权限的Role qingjun。
  • 将SA绑定到角色qingjun上。
  • 界说PodSecurityPolicy策略。
  留意事项:
  

  • 利用复杂,权限模型存在缺陷,控制不明白,一旦出现问题欠好定位,所以将再1.21版本弃用PSP,在1.25版本删除PSP。 弃用官方文献
  • 替代方案KEP 2579。
  • 替代外部控制器方案:K-Rail、 Kyverno、 OPA/Gatekeeper 。
  2.1.1 安全策略限制维度

配置项描述privileged启动特权容器。hostPID,hostIPC利用主机namespaces。hostNetwork,hostPorts利用主机网络和端口。volumes允许利用的挂载卷范例。allowedHostPaths允许hostPath范例挂载卷在主机上挂载的路径,通过pathPrefix字段声明允许挂载的主机路径前缀组。allowedFlexVolumes允许利用的指定FlexVolume驱动。fsGroup配置Pod中挂载卷利用的辅组ID。readOnlyRootFilesystem约束启动Pod利用只读的root文件系统。runAsUser,runAsGroup,supplementalGroups指定Pod中容器启动的用户ID以及主组和辅组ID。allowPrivilegeEscalation,defaultAllowPrivilegeEscalation约束Pod中是否允许配置allowPrivilegeEscalation=true,该配置会控制setuid的利用,同时控制步伐是否可以利用额外的特权系统调用。defaultAddCapabilities,requiredDropCapabilities,allowedCapabilities控制Pod中利用的Linux Capabilities。seLinux控制Pod利用seLinux配置。allowedProcMountTypes控制Pod允许利用的ProcMountTypes。annotations配置Pod中容器利用的AppArmor或seccomp。forbiddenSysctls,allowedUnsafeSysctls控制Pod中容器利用的sysctl配置。 2.2 OPA Gatekeeper方案

   前提了解:
  

  • OPA(Open Policy Agent):是一个开源的、通用策略引擎,可以将策略编写为代码。提供一个种高级声明性语言-Rego来编写策略,并把决策这一步骤从复杂的业务逻辑中解耦出来。
  • 是PSP的替代方案,属于外部准入控制器。
  • OPA官网
  • Gatekeeper项目地址
  • Gatekeeper文档
  OPA可以用来做什么?
  

  • 拒绝不符合条件的YAML部署。
  • 允许利用哪些堆栈中的镜像。
  • 允许在哪个时间段访问系统。
  • 等等。
  OPA Gatekeeper的概念:
  

  • Gatekeeper 是基于 OPA的一个 Kubernetes 策略解决方案,可替代PSP或者部分RBAC功能。因为OPA与K8s对接偏向于底层,欠好利用,所以社区就基于OPA引擎开发了OPA Gatekeeper的解决方案,方便利用。
  • 当在集群中部署了Gatekeeper组件,APIServer所有的创建、更新或者删除操纵都会触发Gatekeeper来处理,如果不满意策略则拒绝。
  工作流程图:

  2.2.1 安装Gatekeeper

1.下载预备安装yaml文件,下载地址

2.导入文件一键部署,并查察。
  1. [root@k8s-master1 opa]# kubectl apply -f gatekeeper.yaml
复制代码

2.2.2 编写策略

   Gatekeeper的策略由两个资源对象组成:
  

  • Template模板:在背面我们需要自界说限制策略,策略是怎么实现的就需要我们先写一个实现逻辑,这里就把这个实现逻辑称之为模板,利用rego语言。
  • Contsraint约束:自界说限制策略,当我们创建任何一个资源时可以先通过这个约束来举行过滤处理,违反约束就代表能限制你创建主动动作,不让你创建这个资源。所以约束就是负责K8s资源对象的过滤或者为Template模板提供输入参数。
  实现思绪:
  

  • 先利用rego语言编写Template模板,在这个模板里自界说实现逻辑,就相当于写一个脚本逻辑。
  • 自界说Constraint约束,约束格式类似rbac授权那种,要限制什么资源,这个资源在什么组。
  • 当我们创建一个资源时,比如创建deployment时,如果触发了约束,则就会带入到模板里,看是否能违反约束,如果违反约束就相当于出发了开关,不让你创建deployment。
  模板核心字段释义:
  

  • 字段containers = input.review.object.spec.template.spec.containers

    • input.review.object :抓取约束模板里自界说的限制资源。比如我在约束模板里限制deployment资源,这里就是是抓取的deployment资源。
    • .spec.template.spec.containers:代表获取资源对象的yaml文件里.spec.template.spec.containers内容,可以用kubectl explan获取。比如我在约束模板里限制的是deployment资源,那么当我创建deployment资源时就会抓取deployment.yaml文件里的spec.template.spec.containers下的内容,如下图,就是获取下图中的红框内容。此时拿到这块内容,再根据模板里的逻辑举行判断,看最后是否输出的true,如果则代表违反约束,你就不能创建deployment资源。


  2.2.3 案例1

   需求
  

  • 禁止容器启用特权。若用户创建的资源里有开启特权容器参数,则限制用户不能创建资源。
  实现代码逻辑:
  

  • violation函数返回的是布尔值True或者False。我取deployment.yaml文件里的特权容器参数,若返回值为false,则不会继承往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违反约束需要拦截,就相当于触碰到开关起到拦截作用。
  1.编写模板和约束,在模板里利用rego脚本匹配约束内里的的资源。
  1. ##模板
  2. [root@k8s-master1 opa]# cat privileged_tpl.yaml
  3. apiVersion: templates.gatekeeper.sh/v1beta1
  4. kind: ConstraintTemplate
  5. metadata:
  6.   name: qingjun
  7. spec:
  8.   crd:
  9.     spec:
  10.       names:
  11.         kind: qingjun
  12.   targets:
  13.     - target: admission.k8s.gatekeeper.sh
  14.       rego: |                                ##自定义匹配策略,使用rego语言编写。
  15.         package admission                    ##传入一个依赖包,唯一的,类似命名空间。
  16.         violation[{"msg": msg}] {            ##定义一个key-value变量。
  17.           containers = input.review.object.spec.template.spec.containers    ##获取自定义对象的pod.yaml里对应的字段。
  18.           c_name := containers[0].name       ##获取第一个容器名称并赋予变量名为c_name,0代表第一个。
  19.           containers[0].securityContext.privileged       ##获取第一个容器的securityContext.privileged值。这里是判断返回是否为true,返回true说明违反约束。
  20.           msg := sprintf("提示:'%v'容器禁止启用特权!",[c_name])   ##打印结果。
  21.         }
  22. [root@k8s-master1 opa]# kubectl  apply -f privileged_tpl.yaml
  23. ##约束
  24. [root@k8s-master1 opa]# cat privileged_constraints.yaml
  25. apiVersion: constraints.gatekeeper.sh/v1beta1
  26. kind: qingjun
  27. metadata:
  28.   name: baimu
  29. spec:
  30.   match:
  31.     kinds:
  32.       - apiGroups: ["apps"]
  33.         kinds:
  34.         - "Deployment"
  35.         - "DaemonSet"
  36.         - "StatefulSet"
  37. [root@k8s-master1 opa]# kubectl  apply -f privileged_constraints.yaml
复制代码

2.测试一,当创建deployment资源时,没有匹配到“securityContext.privileged: true” 字段,说明该容器没有启用特权,可以正常创建,没有违反约束,所以最后可以正常创建deploy。

3.测试二,当创建deployment资源时,匹配到“securityContext.privileged: false ”字段,说明没有开启特权容器,最后可以正常创建deploy。

4.测试三,当创建deployment资源时,匹配到“securityContext.privileged: true”字段,说明容器需要开启特权,需要限制不能开启,所以最后不能创建deployment资源。

5.修改deploy.yaml容器名称,最后会打印出详细错误信息,方便测试。



2.2.4 案例2

   需求
  

  • 只允许利用特定的镜像堆栈。
  实现代码逻辑:
  

  • violation函数返回的是布尔值True或者False。我取deployment.yaml文件里的容器名前缀参数,若返回值为false,则不会继承往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违反约束需要拦截,就相当于触碰到开关起到拦截作用。
  1.编写模板和约束。在约束里指定要限制的字段,这里就是限制只能拉取192.168.130.152/qingjun个人堆栈的镜像,然后再把参数传入到模板里。
  1. ##模板。
  2. [root@k8s-master1 test2]# cat image-check_tpl.yaml
  3. apiVersion: templates.gatekeeper.sh/v1beta1
  4. kind: ConstraintTemplate
  5. metadata:
  6.   name: image-check
  7. spec:
  8.   crd:
  9.     spec:
  10.       names:
  11.         kind: image-check
  12.       validation:
  13.         openAPIV3Schema:
  14.           properties:           ##接受约束传过来的参数prefix值。
  15.             prefix:
  16.               type: string
  17.   targets:
  18.     - target: admission.k8s.gatekeeper.sh
  19.       rego: |
  20.         package image
  21.         violation[{"msg": msg}] {
  22.           containers = input.review.object.spec.template.spec.containers   ##获取containers下的内容。
  23.           image := containers[0].image                     ##获取镜像名。
  24.           not startswith(image, input.parameters.prefix)   ##input.parameters.prefix获取约束中的参数,并和image比较,判断两者是否相同为true。
  25.           msg := sprintf("提示:'%v'镜像地址不在可信任仓库!", [image])
  26.         }
  27. ##函数逻辑释义:
  28. 1、startswith(qingjun/flask-demo:v4, qingjun/) ,两者比较相等,输入值为true。   
  29. 2、startswith(192.168.130.152/qingjun/, qingjun/) ,两者比较不相等,输出值为false。
  30. 3、startswith(A,B),当A=B,则为true,此时会拦截,所以跟我们想要的结果相反,需要把输出值变成false才能放行,所以前面加个not取反即可。
  31. 4、not sartswith(qingjun/flask-demo:v4,  qingjun/) ,对输出值取反,最终返回false,放行不拦截。
  32. 5、not startswith(192.168.130.152/qingjun/,qingjun/),对输出值取反,最红返回true,不放行会拦截。
  33. ##约束。
  34. [root@k8s-master1 test2]# cat image-check_constraints.yaml
  35. apiVersion: constraints.gatekeeper.sh/v1beta1
  36. kind: image-check
  37. metadata:
  38.   name: image-check
  39. spec:
  40.   match:
  41.     kinds:
  42.       - apiGroups: ["apps"]
  43.         kinds:
  44.         - "Deployment"
  45.         - "DaemonSet"
  46.         - "StatefulSet"
  47.   parameters:  # 传递给opa的参数
  48.     prefix: "192.168.130.152/qingjun"   ##镜像前缀,仓库名称。
复制代码
2.先导入模板,再导入约束测试效果。拉取公共堆栈的镜像直接拦截,拉取192.168.130.152/qingjun/个人堆栈的镜像可以创建成功。
  1. [root@k8s-master1 test2]# kubectl  apply -f image-check_tpl.yaml
  2. [root@k8s-master1 test2]# kubectl  apply -f image-check_constraints.yaml
复制代码

三、gVisor

   前提了解:
  

  • 容器的应用步伐可以直接访问Linux内核的系统调用,容器在安全隔离上还是比较弱,虽然内核在不断地增强自身的安全特性,但由于内核自身代码极度复杂,CVE 漏洞层出不穷。所以要想减少这方面安全风险,就是做好安全隔离,阻断容器内步伐对物理机内核的依靠。
  • Google开源的一种gVisor容器沙箱技术就是采用这种思绪,gVisor隔离容器内应用和内核之间访问,提供了大部分Linux内核的系统调用,巧妙的将容器内进程的系统调用转化为对gVisor的访问。
  • gVisor兼容OCI,与Docker和K8s无缝集成,很方面利用。
  • 项目地址
  gVisor 组件架构图:
  

  • Runsc 是一种 Runtime 引擎,负责容器的创建与烧毁。
  • Sentry 负责容器内步伐的系统调用处理。
  • Gofer 负责文件系统的操纵代理,IO 哀求都会由它转接到 Host 上。

  作用原理:

  1.1 gVisor与Docker集成

   前提了解:
  

  • gVisor内核要求:Linux 3.17以上版本。
  • 如果用的是Centos7则需要升级内核,Ubuntu不需要。
  • docker利用的Runtime引擎是runc,与gVisor集成后就可以支持runsc。利用默认runc创建的容器内核是宿主机的内核,没有隔离,利用runsc创建的容器内核时gvisor模仿创建的内核,是与宿主机内核隔离起来了。

  CentOS7内核升级步骤:
  1. rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
  2. rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
  3. yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml –y
  4. grub2-set-default 0
  5. reboot
  6. uname -r
复制代码
3.1.1 内核版本升级

1.升级前内核版本。

2.安装一个源。
  1. [root@k8s-master1 ~]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
  2. [root@k8s-master1 ~]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
复制代码
3.升级内核一个包,指定利用刚才安装的那个源,源内里有最新的内核包。
  1. [root@k8s-master1 ~]# yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml –y
复制代码
4.设置开机自启,并重启系统查察当前内核版本。
  1. [root@k8s-master1 ~]# grub2-set-default 0
  2. [root@k8s-master1 ~]# reboot
复制代码

3.1.2 安装gvisor

1.集成之前,docker只支持默认的runc。

2.预备二进制文件,官方参考地址。
  1. [root@k8s-master2 ~]# cat test.sh
  2.   set -e
  3.   ARCH=$(uname -m)
  4.   URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
  5.   wget ${URL}/runsc ${URL}/runsc.sha512 \
  6.     ${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512
  7. [root@k8s-master2 ~]# sh test.sh
复制代码

3.配置环境变量,并将runsc集成到docker配置文件里。
  1. [root@k8s-master1 gvisor]# mv runsc /usr/local/bin/
  2. [root@k8s-master1 gvisor]# chmod +x /usr/local/bin/runsc
  3. [root@k8s-master1 gvisor]# runsc  install
复制代码

4.修改docker配置文件,去掉systemctl ,因为gvisor与systemctl不兼容,去掉之后的配置如下。
  1. [root@k8s-master1 gvisor]# cat /etc/docker/daemon.json
  2. {
  3.     "registry-mirrors": [
  4.         "https://b9pmyelo.mirror.aliyuncs.com"
  5.     ],
  6.     "runtimes": {
  7.         "runsc": {
  8.             "path": "/usr/local/bin/runsc"
  9.         }
  10.     }
  11. }
复制代码
5.重启docker,再次查察docker就支持runsc了。

3.1.3 docker中验证

1.先用默认的runc创建一个容器,进去查察容器内核版本,实际的是宿主机的内核版本。
  1. [root@k8s-master1 gvisor]# docker run -d --name=qingjun_1 nginx
  2. [root@k8s-master1 gvisor]# docker exec -it qingjun_1 /bin/bash
复制代码

2.再利用runsc创建一个容器,进入查察容器里的内核不再是宿主机的内核版本,而是gvisor本身模仿的内核,容器内所有的命令操纵系统调用都是发送给模仿出来的内核,然后有它再转发到实际的宿主机内核。

3.1.4 兼容服务

   留意事项:
  

  • gvisor也存在兼容问题,并不是所有的容器都可以很好的利用此技术方案,现在支持的服务如下:
  • 查察地址

  3.2 与Containerd集成

   

  • 留意:这里我是在K8s集群中第二个节点测试的,同样要保证系统内核版本。
  3.2.1 切换containerd容器引擎

1.预备配置,如果直接利用docker容器引擎的,则可以忽略这步,在搭建k8s集群时已经配置了。
  1. cat > /etc/sysctl.d/99-kubernetes-cri.conf << EOF
  2. net.bridge.bridge-nf-call-iptables = 1
  3. net.ipv4.ip_forward = 1
  4. net.bridge.bridge-nf-call-ip6tables = 1
  5. EOF
  6. sysctl -system
复制代码
2.安装containerd,安装docker时已经给装上了,若没有装需要安装一下。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

缠丝猫

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表