自由的羽毛 发表于 2024-10-15 16:11:19

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

1.2.1 dockerfile方式

1.准备程序源码。我这里是使用python写的一个脚本,渲染了一个index.html的模板首页,端口是8080。
# cat Dockerfile
FROM python                        ##基于python基础镜像构建。
RUN mkdir /data/www -p      
COPY . /data/www            ##将当前目录下的文件目录拷贝到/data/www目录。
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/##安装flask,是Python 的一个web框架
WORKDIR /data/www               ##定义默认目录。
CMD python qingjun.py                ##使用python启动这个man.py脚本。


##使用python写了一个web的demo。
# cat qingjun.py
from flask import Flask,render_template

app = Flask(__name__)

@app.route('/')
def index():
    return render_template("index.html")

if __name__ == "__main__":
    app.run(host="0.0.0.0",port=8080)

# cat templates/index.html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h1>你好qingjun!</h1>

</body>
</html>

2.构建镜像。
# docker build -t flask-demo:v1 .

https://img-blog.csdnimg.cn/3d90cc7025004419a0836ede07ed7f73.png
3.使用构建镜像运行容器qingjun,此时可以检察到容器运行的进程是以root用户举行的。
# docker run -d --name=qingjun -p 80:8080 flask-demo:v1

https://img-blog.csdnimg.cn/b88a6abf5dd24bd782cb2fd8e3474337.png
https://img-blog.csdnimg.cn/b5c77670c3e9478083b4d7478bf053dd.png
4.访问web页面。
https://img-blog.csdnimg.cn/f0c696000a6248dd9e837964112b9b0a.png
5.此时我们修改dockerfile,指定使用普通用户启用。
# cat Dockerfile
FROM python
RUN useradd qingjun    ##创建普通用户qingjun。
RUN mkdir -p/data/www
COPY . /data/www
RUN chown -R qingjun /data   #数据目录修改权限,不然还是root用户。
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
USER qingjun       ##指定使用什么用户启用python程序。
CMD python qingjun.py

6.再次构建镜像。
# docker build -tflask-demo:v2 .

7.新镜像运行容器baimu,此时可以检察到容器运行的进程是以普通用户pyuser举行的。
# docker run -d --name=baimu -p 81:8080 flask-demo:v2

https://img-blog.csdnimg.cn/19a74c2452bf42e2b0ddd14bd28ea2e1.png
https://img-blog.csdnimg.cn/17749ab983c84dda9a172b91467cb70d.png
8.访问网页。
https://img-blog.csdnimg.cn/b7a4536737f84b1b9b1fa70613dc568d.png
1.2.2 pod安全上下文方式

   deployment.yaml指定示例:
spec:
securityContext:
   runAsUser: 1000          ##镜像里必须有这个用户UID。
   fsGroup: 1000            ##数据卷挂载后的目录属组设置为该组。
containers:
- image: 192.168.130.152/qingjun/flask-demo:v3
   name: web
   securityContext:
   allowPrivilegeEscalation: false   ##不允许提权。

1.使用dockerfile构建一个镜像,创建一个普通用户qingjun用户,但不指定qingjun用户启用程序。
# cat dockerfile
FROM python
RUN mkdir /data/www -p
RUN useradd qingjun
COPY . /data/www
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
CMD python qingjun.py

##构建镜像。
# docker build -t dockerfile flask-demo:v3 ./

##将进项推送到harbor仓库的qingjun库里。
# docker tag flask-demo:v3 192.168.130.152/qingjun/flask-demo:v3
# docker push 192.168.130.152/qingjun/flask-demo:v3

https://img-blog.csdnimg.cn/e2f214db28964a27b496b8db5e4845c2.png
2.使用刚创建的镜像运行容器,进入容器看到程序是用root用户运行的。
# docker run -d --name=k8s_qingjun 192.168.130.152/qingjun/flask-demo:v3
# docker exec -it k8s_qingjun /bin/bash


https://img-blog.csdnimg.cn/26e2d526f4c4419fa79c16ed36208a91.png
2.创建pod,使用安全上下文指定普通用户id。
# cat deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
    app: qingjun
name: qingjun
spec:
replicas: 1
selector:
    matchLabels:
      app: qingjun
template:
    metadata:
      labels:
      app: qingjun
    spec:
      containers:
      - image: 192.168.130.152/qingjun/flask-demo:v3
      name: flask-demo
      securityContext:   ##指定普通用户id。
          runAsUser: 1000    ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。

