容器安全:非根用户容器与准入控制器实践
1. 非根用户容器概述
在容器部署中,一个常见的安全建议是不以根用户身份运行容器。尽管容器采用了沙箱技术(如 Linux cgroups 和命名空间),但如果容器以根用户身份运行,一旦存在“容器逃逸”漏洞,攻击者可能获得节点的完全根访问权限。Docker 默认以根用户运行所有进程,这使得容器逃逸漏洞成为一个潜在的安全问题。为了实现“深度防御”原则,建议以非根用户身份运行容器,这样即使攻击者突破容器并利用容器逃逸漏洞,也无法获得节点的提升权限。
Docker 默认以根用户构建容器是为了方便开发者,因为根用户可以使用特权端口(端口号低于 1024),并且无需处理文件夹权限问题。但从安全角度考虑,以非根用户运行容器可以增加系统的安全层。
2. 在 Kubernetes 中防止容器以根用户运行
在 Kubernetes 中,可以通过注解 Pod 来防止其以根用户运行。以下是一个示例 Deployment 配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 1
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:6
securityContext:
runAsNonRoot: true
然而,仅设置
runAsNonRoot: true
可能不够,因为容器本身可能没有配置非根用户。如果尝试创建此 Deployment,Kubernetes 会强制执行
securityContext
,但容器可能无法运行。例如:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
timeserver-pod-fd574695c-5t92p 0/1 CreateContainerConfigError 0 34s
$ kubectl describe pod timeserver-pod-fd574695c-5t92p
Name: timeserver-pod-fd574695c-5t92p
Events:
Type Reason Age From Message
---- ------ ---- ---- -------
Warning Failed 10s (x3 over 23s) kubelet Error:
container has runAsNonRoot and image will run as root (pod: "timeserver-pod-
fd574695c-5t92p_default(8cfaf4a9-c2e6-4dde-b506-14ab04444f50)", container:
timeserver-container)
为了解决这个问题,需要配置 Pod 运行的用户。可以在 Dockerfile 中使用
USER 1001
或在 Kubernetes 配置中使用
runAsUser: 1001
。当两者都存在时,Kubernetes 配置优先。以下是 Dockerfile 的示例:
FROM python:3
COPY . /app
WORKDIR /app
RUN mkdir logs
CMD python3 server.py
USER 1001
或者在 Deployment 中配置:
apiVersion: apps/v1
kind: Deployment
metadata:
name: timeserver
spec:
replicas: 1
selector:
matchLabels:
pod: timeserver-pod
template:
metadata:
labels:
pod: timeserver-pod
spec:
containers:
- name: timeserver-container
image: docker.io/wdenniss/timeserver:7
securityContext:
runAsNonRoot: true
runAsUser: 1001
建议在 Kubernetes 端进行配置,这样可以更好地分离开发和生产环境。
3. 更新容器以非根用户运行
当容器以非根用户运行时,可能会遇到两个主要问题:无法监听特权端口(1 - 1023)和默认没有对容器可写层的写访问权限。
3.1 端口问题
对于端口问题,可以更改容器使用的端口,同时保持负载均衡器使用标准端口 80。以下是更新后的 Python 代码:
def startServer():
try:
server = ThreadingHTTPServer(('',8080), RequestHandler)
print("Listening on " + ":".join(map(str, server.server_address)))
server.serve_forever()
except KeyboardInterrupt:
server.shutdown()
if __name__== "__main__":
startServer()
同时,需要更新 Kubernetes 服务配置以匹配新端口:
apiVersion: v1
kind: Service
metadata:
name: timeserver
spec:
selector:
pod: timeserver-pod
ports:
- port: 80
targetPort: 8080
protocol: TCP
type: LoadBalancer
3.2 写权限问题
如果在以非根用户运行容器时遇到文件写入权限被拒绝的错误,说明文件夹权限未正确配置。可以通过设置文件夹的组权限来解决这个问题。以下是更新后的 Dockerfile:
FROM python:3.10
ENV PYTHONUNBUFFERED 1
COPY . /app
WORKDIR /app
RUN mkdir logs
RUN chgrp -R 0 logs \
&& chmod -R g+rwX logs
CMD python3 server.py
如果想在 Docker 中以非根用户运行容器,可以在运行时覆盖用户设置:
docker run --user 1001:0
4. 准入控制器概述
在前面的部分,我们手动为 Pod 添加了
runAsNonRoot
配置。如果希望对所有 Pod 应用此设置,可以使用准入控制器。准入控制器是在创建 Kubernetes 对象时通过 Webhook 执行的代码,分为验证型和变异型两种。验证型准入 Webhook 可以接受或拒绝 Kubernetes 对象,变异型准入 Webhook 可以修改传入的对象。
5. Pod 安全准入
Kubernetes 提供了 Pod 安全准入(Pod Security Admission)来强制执行安全策略。在 Kubernetes 1.25 之前,PodSecurityPolicy 用于此目的,但该功能一直处于 Beta 阶段并最终被移除。Pod 安全准入从 Kubernetes 1.23 开始作为 Beta 功能默认启用,也可以手动部署到较旧版本的 Kubernetes 集群中。
5.1 安装 Pod 安全准入
首先,验证
pod-security-webhook
是否已安装在集群中:
$ kubectl get ValidatingWebhookConfiguration | grep pod-security-webhook.kubernetes.io
pod-security-webhook.kubernetes.io 2 26h
如果需要安装,可以按以下步骤操作:
$ git clone https://github.com/kubernetes/pod-security-admission.git
$ cd pod-security-admission/webhook
$ make certs
$ kubectl apply -k .
5.2 Pod 安全策略
Pod 安全定义了三种在命名空间级别应用的安全策略级别:
| 策略级别 | 描述 |
| ---- | ---- |
| 特权(Privileged) | Pod 具有无限制的管理访问权限,可以获得节点的根访问权限。 |
| 基线(Baseline) | Pod 不能提升权限以获得管理访问权限,但仍保留大部分功能。 |
| 受限(Restricted) | 实现当前的最佳安全实践(即深度防御),在基线配置文件的基础上增加额外的保护层,包括限制以根用户运行。 |
5.3 创建具有 Pod 安全策略的命名空间
为了实现最安全的配置,我们可以创建一个使用受限策略的命名空间。以下是一个示例命名空间配置:
apiVersion: v1
kind: Namespace
metadata:
name: team1
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/enforce-version: v1.23
创建命名空间并设置当前上下文:
$ kubectl create -f 12.3.1_PodSecurityAdmission/namespace.yaml
$ kubectl config set-context --current --namespace=team1
如果尝试部署一个未设置
runAsNonRoot
的 Pod,将会被拒绝:
$ kubectl create -f Chapter03/3.2.4_ThePodspec/pod.yaml
Error from server (Forbidden): error when creating "Chapter03/3.2.4_ThePodspec/pod.yaml":
admission webhook "pod-security-webhook.kubernetes.io" denied the request: pods
"timeserver" is forbidden: violates PodSecurity "restricted:v1.23":
allowPrivilegeEscalation != false (container "timeserver-container" must set
securityContext.allowPrivilegeEscalation=false), unrestricted capabilities
(container "timeserver-container" must set
securityContext.capabilities.drop=["ALL"]), runAsNonRoot != true (pod or container
"timeserver-container" must set securityContext.runAsNonRoot=true)
添加适当的
securityContext
以满足 Pod 安全准入策略后,Pod 将被接受:
apiVersion: v1
kind: Pod
metadata:
name: timeserver-pod
spec:
containers:
- name: timeserver-container
image: wdenniss/timeserver:7
securityContext:
runAsNonRoot: true
allowPrivilegeEscalation: false
runAsUser: 1001
capabilities:
drop:
- ALL
6. 调试 Deployment 的 Pod 准入拒绝问题
在调试时,使用 Pod 比 Deployment 更容易。如果创建的 Deployment 违反了 Pod 安全策略,创建 Deployment 对象本身可能会成功,但在创建 Pod 时会失败。由于 Deployment 在底层使用 ReplicaSet 来管理 Pod,因此需要检查 ReplicaSet 才能找到错误信息。
以下是调试步骤:
1. 创建 Deployment 后,使用
kubectl get pods
检查是否有 Pod 对象。如果没有,可能是 Pod 在准入时被拒绝。
2. 使用
kubectl describe rs
检查 ReplicaSet 的事件信息,以查找错误原因。
通过以上实践,可以提高容器部署的安全性,确保容器以非根用户运行,并通过 Pod 安全准入强制执行安全策略。
容器安全:非根用户容器与准入控制器实践
7. 非根用户容器与准入控制器的关联及优势
非根用户容器和准入控制器在保障容器安全方面相互关联且发挥着重要作用。非根用户容器从根本上减少了容器逃逸漏洞带来的风险,即使攻击者突破容器,也难以获得节点的高权限。而准入控制器则从全局层面确保所有 Pod 遵循安全策略,防止不符合安全要求的 Pod 进入集群。
两者结合使用的优势主要体现在以下几个方面:
-
增强安全性
:非根用户容器限制了容器内进程的权限,准入控制器则进一步过滤掉不安全的配置,双重保障了集群的安全。
-
提高合规性
:通过设置统一的安全策略,确保所有 Pod 符合安全标准,满足企业或组织的合规要求。
-
简化管理
:准入控制器可以自动应用安全配置,减少手动配置的工作量,提高管理效率。
8. 实际应用中的注意事项
在实际应用非根用户容器和准入控制器时,还需要注意以下几点:
-
兼容性问题
:并非所有容器都设计为以非根用户运行,在迁移现有容器时,可能需要对容器进行修改以适应非根用户环境。
-
性能影响
:虽然非根用户容器和准入控制器主要是为了提高安全性,但在某些情况下,可能会对性能产生一定的影响。例如,频繁的权限检查可能会增加系统开销。
-
策略更新
:随着安全威胁的不断变化,需要定期更新 Pod 安全策略,以确保集群始终受到最新的安全保护。
9. 总结与建议
通过以上对非根用户容器和准入控制器的介绍和实践,我们可以得出以下结论:
-
非根用户容器是容器安全的基础
:以非根用户身份运行容器可以有效降低容器逃逸漏洞带来的风险,是保障容器安全的重要措施。
-
准入控制器是集群安全的保障
:使用准入控制器可以确保所有 Pod 遵循安全策略,提高集群的整体安全性。
-
持续关注安全动态
:安全是一个持续的过程,需要不断关注新的安全威胁和技术,及时更新安全策略和配置。
以下是一些建议:
-
从开发阶段开始考虑安全
:在开发容器时,就应该考虑以非根用户运行,避免后期修改带来的麻烦。
-
定期进行安全审计
:定期检查集群中的 Pod 是否符合安全策略,及时发现和解决潜在的安全问题。
-
培训和教育
:对开发人员和运维人员进行安全培训,提高他们的安全意识和技能。
10. 流程图展示
下面通过 mermaid 格式的流程图来展示整个容器部署和安全检查的流程:
graph LR
classDef startend fill:#F5EBFF,stroke:#BE8FED,stroke-width:2px;
classDef process fill:#E5F6FF,stroke:#73A6FF,stroke-width:2px;
classDef decision fill:#FFF6CC,stroke:#FFBC52,stroke-width:2px;
A([开始部署容器]):::startend --> B(创建 Deployment):::process
B --> C{Pod 是否符合安全策略?}:::decision
C -->|是| D(创建 Pod):::process
C -->|否| E(拒绝创建 Pod):::process
D --> F{容器是否以非根用户运行?}:::decision
F -->|是| G(容器正常运行):::process
F -->|否| H(尝试调整配置):::process
H --> I{调整后是否符合要求?}:::decision
I -->|是| G
I -->|否| J(容器启动失败):::process
E --> K([结束部署]):::startend
G --> K
J --> K
这个流程图清晰地展示了从容器部署开始,经过 Pod 安全策略检查、非根用户运行检查,到最终容器是否成功运行的整个过程。
通过以上的实践和总结,我们可以更好地理解和应用非根用户容器和准入控制器,提高容器部署的安全性和可靠性。在实际应用中,需要根据具体情况进行调整和优化,以确保集群始终处于安全状态。
超级会员免费看
37

被折叠的 条评论
为什么被折叠?



