您现在的位置是:首页 >技术交流 >k8s进阶5——AppArmor、Seccomp、ImagePolicyWebhook网站首页技术交流

k8s进阶5——AppArmor、Seccomp、ImagePolicyWebhook

百慕倾君 2024-06-26 14:23:38
简介k8s进阶5——AppArmor、Seccomp、ImagePolicyWebhook

一、AppArmor限制容器对资源访问

概念:

  • AppArmor(Application Armor) 是一个 Linux 内核安全模块,用于限制主机操作系统上运行的进程。每个进程都可以拥有自己的安全配置文件,安全配置文件用来允许或禁止特定功能,例如网络访问、文件读/写/执行权限等。
  • 是Linux发行版的内置功能,比如Ubuntu、Debian、SUSE,而CentOS和redhat系统没有这个AppArmor,其功能相当于selinux,但selinux安全程度不高,也不方便使用,所以一般人不用。
  • Ubuntu系统携带了许多没有合并到上游 Linux 内核中的 AppArmor 补丁, 包括添加附加钩子和特性的补丁。Kubernetes 只在上游版本中测试过,不承诺支持其他特性。

工作模式:

  1. Enforcement(强制模式) :在这种模式下,配置文件里列出的限制条件都会得到执行,并且对于违反这些限制条件的程序会进行日志记录,相当于selinux的enforcing模式。
  2. Complain(投诉模式):在这种模式下,配置文件里的限制条件不会得到执行,Apparmor只是对程序的行为进行记录,一般用于调试,相当于selinux的permissive模式。

K8s使用AppArmor的先决条件:

  1. K8s版本v1.4+,检查是否支持:kubectl describe node |grep AppArmor。
  2. Linux内核已启用AppArmor,查看 cat /sys/module/apparmor/parameters/enabled。
  3. 容器运行时需要支持AppArmor,docker、CRI-O 、containerd都支持。
  4. 比如以下ubuntu系统核内核是支持的,但在centos系统是看不到apparmor进程的。
    在这里插入图片描述

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

1.1 实现步骤

****:

  1. 将自定义策略配置文件保存到默认目录/etc/apparmor.d/下。
  2. 加载配置文件到内核。
  3. 定义pod.yaml文件时,在注解参数下指定策略名。因为AppArmor目前还处于测试阶段,因此以注解形式设定,日后进入正式阶段就可以使用安全上下文格式来配置了。

1.1.1 定义策略

配置策略:

