侧边栏壁纸
博主头像
cloudnative-blog 博主等级

I can break through the limitations !

  • 累计撰写 23 篇文章
  • 累计创建 10 个标签
  • 累计收到 0 条评论

目 录CONTENT

文章目录

三.容器系统安全

周锐豪
2024-10-24 / 0 评论 / 0 点赞 / 17 阅读 / 0 字 / 正在检测是否收录...

一.pod安全上下文

1.简介

K8s对Pod和容器提供的安全机制,可以设置Pod特权和访问控制。

2.限制维度

自主访问控制(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:以只读方式加载容器的根文件系统。

3.配置参数

    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的相关配置

4.案例

4.1设置容器以普通用户运行

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

需求:
    设置容器以普通用户运行。

实现思路:
    Dockerfile里使用USER指定运行用户。
    K8s里指定spec.securityContext.runAsUser,指定容器默认用户UID。
#创建镜像并制定运行用户
USER  ww

#pod.yaml文件
spec:
 securityContext:
   runAsUser: 1000        ##这里的id必须是构建镜像里存在的用户id,创建用户时不指定id默认就是从1000开始。
   fsGroup: 1000           ##数据卷挂载后的目录属组设置为该组。
 containers:
 - image: nginx
   name: nginx
   securityContext:
     allowPrivilegeEscalation: false     ##不允许提权。

4.2设置容器以root用户运行

背景:
    容器中有些应用程序可能需要访问宿主机设备、修改内核等需求,默认情况下,容器没有这个能力,这时可以考虑给容器设置特权模式。
需求:
    避免使用特权容器。
使用思路:
docker方式:容器时指定–privileged参数,表示该容器内的程序是以root用户启动,跟在宿主机上用root用户启动一个进程完全没有区别,都可以调用内核的所有参数。
K8s方式:在pod安全上下文设置参数
containers:
- image: nginx
  name: nginx
  securityContext:
    privileged: true

5.Linux Capabilities

5.1简介

    Capabilities 的出现就是弥补直接在deployment.yaml中指定特权从而导致权限过大的弊端。
它是一个内核级别的权限调用解决方案,它允许对内核调用权限进行更细粒度的控制,而不是简单地以 root 身份能力授权,但比之前讲的Seccomp限制的容器进程系统调用更粗略一些。
    Capabilities 包括更改文件权限、控制网络子系统和执行系统管理等功能。在pod安全上下文中添加或删除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 计时器)

5.2使用

5.2.1案例1

需求:添加指定权限。容器默认没有挂载文件系统能力,添加SYS_ADMIN权能增加这个功能。
[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"]      ##添加挂载权限。

5.2.2案例2

需求:只读容器文件系统。只读挂载容器文件系统,防止恶意二进制文件创建。
    spec:
      containers:
      - image: test:latest
        name: flask-demo
        securityContext:
          runAsUser: 1000
          readOnlyRootFilesystem: true    ##添加此行

二.OPA Gatekeeper

1.OPA简介

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

作用:
拒绝不符合条件的YAML部署。
允许使用哪些仓库中的镜像。
允许在哪个时间段访问系统。
所有 Pod 必须有资源限制

2.OPA Gatekeeper简介

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

image-20231205155312031

3.安装安装Gatekeeperd

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

4.实现思路

先使用rego语言编写Template模板,在这个模板里自定义实现逻辑,就相当于写一个脚本逻辑。
自定义Constraint约束,约束格式类似rbac授权那种,要限制什么资源,这个资源在什么组,限制某个命名空间的资源。
当我们创建一个资源时,比如创建deployment时,若是触发了约束,则就会带入到模板里,看是否能违反约束,若是违反约束就相当于出发了开关,则禁止创建deployment。

5.案例1

需求:
    禁止容器启用特权。若用户创建的资源里有开启特权容器参数,则限制用户不能创建资源。
实现代码逻辑:
    violation函数返回的是布尔值True或者False。我取deployment.yaml文件里的特权容器参数,若返回值为false,则不会继续往下执行函数,属于放行不做拦截;若返回值为true,则说明整个函数表达式已通过,代表违反约束需要拦截,就相当于触碰到开关起到拦截作用。

5.1 编写模板

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: hello
spec:
  crd:
    spec:
      names:
        kind: hello
  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])   ##打印结果
        }

