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

news2024/11/23 9:08:09

文章目录

  • 一、Pod安全上下文
    • 1.1 配置参数
    • 1.2 案例1
      • 1.2.1 dockerfile方式
      • 1.2.2 pod安全上下文方式
    • 1.3 案例2
    • 1.4 Linux Capabilities方案
      • 案例1
      • 案例2
  • 二、pod安全策略
    • 2.1 PSP(已废弃)
      • 2.1.1 安全策略限制维度
    • 2.2 OPA Gatekeeper方案
      • 2.2.1 安装Gatekeeper
      • 2.2.2 编写策略
      • 2.2.3 案例1
      • 2.2.4 案例2
  • 三、gVisor
    • 1.1 gVisor与Docker集成
      • 3.1.1 内核版本升级
      • 3.1.2 安装gvisor
      • 3.1.3 docker中验证
      • 3.1.4 兼容服务
    • 3.2 与Containerd集成
      • 3.2.1 切换containerd容器引擎
      • 3.2.2 验证 (K8s使用gVisor运行容器)

一、Pod安全上下文

什么是安全上下文?

  • 安全上下文(Security Context):K8s对Pod和容器提供的安全机制,可以设置Pod特权和访问控制。
  • 官网参考链接

限制维度:

  1. 自主访问控制(Discretionary Access Control):基于用户ID(UID)和组ID(GID),来判定对对象(例如文件)的访问权限。
  2. 安全性增强的 Linux(SELinux): 为对象赋予安全性标签。
  3. 以特权模式或者非特权模式运行。
  4. Linux Capabilities: 为进程赋予 root 用户的部分特权而非全部特权。
  5. AppArmor:定义Pod使用AppArmor限制容器对资源访问限制
  6. Seccomp:定义Pod使用Seccomp限制容器进程的系统调用。
  7. AllowPrivilegeEscalation: 禁止容器中进程(通过 SetUID 或 SetGID 文件模式)获得特权提升。当容器以特权模式运行或者具有CAP_SYS_ADMIN能力时,AllowPrivilegeEscalation总为True。
  8. readOnlyRootFilesystem:以只读方式加载容器的根文件系统。

1.1 配置参数

注意事项:

  • securityContext下配置的参数是针对该pod里的所有容器生效。
  • containers下配置的参数只对某个容器生效,不同容器配置各自需要的参数。
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内核的系统调用权限,不安全,所以我们应该将容器里的程序以普通用户运行,减少应用程序对权限的使用。

需求:

  • 设置容器以普通用户运行。

实现思路:

  1. Dockerfile里使用USER指定运行用户。
  2. K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID。

1.2.1 dockerfile方式

1.准备程序源码。我这里是使用python写的一个脚本,渲染了一个index.html的模板首页,端口是8080。

[root@k8s-master1 flask-demo]# 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。
[root@k8s-master1 flask-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)

[root@k8s-master1 flask-demo]# cat templates/index.html 
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>首页</title>
</head>
<body>

<h1>你好  qingjun!</h1>

</body>
</html>

2.构建镜像。

[root@k8s-master1 flask-demo]# docker build -t flask-demo:v1 .

在这里插入图片描述
3.使用构建镜像运行容器qingjun,此时可以查看到容器运行的进程是以root用户进行的。

[root@k8s-master1 flask-demo]# docker run -d --name=qingjun -p 80:8080 flask-demo:v1 

在这里插入图片描述
在这里插入图片描述
4.访问web页面。
在这里插入图片描述

5.此时我们修改dockerfile,指定使用普通用户启用。

[root@k8s-master1 flask-demo]# 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.再次构建镜像。

[root@k8s-master1 flask-demo]# docker build -t  flask-demo:v2 .

7.新镜像运行容器baimu,此时可以查看到容器运行的进程是以普通用户pyuser进行的。

[root@k8s-master1 flask-demo]# docker run -d --name=baimu -p 81:8080 flask-demo:v2

