Kubernetes 容器平台的安全,牵涉到平台的各个层面:服务器,操作系统, Docker 运行环境,容器, Kubernetes 运行环境等。
容器运行在服务器上,服务器和操作系统安全应该是容器平台安全的第一道防线。关于主机和内核安全的资料和文档已经非常的丰富,不是我们这篇文档关注的重点,我们以下就其他几个层面展开。
Docker 运行环境
Docker 运行环境是和运行 Docker 进程相关的操作系统环境,安全实施方案如下:
- 参考 CIS 的 Docker 安全基线[1] 对 Docker 运行环境进行安全审计,比如不使用 Aufs,为容器存储建立单独的系统分区等。另外,Docker-bench-security[2] 是基于 CIS 安全基线定制的检测脚本,可以用于安全基线的审计。
- 运行 Docker 进程监听在 UNIX 套接字,一般是 /var/run/docker. sock,不使用网络套接字。
- 尽量避免把 Docker 套接字挂接到容器里。
容器运行安全
容器不运行的时候叫做镜像,运行的时候叫做容器。容器运行安全自然也包括镜像安全。安全实施方案如下:
- 只从信任的镜像仓库获取镜像,避免随意下载镜像。
- 对私有镜像仓库的镜像进行安全扫描。比如 CoreOS 的 Clair,已经集成到 Harbor 里。
- 上传镜像的时候对镜像进行签名。容器内容信任机制[3]能够保证镜像发布者和镜像内容的完整性,Docker 集成了 Notray[4] 来管理镜像签名。
- 客户端开启内容安全机制。设置环境变量 DOCKERCONTENTTRUST 为 1 ,开启内容安全机制,只下载签名的镜像。
- 生产和预生产系统只允许从私有镜像仓库下载镜像。
- 尽量用非 root 用户运行容器。
- 如果用 root 用户运行容器,避免使用 priviledged 模式,而是根据应用的需求把特定的 Linux 能力授权给容器。
- 在容器中设置 Seccomp、AppArmor、SELinux 安全策略,减少能够调用的系统资源,增强容器安全边界。
Kubernetes 运行安全
Kubernetes 运行安全包括两个主要方面,一是 Kubernetes 核心控制组件运行安全,二是通过配置、策略保障运行在 Kubernetes 集群中的应用、服务、数据的安全。下面我们分别描述其安全实施方案。
Kubernetes 核心组件安全
Kubernetes 集群核心组件包括 etcd 集群、API Server、Controller Manager、Scheduler、Kubelet、Kube-proxy、Calico 网络插件,这些组件共同构成了 Kubernetes 控制平面。其安全主要是通过 TLS 双向验证和通信加密来实现。
etcd 集群
- 配置参数 cert-file、key-file、trusted-ca-file,并将 client-cert-auth 设置为 true ,对所有来自客户端的 https 请求进行 CA 验证。
- 配置参数 peer-cert-file、 peer-key-file、 peer-trusted-ca-file,并将 peer-client-cert-auth 设置为 true,对所有来自对端的 https 请求进行 CA 验证。
* API Server* - 设置参数 tls-cert-file、-tls-private-key-file、client-ca-file,对所有来自客户端的 https 请求进行 CA 验证。
- 设置参数 etcd-certfile、etcd-keyfile、etcd-cafile,并设置 etcd-servers 的访问方式为 https ,用 TLS 加密协议访问 etcd 集群。
- 设置参数 kubelet-client-certificate、 kubelet-client-key、kubelet-certificate-authority,用 TLS 加密协议访问 Kubelet。
Controller Manager
- 设置 kubeconfig 参数,在 kubeconfig 配置文件中使用 TLS 加密协议访问 API Server,并设置 CA,cert,key 等密钥。
Scheduler
- 设置 kubeconfig 参数,在 kubeconfig 配置文件中使用 TLS 加密协议访问 API Server ,并设置 CA,cert,key 等密钥。
* Kubelet* - 设置 kubeconfig 参数,在 kubeconfig 配置文件中使用 TLS 加密协议访问 API Server ,并设置 CA,cert,key 等密钥。
- 设置参数 anonymous-auth 为 false,禁止非授权访问。同时设置 client-ca-file 参数,对由该 CA 签发的客户端请求进行身份认证。
- 设置参数 read-only-port 为 0 ,关闭不安全的只读端口。
- 设置参数 tls-cert-file、tls-private-key-file,提供 Kubelet 服务端密钥。
如果不设置这两个参数, Kubelet 会自动生成自签名证书和密钥,这时 Prometheus 无法直接访问其安全端口 10250 获取 Kubelet 服务指标和 cAdvisor 指标。
Kube-proxy
- 设置 kubeconfig 参数,在 kubeconfig 配置文件中使用 TLS 加密协议访问 API Server,并设置 CA,cert,key 等密钥。
Calico 网络插件
- 设置参数 etcd ca cert_ file、etcd certfile、etcdkeyfile,并设置 etcd _endpoint 参数使用 TLS 加密协议访问 etcd 集群。
Kubernetes 应用环境安全
* API 认证*
Kubernetes 集群有两类用户:服务帐号、普通用户。
普通用户认证建议采用以下几种方式:
- X509 客户端证书,通过 CA 进行双向授权,具有很好的安全性。
- OpenID Connect Tokens,支持通过第三方平台,比如 LDAP 来进行用户身份认证;
- Webhook 令牌认证,可以让用户使用自己订制的认证方式,用户只需要按照约定的请求格式和应答格式提供 HTTPS 服务。当用户把承载令牌放到请求的头部, Kubernetes 会把令牌发送给事先配置的地址进行认证,如果认证结果成功,则认为请求用户合法。
服务帐号应用程序身份认证通过服务帐号来实现。对服务帐号的使用建议如下:
- 为每个需要访问 API Server 的应用设置独立的服务帐号
- 不需要访问 API Server 的应用可以在 Pod 里设置参数 automountServiceAccountToken 为 false 来避免挂载服务帐号。
* API 授权*
在 Kubernetes 中,用户通过认证之后,还需要授权,来确定在集群中能干些什么。API 授权通过配置 API Server 参数 authorization-mode 来指定要运行的授权模块插件。
建议的 authorization-mode 配置是:Node,RBAC 。
其中,Node 模块用于对 Kubelet 授权,设定 Kubelet 允许对 API 进行的操作。结合准入控制插件 NodeRestriction,可以限制 Kubelet 只能修改本节点的 Node API 对象,和运行在本节点的 Pod API 对象。前提是 Kubelet 的身份认证为属于“ system: nodes ”组,并且具有“ system :node:”格式的用户名。
RBAC 模块对所有的用户基予角色授权。每个用户和一个或多个角色绑定,角色具有对 API 对象的各种操作权限,从而灵活地赋予每个用户不同的权限。
准入控制
在用户认证、授权之后,还需要经过准入控制插件处理通过,API Server 才会处理用户的请求。
准入控制插件可以对用户请求的资源对象进行校验、修改,提高集群安全性。通过设置 API Server 参数 enable-admission-plugins(Kubernetes v 1.10 之前使用参数 admission-controller)来开启准入控制插件,系统默认和我们推荐开启的准入控制插件列表为( Kubernetes v1.13 为例):
NamespaceLifecycle,LimitRanger,ServiceAccount,PersistentVolumeClaimResize,DefaultStorageClass,DefaultTolerationSeconds,MutatingAdmissionWebhook,ValidatingAdmissionWebhook,ResourceQuota,Priority ,NodeRes triction
这些准入控制插件的具体功能请参考 Kubernetes 官方文档[5]。* 限制集群资源使用*
结合准入控制插件 ResourceQuota 和 LimitRanger,我们可以限制一个命名空间允许使用的资源,避免恶意攻击或者用户的失误大量使用各种资源导致集群的崩溃。
这些资源包括内存、CPU、存储、Pods、Services、卷和 PVC 等。
例如,为命名空间 default 创建一个 ResourceQuota:
- apiVersion: v1
- kind: ResourceQuota
- metadata:
- name: compute-resources
- spec:
- hard:
- pods: "500"
- requests.cpu: "100"
- requests.memory: 500Gi
- limits.cpu: "200"
- limits.memory: 2000Gi
- glusterfs.storageclass.storage.k8s.io/requests.storage: 5000Gi
以上 Resource Quota 创建后,要在这个命名空间运行的所有 Pod 必须设置内存和 CPU 资源需求。建议设置 LimitRange 为没有配置计算资源的 Pod 设置缺省值。
例 如:
- apiVersion: v1
- kind: LimitRange
- metadata:
- name: custom-limit-range
- spec:
- limits:
- memory: 512Mi
- cpu: "1"
- defaultRequest:
- memory: 128Mi
- cpu: "0. 1 "
- type: Container
限制网络访问
使用网络策略来限制 Pod 之间的网络访问,特别是命名空间之间的网络隔离,对 Kubernetes 生产环境的安全尤为重要。
控制容器安全级别
使用 Pod Security Policy(PSP)来控制容器运行的安全级别。
PSP 能够限制容器以非 root 身份,或是以非特权模式运行,还能屏蔽运行时的系统特权,如访问主机网络,主机进程空间等,特别是限制对主机文件系统目录的挂载,极大地增强了 Kubernetes 容器平台的安全性。
同时, PSP 还能够和节点操作系统的安全特性 Seccomp、AppArmor、SELinux 相结合,进一步加强容器的隔离性。* 控制 Pod 运行的节点分布和优先级*
Kubernetes 提供了丰富的策略来控制 Pod 在集群节点上的分布,如节点亲和特性、Pod 间亲和和反亲和性、Taints 和 Tolerations 等。我们可以灵活运用这些能力来实现以下安全策略:
- 节点分组,把对隔离性要求很高的应用运行在单独的节点组里,实现物理上的隔离。
- 把关键服务的多个副本运行在不同的物理节点,甚至是不同的故障域内,增强对安全风险的抵抗力。
- 系统服务以高优先级运行,优先得到调度,除了保证 Kubernetes 生产环境的稳定性,也提高了对 DDOS 攻击的承压能力。
* 输出审计日志*
Kuberentes 审计功能提供了一组与安全相关的记录,按照时间顺序,记录了各个用户、系统管理员或各系统组件对系统有影响的活动。这些审计日志建议保存到独立的安全服务器,以便在安全事件发生的时候可以进行事后分析和追溯。* 密码保护*
密码在各种应用中使用非常广泛,尤其在 Kubernetes 环境中,密码必须通过非常安全的方式传递到 Pod 里面,并且不能记录在 yaml 文件中,因为 yaml 文件可能会被上传到 Git 代码仓库里。
我们对密码保护有以下建议: - 对需要密码的应用,如数据库,最好采用 Helm 来部署,可以通过命令行参数把密码传递进去,并且尽量采用随机生成密码。
- 密码统一存放在 Secret 资源里, Pod 通过环境变量从 Secret 读入密码。
- 为 Secret 资源设定更高的访问权限,不开放给普通用户。
- 配置 Encryption at rest 功能,对存放在 etcd 中的 Secret 加密。
- 使用密码管理软件 Hashicorp Vault[6] 来集中管理密码。
* 节点安全*
关于集群节点的安全建议如下: - 通过防火墙或 Iptables 限制对集群节点的访问。
- 不使用口令,而是采用 SSH 密钥登录节点。
相关链接:
- https://www.cisecurity.org/cis-benchmarks/
- https://github.com/docker/docker-bench-security
- https://docs.docker.com/engine/security/trust/content_trust/
- https://github.com/theupdateframework/notary
- https://kubernetes.io/docs/reference/access-authn-authz/admission-controllers/
- https://www.vaultproject.io/
作者:鲁肃,来自瑞幸咖啡基础架构团队。
添加新评论0 条评论