ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Kubernetes AppArmor Profile Loader:通过 DaemonSet 将 AppArmor 配置注入集群节点

Kubernetes AppArmor Profile Loader:通过 DaemonSet 将 AppArmor 配置注入集群节点 Kubernetes AppArmor Profile Loader通过 DaemonSet 将 AppArmor 配置注入集群节点【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetesAppArmor Profile Loader 是 Kubernetes 仓库test/images/apparmor-loader目录下的一个概念验证proof-of-concept守护进程它演示了如何把宿主机上的 AppArmor profile 自动加载到 Kubernetes 集群的各个节点上从而让 Pod 能借助container.apparmor.security.beta.kubernetes.io/容器名注解直接引用该 profile。本文以 test/images/apparmor-loader/README.md 为骨架结合 loader.go、example-daemon.yaml、example-configmap.yaml 等源码与示例清单完整还原从部署到验证的实操流程并解释其轮询加载的底层原理与设计局限。读完你可以在自己的集群里跑通ConfigMap 下发 profile → Loader 载入节点 → Pod 安全加固的完整链路。重要前提AppArmor 仅支持在 AppArmor 内核模块已启用的 Linux 节点上运行且 Kubernetes 需 v1.4。AppArmor Profile Loader 官方定位为演示用工具不视为生产就绪也不会作为长期解决方案被支持。一、它解决什么问题集群里 AppArmor profile 从哪来AppArmorApplication Armor是 Linux 内核的强制访问控制MAC模块通过为每个程序绑定 profile限制其文件、网络、能力等访问范围实现默认拒绝 显式放行。在 Kubernetes 中Pod 可以在注解中声明要应用某个宿主机上的 profilecontainer.apparmor.security.beta.kubernetes.io/container_name: localhost/profile_name问题在于profile 必须先存在于节点内核中Pod 才能引用它。而 Kubernetes 自身不会把 profile 分发到节点。AppArmor Profile Loader 正是为此设计的一个小而完整的示例它周期性地从一个 ConfigMap 中读取 AppArmor profile 文本调用节点上的apparmor_parser将其装载进内核。从源码结构看这套示例由以下资源组成均位于 test/images/apparmor-loader文件作用loader.goLoader 本体Go 编写负责扫描目录、比对已加载 profile、调用解析器example-namespace.yaml隔离用的独立 Namespaceapparmorexample-configmap.yaml存放k8s-nginxprofile 的 ConfigMapexample-daemon.yaml以 DaemonSet 方式在每个节点运行 Loaderexample-pod.yaml引用已加载 profile 的示例 Nginx PodDockerfile打包 Loader 镜像默认alpine:3.22基底Makefile / VERSION构建入口与镜像版本号1.6.0二、核心原理loader.go 的目录轮询加载机制先读懂守护进程的逻辑再上手部署会清晰很多。loader.go 的命令行约定是loader [FLAG]... [PROFILE_DIR]...关键行为可归纳为四步校验运行环境。启动后立即检查两件事apparmor_parser是否在 PATH 中exec.LookPath见 loader.go能否读取/sys/kernel/security/apparmor/profilesloader.go。这两个是硬前置任一失败都会Exitf退出。可见 Loader 必须运行在真实 AppArmor 已启用的主机上且能访问/sys。选择执行模式loader.go-poll 间隔为正时进入pollForever()先立即执行一次加载再以该间隔用time.Ticker无限轮询-poll为负默认-1时进入runOnce()加载一次即退出适合 standalone 一次性用法。扫描与去重。每次轮询会做当前已加载集合比对loader.go通过getLoadedProfiles()读取/sys/kernel/security/apparmor/profiles把每行形如profile-name (mode)或 namespaced 的namespace://profile-name (mode)解析出 profile 名mode 取值含 enforce / complain / killloader.go遍历每个 PROFILE_DIR 下的条目目录或指向目录的符号链接会被跳过浅扫描不递归普通文件则用apparmor_parser --names file解析其中声明的 profile 名loader.go只有当文件中存在尚未加载的 profileunloadedProfiles时才执行加载避免对已加载 profile 做无谓的重复解析。真正加载。对需要加载的文件执行apparmor_parser --verbose file并把 stderr 捕获进日志loader.go。这也是下文日志中Addition succeeded for k8s-nginx.一行的来源——它是apparmor_parser --verbose的成功输出。值得留意代码注释指出getLoadedProfiles是从 pkg/security/apparmor 复制而来loader.go这恰好说明 Loader 与 kubelet 侧 AppArmor 校验逻辑同源示例工具与实际运行时校验保持一致。三、以 DaemonSet 部署ConfigMap 承载 profile逐节点装载推荐把 Loader 与 ConfigMap 放进独立且受限的 Namespace。三种资源按序创建即可$ kubectl create -f test/images/apparmor-loader/example-namespace.yaml $ kubectl create -f test/images/apparmor-loader/example-configmap.yaml # 内含 k8s-nginx profile $ kubectl create -f test/images/apparmor-loader/example-daemon.yaml3.1 Namespace 与 ConfigMapexample-namespace.yaml 仅创建名为apparmor的独立 Namespace用于把管理面工具与业务负载隔离。example-configmap.yaml 的关键设计是ConfigMap 的 data key 即文件名文件内容即 AppArmor profile 定义。data 中k8s-nginx的完整定义被挂载进 Loader 的/profiles/k8s-nginx内容为一份典型 Nginx 加固 profile声明flags(attach_disconnected,mediate_deleted)放行 TCP/UDP/ICMP 网络与nginx可执行同时deny /bin/** wl、deny /tmp/** wl等禁止写入关键目录并拒绝mount、capability白名单以外的危险操作。整份 profile 通过-块标量保持换行原样可直接交给apparmor_parser解析。3.2 DaemonSet 的挂载与权限设计example-daemon.yaml 是整套方案的核心它用 DaemonSet 保证每个节点恰好一个 Loader Pod启动参数为args: - -poll - 30s # 每 30 秒轮询一次 /profiles - /profiles securityContext: privileged: true # 装载 profile 需要 root 权限 volumeMounts: - name: sys # mountPath: /sys readOnly: true - name: apparmor-includes # mountPath: /etc/apparmor.d readOnly: true - name: profiles # mountPath: /profiles readOnly: true volumes: - name: sys hostPath: { path: /sys } # 与 AppArmor 内核模块交互 - name: apparmor-includes hostPath: { path: /etc/apparmor.d } # 大多数 apparmor include 模板依赖宿主机此目录 - name: profiles configMap: { name: apparmor-profiles } # profile 数据来源三个卷各司其职源码注释亦逐一说明见 example-daemon.yaml/syshostPathLoader 通过/sys/kernel/security/apparmor/profiles读取已加载列表以去重而apparmor_parser实际写入内核也需要访问 AppArmor 安全文件系统/etc/apparmor.dhostPathprofile 中#include tunables/global、#include abstractions/base等指令会展开为宿主机此目录下的模板因此必须映射宿主机同名目录否则解析会失败/profilesConfigMap只读挂载由 ConfigMap 生成的 profile 文件。privileged: true是必需项向内核装载 AppArmor profile 要求 root 权限。也正因如此文档强调应将 Loader 与其 ConfigMap 放在独立、受限的 Namespace中运行控制这类高权限 DaemonSet 的影响面。3.3 验证 profile 是否加载成功取第一个 Pod 名并查看日志$ POD$(kubectl --namespace apparmor get pod -o jsonpath{.items[0].metadata.name}) $ kubectl --namespace apparmor logs $POD I0829 22:48:24.917263 1 loader.go:139] Polling /profiles every 30s I0829 22:48:24.954295 1 loader.go:196] Loading profiles from /profiles/k8s-nginx: Addition succeeded for k8s-nginx. I0829 22:48:24.954328 1 loader.go:100] Successfully loaded profiles: [k8s-nginx]对照源码可精确对应日志含义loader.go:139处为pollForever中的Polling ... every 30s-v2级别的 Info见 loader.goloader.go:196为loadProfiles中Loading profiles from ...并把apparmor_parser --verbose的标准输出透传loader.goAddition succeeded for k8s-nginx.即 parser 的成功提示loader.go:100为pollForever中成功加载的 profile 列表汇总。此外直接在节点上执行aa-status或查看/sys/kernel/security/apparmor/profiles也能确认k8s-nginx已进入内核Loader 自身正是读取该文件实现去重的。四、让 Pod 用上 profile注解绑定与行为验证profile 装入节点内核后即可创建引用它的 Pod。示例 example-pod.yaml 用注解把 profile 绑定到名为nginx的容器apiVersion: v1 kind: Pod metadata: name: nginx-apparmor annotations: # 注意Pod 无需与 Loader 处于同一 Namespace container.apparmor.security.beta.kubernetes.io/nginx: localhost/k8s-nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80注解取值localhost/k8s-nginx表示使用本节点上已加载的k8s-nginxprofile。需要 Kubernetes v1.4该版本引入注解机制否则注解会被静默忽略。从 Kubernetes 侧看kubelet 在真正应用前还会对 profile 名做白名单校验profile 名只能含字母、数字、-、_与.且必须以localhost/前缀引用本地 profile相关实现见 pkg/security/apparmor 的 validate/helpers 逻辑getProfileFromPodAnnotations负责从DeprecatedAppArmorBetaContainerAnnotationKeyPrefix 容器名的键解析注解见 pkg/security/apparmor/helpers.go。创建并验证预期输出如下$ kubectl create -f test/images/apparmor-loader/example-pod.yaml # 确认进程确实跑在目标 profile 下内核会显示 k8s-nginx (enforce) $ kubectl exec nginx-apparmor cat /proc/1/attr/current k8s-nginx (enforce) # 尝试写入被 profile deny 的 /tmp/foo应当被拒绝 $ kubectl exec nginx-apparmor touch /tmp/foo touch: cannot touch /tmp/foo: Permission denied error: error executing remote command: command terminated with non-zero exit code: Error executing in Docker Container: 1cat /proc/1/attr/current返回k8s-nginx (enforce)是 profile 生效的最直接证据——enforce表示处于强制模式另有complain仅记录不拦截。随后touch /tmp/foo失败正是因为 k8s-nginx profile 中写了deny /tmp/** wl见 example-configmap.yaml从行为层面印证了 profile 的真实约束力。若想把某容器恢复为无约束状态可把注解值改为localhost/unconfined。五、Standalone 运行与镜像方式除 DaemonSet 外Loader 也能脱离 Kubernetes 直接在宿主机跑。5.1 直接运行二进制二进制必须以 root 权限执行把 profile 目录作为位置参数传入sudo loader -logtostderr /path/to/profile/dir该用法对应runOnce()路径未指定-poll时默认-1加载一次即退出适合一次性装载、脚本化调用。注意 Loader 硬性要求本机装有apparmor_parser且能访问/sys/kernel/security/apparmor否则启动即退出。5.2 运行官方 Docker 镜像也可以直接使用项目提供的 Loader 镜像google/apparmor-loader:latest通过docker run挂载宿主机目录PROFILES_PATH/path/to/profile/dir sudo docker run \ --privileged \ --detachtrue \ --volume/sys:/sys:ro \ --volume/etc/apparmor.d:/etc/apparmor.d:ro \ --volume$PROFILES_PATH:/profiles:ro \ --nameaa-loader \ google/apparmor-loader:latest容器参数与 DaemonSet 卷设计一一对应--privileged提供装载所需权限/sys供读写 AppArmor 内核接口/etc/apparmor.d提供 include 模板/profiles注入 profile 源。镜像的构建信息可参见 Dockerfile以 Alpine 为基础apk安装apparmor、libapparmor把编译出的loader放到/usr/bin/loaderENTRYPOINT 固定为loader -logtostderr -v2默认 CMD 为/profiles所以镜像直接跑就监控/profiles目录。六、构建 Loader 镜像Loader 是简单 Go 程序仓库提供了构建入口。在test/images目录下执行make all-push WHATapparmor-loader其中 Makefile 的逻辑是调用上游共享脚本../image-util.sh bin编译SRCSloader并支持OS默认 linux与ARCH默认 amd64覆盖。镜像版本号记录在 VERSION当前为1.6.0。若只想本地产出二进制而不同时推送镜像可自行按 Makefile 中的bin目标执行对应编译命令具体构建/推送流程由 test/images/image-util.sh 统一编排。七、设计局限与安全注意README 明确交代了 Loader 的两条设计性局限见 README.md不会卸载被删除的 profile若 ConfigMap 中移除某 profileLoader 不会将其从节点内核卸载不会更新被修改的 profile若同一 profile 内容发生变化Loader 也不会重新装载新版本。这两点故意为之因为修改正在被进程使用的 AppArmor profile 存在诸多微妙问题——强行替换可能导致运行中容器行为突变甚至被拒。从代码看unloadedProfiles只做全量已加载去重loader.go并未比较 profile 内容或版本因此更新场景需要运维人员自行处理如改名后重载或滚动重启相关 Pod。除此之外还有两点必须强调的边界非生产就绪这是概念验证工具官方不提供长期支持生产环境应评估成熟方案或自行实现带校验、卸载与审计的 loader高权限风险DaemonSet 以privileged: true运行并挂载宿主机/sys、/etc/apparmor.d务必放在独立受限 Namespace并通过 RBAC/NetworkPolicy 等限制其访问范围。八、小结从端到端视角看这套示例勾勒出 Kubernetes 安全加固的一条清晰链路声明把 AppArmor profile 文本放进 ConfigMapkey 即文件名分发DaemonSet 形式的 Loader 在每个节点挂载/profiles每 30 秒轮询用apparmor_parser把新 profile 装载进内核消费业务 Pod 通过container.apparmor.security.beta.kubernetes.io/容器: localhost/profile注解声明约束由 kubelet 在启动容器时应用验证/proc/1/attr/current确认运行模式越权操作被拒绝即证明策略生效。核心代码loader.go仅 268 行即完整覆盖环境自检、目录扫描、已加载去重、parser 调用与日志输出加上 example-configmap.yaml 中可直接使用的k8s-nginx加固 profile非常适合作学习 AppArmor 与 Kubernetes 集成机制的起点也可作为自研 profile 分发组件的参考蓝本。【免费下载链接】kubernetesProduction-Grade Container Scheduling and Management项目地址: https://gitcode.com/GitHub_Trending/kuber/kubernetes创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表