在这里插入图片描述
在这里插入图片描述
8.访问网页。
在这里插入图片描述

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用户启用程序。

[root@k8s-master1 ~]# 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 

##构建镜像。
[root@k8s-master1 ~]# docker build -t dockerfile flask-demo:v3 ./

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

在这里插入图片描述

2.使用刚创建的镜像运行容器,进入容器看到程序是用root用户运行的。

[root@k8s-master1 docker]# docker run -d --name=k8s_qingjun 192.168.130.152/qingjun/flask-demo:v3
[root@k8s-master1 docker]# docker exec -it k8s_qingjun /bin/bash

在这里插入图片描述

2.创建pod,使用安全上下文指定普通用户id。

[root@k8s-master1 flask-demo]# 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开始。

[root@k8s-master1 flask-demo]# kubectl  apply  -f deploy.yaml 

3.进入pod容器查看,是以普通用户id为1000的qingjun用户启用程序。
在这里插入图片描述

4.若构建镜像时没有提前创建普通用户,则在pod.yaml里指定安全上下文创建的容器程序里的普通用户id就是从1000开始。

##构建镜像没有提前创建普通用户。
[root@k8s-master1 ~]# 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 
[root@k8s-master1 ~]# docker build -t flask-demo:v4 .

##新镜像推送到镜像仓库。
[root@k8s-master1 ~]# docker tag flask-demo:v4 192.168.130.152/qingjun/flask-demo:v4
[root@k8s-master1 ~]# docker push 192.168.130.152/qingjun/flask-demo:v4

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

##创建一个pod,使用安全上下文指定普通用户id启动。
[root@k8s-master1 ~]# 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的普通用户启动的容器。
[root@k8s-master1 ~]# kubectl  apply -f deploy2.yaml 

在这里插入图片描述

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容器就会生效。
在这里插入图片描述

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权能。

