CIS 加固指南
本文档为 K3s 生产环境安装的加固提供了规范性指导。它概述了满足来自互联网安全中心 (CIS) 的 Kubernetes 基准控制所需的配置和控制。
K3s 默认应用并启用了许多安全缓解措施,并且无需修改即可通过许多 Kubernetes CIS 控制。但有一些值得注意的例外情况,需要手动干预才能完全符合 CIS 基准。
- K3s 不会修改主机操作系统。任何主机级别的修改都需要手动完成。
- 某些针对
NetworkPolicies
和PodSecurityStandards
(v1.24 及更早版本上的PodSecurityPolicies
)的 CIS 策略控制将限制集群的功能。您必须选择让 K3s 配置这些内容,方法是在命令行标志或配置文件中添加相应的选项(启用准入插件),并手动应用相应的策略。以下各节将提供更多详细信息。
CIS 基准的第一部分 (1.1) 主要关注 pod 清单权限和所有权。K3s 不使用这些权限来管理核心组件,因为所有内容都打包到单个二进制文件中。
主机级要求
主机级要求有两个方面:内核参数和 etcd 进程/目录配置。本节将对这些方面进行概述。
确保设置 protect-kernel-defaults
这是一个 kubelet 标志,如果未设置所需的内核参数或其值与 kubelet 的默认值不同,则会导致 kubelet 退出。
注意:
protect-kernel-defaults
作为 K3s 的顶级标志公开。
设置内核参数
创建一个名为 /etc/sysctl.d/90-kubelet.conf
的文件,并添加以下代码段。然后运行 sysctl -p /etc/sysctl.d/90-kubelet.conf
。
vm.panic_on_oom=0
vm.overcommit_memory=1
kernel.panic=10
kernel.panic_on_oops=1
Kubernetes 运行时要求
为了符合 CIS 基准,运行时要求主要集中在 pod 安全性(通过 PSP 或 PSA)、网络策略和 API 服务器审计日志上。本节将对这些方面进行概述。
默认情况下,K3s 不包含任何 pod 安全性或网络策略。但是,K3s 附带一个控制器,如果创建了任何网络策略,该控制器将强制执行这些策略。K3s 默认情况下不启用审计,因此必须手动创建审计日志配置和审计策略。默认情况下,K3s 同时启用了 PodSecurity
和 NodeRestriction
准入控制器,以及其他控制器。
Pod 安全性
- v1.25 及更高版本
- v1.24 及更早版本
K3s v1.25 及更高版本支持 Pod 安全性准入 (PSA) 来控制 pod 安全性。通过将以下标志传递给 K3s 服务器,可以启用 PSA
--kube-apiserver-arg="admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml"
策略应写入 /var/lib/rancher/k3s/server
目录中的名为 psa.yaml
的文件中。
以下是一个符合要求的 PSA 示例
apiVersion: apiserver.config.k8s.io/v1
kind: AdmissionConfiguration
plugins:
- name: PodSecurity
configuration:
apiVersion: pod-security.admission.config.k8s.io/v1beta1
kind: PodSecurityConfiguration
defaults:
enforce: "restricted"
enforce-version: "latest"
audit: "restricted"
audit-version: "latest"
warn: "restricted"
warn-version: "latest"
exemptions:
usernames: []
runtimeClasses: []
namespaces: [kube-system, cis-operator-system]
K3s v1.24 及更早版本支持 Pod 安全性策略 (PSP) 来控制 pod 安全性。通过将以下标志传递给 K3s 服务器,可以启用 PSP
--kube-apiserver-arg="enable-admission-plugins=NodeRestriction,PodSecurityPolicy"
这将保持 NodeRestriction
插件以及启用 PodSecurityPolicy
。
启用 PSP 后,可以应用策略以满足 CIS 基准第 5.2 节中描述的必要控制。
以下是一个符合要求的 PSP 示例
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
spec:
privileged: false # CIS - 5.2.1
allowPrivilegeEscalation: false # CIS - 5.2.5
requiredDropCapabilities: # CIS - 5.2.7/8/9
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'csi'
- 'persistentVolumeClaim'
- 'ephemeral'
hostNetwork: false # CIS - 5.2.4
hostIPC: false # CIS - 5.2.3
hostPID: false # CIS - 5.2.2
runAsUser:
rule: 'MustRunAsNonRoot' # CIS - 5.2.6
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
readOnlyRootFilesystem: false
为了使上述 PSP 生效,我们需要创建一个 ClusterRole 和一个 ClusterRoleBinding。我们还需要包含一个“系统不受限制的策略”,该策略对于需要额外权限的系统级 pod 很有必要,以及允许 servicelb 正确运行所需的 sysctls 的其他策略。
将上述配置与下一节中描述的 网络策略 相结合,一个文件可以放置在 /var/lib/rancher/k3s/server/manifests
目录中。以下是一个 policy.yaml
文件示例
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: restricted-psp
spec:
privileged: false
allowPrivilegeEscalation: false
requiredDropCapabilities:
- ALL
volumes:
- 'configMap'
- 'emptyDir'
- 'projected'
- 'secret'
- 'downwardAPI'
- 'csi'
- 'persistentVolumeClaim'
- 'ephemeral'
hostNetwork: false
hostIPC: false
hostPID: false
runAsUser:
rule: 'MustRunAsNonRoot'
seLinux:
rule: 'RunAsAny'
supplementalGroups:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
fsGroup:
rule: 'MustRunAs'
ranges:
- min: 1
max: 65535
readOnlyRootFilesystem: false
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: system-unrestricted-psp
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
allowPrivilegeEscalation: true
allowedCapabilities:
- '*'
fsGroup:
rule: RunAsAny
hostIPC: true
hostNetwork: true
hostPID: true
hostPorts:
- max: 65535
min: 0
privileged: true
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
volumes:
- '*'
---
apiVersion: policy/v1beta1
kind: PodSecurityPolicy
metadata:
name: svclb-psp
annotations:
seccomp.security.alpha.kubernetes.io/allowedProfileNames: '*'
spec:
allowPrivilegeEscalation: false
allowedCapabilities:
- NET_ADMIN
allowedUnsafeSysctls:
- net.ipv4.ip_forward
- net.ipv6.conf.all.forwarding
fsGroup:
rule: RunAsAny
hostPorts:
- max: 65535
min: 0
runAsUser:
rule: RunAsAny
seLinux:
rule: RunAsAny
supplementalGroups:
rule: RunAsAny
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: psp:restricted-psp
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
verbs:
- use
resourceNames:
- restricted-psp
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: psp:system-unrestricted-psp
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
resourceNames:
- system-unrestricted-psp
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: psp:svclb-psp
rules:
- apiGroups:
- policy
resources:
- podsecuritypolicies
resourceNames:
- svclb-psp
verbs:
- use
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: default:restricted-psp
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp:restricted-psp
subjects:
- kind: Group
name: system:authenticated
apiGroup: rbac.authorization.k8s.io
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: system-unrestricted-node-psp-rolebinding
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp:system-unrestricted-psp
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:nodes
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: system-unrestricted-svc-acct-psp-rolebinding
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp:system-unrestricted-psp
subjects:
- apiGroup: rbac.authorization.k8s.io
kind: Group
name: system:serviceaccounts
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: svclb-psp-rolebinding
namespace: kube-system
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: psp:svclb-psp
subjects:
- kind: ServiceAccount
name: svclb
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: intra-namespace
namespace: kube-system
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: kube-system
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: intra-namespace
namespace: default
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: default
---
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: intra-namespace
namespace: kube-public
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
name: kube-public
注意:Kubernetes 的关键新增功能(如 CNI、DNS 和 Ingress)作为
kube-system
命名空间中的 pod 运行。因此,此命名空间将具有限制较少的策略,以便这些组件能够正常运行。
NetworkPolicies
CIS 要求所有命名空间都应用一个网络策略,该策略合理地限制了进入命名空间和 pod 的流量。
网络策略应放置在 /var/lib/rancher/k3s/server/manifests
目录中,在那里它们将在启动时自动部署。
以下是一个符合要求的网络策略示例。
kind: NetworkPolicy
apiVersion: networking.k8s.io/v1
metadata:
name: intra-namespace
namespace: kube-system
spec:
podSelector: {}
ingress:
- from:
- namespaceSelector:
matchLabels:
kubernetes.io/metadata.name: kube-system
在应用了这些限制后,DNS 将被阻止,除非有意允许。以下是一个网络策略,允许 DNS 流量存在。
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: default-network-dns-policy
namespace: <NAMESPACE>
spec:
ingress:
- ports:
- port: 53
protocol: TCP
- port: 53
protocol: UDP
podSelector:
matchLabels:
k8s-app: kube-dns
policyTypes:
- Ingress
如果未创建网络策略以允许访问,则 metrics-server 和 Traefik ingress 控制器将默认被阻止。K3s 版本 1.20 及以下版本中打包的 Traefik v1 使用的标签与 Traefik v2 不同。确保仅使用与集群上存在的 Traefik 版本关联的以下示例 yaml。
- v1.21 及更高版本
- v1.20 及更早版本
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-metrics-server
namespace: kube-system
spec:
podSelector:
matchLabels:
k8s-app: metrics-server
ingress:
- {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-svclbtraefik-ingress
namespace: kube-system
spec:
podSelector:
matchLabels:
svccontroller.k3s.cattle.io/svcname: traefik
ingress:
- {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-traefik-v121-ingress
namespace: kube-system
spec:
podSelector:
matchLabels:
app.kubernetes.io/name: traefik
ingress:
- {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-metrics-server
namespace: kube-system
spec:
podSelector:
matchLabels:
k8s-app: metrics-server
ingress:
- {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-svclbtraefik-ingress
namespace: kube-system
spec:
podSelector:
matchLabels:
svccontroller.k3s.cattle.io/svcname: traefik
ingress:
- {}
policyTypes:
- Ingress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-all-traefik-v120-ingress
namespace: kube-system
spec:
podSelector:
matchLabels:
app: traefik
ingress:
- {}
policyTypes:
- Ingress
---
操作员必须像往常一样管理创建的其他命名空间的网络策略。
API 服务器审计配置
CIS 要求 1.2.22 到 1.2.25 与为 API 服务器配置审计日志相关。K3s 默认情况下不会创建日志目录和审计策略,因为审计要求特定于每个用户的策略和环境。
理想情况下,日志目录必须在启动 K3s 之前创建。建议使用限制性访问权限,以避免泄露潜在的敏感信息。
sudo mkdir -p -m 700 /var/lib/rancher/k3s/server/logs
以下提供了一个用于记录请求元数据的入门审计策略。策略应写入 /var/lib/rancher/k3s/server
目录中的名为 audit.yaml
的文件中。有关 API 服务器策略配置的详细信息,请参阅 Kubernetes 文档。
apiVersion: audit.k8s.io/v1
kind: Policy
rules:
- level: Metadata
这两个配置都必须作为参数传递给 API 服务器,如下所示:
- 配置
- 命令行
kube-apiserver-arg:
- 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'
- 'audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log'
- 'audit-policy-file=/var/lib/rancher/k3s/server/audit.yaml'
- 'audit-log-maxage=30'
- 'audit-log-maxbackup=10'
- 'audit-log-maxsize=100'
--kube-apiserver-arg='audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log'
--kube-apiserver-arg='audit-policy-file=/var/lib/rancher/k3s/server/audit.yaml'
必须重新启动 K3s 才能加载新配置。
sudo systemctl daemon-reload
sudo systemctl restart k3s.service
Kubernetes 组件的配置
以下配置应放置在 配置文件 中,其中包含所有必要的缓解措施来加固 Kubernetes 组件。
- v1.25 及更高版本
- v1.24 及更早版本
protect-kernel-defaults: true
secrets-encryption: true
kube-apiserver-arg:
- "enable-admission-plugins=NodeRestriction,EventRateLimit"
- 'admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml'
- 'audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log'
- 'audit-policy-file=/var/lib/rancher/k3s/server/audit.yaml'
- 'audit-log-maxage=30'
- 'audit-log-maxbackup=10'
- 'audit-log-maxsize=100'
kube-controller-manager-arg:
- 'terminated-pod-gc-threshold=10'
kubelet-arg:
- 'streaming-connection-idle-timeout=5m'
- "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
protect-kernel-defaults: true
secrets-encryption: true
kube-apiserver-arg:
- 'enable-admission-plugins=NodeRestriction,PodSecurityPolicy,NamespaceLifecycle,ServiceAccount'
- 'audit-log-path=/var/lib/rancher/k3s/server/logs/audit.log'
- 'audit-policy-file=/var/lib/rancher/k3s/server/audit.yaml'
- 'audit-log-maxage=30'
- 'audit-log-maxbackup=10'
- 'audit-log-maxsize=100'
kube-controller-manager-arg:
- 'terminated-pod-gc-threshold=10'
kubelet-arg:
- 'streaming-connection-idle-timeout=5m'
- 'make-iptables-util-chains=true'
- "tls-cipher-suites=TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305,TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305"
手动操作
以下是 K3s 目前在应用上述配置后无法通过的控制。这些控制需要手动干预才能完全符合 CIS 基准。
控制 1.1.20
确保 Kubernetes PKI 证书文件的权限设置为 600 或更严格(手动)
详细信息
缓解措施
K3s PKI 证书文件存储在/var/lib/rancher/k3s/server/tls/
中,权限为 644。要进行修复,请运行以下命令chmod -R 600 /var/lib/rancher/k3s/server/tls/*.crt
控制 1.2.9
确保设置了准入控制插件 EventRateLimit
详细信息
缓解措施
请遵循 Kubernetes 文档 并在配置文件中设置所需的限制。对于此和其他 psa 配置,此文档使用 /var/lib/rancher/k3s/server/psa.yaml。然后,编辑 K3s 配置文件 /etc/rancher/k3s/config.yaml 并设置以下参数。kube-apiserver-arg:
- "enable-admission-plugins=NodeRestriction,EventRateLimit"
- "admission-control-config-file=/var/lib/rancher/k3s/server/psa.yaml"
控制 1.2.11
确保设置了准入控制插件 AlwaysPullImages
详细信息
缓解措施
根据 CIS 指南,允许使用此设置,“此设置可能会影响离线或隔离集群,这些集群预先加载了映像,并且无法访问注册表以提取正在使用的映像。对于使用此配置的集群,此设置不适用。” 编辑 K3s 配置文件 /etc/rancher/k3s/config.yaml 并设置以下参数。kube-apiserver-arg:
- "enable-admission-plugins=...,AlwaysPullImages,..."
控制 1.2.21
确保根据需要设置了 --request-timeout 参数
详细信息
缓解措施
根据 CIS 指南,允许使用此设置,“建议根据需要设置此限制,并且仅在必要时更改 60 秒的默认限制”。根据需要编辑 K3s 配置文件 /etc/rancher/k3s/config.yaml 并设置以下参数。例如:kube-apiserver-arg:
- "request-timeout=300s"
控制 4.2.13
确保为 pod PID 设置了限制
详细信息
缓解措施
确定此参数的适当级别并进行设置,如果使用 K3s 配置文件 /etc/rancher/k3s/config.yaml,请编辑该文件以将podPidsLimit
设置为kubelet-arg:
- "pod-max-pids=<value>"
控制 5.X
所有 5.X 控制都与 Kubernetes 策略配置相关。K3s 默认情况下不会强制执行这些控制。
有关如何创建和应用这些策略的更多信息,请参阅 CIS 1.8 第 5 节。
结论
如果您已按照本指南操作,您的 K3s 集群将配置为符合 CIS Kubernetes 基准。您可以查看CIS 1.8 自我评估指南,以了解基准测试中每个检查的预期以及如何在您的集群中执行相同操作。