K8s进阶6——pod安全上下文、Linux Capabilities、OPA Gatekeeper、gvisor
这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。容器中的应用程序默认以root账号运行的,这个root与宿主机root用户权限是一样的,拥有大部分对Linux内核的系统调用权限,不安全,所以我们应该将容器里的程序以普通用户运行,减少应用程序对权限的使用。2.再使用runsc创建一个容器,进入查看容器里的内核不再是宿主机的内核版本,而是gvisor自己
文章目录
- 一、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特权和访问控制。
- [官网参考链接](https://kubernetes.io/zh-cn/docs/tasks/configure-pod-
container/security-context/)
限制维度:
- 自主访问控制(Discretionary Access
Control):基于用户ID(UID)和组ID(GID),来判定对对象(例如文件)的访问权限。- 安全性增强的 Linux(SELinux): 为对象赋予安全性标签。
- 以特权模式或者非特权模式运行。
- Linux Capabilities: 为进程赋予 root 用户的部分特权而非全部特权。
- AppArmor:定义Pod使用AppArmor限制容器对资源访问限制
- Seccomp:定义Pod使用Seccomp限制容器进程的系统调用。
- AllowPrivilegeEscalation: 禁止容器中进程(通过 SetUID 或 SetGID
文件模式)获得特权提升。当容器以特权模式运行或者具有CAP_SYS_ADMIN能力时,AllowPrivilegeEscalation总为True。- readOnlyRootFilesystem:以只读方式加载容器的根文件系统。
1.1 配置参数
注意事项:
- securityContext下配置的参数是针对该pod里的所有容器生效。
- containers下配置的参数只对某个容器生效,不同容器配置各自需要的参数。
pod安全上下文配置参数 | 释义 |
---|---|
spec.securityContext | Pod级别的安全上下文,对内部所有容器均有效。 |
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。
[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安全策略:
- 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。
[弃用官方文献](https://kubernetes.io/zh-cn/blog/2021/04/06/podsecuritypolicy-
deprecation-past-present-and-future/)- 替代方案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文档](https://open-policy-
agent.github.io/gatekeeper/website/docs/howto/)
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://github.com/open-policy-
agent/gatekeeper/releases)
2.导入文件一键部署,并查看。
[root@k8s-master1 opa]# kubectl apply -f gatekeeper.yaml
2.2.2 编写策略
Gatekeeper的策略由两个资源对象组成:
Template模板:在后面我们需要自定义限制策略,策略是怎么实现的就需要我们先写一个实现逻辑,这里就把这个实现逻辑称之为模板,使用rego语言。
2.
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脚本匹配约束里面的的资源。
##模板
[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 上。
![在这里插入图片描述](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.升级前内核版本。
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也存在兼容问题,并不是所有的容器都可以很好的使用此技术方案,目前支持的服务如下:
- 查看地址
![在这里插入图片描述](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
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
- 重启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.进入容器查看内核。
学习网络安全技术的方法无非三种:
第一种是报网络安全专业,现在叫网络空间安全专业,主要专业课程:程序设计、计算机组成原理原理、数据结构、操作系统原理、数据库系统、 计算机网络、人工智能、自然语言处理、社会计算、网络安全法律法规、网络安全、内容安全、数字取证、机器学习,多媒体技术,信息检索、舆情分析等。
第二种是自学,就是在网上找资源、找教程,或者是想办法认识一-些大佬,抱紧大腿,不过这种方法很耗时间,而且学习没有规划,可能很长一段时间感觉自己没有进步,容易劝退。
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
第三种就是去找培训。
接下来,我会教你零基础入门快速入门上手网络安全。
网络安全入门到底是先学编程还是先学计算机基础?这是一个争议比较大的问题,有的人会建议先学编程,而有的人会建议先学计算机基础,其实这都是要学的。而且这些对学习网络安全来说非常重要。但是对于完全零基础的人来说又或者急于转行的人来说,学习编程或者计算机基础对他们来说都有一定的难度,并且花费时间太长。
第一阶段:基础准备 4周~6周
这个阶段是所有准备进入安全行业必学的部分,俗话说:基础不劳,地动山摇
第二阶段:web渗透
学习基础 时间:1周 ~ 2周:
① 了解基本概念:(SQL注入、XSS、上传、CSRF、一句话木马、等)为之后的WEB渗透测试打下基础。
② 查看一些论坛的一些Web渗透,学一学案例的思路,每一个站点都不一样,所以思路是主要的。
③ 学会提问的艺术,如果遇到不懂得要善于提问。
配置渗透环境 时间:3周 ~ 4周:
① 了解渗透测试常用的工具,例如(AWVS、SQLMAP、NMAP、BURP、中国菜刀等)。
② 下载这些工具无后门版本并且安装到计算机上。
③ 了解这些工具的使用场景,懂得基本的使用,推荐在Google上查找。
渗透实战操作 时间:约6周:
① 在网上搜索渗透实战案例,深入了解SQL注入、文件上传、解析漏洞等在实战中的使用。
② 自己搭建漏洞环境测试,推荐DWVA,SQLi-labs,Upload-labs,bWAPP。
③ 懂得渗透测试的阶段,每一个阶段需要做那些动作:例如PTES渗透测试执行标准。
④ 深入研究手工SQL注入,寻找绕过waf的方法,制作自己的脚本。
⑤ 研究文件上传的原理,如何进行截断、双重后缀欺骗(IIS、PHP)、解析漏洞利用(IIS、Nignix、Apache)等,参照:上传攻击框架。
⑥ 了解XSS形成原理和种类,在DWVA中进行实践,使用一个含有XSS漏洞的cms,安装安全狗等进行测试。
⑦ 了解一句话木马,并尝试编写过狗一句话。
⑧ 研究在Windows和Linux下的提升权限,Google关键词:提权
以上就是入门阶段
第三阶段:进阶
已经入门并且找到工作之后又该怎么进阶?详情看下图
给新手小白的入门建议:
新手入门学习最好还是从视频入手进行学习,视频的浅显易懂相比起晦涩的文字而言更容易吸收,这里我给大家准备了一套网络安全从入门到精通的视频学习资料包免费领取哦!
如果你对网络安全入门感兴趣,那么你需要的话可以点击这里👉网络安全重磅福利:入门&进阶全套282G学习资源包免费分享!
更多推荐
所有评论(0)