Kubernetes 安全与部署实践指南
1. 秘密管理与 GitOps
在 Kubernetes 中,使用 Secret 对象来管理敏感信息是一个重要的实践。以下是一个简单的 Deployment 示例,展示了如何将 Secret 挂载到容器中:
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:4
volumeMounts:
- name: secret-volume
mountPath: "/etc/config"
readOnly: true
volumes:
- name: secret-volume
secret:
secretName: secret-files
要验证是否一切正常,可以按照以下步骤操作:
1. 创建 Deployment 和 Secret。
2. 运行
kubectl get pods
,获取一个 Pod 的名称。
3. 使用
kubectl exec
命令列出挂载目录的内容:
$ kubectl exec POD_NAME -- ls /etc/config
example.key
现在,你的代码可以像访问系统中的其他文件一样访问这个文件。
然而,仅仅使用 Secret 是不够的,还需要考虑如何安全地存储它们。以下是几种不同的存储方法,按复杂度递增排列:
-
单独的仓库
:创建一个单独的配置仓库来存储 Secret,限制访问用户数量。可以将其放在云提供商的生产资源所在的位置,并使用与生产环境相同的访问控制。这样,即使账户被攻破,也不会增加额外的访问风险。
-
密封秘密(Sealed Secrets)
:Sealed Secrets 项目使用主密钥对所有 Secret 进行编码。虽然仍然存在主密钥的存储问题,但编码后的 Secret 可以包含在主配置仓库中,享受配置即代码的所有好处,如回滚。
-
秘密服务
:运行一个单独的服务来将 Secret 注入到集群中。HashiCorp 的 Vault 是一个非常流行的实现,并且可以开源运行。
2. Kubernetes 安全概述
Kubernetes 的安全是一个广泛的领域,对于开发者和集群操作员来说,确保集群的安全和更新是一项关键责任。以下是一些关键的安全主题:
- 保持集群和节点的更新,以修复已知的漏洞。
- 使用 PodDisruptionBudget 管理与更新相关的中断。
- 使用 DaemonSet 在每个节点上部署代理。
- 配置 Pod 的安全上下文。
- 以非根用户身份构建和运行容器。
- 使用准入控制器修改和/或验证 Kubernetes 对象。
- 使用内置的 Pod 安全准入在命名空间中强制执行安全标准。
- 使用基于角色的访问控制(RBAC)控制用户对命名空间的访问。
3. 保持集群和容器的更新
Kubernetes 有很大的攻击面,包括 Linux 内核、Kubernetes 软件、容器及其依赖项。因此,保持这些组件的更新至关重要。
3.1 集群和节点更新
Kubernetes 操作员的一项关键任务是确保集群和节点的更新。这有助于减轻 Kubernetes 和节点操作系统中的已知漏洞。更新集群和节点不是 Kubernetes API 的一部分,需要参考具体的 Kubernetes 平台文档。
以 Google Kubernetes Engine(GKE)为例,保持更新很简单。只需加入以下三个发布通道之一:
-
稳定版(Stable)
:安全补丁会快速推出,新功能的推出速度较慢。
-
常规版(Regular)
:安全补丁和新功能的推出速度适中。
-
快速版(Rapid)
:安全补丁和新功能的推出速度最快。
加入发布通道后,集群版本和节点会自动保持更新。不推荐使用旧的“静态版本”选项,因为需要手动跟踪更新。
3.2 容器更新
除了集群和节点更新,还需要定期更新容器。安全漏洞经常出现在基础镜像的组件中,因此需要定期重建和更新容器。许多开发者和企业使用漏洞扫描器(CVE 扫描器)来检查已构建的容器中是否存在报告的漏洞,以便优先进行重建和部署。
在更新容器时,应指定包含最新修复的基础镜像。通常,只指定基础镜像的次要版本,而不是特定的补丁版本。例如,对于 Python 基础镜像,可以选择
3.10-bullseye
这样的版本,既能获得补丁更新,又能避免重大更改。
以下是一些选择基础镜像版本的建议:
| 版本选择 | 优点 | 缺点 |
| ---- | ---- | ---- |
| 主要.次要版本(如 3.10-bullseye) | 自动获得补丁更新,避免重大更改 | 需要关注支持终止时间并迁移 |
| 主要版本(如 3-bullseye) | 更长的支持时间 | 有轻微的中断风险 |
| 最新版本(latest) | 从安全角度来看很好 | 中断风险极高,不推荐 |
为了减少容器更新的频率,可以构建极其轻量级的容器,只包含运行应用程序所需的最小代码和依赖项。Google 的 distroless 项目提供了超轻量级的运行时容器,可以帮助实现这一目标。以下是一个使用 distroless 构建 Java 容器的示例:
FROM openjdk:11-jdk-slim-bullseye AS build-env
COPY . /app/examples
WORKDIR /app
RUN javac examples/*.java
RUN jar cfe main.jar examples.HelloJava examples/*.class
FROM gcr.io/distroless/java11-debian11
COPY --from=build-env /app /app
WORKDIR /app
CMD ["main.jar"]
4. 处理更新中断
在进行更新时,不可避免地会删除和重新创建 Pod,这可能会对运行的工作负载造成影响。Kubernetes 提供了几种方法来减少这种影响:
-
就绪检查(Readiness Checks)
:确保容器在准备好处理生产流量时才被认为是就绪的。如果没有设置就绪检查,Kubernetes 可能会在应用程序尚未初始化完成时就认为它已就绪,导致请求失败。
-
信号处理和优雅终止(Signal Handling and Graceful Termination)
:在应用程序代码中处理 SIGTERM 事件,启动关闭过程,并设置足够长的优雅终止窗口(
terminationGracePeriodSeconds
),以确保应用程序有足够的时间完成终止。
-
滚动更新(Rolling Updates)
:在更新 Deployment 或 StatefulSet 中的容器时,使用滚动更新策略,分批更新 Pod,同时保持应用程序的可用性。对于 Deployment,确保配置
maxSurge
参数,通过临时增加 Pod 副本数来进行滚动更新,这样更安全。
-
Pod 中断预算(Pod Disruption Budgets,PDB)
:当节点更新时,使用 PDB 来确保在中断期间,不会有超过指定数量或百分比的 Pod 不可用。以下是一个 PDB 的示例:
apiVersion: policy/v1
kind: PodDisruptionBudget
metadata:
name: timeserver-pdb
spec:
maxUnavailable: 1
selector:
matchLabels:
pod: timeserver-pod
部署这个 PDB 到集群中,将确保在任何中断期间,不会有超过 1 个 Pod 不可用。
为了确保应用程序在部署更新和集群更新期间都能保持可用,需要同时配置滚动更新和 PDB。
5. 使用 DaemonSet 部署节点代理
DaemonSet 是一种 Kubernetes 工作负载类型,用于在每个节点上运行一个 Pod。通常用于集群操作目的,如日志记录、监控和安全。以下是一些 DaemonSet 的常见用途:
- 读取节点上的日志并上传到中央日志解决方案。
- 查询 kubelet API 获取性能指标。
- 监控容器和主机行为。
以下是一个简单的 DaemonSet 示例,用于读取节点上的日志并输出到标准输出:
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: logreader
spec:
selector:
matchLabels:
ds: logreaderpod
template:
metadata:
labels:
ds: logreaderpod
spec:
containers:
- image: ubuntu
command: ["tail", "-f", "/var/log/kube-proxy.log"]
name: logreadercontainer
resources:
requests:
cpu: 50m
memory: 100Mi
ephemeral-storage: 100Mi
volumeMounts:
- name: logpath
mountPath: /var/log
readOnly: true
volumes:
- hostPath:
path: /var/log
name: logpath
创建 DaemonSet:
$ kubectl create -f 12.2_DaemonSet/logreader.yaml
daemonset.apps/logreader created
当 Pod 准备好后,可以查看其日志输出:
$ kubectl get pods
NAME READY STATUS RESTARTS AGE
logreader-2nbt4 1/1 Running 0 4m14s
$ kubectl logs -f logreader-2nbt4
I0125 01:39:28.576504 1 proxier.go:845] "Syncing iptables rules"
I0125 01:39:28.615108 1 proxier.go:812] "SyncProxyRules complete"
elapsed="38.697941ms"
6. Pod 安全上下文
PodSpec 中的
securityContext
属性用于定义 Pod 及其容器的安全属性。以下是两个不同的示例:
-
请求特权的 Pod
:如果 Pod 需要执行某种管理功能,可以请求节点上的特权(root 访问):
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: admin-workload
spec:
selector:
matchLabels:
name: admin-app
template:
metadata:
labels:
name: admin-app
spec:
containers:
- name: admin-container
image: ubuntu
securityContext:
privileged: true
- 受限特权的 Pod :作为非管理应用程序的开发者,更可能使用这些属性来限制 Pod 的功能,以降低风险。以下是一个以非根用户身份运行且不能提升特权的 Pod 示例:
apiVersion: v1
kind: Pod
metadata:
name: timeserver-demo
labels:
app: timeserver
spec:
containers:
- name: timeserver-container
image: wdenniss/timeserver2:latest
securityContext:
runAsNonRoot: true
runAsUser: 1001
allowPrivilegeEscalation: false
capabilities:
drop:
- ALL
默认情况下,任何 Pod 都可以请求它想要的任何功能,甚至是 root 访问(除非 Kubernetes 平台进行了限制)。作为集群操作员,可能需要限制这种行为,以避免任何具有
kubectl
访问权限的人获得 root 特权。此外,建议不要以 root 用户身份运行容器,这可以通过
runAsNonRoot: true
配置来强制执行。
Kubernetes 安全与部署实践指南
7. 准入控制器与 Pod 安全准入
准入控制器是 Kubernetes 中的一个重要特性,用于修改和/或验证 Kubernetes 对象。它们在对象被持久化到 API 服务器之前拦截请求,并可以根据预定义的规则进行处理。
内置的 Pod 安全准入是一种特殊的准入控制器,用于在命名空间中强制执行安全标准。通过配置 Pod 安全准入,可以确保只有符合特定安全策略的 Pod 才能在命名空间中创建。
以下是使用 Pod 安全准入的一般步骤:
1.
定义安全策略
:创建一个或多个 Pod 安全标准(PodSecurityStandard),定义允许的 Pod 配置。例如,可以定义一个严格的策略,禁止以根用户身份运行 Pod。
2.
应用策略到命名空间
:将定义的安全策略应用到特定的命名空间。可以使用 PodSecurityConfiguration 资源来完成这个操作。
3.
验证和管理
:在命名空间中创建 Pod 时,Pod 安全准入会验证 Pod 是否符合应用的安全策略。如果不符合,请求将被拒绝。
# 示例:定义一个 Pod 安全标准
apiVersion: security.openshift.io/v1
kind: PodSecurityStandard
metadata:
name: restricted
spec:
allowPrivilegeEscalation: false
defaultAllowPrivilegeEscalation: false
fsGroup:
ranges:
- max: 65535
min: 1
rule: MustRunAs
runAsUser:
rule: MustRunAsNonRoot
seLinux:
rule: RunAsAny
supplementalGroups:
ranges:
- max: 65535
min: 1
rule: MustRunAs
volumes:
- configMap
- downwardAPI
- emptyDir
- persistentVolumeClaim
- projected
- secret
# 示例:将策略应用到命名空间
apiVersion: security.openshift.io/v1
kind: PodSecurityConfiguration
metadata:
name: my-namespace-config
spec:
namespaces:
- my-namespace
standards:
- restricted
8. 基于角色的访问控制(RBAC)
基于角色的访问控制(RBAC)是 Kubernetes 中用于控制用户对命名空间和资源的访问的机制。通过 RBAC,可以定义不同的角色和角色绑定,从而精确地控制用户可以执行的操作。
以下是 RBAC 的基本概念和操作步骤:
8.1 角色和集群角色
- 角色(Role) :用于在单个命名空间内定义一组权限。例如,可以创建一个角色,允许用户在特定命名空间内创建和删除 Pod。
- 集群角色(ClusterRole) :用于在整个集群范围内定义一组权限。集群角色可以应用于所有命名空间或特定的资源类型。
# 示例:创建一个角色
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
namespace: my-namespace
name: pod-reader
rules:
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "watch", "list"]
# 示例:创建一个集群角色
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: node-reader
rules:
- apiGroups: [""]
resources: ["nodes"]
verbs: ["get", "watch", "list"]
8.2 角色绑定和集群角色绑定
- 角色绑定(RoleBinding) :将角色与用户、组或服务账户关联起来,授予他们在特定命名空间内的权限。
- 集群角色绑定(ClusterRoleBinding) :将集群角色与用户、组或服务账户关联起来,授予他们在整个集群范围内的权限。
# 示例:创建一个角色绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: read-pods
namespace: my-namespace
subjects:
- kind: User
name: jane
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: Role
name: pod-reader
apiGroup: rbac.authorization.k8s.io
# 示例:创建一个集群角色绑定
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: read-nodes
subjects:
- kind: User
name: john
apiGroup: rbac.authorization.k8s.io
roleRef:
kind: ClusterRole
name: node-reader
apiGroup: rbac.authorization.k8s.io
9. 总结
Kubernetes 提供了丰富的功能和工具来确保集群的安全和高效运行。为了充分利用这些功能,可以采取以下最佳实践:
-
使用命名空间
:将不同的环境(如生产和测试)和应用程序分隔开来,提高管理效率和安全性。
-
配置即代码
:将 Kubernetes 配置存储在代码仓库中,像管理代码一样管理配置,进行同行评审和版本控制。
-
持续部署
:配置基于 Git 推送的持续部署管道,实现自动化部署和更新。
-
安全管理
:
- 保持集群和容器的更新,及时修复漏洞。
- 使用 PodDisruptionBudget 管理更新中断。
- 以非根用户身份构建和运行容器。
- 使用准入控制器和 RBAC 控制用户访问和资源使用。
通过遵循这些最佳实践,可以构建一个安全、稳定和高效的 Kubernetes 环境,为应用程序的部署和运行提供可靠的基础。
以下是一个简单的流程图,展示了 Kubernetes 安全管理的主要步骤:
graph LR
A[集群和节点更新] --> B[容器更新]
B --> C[处理更新中断]
C --> D[部署节点代理]
D --> E[配置 Pod 安全上下文]
E --> F[使用准入控制器]
F --> G[应用 RBAC]
通过以上步骤和方法,可以全面地管理 Kubernetes 集群的安全和部署,确保应用程序的稳定运行和数据的安全。
超级会员免费看
1960

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