# kubectlapply-f deploy.yaml

3.进入pod容器检察,是以普通用户id为1000的qingjun用户启用程序。
https://img-blog.csdnimg.cn/2b1948957bb345cbb7bab74092539cbb.png
4.若构建镜像时没有提前创建普通用户,则在pod.yaml里指定安全上下文创建的容器程序里的普通用户id就是从1000开始。
##构建镜像没有提前创建普通用户。
# cat dockerfile
FROM python
RUN mkdir /data/www -p
COPY . /data/www
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
CMD python qingjun.py
# docker build -t flask-demo:v4 .

##新镜像推送到镜像仓库。
# docker tag flask-demo:v4 192.168.130.152/qingjun/flask-demo:v4
# docker push 192.168.130.152/qingjun/flask-demo:v4

##使用新镜像运行一个容器,进入容器查看是否是默认的root用户启动程序的。
# docker run -d --name=qingjun1 192.168.130.152/qingjun/flask-demo:v4
# docker exec -it qingjun1 /bin/bash

##创建一个pod,使用安全上下文指定普通用户id启动。
# cat deploy2.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
    app: qingjun1
name: qingjun1
spec:
replicas: 1
selector:
    matchLabels:
      app: qingjun1
template:
    metadata:
      labels:
      app: qingjun1
    spec:
      containers:
      - image: 192.168.130.152/qingjun/flask-demo:v4
      name: flask-demo1
      securityContext:   ##指定普通用户id。
          runAsUser: 1000    ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。

##导入yaml,进入pod容器查看是否是用户id为1000的普通用户启动的容器。
# kubectlapply -f deploy2.yaml

https://img-blog.csdnimg.cn/4c60d622198f482e876a9d9c6d36ea40.png
1.3 案例2

   配景:


[*]容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,默认环境下,容器没有这个能力,这时可以考虑给容器设置特权模式。
需求:


[*]避免使用特权容器。
启用特权模式:


[*]docker方式:容器时指定–privileged参数,表示该容器内的程序是以root用户启动,跟在宿主机上用root用户启动一个进程完全没有区别,都可以调用内核的全部参数。
[*]K8s方式:在pod安全上下文设置参数
containers:
- image: 192.168.130.152/qingjun/flask-demo:v3
name: web
securityContext:
    privileged: true

注意事项:


[*]启用特权模式就意味着,你要为容器提供了访问Linux内核的全部能力,这是很危险的,为了淘汰系统调用的供给,可以使用Capabilities为容器赋予仅所需的能力。
1.在deployment.yaml文件中添加此参数,重新构建pod容器就会收效。
https://img-blog.csdnimg.cn/faaa2487eeff492d9a3a08c16dc196e9.png
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权能。
# yum install man-pages
# man capabilities

https://img-blog.csdnimg.cn/3933e8f4df8b4689aaac4a417e2944e5.png
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容器里具备哪些内核调用权限。
# kubectlrun centos --image=centos -- sleep 24h
# kubectlexec -it centos -- bash
# capsh --print    ##查看权能。

https://img-blog.csdnimg.cn/76f09539a73e4b09bd626cad6e0e61be.png
2.检察特权用户具备的权限。
https://img-blog.csdnimg.cn/9e0b0a545cae444fa120d59d71cc1233.png
3.检察普通用户当前权限。
https://img-blog.csdnimg.cn/5b8101b8172b4385bc7229076e23d1b2.png
4.创建一个测试容器,进入容器检察没有挂载权限,因为是普通用户。
# kubectl run bs --image=busybox -- sleep 24h

https://img-blog.csdnimg.cn/5673467e537944f9b1bf1382bfe87c7d.png
https://img-blog.csdnimg.cn/33bb0a0c5b5547f9bf79569d0b44bee0.png
5.创建pod时指定挂载权限,进入容器测试可以挂载目录。
# cat pod.yaml
apiVersion: v1
kind: Pod
metadata:
name: cap-pod
spec:
containers:
- image: busybox
    name: test
    command:
    - sleep
    - 24h
    securityContext:
      capabilities:
      add: ["SYS_ADMIN"]      ##添加挂载权限。

# kubectlapply -f pod.yaml

https://img-blog.csdnimg.cn/4ec66b3b393c49f1ab50d6626252e87c.png
https://img-blog.csdnimg.cn/dc63adba2488473c9c3af0aed2d2d8ca.png
案例2

   