[root@k8s-master1 ~]# yum install man-pages
[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允许改变进程的 GID
CAP_SETFCAP允许为文件设置任意的 capabilities
CAP_SETPCAP参考 capabilities man page
CAP_SETUID允许改变进程的 UID
CAP_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容器里具备哪些内核调用权限。

[root@k8s-master1 flask-demo]# kubectl  run centos --image=centos -- sleep 24h
[root@k8s-master1 flask-demo]# kubectl  exec -it centos -- bash
[root@centos /]# capsh --print    ##查看权能。

在这里插入图片描述

2.查看特权用户具备的权限。
在这里插入图片描述
3.查看普通用户当前权限。
在这里插入图片描述
4.创建一个测试容器,进入容器查看没有挂载权限,因为是普通用户。

[root@k8s-master1 flask-demo]# kubectl run bs --image=busybox -- sleep 24h

在这里插入图片描述
在这里插入图片描述
5.创建pod时指定挂载权限,进入容器测试可以挂载目录。

[root@k8s-master1 flask-demo]# 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"]      ##添加挂载权限。

[root@k8s-master1 flask-demo]# kubectl  apply -f pod.yaml

在这里插入图片描述
在这里插入图片描述

案例2

  • 需求:只读容器文件系统。只读挂载容器文件系统,防止恶意二进制文件创建。

1.容器默认的普通用户是可读可写。
在这里插入图片描述
2.deployment.yaml中指定只读参数。

[root@k8s-master1 flask-demo]# 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    ##添加此行。
          
[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安全策略:

  1. kube-apiserver.yaml配置文件添加准入控制器。
  2. 重启kubelet,systemctl restart kubelet
    在这里插入图片描述

玩法思路:

  1. 创建SA服务账号。
  2. 将SA绑定到系统内置Role edit。
  3. 创建使用PSP权限的Role qingjun。
  4. 将SA绑定到角色qingjun上。
  5. 定义PodSecurityPolicy策略。

注意事项:

  1. 使用复杂,权限模型存在缺陷,控制不明确,一旦出现问题不好定位,所以将再1.21版本弃用PSP,在1.25版本删除PSP。 弃用官方文献
  2. 替代方案KEP 2579。
  3. 替代外部控制器方案: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可以用来做什么?

  1. 拒绝不符合条件的YAML部署。
  2. 允许使用哪些仓库中的镜像。
  3. 允许在哪个时间段访问系统。
  4. 等等。

OPA Gatekeeper的概念:

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

工作流程图:
在这里插入图片描述

2.2.1 安装Gatekeeper

1.下载准备安装yaml文件,下载地址
在这里插入图片描述
2.导入文件一键部署,并查看。

[root@k8s-master1 opa]# kubectl apply -f gatekeeper.yaml

在这里插入图片描述

2.2.2 编写策略

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

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

实现思路:

  1. 先使用rego语言编写Template模板,在这个模板里自定义实现逻辑,就相当于写一个脚本逻辑。
  2. 自定义Constraint约束,约束格式类似rbac授权那种,要限制什么资源,这个资源在什么组。
  3. 当我们创建一个资源时,比如创建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脚本匹配约束里面的的资源。

##模板
[root@k8s-master1 opa]# 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[0].name       ##获取第一个容器名称并赋予变量名为c_name,0代表第一个。
          containers[0].securityContext.privileged       ##获取第一个容器的securityContext.privileged值。这里是判断返回是否为true,返回true说明违反约束。
          msg := sprintf("提示:'%v'容器禁止启用特权!",[c_name])   ##打印结果。
        }
[root@k8s-master1 opa]# kubectl  apply -f privileged_tpl.yaml 


##约束
[root@k8s-master1 opa]# cat privileged_constraints.yaml 
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: qingjun
metadata:
  name: baimu
spec:
  match: 
    kinds:
      - apiGroups: ["apps"]
        kinds:
        - "Deployment"
        - "DaemonSet"
        - "StatefulSet"
[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个人仓库的镜像,然后再把参数传入到模板里。

##模板。
[root@k8s-master1 test2]# 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[0].image                     ##获取镜像名。
          not startswith(image, input.parameters.prefix)   ##input.parameters.prefix获取约束中的参数,并和image比较,判断两者是否相同为true。
          msg := sprintf("提示:'%v'镜像地址不在可信任仓库!", [image])
        }

##函数逻辑释义:
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,不放行会拦截。




##约束。
[root@k8s-master1 test2]# 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/个人仓库的镜像可以创建成功。

[root@k8s-master1 test2]# kubectl  apply -f image-check_tpl.yaml 
[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内核升级步骤:

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.升级前内核版本。
在这里插入图片描述
2.安装一个源。

[root@k8s-master1 ~]# rpm --import https://www.elrepo.org/RPM-GPG-KEY-elrepo.org
[root@k8s-master1 ~]# rpm -Uvh http://www.elrepo.org/elrepo-release-7.0-2.el7.elrepo.noarch.rpm

3.升级内核一个包,指定使用刚才安装的那个源,源里面有最新的内核包。

[root@k8s-master1 ~]# yum --enablerepo=elrepo-kernel install kernel-ml-devel kernel-ml –y

4.设置开机自启,并重启系统查看当前内核版本。

[root@k8s-master1 ~]# grub2-set-default 0
[root@k8s-master1 ~]# reboot

在这里插入图片描述

3.1.2 安装gvisor

1.集成之前,docker只支持默认的runc。
在这里插入图片描述
2.准备二进制文件,官方参考地址。

[root@k8s-master2 ~]# 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

[root@k8s-master2 ~]# sh test.sh 

在这里插入图片描述
3.配置环境变量,并将runsc集成到docker配置文件里。

[root@k8s-master1 gvisor]# mv runsc /usr/local/bin/
[root@k8s-master1 gvisor]# chmod +x /usr/local/bin/runsc 

[root@k8s-master1 gvisor]# runsc  install

在这里插入图片描述
4.修改docker配置文件,去掉systemctl ,因为gvisor与systemctl不兼容,去掉之后的配置如下。

[root@k8s-master1 gvisor]# cat /etc/docker/daemon.json
{
    "registry-mirrors": [
        "https://b9pmyelo.mirror.aliyuncs.com"
    ],
    "runtimes": {
        "runsc": {
            "path": "/usr/local/bin/runsc"
        }
    }
}

5.重启docker,再次查看docker就支持runsc了。
在这里插入图片描述

3.1.3 docker中验证

1.先用默认的runc创建一个容器,进去查看容器内核版本,现实的是宿主机的内核版本。

[root@k8s-master1 gvisor]# docker run -d --name=qingjun_1 nginx
[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集群时已经配置了。

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

在这里插入图片描述
3.准备runsc二进制文件,安装gvisor。

[root@k8s-master2 gv]# mv runsc /usr/local/bin/  
[root@k8s-master2 gv]# mv containerd-shim-runsc-v1 /usr/local/bin/
[root@k8s-master2 gv]# chmod +x /usr/local/bin/runsc 
[root@k8s-master2 gv]# 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"

在这里插入图片描述

参数二:Cgroup驱动改为systemd,可选项。

SystemdCgroup = true

在这里插入图片描述

参数三:增加runsc容器运行时,注意需要安装gvisor,且需要安装gvisor时的那两个文件。

 [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
   runtime_type = "io.containerd.runsc.v1"

在这里插入图片描述

参数四:配置docker镜像加速器。

https://b9pmyelo.mirror.aliyuncs.com

在这里插入图片描述

  1. 重启containerd,配置kubelet使用containerd。
[root@k8s-master2 pod]# systemctl  restart containerd

#可以使用默认的,修改成containerd即可。
[root@k8s-master2 pod]# cat /var/lib/kubelet/kubeadm-flags.env 
unix:///run/containerd/containerd.sock 

#也可以再生成定义一个。
vi /etc/sysconfig/kubelet 
KUBELET_EXTRA_ARGS=--container-runtime=remote --container-runtime-endpoint=unix:///run/containerd/containerd.sock --cgroup-driver=systemd

#重启
[root@k8s-master2 pod]# systemctl restart kubelet

在这里插入图片描述
5.检查。
在这里插入图片描述
6.准备crictl连接containerd配置文件。containerd也有 ctr 管理工具,但功能比较简单,一般使用crictl工具检查和调试容器。

cat > /etc/crictl.yaml << EOF
runtime-endpoint: unix:///run/containerd/containerd.sock
EOF

7.命令查看。
在这里插入图片描述

3.2.2 验证 (K8s使用gVisor运行容器)

  • RuntimeClass 是一个用于选择容器运行时配置的特性,容器运行时配置用于运行 Pod 中的容器。

1.创建runtimeclass策略。

[root@k8s-master2 pod]# cat runtimeclass.yaml 
apiVersion: node.k8s.io/v1 
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc   ##不能自定义,相当于docker run时--runtime参数指定的。

[root@k8s-master2 pod]# kubectl  apply -f runtimeclass.yaml 

2.创建pod,指定runtimeclass。

[root@k8s-master2 pod]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: containerd-gvisor
spec:
  nodeName: k8s-master2     ##确定pod被分配的机器是具备gvisor。
  runtimeClassName: gvisor   ##指定名称。
  containers:
  - name: web2
    image: nginx

[root@k8s-master2 pod]# kubectl  apply -f pod.yaml 

3.进入容器查看内核。
在这里插入图片描述

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

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

相关文章

百度搜索迎来奇点 大模型掀起代际变革

每一轮技术革命掀起的浪潮&#xff0c;大部多数人还没来得及思考或者布局&#xff0c;已经消失于海浪中。机会是给有准备的人的&#xff0c;要发现新兴技术的亮点&#xff0c;并立足自身去积极拥抱它&#xff0c;最后转化为自身前进的动力&#xff0c;跨越周期&#xff0c;迎来…

网站出现403 Forbidden错误的原因以及怎么解决的方法

这几天刚接手一批新做的网站&#xff0c;在访问网站的时候&#xff0c;会时不时的出现403 Forbidden错误&#xff0c;浏览器会给出403 Forbidden错误提示&#xff0c;在打开Access Error中列出的URL之后, 出现以下错误&#xff1a; 403 Forbidden Access to this resource on…

SAP工具箱 批量下载指定表数据到EXCEL

点击蓝字 关注我们 一 前言 下载指定表内容到指定的EXCEL是一个比较简单的程序.但仔细考虑这个程序,还是可以在细节上找出一些关注点 多表内容同时下载,每个表生成一个文件多表选择时,先查看表的记录数大表下载时,拆分下载拆分到不同的文件中拆分到同一个文件中的不同的工作表下…

windows server 2016 ftp搭建详细教程

一.什么是FTP&#xff1f; FTP(File Transfer Protocol)是TCP/IP网络上两台计算机传送文件的协议&#xff0c;使得主机间可以共享文件。 接下来我给大家分享快速搭建FTP服务器的方法。 二.安装FTP服务器 1.进入服务器系统打开“服务器管理器”&#xff0c;点击“添加角色和功…

【JavaSE】Java基础语法(二十三):递归与数组的高级操作

文章目录 1. 递归1.1 递归1.2 递归求阶乘 2. 数组的高级操作2.1 二分查找2.2 冒泡排序2.3 快速排序2.4 Arrays (应用) 1. 递归 1.1 递归 递归的介绍 以编程的角度来看&#xff0c;递归指的是方法定义中调用方法本身的现象把一个复杂的问题层层转化为一个与原问题相似的规模较…

C语言2:说心里话

描述 分两次从控制台接收用户的两个输入&#xff1a;第一个内容为“人名”&#xff0c;第一个内容为“心里 话”。 然后将这两个输入内容组成如下句型并输出出来&#xff1a; 1.(人名&#xff09;&#xff0c;I want to say&#xff0c;(心里话 2. 输入输出示例: 输入&#xff…

MybatisPlus SpringCloud Docker RabbitMQ ElasticSearch、Redis高级技术,分布式事务的综合应用

一、配置SpringCloud中的网关 1. nginx搭建 搭建好了启动nginx.exe即可出静态页面图 1.网关搭建 server:port: 10010 spring:application:name: api-gatewaycloud:nacos:server-addr: localhost:8848gateway:routes: #用户服务的路由&#xff1a;什么样的请求&#xff0c;让网…

Emacs之定制化mode line(第一百零二)

简介: CSDN博客专家,专注Android/Linux系统,分享多mic语音方案、音视频、编解码等技术,与大家一起成长! 优质专栏:Audio工程师进阶系列【原创干货持续更新中……】🚀 人生格言: 人生从来没有捷径,只有行动才是治疗恐惧和懒惰的唯一良药. 更多原创,欢迎关注:Android…

第二期:链表经典例题(两数相加,删除链表倒数第N个节点,合并两个有序列表)

每道题后都有解析帮助你分析做题&#xff0c;答案在最下面&#xff0c;关注博主每天持续更新。 PS&#xff1a;每道题解题方法不唯一&#xff0c;欢迎讨论&#xff01; 1.两数相加 题目描述 给你两个非空的链表&#xff0c;表示两个非负的整数。它们每位数字都是按照逆序的方式…

【Vue】二:Vue核心处理---模板语法

文章目录 1.模板语法---插值2.模板语法---指令语法2.1v-once2.2 v-bind2.3 v-model2.4 v-on 3.MVVM4.事件回调函数中的this 1.模板语法—插值 {{可以写什么}} &#xff08;1&#xff09;在data中声明的变量&#xff0c;函数 &#xff08;2&#xff09;常量 &#xff08;3&…

【蓝桥杯省赛真题22】python剩余空间问题 青少年组蓝桥杯比赛python编程省赛真题解析

目录 python剩余空间问题 一、题目要求 1、编程实现 二、解题思路

【JavaEE】锁策略、CAS和synchronized的优化

目录 1、常见的锁策略 1.1、乐观锁 vs 悲观锁 1.2、轻量级锁 vs 重量级锁 1.3、自旋锁 vs 挂起等待锁 1.4、互斥锁 vs 读写锁 1.4.1、读写锁的使用场景&#xff08;适用于"频繁 读&#xff0c;不频繁写"的场景&#xff09; 1.5、可重入锁 vs 不可重入锁 1.…

计算机专业学习的核心是什么?

既然是学习CS&#xff0c;那么在这里&#xff0c;我粗浅的把计算机编程领域的知识分为三个部分&#xff1a; 基础知识 特定领域知识 框架和开发技能 基础知识是指不管从事任何方向的软件工程师都应该掌握的&#xff0c;比如数据结构、算法、操作系统。 特定领域知识就是你…

Python花瓣雨

目录 前言 小海龟 花朵类 移动函数 画花朵 尾声 前言 来啦来啦来啦&#xff0c;小伙伴们快快来领取七彩花瓣雨吧&#xff01;&#xff01; 小海龟 老生常谈啦&#xff0c;在用python画樱花树前&#xff0c;我们先来了解一下turtle吧&#xff01; 小海龟(Turtle)是P…

Java学习路线(13)——Collection集合类:List集合与Set集合

一、集合类体系结构 二、部分Collection类型对象 Collection集合特点 List系列集合是有序、可重复、有索引。 ArrayList&#xff1a;有序、可重复、有索引LinkedList&#xff1a;有序、可重复、有索引 Set系列集合是无序、不重复、无索引。 HashSet&#xff1a;无序、不重复…

0202条件过滤-自动装配原理-springboot2.7.x系列

1前言 在springboot的自动装配过程中&#xff0c;执行完候选配置类导入后&#xff0c;会进行条件过滤。那么在讲解条件过滤前&#xff0c;我们先来了解springboot常用的条件注解&#xff0c;以及它们底层执行原理。 在Spring Boot中&#xff0c;条件&#xff08;Condition&am…

使用qemu模拟CXL.mem设备

CXL可以说是自PCIe技术诞生几十年以来最具变革性的新技术了。可以想象有了CXL以后机箱的边界将被彻底打破&#xff0c;服务器互相使用对方的内存&#xff0c;网卡&#xff0c;GPU 。整个机架甚至跨机架的超级资源池化成为可能&#xff0c;云计算也将进入一个新的时代。 当前In…

C++寄存器优化

在C里面有个有趣的现象&#xff0c;先看代码 #include<iostream> using namespace std; int main() {int const tmp 100; //定义常量tmp tmp不能修改int const* p &tmp; //不能通过指针修改指向的值 int* const q const_cast<int*>(p); //去常属性 可以通过…

【C++11】C++11新增语法特性 右值引用/移动语义/完美转发

C11 右值引用 1 右值引用1.1 左值 、 右值1.2 左值引用 VS 右值引用1.3 谈谈C11引入右值引用的意义1.4 左值引用和右值引用的一些细节问题 2 移动语义3 完美转发4 总结 1 右值引用 1.1 左值 、 右值 在C中所有的值不是左值就是右值。左值是指表达式结束后依然存在的持久化对象…

【JavaSE】Java基础语法(二十二):包装类

文章目录 1. 基本类型包装类2. Integer类3. 自动拆箱和自动装箱4. int和String类型的相互转换 1. 基本类型包装类 基本类型包装类的作用 将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据常用的操作之一&#xff1a;用于基本数据类型与字符串之间的…