vim /etc/apparmor.d/k8s-deny-write      ##文件名称自定义。
#include <tunables/global>     ##默认格式,导入依赖,遵循C语言约定。
profile k8s-deny-write flags=(attach_disconnected) {    ##指定策略名,也就是命令能查到显示出来的策略名称。
 #include <abstractions/base>      ##定义策略。
 file,                             ##允许所有文件读写。
 deny /bin/** w,                   ##递归匹配,拒绝/bin目录下的所有目录和文件写权限。
 deny /data/www/** w,              ##递归匹配,拒绝/data/www目录下的所有目录和文件写权限。
}

1.1.2 加载策略

  • 使用以下命令将策略加载到内核。
常用命令释义
apparmor_status查看AppArmor配置文件的当前状态的。
apparmor_parser将AppArmor配置文件加载到内核中。
apparmor_parser < profile >加载到内核中。
apparmor_parser -r < profile >重新加载配置。
apparmor_parser -R < profile >删除配置。
aa-complain将AppArmor配置文件设置为投诉模式,需要安装apparmor-utils软件包。
aa-enforce将AppArmor配置文件设置为强制模式,需要安装apparmor-utils软件包。

1.1.3 引用策略

  • 定义pod.yaml时添加注解加以引用。
  • 注意,引用的策略文件必须是要pod所在的宿主机上,实际生产中是需要运维来维护的,可以写脚本把策略文件给K8s集群每个节点分配上去并加载到内核。
apiVersion: v1
kind: Pod
metadata:
  name: hello-apparmor
  annotations:
    container.apparmor.security.beta.kubernetes.io/<container_name>: localhost/<profile_ref>
   ......
   ##< container_name >: Pod中容器名称。
   ##< profile_ref >: Pod所在宿主机上策略名,默认目录/etc/apparmor.d。
访问文件权限模式字符描述
r
w
a追加
k文件锁定
l链接
x可执行
通配符描述示例
*在目录级别匹配零个或多个字符。/dir/* :匹配目录中的任何文件。
/dir/a* :匹配目录中以a开头的任意文件。
/dir/.png: 匹配目录中以.png结尾的任意文件。
/dir/a
/ :匹配/dir里面以a开头的目录。
/dir/*a/ :匹配/dir里面以a结尾的目录。
**在多个目录级别匹配零个或多个字符。/dir/** : 匹配/dir目录或者/dir目录下任何文件和目录。
/dir/**/ : 匹配/dir或者/dir下面任何目录。
[]
[^]
字符串,匹配其中任意字符。/dir/[^.]* :匹配/dir目录中以.之外的任何文件。
/dir/**[^/] :匹配/dir目录或者/dir下面的任何目录中的任何文件。
pod.yaml字段释义
<container_name>pod.yaml文件里所针对的容器名称。
<profile_def>Pod所在宿主机上策略名,引用刚刚定义的配置文件。
<profile_ref>可以是以下取值之一:
runtime/default:应用运行时的默认配置。
localhost/<profile_name>:应用在主机上加载的名为 <profile_name> 的配置文件。
unconfined:表示不加载配置文件。

2.2 案例

案例:容器文件系统访问限制。

1.先查看默认生效的策略名称,我们定义的策略生效后就会显示在该列表中。
在这里插入图片描述
2.查看策略文件所在目录。
在这里插入图片描述
3.在K8s每个节点的/etc/apparmor.d目录下生成策略文件。策略是禁止在/bin目录下写,然后将策略文件加载到内核,查看结果。因为在编写pod.yaml里指定策略地址时,是使用的localhost读取本地。
在这里插入图片描述
在这里插入图片描述
4.先测试下使用我们生成策略之前效果,进入测试容器,可以在/bin目录下正常创建文件。
在这里插入图片描述
5.创建一个pod,其yaml文件里指定策略名称。
在这里插入图片描述
在这里插入图片描述
6.进入该容器创建目录测试效果。
在这里插入图片描述
7.若要修改策略,则修改后需要重新加载,并且需要重建pod容器。

apparmor parser -r k8s-deny-write

二、Seccomp 限制容器进程系统调用

前提了解:

  • 在Linux上, 我们操作的所有跟资源相关的命令都是通过系统调用来完成的。
  • 系统调用实现技术层次上解耦,内核只关心系统调用API的实现,而不必关心谁调用的。

调用关系图:
在这里插入图片描述

基本概念:

  1. Seccomp(Secure computing mode) 是一个 Linux 内核安全模块,可用于应用进程允许使用的系统调用。容器实际上是在宿主机上运行的一个进程,共享宿主机内核,如果所有容器都具有任何系统调用的能力,那么容器如果被入侵,就很轻松绕过容器隔离更改宿主机系统权限或者进入宿主机。这就可以使用Seccomp机制限制容器系统调用,有效减少攻击面。
  2. Linux发行版内置:CentOS、Ubuntu。

Type类型:

  • pod.yaml中会定义Type字段,表示将应用哪种类型的seccomp配置文件,具有如下选项:
    • Localhost:应该使用在节点上的文件中定义的配置文件。
    • RuntimeDefault:应该使用容器运行时默认配置文件。
    • Unconfined:无限制,不使用策略。

注意事项:

  1. 引用的策略文件必须是要pod所在的宿主机上,实际生产中是需要运维来维护的,可以写脚本把策略文件给K8s集群每个节点分配上去并加载到内核。
  2. Seccomp在Kubernetes 1.3版本引入,在1.19版本成为GA版本,因此K8s中使用Seccomp可以通过以下两种方式:
    • 1.19版本之前使用注解:
    annotations:
      seccomp.security.alpha.kubernetes.io/pod: "localhost/<profile>"
    
    • 1.19版本+配置安全上下文:
    apiVersion: v1
    kind: Pod
    metadata:
      name: hello-seccomp
    spec:
      securityContext:
        seccompProfile:
          type: Localhost
          localhostProfile: <profile> # Pod所在宿主机上策略文件名,默认目录:/var/lib/kubelet/seccomp
      containers:
    ...
    

案例一:使用自定义策略

  • 需求:禁止容器使用chmod。

1.创建默认文件路径。

[root@k8s-master1 ~]# mkdir /var/lib/kubelet/seccomp

2.在默认路径下生成限制策略。

[root@k8s-master1 seccomp]# cat chmod.json 
{
  "defaultAction": "SCMP_ACT_ALLOW",       ##在syscalls部分未定义的任何系统调用默认动作为允许。
  "syscalls": [
    {
        "names": [                      ##系统调用名称,可以换行写多个。
          "chmod"
        ],
        "action": "SCMP_ACT_ERRNO"      ## 阻止自定义的系统调用。
    }
  ]  
}

3.创建pod时,引用该策略。

[root@k8s-master1 ResourceQuota]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: baimu
spec:
  nodeName: k8s-master1    ##需要执行该pod被分配的机器上是有限制策略的。
  securityContext:         ##添加此参数。
    seccompProfile:
      type: Localhost      ##指定类型,调用本地自定义策略文件。
      localhostProfile: chmod.json       ##使用相对路径,指定应用策略名称。
  containers:
  - image: busybox
    name: test
    command:
    - sleep
    - 24h

4.进入普通容器测试可以修改权限。
在这里插入图片描述
5.进入指定限制策略的容器修改权限失败,限制成功。
在这里插入图片描述
6.此时需要再限制创建目录权限,就直接在chmod.json文件里加。
在这里插入图片描述
7.限制策略文件内容更改后,指定该策略的pod需要重新创建,先删除再创建容器,最后进入容器测试。
在这里插入图片描述

案例二:使用容器运行时默认策略

  • 大多数容器运行时都提供一组允许或不允许的默认系统调用。

  • 通过使用 runtime/default 注释 或将Pod 或容器的安全上下文中的 seccomp 类型设置为 RuntimeDefault,可以轻松地在 Kubernetes中应用默认值。

  • docker默认配置说明
    在这里插入图片描述

1.指定docker默认策略创建pod。

[root@k8s-master1 ResourceQuota]# cat pod.yaml 
apiVersion: v1
kind: Pod
metadata:
  name: baimu
spec:
  nodeName: k8s-master1
  securityContext:
    seccompProfile:
      type: RuntimeDefault    ##修改类型,使用容器运行时默认策略。
    #  localhostProfile: chmod.json    ##注释掉。
  containers:
  - image: busybox
    name: test
    command:
    - sleep
    - 24h

2.查看系统调用。查看操作命令用到哪些系统调用可以使用strace命令查看,一个命令通常会使用多个系统调用,若是在实际生产中使用需要挨个调试。

[root@k8s-master1 seccomp]# yum -y install strace

在这里插入图片描述

三、动态准入控制Webhook

基本概念:

  • 前面讲过的准入控制是通过添加K8s内置的准入插件来实现,当然也可以作为扩展独立开发,并以运行时所配置的 Webhook 的形式运行。
  • 准入控制器Webhook是准入控制插件的一种,是一种用于接收准入请求并对其进行处理的 HTTP 回调机制 ,常用于拦截所有向apiserver发送的请求,并且可以修改请求或拒绝请求。
  • Admission webhook为开发者提供了非常灵活的插件模式,在kubernetes资源持久化之前,管理员通过程序可以对指定资源做校验、修改等操作。例如为资源自动打标签、pod设置默认SA,自动注入sidecar容器等。

Webhook准入控制器分类:

  1. Mutating Admission Webhook:先调用,修改资源,理论上可以监听并修改任何经过ApiServer处理的请求。
  2. Validating Admission Webhook:后调用,验证资源。
  3. ImagePolicyWebhook:镜像策略,主要验证镜像字段是否满足条件,允许使用后端 Webhook 做出准入决策。

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

3.1 ImagePolicyWebhook控制器

工作流程图:
在这里插入图片描述
玩法思路:

  1. 签发证书。镜像策略服务器是以https启用,需要给它自签证书,访问它时也要携带根证书或者客户端证书。
  2. 准备imgaepolicywebhook配置文件,指定镜像策略服务器配置文件存放目录。
  3. 准备镜像策略服务器配置文件,指定连接服务器IP地址、端口、API、根证书、客户端证书。
  4. 启用准入控制插件。

3.1.1 apiserver服务器准备操作

1.定义镜像策略控制器配置文件,指定配置文件存放路径。

##镜像策略控制器配置文件,指定连接镜像策略服务器配置文件地址,连接服务器通用策略。
[root@k8s-master1 imagepolicywebhook]# cat admission_configuration.yaml 
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: ImagePolicyWebhook
  configuration:
    imagePolicy:
      kubeConfigFile: /etc/kubernetes/image-policy/connect_webhook.yaml  # 指定镜像策略服务器配置文件。
      allowTTL: 50       # 控制批准请求的缓存时间,单位秒
      denyTTL: 50        # 控制批准请求的缓存时间,单位秒
      retryBackoff: 500    # 控制重试间隔,单位毫秒
      defaultAllow: true    # 确定webhook后端失效的行为

2.定义镜像策略服务器连接配置文件,指定它的根证书、连接它的客户端证书、连接地址、端口和API。

[root@k8s-master1 imagepolicywebhook]# cat connect_webhook.yaml 
apiVersion: v1
kind: Config
clusters:
- cluster:
    certificate-authority: /etc/kubernetes/image-policy/webhook.pem # 数字证书,用于验证远程服务
    server: https://192.168.130.146:8080/image_policy # 镜像策略服务器地址,必须是https
  name: webhook
contexts:
- context:
    cluster: webhook
    user: apiserver
  name: webhook
current-context: webhook 
preferences: {}
users:
- name: apiserver
  user:
    client-certificate: /etc/kubernetes/image-policy/apiserver-client.pem # webhook准入控制器使用的证书
    client-key: /etc/kubernetes/image-policy/apiserver-client-key.pem # 对应私钥证书

3.统一两文件存放目录。

[root@k8s-master1 imagepolicywebhook]# mkdir -p /etc/kubernetes/image-policy  
[root@k8s-master1 imagepolicywebhook]# mv admission_configuration.yaml connect_webhook.yaml /etc/kubernetes/image-policy/

4.签发证书。

[root@k8s-master1 ssl]# cat image-policy-certs.sh 
cat > ca-config.json <<EOF    ##k8s根证书。
{
  "signing": {
    "default": {
      "expiry": "87600h"
    },
    "profiles": {
      "kubernetes": {
         "expiry": "87600h",
         "usages": [
            "signing",
            "key encipherment",
            "server auth",
            "client auth"
        ]
      }
    }
  }
}
EOF

cat > ca-csr.json <<EOF
{
    "CN": "kubernetes",
    "key": {
        "algo": "rsa",
        "size": 2048
    },
    "names": [
        {
            "C": "CN",
            "L": "Beijing",
            "ST": "Beijing"
        }
    ]
}
EOF

cfssl gencert -initca ca-csr.json | cfssljson -bare ca -

cat > webhook-csr.json <<EOF   ##镜像策略服务器根证书。
{
  "CN": "webhook",
  "hosts": [
   "192.168.130.146"     ##镜像策略服务器地址。
   ],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing"
    }
  ]
}
EOF

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes webhook-csr.json | cfssljson -bare webhook

cat > apiserver-client-csr.json <<EOF            ##连接镜像策略服务器的客户端证书。
{
  "CN": "apiserver",
  "hosts": [],
  "key": {
    "algo": "rsa",
    "size": 2048
  },
  "names": [
    {
      "C": "CN",
      "L": "BeiJing",
      "ST": "BeiJing"
    }
  ]
}
EOF

cfssl gencert -ca=ca.pem -ca-key=ca-key.pem -config=ca-config.json -profile=kubernetes apiserver-client-csr.json | cfssljson -bare apiserver-client

在这里插入图片描述

5.将证书放入前面两文件目录里,文件里有指定读取这两个证书。

[root@k8s-master1 ssl]# mv apiserver-client*.pem webhook.pem /etc/kubernetes/image-policy/

6.修改apiserver配置文件,开启ImagePolicyWebhook插件,指定imagepolicywebhook配置文件位置,并挂载到容器里面。

 - --enable-admission-plugins=NodeRestriction,ImagePolicyWebhook
 - --admission-control-config-file=/etc/kubernetes/image-policy/admission_configuration.yaml

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

3.1.2 部署webhook服务器

在这里插入图片描述

1.部署webhook站点服务器,我这里是写了一个python程序来实现对部署时的镜像没有指定标签(即latest)进行拒绝。

[root@k8s-master2 imagepolicywebhook]# cat main.py 
from flask import Flask,request
import json

app = Flask(__name__)

@app.route('/image_policy',methods=["POST"])     ##处理发送过来的post请求。
def image_policy():
    post_data = request.get_data().decode()           ##接受发送过来的post请求。
    #print("POST数据: %s" %post_data)                  ##将post请求打印出来。
    data = json.loads(post_data)                      ##解析这个post请求。
    for c in data['spec']['containers']:                  ##过滤这个post请求中的spec.containers字段下的内容。
        if ":" not in c['image'] or ":latest" in c['image']:  ##如果镜像里不带冒号或者带:latest说明是镜像使用latest标签。
            allowed, reason = False, "检查镜像失败!镜像标签不允许使用latest!"
            break
        else:
            allowed, reason = True, "检查镜像通过."
    print("检查结果: %s" %reason)
    result = {"apiVersion": "imagepolicy.k8s.io/v1alpha1","kind": "ImageReview",     ##返回个apiserver的api,固定格式。
              "status": {"allowed": allowed,"reason": reason}}

    return json.dumps(result,ensure_ascii=False) 

if __name__ == "__main__":                               ##监听服务端口8999,需要把证书传到该服务器,后面会挂载到容器里。
    app.run(host="0.0.0.0",port=8080,ssl_context=('/data/www/webhook.pem','/data/www/webhook-key.pem'))

2.构建镜像。

[root@k8s-master2 imagepolicywebhook]# cat Dockerfile 
FROM python
RUN useradd python
RUN mkdir /data/www -p
COPY . /data/www
RUN chown -R python /data
RUN pip install flask -i https://mirrors.aliyun.com/pypi/simple/
WORKDIR /data/www
USER python
CMD python main.py 

[root@k8s-master2 imagepolicywebhook]# docker build -t qingjun:v1  .

3.准备好之前在apiserver服务器上创建的证书,并运行一个容器进入查看,容器日志会显示python程序检查apiserver发过来的请求结果。

docker run -d -u root --name=baimu1 
-v $PWD/webhook.pem:/data/www/webhook.pem 
-v $PWD/webhook-key.pem:/data/www/webhook-key.pem 
-e PYTHONUNBUFFERED=1 -p 8080:8080 
qingjun:v1

在这里插入图片描述

3.1.3 测试验证

1.创建pod容器时,使用不带标签的镜像,则返回false。
在这里插入图片描述
2.创建pod容器时,使用带latest标签的镜像,返回false。
在这里插入图片描述
3.创建pod容器时,使用带标签的镜像,返回true。
在这里插入图片描述

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。