5.2编写约束

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: hello
metadata:
  name: hello
spec:
  match:
    kinds:
      - apiGroups: ["apps"]
        kinds:
        - "Deployment"
        - "DaemonSet"
        - "StatefulSet"

5.3创建测试

apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - name: nginx
        image: nginx
        securityContext:
          privileged: true

root@master01:~# kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [hello] 提示:'nginx'容器禁止启用特权!

6.案例2

需求:
    禁止创建service为NodePort类型的资源。若用户创建的service资源里有NodePort参数,则限制用户不能创建service资源。

6.1 编写模板

apiVersion: templates.gatekeeper.sh/v1beta1
kind: ConstraintTemplate
metadata:
  name: hello
spec:
  crd:
    spec:
      names:
        kind: hello
  targets:
    - target: admission.k8s.gatekeeper.sh
      rego: |
        package kubernetes.admission
        violation[{"msg": msg}] {
                    input.review.kind.kind = "Service"
                    input.review.operation = "CREATE"
                    input.review.object.spec.type = "NodePort"
                    msg := "不允许创建NodePort类型的service!"
        }

6.2编写约束

apiVersion: constraints.gatekeeper.sh/v1beta1
kind: hello
metadata:
  name: hello
spec:
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Service"]
    namespaces:
      - "default"

6.3创建测试

apiVersion: v1
kind: Service
metadata:
  name: nginx
spec:
  type: NodePort
  selector:
    app: nginx
  ports:
  - port:  80
    targetPort: 80
    nodePort: 33333
root@master01:~# kubectl apply -f nginx.yaml
Error from server (Forbidden): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [hello] 不允许创建NodePort类型的service!

三.gvisor

1.简介

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

image-20231206135025247

2.gvisor架构

Runsc 是一种 Runtime 引擎,负责容器的创建与销毁。
Sentry 负责容器内程序的系统调用处理。
Gofer 负责文件系统的操作代理,IO请求都会由它转接到Host上。

image-20231206135112212

3.k8s+containerd集成gvisor

3.1下载gvisor二进制文件

  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
  sha512sum -c runsc.sha512 \
    -c containerd-shim-runsc-v1.sha512
  rm -f *.sha512
  chmod a+rx runsc containerd-shim-runsc-v1
  sudo mv runsc containerd-shim-runsc-v1 /usr/local/bin

3.2修改containerd

root@master01:~# vim /etc/containerd/config.toml
      [plugins."io.containerd.grpc.v1.cri".containerd.runtimes]
--------------------------------------------------------------------------
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runsc]
          runtime_type = "io.containerd.runsc.v1"
---------------------------------------------------------------------------
        [plugins."io.containerd.grpc.v1.cri".containerd.runtimes.runc]
root@master01:~# systemctl restart containerd

3.3创建runtimeclass策略

apiVersion: node.k8s.io/v1 
kind: RuntimeClass
metadata:
  name: gvisor
handler: runsc 

3.4创建pod指定runtimeclass

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  nodeName: master01
  runtimeClassName: gvisor
  containers:
  - name: nginx
    image: nginx

3.5创建查看

root@nginx:/# uname -a
Linux nginx 4.4.0 #1 SMP Sun Jan 10 15:06:54 PST 2016 x86_64 GNU/Linux
root@nginx:/# uname -r
4.4.0
root@nginx:/# dmesg
[    0.000000] Starting gVisor...
[    0.468654] Creating process schedule...
[    0.724332] Digging up root...
[    1.008464] Synthesizing system calls...
[    1.437898] Daemonizing children...
[    1.644510] Checking naughty and nice process list...
[    2.071129] Searching for socket adapter...
[    2.374845] Conjuring /dev/null black hole...
[    2.792060] Committing treasure map to memory...
[    3.253193] Accelerating teletypewriter to 9600 baud...
[    3.532026] Creating bureaucratic processes...
[    3.939059] Setting up VFS...
[    4.127618] Setting up FUSE...
[    4.476305] Ready!
0

评论区