Kubernetes——X.509数字证书认证
X.509数字证书认证
Kubernetes 支持的 HTTPS 客户端证书认证、token 认证及 HTTP basic 认证几种认证方式中,基于 SSL/TLS 协议的客户端证书认证以其安全性高且易于实现等特性,而成为主要使用的认证方式之一。
SSL/TLS 最常见的使用场景是将 X.509 证书与服务器端相关联,但不为客户端使用证书(这意味着,服务端无法验证客户端的身份,起码不能通过 SSL/TLS 协议进行)。
那如果从安全要求更高的场看,需要验证客户端时,使用其他几种(如 HTTP 基本认证)通常会更容易些,而且这些机制不会产生生成和分发 X.509 证书的高昂维护开销。不过也可以使用组织私有的证书分发系统也一样能够使用数字证书进行客户端认证。(比如 服务端和客户端的双向认证机制。)
服务端与客户端互相认证的场景中,双方需要各自配备一套证书,并拥有信任的签证机构的证书列表。使用私有签证机构颁发的数字证书时,用户通常需要手动将此私有签证机构的证书添加到信任的签证机构列表中。
一、Kubernetes 中的 SSL/TLS 认证
构建安全基础通信环境的 Kubernetes 集群时,需要用到 TLS 及数字证书通信场景有很多。
API Server 是整个 Kubernetes 集群的通信网关。Controller-Manager、Scheduler、kubelet 及 kube-proxy 等均需要经由 API Server 与 etcd 通信完成资源状态信息的获取及更新等。同样处于安全通信的目的,master 的各个组件(API Server、Controller-Manager 和 Scheduler)需要基于 SSL/TLS 向外提供服务,而且与集群内部组件间进行通信时(主要是各节点上的 kubelet 和 kube-proxy)还需要进行双向身份验证。
Kubernetes 集群中各资源的状态信息,包括 Secret 对象中的敏感信息等均以明文方式存储于 etcd 中。因此,etcd 集群内各节点之间的通信,以及各节点与其客户端(主要是 API Server)之间的通信都应该以加密的方式进行,并需要进行身份验证。
-
- etcd 集群内对等节点通信:etcd集群内各节点间的集群事务通信,默认监听于 TCP 的 2380 端口,基于 SSL/TLS 通信时需要 peer 类型的数字证书,可实现节点间的身份认证及通信安全,这些证书需要由一个专用 CA 进行管理。
- etcd 服务器与客户端通信:etcd 的 REST API 服务,默认监听于 TCP 的 2379 端口,用于接收并响应客户端请求,基于 SSL/TLS 通信,支持服务端认证和双向认证,而且要使用一个专用 CA 来管理此类证书。kube-apiserver 就是 etcd 服务的主要客户端。
API Server 与其客户端之间采用 HTTPS 通信可兼顾实现通信与认证的功能,它们之间通信的证书可由同一个 CA 进行管理,其客户端大体可分为如下三类:
-
- 控制平面的 kube-scheduler 和 kube-controller-manager。
- 工作节点组件 kubelet 和 kube-proxy。初次接入集群时,kubelet 可自动生成私钥和证书签署亲请求,并由 Maser 为其自动进行证书签名和颁发,这就是所谓的 tls bootstraping。
- kubelet 及其他形式的客户端,如 Pod 对象等。
二、客户端配置文件 kubeconfig
包括 kubectl、kubelet 和 kube-controller-manager 等在内的 API Server 的各类客户端都可以使用 kubeconfig 配置文件提供接入多个集群的相关配置信息,包括各 API Server 的 URL 及认证信息等,而且能够设置成不同的上下文环境,并在各环境之间快速切换。
使用 kubeadm 初始化集群后生成的 /etc/kubernetes/admin.conf 文件即为 kubeconfig 格式的配置文件,其由 kubeadm init 命令自动生成,可由 kubectl 加载(默认路劲为:$HOME/.kube/config)后用于接入服务器。
"kubectl config view" 命令能够显示当前正在使用的配置文件,下面的命令结果显示了配置的集群列表,用户列表,上下文列表以及当前使用的上下文(current-context)等。
[root@k8s-master01-test-2-26 ~]# kubectl config view
apiVersion: v1
clusters:
- cluster:
certificate-authority-data: DATA+OMITTED
server: https://lb.zuoyang.tech:6443
name: cluster.local
contexts:
- context:
cluster: cluster.local
user: kubernetes-admin
name: kubernetes-admin@cluster.local
current-context: kubernetes-admin@cluster.local
kind: Config
preferences: {}
users:
- name: kubernetes-admin
user:
client-certificate-data: REDACTED
client-key-data: REDACTED
[root@k8s-master01-test-2-26 ~]#
事实上,任何类型的 API Server 客户端都可以使用 kubeconfig 进行配置,例如 Kubernetes Node 之上的 kubelet 和 kube-proxy 也需要将其用到的认证信息保存于专用的 kubecofnig 文件中,并通过 --kubecofig 选项进行加载。
上面的kubeconfig 文件的定义包含以下几个主要配置:
-
- cluster: 集群列表。包含访问 API Server 的 URL 和所属集群的名称等。
- users: 用户列表,包含访问 API Server 时的用户名和认证信息。
- contexts: kubelet 的可用上下文列表,由用户列表中的某特定用户名称和集群列表中的某特定集群名称组合而成。
- current-context: kubelet 当前使用的上下文名称,即上下文列表中的某个特定项。
用户可以按需自定义相关的配置信息于 kubeconfig 配置文件中,以实现使用不同的用户账户接入集群的功能。
kubeconfig 是一个文本文件,虽然支持用文本处理工具直接编辑,但更建议使用 "kubectl config" 命令进行设定,除了能够自动进行语法检查外,也能避免不必要的错误。
kubeconfig 命令的常用操作包含如下几项:
-
- kubectl config view:打印 kubeconfig 文件内容。
- kubectl config set-cluster:设置 kubeconfig 的 clusters 配置段。
- kubectl config set-credentials: 设置 kubeconfig 的 users 配置段。
- kubectl config set-context: 设置 kubeconfig 的 contexts 配置段。
- kubectl config use-context: 设置 kubeconfig 的 current-context 配置段。
使用 kubeadm 部署的 Kubernetes 集群默认提供了拥有集群管理权限的 kubeconfig 配置文件 /etc/kubernetese/admin.conf,它可被复制到任何有着 kubelet 的主机上以用于管理整个集群。
管理员还可以创建其他基于 SSL/TLS 认证的自定义用户帐号,以授权费管理员级集群资源使用权限,其配置过程分为两个部分:
1)为用户创建专用私钥及证书文件。
2)将其配置于某 kubeconfig 文件中。
第一步,为目标用户帐号 kube-user1 创建私钥及证书文件,保存于/etc/kubernetes/pki 目录中:
- 生成私钥文件,注意其权限应该为 600 以阻止其他用户随意获取,这里在 Master 节点上以 root 用户进行操作,并将文件放置于 /etc/kubernetes/pki 专用目录中:
[root@k8s-master01-test-2-26 ~]# cd /etc/kubernetes/pki/ [root@k8s-master01-test-2-26 pki]# (umask 077; openssl genrsa -out kube-user1.key 2048) Generating RSA private key, 2048 bit long modulus .....................................................................................................................................................................................................+++ .............................................................................+++ e is 65537 (0x10001) [root@k8s-master01-test-2-26 pki]#
- 创建证书签署请求,-subj 选项中 CN 的值将被 kubeconfig 作为用户名使用,O(这个是大写字母O)的值将被视为用户组:
[root@k8s-master01-test-2-26 pki]# openssl req -new -key kube-user1.key -out kube-user1.csr -subj "/CN=kube-user1/O=kubernetes"
- 基于 kubeadm 安装 Kubernetes 集群时生成的 CA 签署证书,这里设置其有效时长为 3650 天:
[root@k8s-master01-test-2-26 pki]# openssl x509 -req -in kube-user1.csr -CA ca.crt -CAkey ca.key -CAcreateserial -out kube-user1.crt -days 3650 Signature ok subject=/CN=kube-user1/O=kubernetes Getting CA Private Key [root@k8s-master01-test-2-26 pki]#
- 验证证书信息:
[root@k8s-master01-test-2-26 pki]# openssl x509 -in kube-user1.crt -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: c4:7c:ef:f3:90:60:70:e4 Signature Algorithm: sha256WithRSAEncryption Issuer: CN=kubernetes Validity Not Before: Jul 2 08:14:12 2022 GMT Not After : Jun 29 08:14:12 2032 GMT Subject: CN=kube-user1, O=kubernetes Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ac:01:43:70:a0:79:58:2d:3f:5c:36:6c:09:c5: 3e:66:6a:c1:8a:b4:70:02:ad:09:85:32:d4:8a:eb: b5:6d:39:ad:03:b7:2a:17:25:59:b4:04:07:45:9c: 80:7e:ea:83:0c:31:00:4c:a5:86:6b:04:1c:36:22: 8f:1d:f5:9d:86:7b:99:1b:79:d5:64:2b:30:a4:c6: 0b:11:0e:b3:b9:f5:74:70:16:57:62:b2:e8:0c:5d: 3d:f3:4c:9b:c4:48:e3:c8:ab:2f:fe:2f:5f:7c:31: 65:d8:cd:30:d9:bf:aa:78:0b:3e:c3:9e:a6:ed:41: 33:7e:50:ac:85:fa:0c:e2:ea:75:44:ad:33:c5:21: 74:f4:30:d1:00:b2:8d:41:75:0d:48:28:ec:7a:13: cb:35:09:52:3d:ef:0d:82:88:4f:42:e1:0a:6f:d6: 85:a0:15:c7:14:88:d8:7a:72:64:87:47:03:de:52: 9f:28:8a:73:c9:50:b5:8d:35:3b:cf:3a:8d:15:06: 8c:17:44:51:0f:34:24:de:af:ef:96:66:7f:21:58: 73:6b:ef:17:da:ee:ec:a3:be:62:3e:5f:9e:70:fe: 30:36:52:39:75:f3:aa:99:69:9a:b0:fc:bf:fc:a6: 29:23:5e:9b:13:01:de:cc:d8:75:5c:bc:3f:7b:b3: 5a:21 Exponent: 65537 (0x10001) Signature Algorithm: sha256WithRSAEncryption 3f:65:ea:cc:c7:4e:d6:fe:43:78:4d:ee:8c:7f:9c:ed:b0:f0: 70:97:19:72:2d:86:dd:3b:10:b9:e5:e1:82:b8:5b:0a:dc:2e: 49:31:2c:b0:6c:40:f1:34:76:4b:97:12:d1:17:33:a2:79:e4: 83:6e:14:74:73:b8:b3:b5:03:4a:26:47:09:e9:20:dd:2c:32: 52:3b:59:ad:bf:10:09:2b:dc:1c:9a:1c:1e:a5:b0:af:d6:14: d7:ad:85:65:8f:02:0d:a0:ea:c2:d9:7c:51:77:c8:79:c1:5c: ed:f6:c1:0e:c2:70:72:32:79:d6:16:db:8e:0d:c6:00:ea:07: 4d:d5:86:ca:e7:5e:93:16:0e:d8:f8:50:3c:5e:cf:58:a8:3b: ba:ef:4e:c7:f5:94:cf:a1:cf:dd:a2:d6:c7:c5:fa:50:ca:55: 83:fd:32:03:fb:43:08:f6:fc:c5:56:b0:9e:47:8d:74:a9:fa: 17:55:1f:ad:55:ec:6e:62:fb:9e:fd:b9:23:a0:85:ec:7a:7d: 61:2b:68:7a:50:23:9b:2a:e7:0e:c8:1c:b7:3c:b4:1b:e9:4b: 73:af:54:b8:c4:18:87:ff:6e:03:7d:6a:19:53:1b:d6:c8:22: d2:17:7d:e5:22:e7:45:c1:bf:79:b1:90:b0:a4:73:c0:a5:25: 1d:06:52:72 [root@k8s-master01-test-2-26 pki]#
第二步,以默认的管理员 kubernetes-admin@cluster.local 为新建的 kube-user1 设定 kube-config 配置文件。配置结果将默认保存于当前系统用户的 .kube/config 文件中,当然也可以为 kubectl 使用 --kubecofig 选项指定自定义的专用文件路径。
- 配置新集群信息(集群名称是 cluster.local 是已经存在了,我新建集群名称为 kubernets 集群),包括集群名称、API Server URL 和 CA 证书,若默认集群已然存在,则可省略此步骤,另外,(注意)提供的新配置不能与现有配置中的集群名称相同,否则将覆盖它们!
[root@k8s-master01-test-2-26 pki]# kubectl config set-cluster kubernetes --embed-certs=true --certificate-authority=/etc/kubernetes/pki/ca.crt --server="https://172.16.0.26:6443" Cluster "kubernetes" set. [root@k8s-master01-test-2-26 pki]#
- 配置客户端证书及密钥,用户信息会通过命令从证书 Subject 的 CN 值中自动提取,例如前面创建 csr 时使用的 "CN=kube-user1",而组名则来自于 "O=kubernetes" 的定义。
[root@k8s-master01-test-2-26 pki]# kubectl config set-credentials kube-user1 --embed-certs=true --client-certificate=/etc/kubernetes/pki/kube-user1.crt --client-key=/etc/kubernetes/pki/kube-user1.key User "kube-user1" set. [root@k8s-master01-test-2-26 pki]#
- 配置 context,用来组合 cluster 和 credentials,即访问的集群的上下文。如果为管理多个集群而设置了多个环境,则可以使用 use-context 来进行切换:
[root@k8s-master01-test-2-26 pki]# kubectl config set-context kube-user1@kubernetes --cluster=kubernetes --user=kube-user1 Context "kube-user1@kubernetes" created. [root@k8s-master01-test-2-26 pki]#
- 最后指定要使用的上下文,切换为以 kube-user1 访问集群:
[root@k8s-master01-test-2-26 pki]# kubectl config use-context kube-user1@kubernetes Switched to context "kube-user1@kubernetes". [root@k8s-master01-test-2-26 pki]#
- 测试访问集群资源,不过在启用 RBAC 的集群上执行命令时,kube-user1 并未获得集群资源的访问权限,因此会出现错误提示:
[root@k8s-master01-test-2-26 pki]# kubectl --context=kube-user1@kubernetes get pods Error from server (Forbidden): pods is forbidden: User "kube-user1" cannot list resource "pods" in API group "" in the namespace "default" [root@k8s-master01-test-2-26 pki]#
此时,
若需要切换至管理员帐号,则可使用 "kubeclt config use-context kubernetes-admin@kubernetes" 命令来完成(1.24.1版本:kubectl config use-context kubernetes-admin@cluster.local)。
若需要临时使用某 context 时,不必设置 current-context,只需要为 kubectl 命令使用 --context 选项指定目标 context 的名称即可,如"kubectl --context=kube-user1@kubernetes get pods"。
三、TLS bootstrapping 机制
新的工作节点接入集群时需要事先配置好相关的证书和私钥等以进行安全通信,管理员既可以手动提供相关的配置,也可以选择由 kubelet 自行生成私钥和自签证书。每次添加节点,都会给管理员带来不小的负担,Kubernetes 采用了一种新的方案:由 kubernetes 自行生成私钥和证书签署请求,而后发送给集群上的证书签署进程(CA),由管理员验证请求后予以签署或直接控制器进程自动统一签署。这种方式就称为 Kubernetes TLS Bootstrapping 机制。
然而,启用了 kubelet bootstrap 功能后,任何 kubelet 进程都可以向 API Server 发起签证请求并加入到集群中,包括那些来自非计划或非授权主机的 kubelet,这必将增大安全隐患。此时,就需要配合使用某种认证机制对发出请求的 kubelet 加以认证,而后仅放行那些通过认证的签证请求。
API Server 使用了一个基于认证令牌对 "system:bootstrappers" 组内的用户完成认证的认证器。
controller-manager 会用到这个用户组配置默认的审批控制器(approval controller)适用的审批范围,它依赖于由管理员将此 token 正确绑定于 RABC 策略,以限制其仅能用于签证操作相关的流程之中。
请求证书时,kubelet 要使用包含有 bootstrap token 的 kubeconfig 文件向 kube-apiserver 发送请求,此 kubeconfig 在 credentials 中指定要调用的 token 文件名称必须是 kubelet-bootstrap。若指定的 kubeconfig 配置文件不存在,则 kubelet 会转而使用 bootstrap token 从 API Server 中自动请求证书。证书请求审批通过后,kubelet 把接收到的证书和私钥的相关信息存储于 --kubeconfig 选项指定的文件中,而证书和私钥文件则存储于 --cert-dir 选项指定的目录下。
kube-controller-manager 内部有一个用于证书颁发的控制循环,它是采用了类似 cfssl 签证器格式的自动签证器,颁发的所有证书都仅有一年有效期。签证依赖于 CA 提供加密组件支撑,此 CA 还必须被 kube-apiserver 的 "--client-ca-file" 选项指定的 CA 信任以用于认证。
kubernetes 1.8 之后的版本中使用的 csrapproving 审批控制器内置于 kube-controller-manager 中,并且默认为启用状态。此审批控制器使用 SubjectAccessview API 确认给定的用户是否有权限请求 CSR(证书签署请求),而后再根据授权结果判定是否予以签署。不过为了避免其他审批器发生冲突,内建的审批器并不显示拒绝 CSR,而只是忽略它们。