[*]需求:只读容器文件系统。只读挂载容器文件系统,防止恶意二进制文件创建。
1.容器默认的普通用户是可读可写。
https://img-blog.csdnimg.cn/026d2f2ba8fc4a068e648d3ec3d0857b.png
2.deployment.yaml中指定只读参数。
# cat deploy.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
labels:
    app: qingjun
name: qingjun
spec:
replicas: 1
selector:
    matchLabels:
      app: qingjun
template:
    metadata:
      labels:
      app: qingjun
    spec:
      containers:
      - image: 192.168.130.152/qingjun/flask-demo:v4
      name: flask-demo
      securityContext:
          runAsUser: 1000
          readOnlyRootFilesystem: true    ##添加此行。
         
# kubectlapply -f deploy.yaml

3.进入容器检察,没有写权限,只能读。
https://img-blog.csdnimg.cn/54aaee9090dc4324855f292ee57e2e46.png
二、pod安全策略

2.1 PSP(已废弃)

   概念:


[*]PodSecurityPolicy,简称PSP,是K8s中Pod部署时重要的安全校验本领,可以或许有效地约束应用运行时行为安全。
[*]使用PSP对象定义一组Pod在运行时必须遵照的条件及相关字段的默认值,只有Pod满足这些条件才会被K8s接受。
[*]Pod安全策略实现为一个准入控制器,默认没有启用,当启用后会强制实施Pod安全策略,没有满足的Pod将无法创建。因此,建议在启用PSP之前先添加策略并对其授权。
启用Pod安全策略:

[*]kube-apiserver.yaml配置文件添加准入控制器。
[*]重启kubelet,systemctl restart kubelet
https://img-blog.csdnimg.cn/6b4590ab822847949636b85403acf31e.png
玩法思路:

[*]创建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来处理,如果不满足策略则拒绝。
工作流程图:
https://img-blog.csdnimg.cn/c75061ba89b147bfb7c4d6557ec9e6b8.png
2.2.1 安装Gatekeeper

1.下载准备安装yaml文件,下载地址
https://img-blog.csdnimg.cn/43e35c65a3e94213b389dc051ed1816a.png
2.导入文件一键部署,并检察。
# kubectl apply -f gatekeeper.yaml

https://img-blog.csdnimg.cn/fdbe9f495ff4460690659c399a316c3a.png
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资源。
https://img-blog.csdnimg.cn/02ac138c4fa24122a5459cca76a0a604.png

2.2.3 案例1

   需求:


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


[*]violation函数返回的是布尔值True大概False。我取deployment.yaml文件里的特权容器参数,若返回值为false,则不会继续往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违背约束需要拦截,就相称于触碰到开关起到拦截作用。
1.编写模板和约束,在模板里使用rego脚本匹配约束里面的的资源。
##模板
# cat privileged_tpl.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: qingjun
spec:
crd:
    spec:
      names:
      kind: qingjun
targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |                              ##自定义匹配策略,使用rego语言编写。
      package admission                  ##传入一个依赖包,唯一的,类似命名空间。
      violation[{"msg": msg}] {            ##定义一个key-value变量。
          containers = input.review.object.spec.template.spec.containers    ##获取自定义对象的pod.yaml里对应的字段。
          c_name := containers.name       ##获取第一个容器名称并赋予变量名为c_name,0代表第一个。
          containers.securityContext.privileged       ##获取第一个容器的securityContext.privileged值。这里是判断返回是否为true,返回true说明违反约束。
          msg := sprintf("提示:'%v'容器禁止启用特权!",)   ##打印结果。
      }
# kubectlapply -f privileged_tpl.yaml


##约束
# cat privileged_constraints.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: qingjun
metadata:
name: baimu
spec:
match:
    kinds:
      - apiGroups: ["apps"]
      kinds:
      - "Deployment"
      - "DaemonSet"
      - "StatefulSet"
# kubectlapply -f privileged_constraints.yaml

