k3s 2023证书轮转方案
不知不觉已经使用k3s几年了,虽然从使用上来说跟k8s并无区别,而且更加轻量,安装非常方便,但是也存在一些痛点。
今天就讨论下其中的证书轮转问题。
本文使用的 k3s 版本 为 v1.25.4+k3s1 ,相关操作应该在后续版本也适用,最好使用前先测试一下。
在这个版本, k3s 早已经实现了自动轮转,但是需要满足"证书过期或者距离过期时间小于等于90天(证书中 Validity 的 Not After 字段)"的条件并且重启时才会轮转。为什么官方不在安装脚本里面提供一个可以设置定时重启的选项,例如可以使用 systemd timer 或者 crontab。
除了定时任务重启,我们还可以通过延长证书有效期的方式,当然这会有一些安全风险。
k3s 使用的 CA 证书只有10年有效期,如果你对于该时间不满意,例如想可以在安装 k3s 前创建所需的 CA 证书,具体位置由用户设置决定,下面使用默认位置:
```bash
mkdir -p /var/lib/rancher/k3s/server/tls
cd /var/lib/rancher/k3s/server/tls
openssl genrsa -out client-ca.key 2048
openssl genrsa -out server-ca.key 2048
openssl genrsa -out request-header-ca.key 2048
openssl req -x509 -new -nodes -key client-ca.key -sha256 -days 3650 -out client-ca.crt -addext keyUsage=critical,digitalSignature,keyEncipherment,keyCertSign -subj '/CN=k3s-client-ca'
openssl req -x509 -new -nodes -key server-ca.key -sha256 -days 3650 -out server-ca.crt -addext keyUsage=critical,digitalSignature,keyEncipherment,keyCertSign -subj '/CN=k3s-server-ca'
openssl req -x509 -new -nodes -key request-header-ca.key -sha256 -days 3650 -out request-header-ca.crt -addext keyUsage=critical,digitalSignature,keyEncipherment,keyCertSign -subj '/CN=k3s-request-header-ca'
```
接着安装 k3s。安装完成后会在 /var/lib/rancher/k3s/server/tls/ 中出现很多文件,其中 client-admin.crt 就是我们 kubectl 使用的客户端证书。
我们使用openssl查看其内容:
➜ tls openssl x509 -text -in client-admin.crt Certificate: Data: Version: 3 (0x2) Serial Number: 3805033951105087658 (0x34ce3237f5eb78aa) Signature Algorithm: ecdsa-with-SHA256 Issuer: CN = k3s-client-ca@1642582069 Validity Not Before: Jan 19 08:47:49 2022 GMT Not After : Nov 2 08:56:54 2023 GMT Subject: O = system:masters, CN = system:admin Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:0b:85:a1:57:ac:9c:04:e3:c3:21:63:84:7a:85: 35:ac:65:d5:c0:7d:ca:24:cf:06:81:e3:31:ff:e3: 91:30:ef:17:aa:ad:cf:82:a9:f0:da:9b:40:c7:33: 32:39:ac:f4:5c:88:16:a9:a2:7f:91:22:ab:31:6e: a4:9f:57:9c:9c ASN1 OID: prime256v1 NIST CURVE: P-256 X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Authority Key Identifier: keyid:2D:59:5F:73:0E:EB:4A:AC:3C:63:A6:2E:F5:83:C2:F9:91:F7:EA:34 Signature Algorithm: ecdsa-with-SHA256 30:45:02:20:0e:56:1b:12:23:f5:b0:e6:59:85:b5:ad:d8:5b: e2:cc:cc:17:95:77:70:0c:c5:56:4a:d8:c2:99:be:38:d3:bd: 02:21:00:e9:02:4e:d7:c4:43:4e:46:cc:de:93:0c:42:de:e6: e2:cf:1e:94:8b:c8:9c:d7:de:21:bf:da:58:e3:e6:46:ea -----BEGIN CERTIFICATE----- MIIB..................jj5kbq -----END CERTIFICATE-----
然后查看 kubectl 使用的 kubeconfig 文件中的 client-certificate-data 对应的证书:
➜ tls k config view --flatten --minify apiVersion: v1 clusters: ... users: - name: default user: client-certificate-data: LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SU......zJCRnRrL1R3b0dXWnJMdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K ➜ tls echo LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUJrVENDQVRlZ0F3SU......zJCRnRrL1R3b0dXWnJMdz09Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K|base64 -d|openssl x509 -text
Certificate:
Data: Version: 3 (0x2) Serial Number: 3805033951105087658 (0x34ce3237f5eb78aa) Signature Algorithm: ecdsa-with-SHA256 Issuer: CN = k3s-client-ca@1642582069 Validity Not Before: Jan 19 08:47:49 2022 GMT Not After : Nov 2 08:56:54 2023 GMT Subject: O = system:masters, CN = system:admin Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:0b:85:a1:57:ac:9c:04:e3:c3:21:63:84:7a:85: 35:ac:65:d5:c0:7d:ca:24:cf:06:81:e3:31:ff:e3: 91:30:ef:17:aa:ad:cf:82:a9:f0:da:9b:40:c7:33: 32:39:ac:f4:5c:88:16:a9:a2:7f:91:22:ab:31:6e: a4:9f:57:9c:9c ASN1 OID: prime256v1 NIST CURVE: P-256 X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Client Authentication X509v3 Authority Key Identifier: keyid:2D:59:5F:73:0E:EB:4A:AC:3C:63:A6:2E:F5:83:C2:F9:91:F7:EA:34 Signature Algorithm: ecdsa-with-SHA256 30:45:02:20:0e:56:1b:12:23:f5:b0:e6:59:85:b5:ad:d8:5b: e2:cc:cc:17:95:77:70:0c:c5:56:4a:d8:c2:99:be:38:d3:bd: 02:21:00:e9:02:4e:d7:c4:43:4e:46:cc:de:93:0c:42:de:e6: e2:cf:1e:94:8b:c8:9c:d7:de:21:bf:da:58:e3:e6:46:ea -----BEGIN CERTIFICATE----- MIIB..................jj5kbq -----END CERTIFICATE-----
两者完全一致。
这里 client-certificate-data 证书要与 api-server 使用的证书区分开。
有了证书之后我们就可以从证书中提取出 CSR(Certificate Signing Request):
➜ tls openssl x509 -x509toreq -in client-admin.crt -signkey client-admin.key -out client-admin.csr Getting request Private Key Generating certificate request ➜ tls openssl req -in client-admin.csr -text Certificate Request: Data: Version: 1 (0x0) Subject: O = system:masters, CN = system:admin Subject Public Key Info: Public Key Algorithm: id-ecPublicKey ...
然后使用 CSR 文件 获取新的客户端证书 new-client-admin.crt :
➜ tls openssl x509 -req -days 3650 -in client-admin.csr -signkey client-admin.key -CA client-ca.crt -CAkey -CAcreateserial client-ca.key -out new-client-admin.crt ➜ tls openssl x509 -in new-client-admin.crt -text -noout Certificate: Data: Version: 1 (0x0) Serial Number: 42:80:25:5f:6e:39:54:bf:30:e6:0e:ff:ff:45:71:56:b9:53:a3:3c Signature Algorithm: ecdsa-with-SHA256 Issuer: CN = k3s-client-ca@1642582069 Validity Not Before: Feb 12 11:50:59 2023 GMT Not After : Feb 9 11:50:59 2033 GMT Subject: O = system:masters, CN = system:admin Subject Public Key Info: Public Key Algorithm: id-ecPublicKey Public-Key: (256 bit) pub: 04:0b:85:a1:57:ac:9c:04:e3:c3:21:63:84:7a:85: 35:ac:65:d5:c0:7d:ca:24:cf:06:81:e3:31:ff:e3: 91:30:ef:17:aa:ad:cf:82:a9:f0:da:9b:40:c7:33: 32:39:ac:f4:5c:88:16:a9:a2:7f:91:22:ab:31:6e: a4:9f:57:9c:9c ASN1 OID: prime256v1 NIST CURVE: P-256 Signature Algorithm: ecdsa-with-SHA256 30:46:02:21:00:fc:05:b7:50:aa:ab:37:eb:f9:7a:a7:11:da: ce:c8:88:8a:ea:96:a6:7c:5e:d5:38:43:f7:48:15:ec:fd:86: 98:02:21:00:cb:9a:b1:43:bd:49:76:fb:28:79:e1:74:2e:85: 43:b2:1b:98:ea:89:0d:f9:d8:4f:0c:db:ed:6d:45:64:df:9a
然后我们将获得证书内容的base64 编码字符串:
➜ tls base64 new-client-admin.crt LS0tLS1CRUdJTiB......RS0tLS0tCg==
最后将上诉base64编码字符串替换上面 kubeconfig 的 client-certificate-data 字段即可。
除了上述方法,其实还有一个更简洁的方式,通过设置环境变量 CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS来指定证书的过期天数,这部分代码详见 https://github.com/rancher/dynamiclistener/blob/master/cert/cert.go#L118。如果是用官方安装脚本安装运行需要注意,直接设置的环境变量并不能被systemd(这里只说systemd,openrc应该类似)使用,我们需要在/etcdefault/k3s或者/etc/sysconfig/k3s中添加该环境变量:
echo CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=3650 > /etc/default/k3s
//或者
echo CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=3650 > /etc/sysconfig/k3s
如果还未安装k3s,设置上面的环境变量后即可使用官方脚本安装。如果已经安装了k3s,则可以删除目录/var/lib/rancher/k3s/server/tls,然后systemctl restart k3s即可。