https://img-blog.csdnimg.cn/4edc4d9e360c455aa863aa3e7cc7928f.png
2.测试一,当创建deployment资源时,没有匹配到“securityContext.privileged: true” 字段,说明该容器没有启用特权,可以正常创建,没有违背约束,所以最后可以正常创建deploy。
https://img-blog.csdnimg.cn/e85d935208d749469bc1a6fb95fe7642.png
3.测试二,当创建deployment资源时,匹配到“securityContext.privileged: false ”字段,说明没有开启特权容器,最后可以正常创建deploy。
https://img-blog.csdnimg.cn/43fd6bce976848ada0a7a7f059389395.png
4.测试三,当创建deployment资源时,匹配到“securityContext.privileged: true”字段,说明容器需要开启特权,需要限定不能开启,所以最后不能创建deployment资源。
https://img-blog.csdnimg.cn/5b24e6d1ecac4cacb84274197643eeea.png
5.修改deploy.yaml容器名称,最后会打印出详细错误信息,方便测试。
https://img-blog.csdnimg.cn/87c5a14ac43a445bbd674166119717b3.png
https://img-blog.csdnimg.cn/d6e8152d09d1469098998d1c7cabf62a.png
https://img-blog.csdnimg.cn/9e829d2d10d94d69afb1058aeb6c67c5.png
2.2.4 案例2

   需求:


[*]只允许使用特定的镜像仓库。
实现代码逻辑:


[*]violation函数返回的是布尔值True大概False。我取deployment.yaml文件里的容器名前缀参数,若返回值为false,则不会继续往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违背约束需要拦截,就相称于触碰到开关起到拦截作用。
1.编写模板和约束。在约束里指定要限定的字段,这里就是限定只能拉取192.168.130.152/qingjun个人仓库的镜像,然后再把参数传入到模板里。
##模板。
# cat image-check_tpl.yaml
apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
name: image-check
spec:
crd:
    spec:
      names:
      kind: image-check
      validation:
      openAPIV3Schema:
          properties:         ##接受约束传过来的参数prefix值。
            prefix:
            type: string
targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
      package image
      violation[{"msg": msg}] {
          containers = input.review.object.spec.template.spec.containers   ##获取containers下的内容。
          image := containers.image                     ##获取镜像名。
          not startswith(image, input.parameters.prefix)   ##input.parameters.prefix获取约束中的参数,并和image比较,判断两者是否相同为true。
          msg := sprintf("提示:'%v'镜像地址不在可信任仓库!", )
      }

##函数逻辑释义:
1、startswith(qingjun/flask-demo:v4, qingjun/) ,两者比较相等,输入值为true。   
2、startswith(192.168.130.152/qingjun/, qingjun/) ,两者比较不相等,输出值为false。
3、startswith(A,B),当A=B,则为true,此时会拦截,所以跟我们想要的结果相反,需要把输出值变成false才能放行,所以前面加个not取反即可。
4、not sartswith(qingjun/flask-demo:v4,qingjun/) ,对输出值取反,最终返回false,放行不拦截。
5、not startswith(192.168.130.152/qingjun/,qingjun/),对输出值取反,最红返回true,不放行会拦截。




##约束。
# cat image-check_constraints.yaml
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: image-check
metadata:
name: image-check
spec:
match:
    kinds:
      - apiGroups: ["apps"]
      kinds:
      - "Deployment"
      - "DaemonSet"
      - "StatefulSet"
parameters:# 传递给opa的参数
    prefix: "192.168.130.152/qingjun"   ##镜像前缀,仓库名称。

2.先导入模板,再导入约束测试结果。拉取公共仓库的镜像直接拦截,拉取192.168.130.152/qingjun/个人仓库的镜像可以创建成功。
# kubectlapply -f image-check_tpl.yaml
# kubectlapply -f image-check_constraints.yaml

https://img-blog.csdnimg.cn/3bf51d34b9a24da48e030b20fd45b020.png
三、gVisor

   条件相识:


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


[*]Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
[*]Sentry 负责容器内程序的系统调用处理。
[*]Gofer 负责文件系统的操作代理,IO 请求都会由它转接到 Host 上。
https://img-blog.csdnimg.cn/fc251a20de3d4d6e98122fdc77934892.png
作用原理:
https://img-blog.csdnimg.cn/8cbc06118f1c4865bceb9e828a328b0c.png
1.1 gVisor与Docker集成

   条件相识:


[*]gVisor内核要求:Linux 3.17以上版本。
[*]如果用的是Centos7则需要升级内核,Ubuntu不需要。
[*]docker使用的Runtime引擎是runc,与gVisor集成后就可以支持runsc。使用默认runc创建的容器内核是宿主机的内核,没有隔离,使用runsc创建的容器内核时gvisor模拟创建的内核,是与宿主机内核隔离起来了。
https://img-blog.csdnimg.cn/a7abf8bc17984016956bf556d0215c28.png
CentOS7内核升级步调:
rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm
yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml –y
grub2-set-default 0
reboot
uname -r

3.1.1 内核版本升级

1.升级前内核版本。
https://img-blog.csdnimg.cn/fda5a5a03ca149c28d3f156bd441a192.png
2.安装一个源。
# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
# 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
# reboot

https://img-blog.csdnimg.cn/84146caf0686469db7bae3917d842552.png
3.1.2 安装gvisor

1.集成之前,docker只支持默认的runc。
https://img-blog.csdnimg.cn/3572c1186e9e47128a24893481da2eb3.png
2.准备二进制文件,官方参考地址。
# cat test.sh
set -e
ARCH=$(uname -m)
URL=https://storage.googleapis.com/gvisor/releases/release/latest/${ARCH}
wget ${URL}/runsc ${URL}/runsc.sha512 \
    ${URL}/containerd-shim-runsc-v1 ${URL}/containerd-shim-runsc-v1.sha512

# sh test.sh

https://img-blog.csdnimg.cn/ed4026711efc40c889ac43139a65f434.png
3.配置环境变量,并将runsc集成到docker配置文件里。
# mv runsc /usr/local/bin/
# chmod +x /usr/local/bin/runsc

# runscinstall

https://img-blog.csdnimg.cn/76f82735d8874ea88133e5d374f48748.png
4.修改docker配置文件,去掉systemctl ,因为gvisor与systemctl不兼容,去掉之后的配置如下。
# cat /etc/docker/daemon.json
{
    "registry-mirrors": [
      "https://b9pmyelo.mirror.aliyuncs.com"
    ],
    "runtimes": {
      "runsc": {
            "path": "/usr/local/bin/runsc"
      }
    }
}

5.重启docker,再次检察docker就支持runsc了。
https://img-blog.csdnimg.cn/002f33db8519400aba62e3de0d0884a6.png
3.1.3 docker中验证

1.先用默认的runc创建一个容器,进去检察容器内核版本,现实的是宿主机的内核版本。
# docker run -d --name=qingjun_1 nginx
# docker exec -it qingjun_1 /bin/bash

https://img-blog.csdnimg.cn/288b4cc8a2b742b8aec5d46c66abafeb.png
2.再使用runsc创建一个容器,进入检察容器里的内核不再是宿主机的内核版本,而是gvisor本身模拟的内核,容器内全部的命令操作系统调用都是发送给模拟出来的内核,然后有它再转发到实际的宿主机内核。
https://img-blog.csdnimg.cn/f29a4e512c5846859a4bf8f0ece1a4cb.png
3.1.4 兼容服务

   注意事项:


[*]gvisor也存在兼容问题,并不是全部的容器都可以很好的使用此技术方案,目前支持的服务如下:
[*]检察地址
https://img-blog.csdnimg.cn/4a7399c115104007bc55fbdfea3d8c09.png
3.2 与Containerd集成

   

[*]注意:这里我是在K8s集群中第二个节点测试的,同样要包管系统内核版本。
3.2.1 切换containerd容器引擎

1.准备配置,如果直接使用docker容器引擎的,则可以忽略这步,在搭建k8s集群时已经配置了。
cat > /etc/sysctl.d/99-kubernetes-cri.conf << EOF
net.bridge.bridge-nf-call-iptables = 1
net.ipv4.ip_forward = 1
net.bridge.bridge-nf-call-ip6tables = 1
EOF
sysctl -system

2.安装containerd,安装docker时已经给装上了,若没有装需要安装一下。
cd /etc/yum.repos.d
wget http://mirrors.aliyun.com/dockerce/linux/centos/docker-ce.repo
yum install -y containerd.io

https://img-blog.csdnimg.cn/5f4aec4c50cc433aa6a87346a665e7a7.png
3.准备runsc二进制文件,安装gvisor。
# mv runsc /usr/local/bin/
# mv containerd-shim-runsc-v1 /usr/local/bin/
# chmod +x /usr/local/bin/runsc
# chmod +x /usr/local/bin/containerd-shim-runsc-v1

4.生成配置文件,修改相关参数。
containerd config default > /etc/containerd/config.toml

   参数一:修改pause镜像地址,修改成国内地址,方便拉取。
sandbox_image = "registry.aliyuncs.com/google_containers/pause:3.2"

https://img-blog.csdnimg.cn/f3c5fc94d3754b798914d6d0a82e3fcc.png

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: K8s进阶6——pod安全上下文、Linux Capabilities、OPA Gatekeeper、gvisor_