zaki work log

作業ログやら生活ログやらなんやら

[Kubernetes] K3sにおける証明書の期限の延長や設定(平たく言うと塩漬け運用したい場合のx年設定)について

オンプレKubernetesにおいて選択肢の一つになりうるK3sで内部で使用する証明書の期限についての調査&検証のまとめ的な作業ログ
確認した環境のホストOSはUbuntu 22.04とFedora39でK3sは1.28.5

デフォルト設定

K3sにおいて証明書は構築のタイミングで生成され1年が有効期限になっている。
機能的には期限まで90日になると「K3s起動時に」自動更新されるようになっている。

K3s client and server certificates are valid for 365 days from their date of issuance. Any certificates that are expired, or within 90 days of expiring, are automatically renewed every time K3s starts.

https://docs.k3s.io/cli/certificate

期限が切れてしまうと各操作でx509のエラーが発生する。 (期限が切れてしまったあとの再起動でも期限は更新される)

root@ubuntu-dev01:~# kubectl get node
E1027 00:00:03.561891   36698 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z
E1027 00:00:03.562512   36698 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z
E1027 00:00:03.564188   36698 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z
E1027 00:00:03.564776   36698 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z
E1027 00:00:03.566360   36698 memcache.go:265] couldn't get current server API group list: Get "https://127.0.0.1:6443/api?timeout=32s": tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z
Unable to connect to the server: tls: failed to verify certificate: x509: certificate has expired or is not yet valid: current time 2025-10-27T00:00:03Z is after 2025-10-26T00:00:05Z

自動ローテートの確認

初期状態

root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep Validity -A2
        Validity
            Not Before: Jan 24 00:40:08 2024 GMT
            Not After : Jan 23 00:40:08 2025 GMT

期限切れ90日前に設定して再起動しても、以下の通り変化なし。

root@ubuntu-dev01:~# timedatectl set-time "2024-10-25"
root@ubuntu-dev01:~# date
Thu Oct 25 00:00:15 UTC 2024
root@ubuntu-dev01:~# systemctl restart k3s
root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep Validity -A2
        Validity
            Not Before: Jan 24 00:40:08 2024 GMT
            Not After : Jan 23 00:40:08 2025 GMT

期限切れ89日前に設定に設定して再起動すると、以下の通り更新される。

root@ubuntu-dev01:~# timedatectl set-time "2024-10-26"
root@ubuntu-dev01:~# date
Sat Oct 26 00:00:56 UTC 2024
root@ubuntu-dev01:~# systemctl restart k3s
root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep Validity -A2
        Validity
            Not Before: Jan 24 00:40:08 2024 GMT
            Not After : Oct 26 00:00:05 2025 GMT

この通り「起動時の1年後」に更新された。

手動ローテート

期限が近くなる前でも、手動での強制ローテートは可能。
その場合でもK3sの再起動は必要。

証明書のローテートを行うには以下のコマンドを実行。

k3s certificate rotate

具体的な手順と動きについて確認してみる。
事前の証明書ファイル群はこの通り。

root@ubuntu-dev01:~# ls -l /var/lib/rancher/k3s/server/tls/*.crt
-rw-r--r-- 1 root root 1173 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-admin.crt
-rw-r--r-- 1 root root 1182 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-auth-proxy.crt
-rw-r--r-- 1 root root  566 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-ca.crt
-rw-r--r-- 1 root root  566 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-ca.nochain.crt
-rw-r--r-- 1 root root 1161 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-controller.crt
-rw-r--r-- 1 root root 1157 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-k3s-cloud-controller.crt
-rw-r--r-- 1 root root 1149 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-k3s-controller.crt
-rw-r--r-- 1 root root 1177 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt
-rw-r--r-- 1 root root 1145 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-kube-proxy.crt
-rw-r--r-- 1 root root 1149 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-scheduler.crt
-rw-r--r-- 1 root root 1181 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-supervisor.crt
-rw-r--r-- 1 root root  591 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/request-header-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/server-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/server-ca.nochain.crt
-rw-r--r-- 1 root root 1400 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt
root@ubuntu-dev01:~#
root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep
 Validity -A2
        Validity
            Not Before: Jan 24 01:12:19 2024 GMT
            Not After : Jan 23 01:12:19 2025 GMT

ローテートのコマンドを実行すると証明書ファイルは消えてしまうので、安全のためにまずK3sを停止する。

root@ubuntu-dev01:~# systemctl stop k3s
root@ubuntu-dev01:~# 

ローテートのコマンドを実行。

root@ubuntu-dev01:~# k3s certificate rotate
INFO[0000] Server detected, rotating server certificates 
INFO[0000] Rotating certificates for admin service      
INFO[0000] Rotating certificates for etcd service       
INFO[0000] Rotating certificates for api-server service 
INFO[0000] Rotating certificates for controller-manager service 
INFO[0000] Rotating certificates for cloud-controller service 
INFO[0000] Rotating certificates for scheduler service  
INFO[0000] Rotating certificates for k3s-server service 
INFO[0000] Rotating dynamic listener certificate        
INFO[0000] Rotating certificates for k3s-controller service 
INFO[0000] Rotating certificates for auth-proxy service 
INFO[0000] Rotating certificates for kubelet service    
INFO[0000] Rotating certificates for kube-proxy service 
INFO[0000] Successfully backed up certificates for all services to path /var/lib/rancher/k3s/server/tls-1706059051, please restart k3s server or agent to rotate certificates 
root@ubuntu-dev01:~# 

すると、CA以外の証明書ファイルは削除される。

root@ubuntu-dev01:~# systemctl start k3s
root@ubuntu-dev01:~# 
root@ubuntu-dev01:~# ls -l /var/lib/rancher/k3s/server/tls/*.crt
-rw-r--r-- 1 root root  566 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-ca.crt
-rw-r--r-- 1 root root  566 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-ca.nochain.crt
-rw-r--r-- 1 root root 1181 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/client-supervisor.crt
-rw-r--r-- 1 root root  591 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/request-header-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/server-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:12 /var/lib/rancher/k3s/server/tls/server-ca.nochain.crt

この状態でK3sを起動すると、証明書ファイルが生成される。

root@ubuntu-dev01:~# systemctl restart k3s
root@ubuntu-dev01:~# 
root@ubuntu-dev01:~# 
root@ubuntu-dev01:~# ls -l /var/lib/rancher/k3s/server/tls/*.crt
-rw-r--r-- 1 root root 1173 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-admin.crt
-rw-r--r-- 1 root root 1178 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-auth-proxy.crt
-rw-r--r-- 1 root root  570 Jan 24 01:14 /var/lib/rancher/k3s/server/tls/client-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-ca.nochain.crt
-rw-r--r-- 1 root root 1165 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-controller.crt
-rw-r--r-- 1 root root 1165 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-k3s-cloud-controller.crt
-rw-r--r-- 1 root root 1153 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-k3s-controller.crt
-rw-r--r-- 1 root root 1181 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt
-rw-r--r-- 1 root root 1144 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-kube-proxy.crt
-rw-r--r-- 1 root root 1153 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/client-scheduler.crt
-rw-r--r-- 1 root root 1185 Jan 24 01:14 /var/lib/rancher/k3s/server/tls/client-supervisor.crt
-rw-r--r-- 1 root root  591 Jan 24 01:14 /var/lib/rancher/k3s/server/tls/request-header-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:14 /var/lib/rancher/k3s/server/tls/server-ca.crt
-rw-r--r-- 1 root root  570 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/server-ca.nochain.crt
-rw-r--r-- 1 root root 1400 Jan 24 01:30 /var/lib/rancher/k3s/server/tls/serving-kube-apiserver.crt

この通り起動のタイミング基準の期限で証明書が作成された。

root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep
 Validity -A2
        Validity
            Not Before: Jan 24 01:12:19 2024 GMT
            Not After : Jan 23 01:18:31 2025 GMT

期限のカスタム

K3s自体の機能には証明書の期限を設定する機能は意外(?)と無い。
ただし、以下のissueで議論はされており、K3sの機能・設定としては提供されてないが、K3s内で使用されているライブラリが環境変数から有効期限を参照する仕組みになっている。

github.com

ただ、K3s自体のインストールで使用する環境変数ではないため、 以下のようにK3sインストール時の環境変数指定を付加した実行では反映されない。

curl -sfL https://get.k3s.io | CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=750 sh -

証明書期限を設定した環境変数を認識させるには、事前に「k3sの実行時に環境変数として認識させる」必要がある。
具体的には以下の内容のファイルを /etc/default/k3s にK3sインストール前に作成しておけば、K3sインストール時に認識して指定期限の証明書を生成する。

root@ubuntu-dev01:~# cat /etc/default/k3s 
CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=3650
root@ubuntu-dev01:~#
root@ubuntu-dev01:~# date
Wed Jan 24 01:47:34 UTC 2024
root@ubuntu-dev01:~#
root@ubuntu-dev01:~# curl -sfL https://get.k3s.io | sh -
[INFO]  Finding release for channel stable
[INFO]  Using v1.28.5+k3s1 as release
:
:
[INFO]  systemd: Starting k3s
root@ubuntu-dev01:~# 
root@ubuntu-dev01:~# openssl x509 -text -noout -in /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | grep
 Validity -A2
        Validity
            Not Before: Jan 24 01:48:05 2024 GMT
            Not After : Jan 21 01:48:05 2034 GMT

この通り、10年後の期限に設定された。

コンテナ版の場合

ローテート

90日の件は非コンテナ版と同じなので割愛。
ここでは強制ローテートについて。

VM版と異なり「K3s停止時に実行」ができないため、K3s起動中(コンテナ起動中)にローテート処理を行い、そのあとに再起動を行う。

[zaki@cloud-dev2 k3s-compose]$ docker compose ps
NAME                   IMAGE                COMMAND             SERVICE   CREATED         STATUS         PORTS
k3s-compose-agent-1    rancher/k3s:latest   "/bin/k3s agent"    agent     2 minutes ago   Up 2 minutes   
k3s-compose-server-1   rancher/k3s:latest   "/bin/k3s server"   server    2 minutes ago   Up 2 minutes   0.0.0.0:80->80/tcp, :::80->80/tcp, 0.0.0.0:443->443/tcp, :::443->443/tcp, 0.0.0.0:6443->6443/tcp, :::6443->6443/tcp

証明書の確認。

[zaki@cloud-dev2 k3s-compose]$ docker compose exec server sh
/ # 
/ # ls -l /var/lib/rancher/k3s/server/tls/
total 116
-rw-r--r-- 1 0 0 1169 Jan 23 20:16 client-admin.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-admin.key
-rw-r--r-- 1 0 0 1178 Jan 23 20:16 client-auth-proxy.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-auth-proxy.key
-rw-r--r-- 1 0 0  566 Jan 23 20:16 client-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-ca.key
-rw-r--r-- 1 0 0 1161 Jan 23 20:16 client-controller.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-controller.key
-rw-r--r-- 1 0 0 1157 Jan 23 20:16 client-k3s-cloud-controller.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-k3s-cloud-controller.key
-rw-r--r-- 1 0 0 1149 Jan 23 20:16 client-k3s-controller.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-k3s-controller.key
-rw-r--r-- 1 0 0 1177 Jan 23 20:16 client-kube-apiserver.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-kube-apiserver.key
-rw-r--r-- 1 0 0 1145 Jan 23 20:16 client-kube-proxy.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-kube-proxy.key
-rw------- 1 0 0  227 Jan 23 20:16 client-kubelet.key
-rw-r--r-- 1 0 0 1149 Jan 23 20:16 client-scheduler.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-scheduler.key
-rw-r--r-- 1 0 0 3815 Jan 23 20:16 dynamic-cert.json
drwxr-xr-x 2 0 0 4096 Jan 23 20:16 etcd
-rw-r--r-- 1 0 0  591 Jan 23 20:16 request-header-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 request-header-ca.key
-rw-r--r-- 1 0 0  570 Jan 23 20:16 server-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 server-ca.key
-rw------- 1 0 0 1675 Jan 23 20:16 service.key
-rw-r--r-- 1 0 0 1372 Jan 23 20:16 serving-kube-apiserver.crt
-rw------- 1 0 0  227 Jan 23 20:16 serving-kube-apiserver.key
-rw------- 1 0 0  227 Jan 23 20:16 serving-kubelet.key
drwx------ 2 0 0   84 Jan 23 20:16 temporary-certs
/ # 

ローテート要求処理の実行。
(ブログ用にシェルを起動してその中で実行してるが、docker compose execの引数で実行してもよい)

/ # k3s certificate rotate
INFO[0000] Server detected, rotating server certificates 
INFO[0000] Rotating certificates for admin service      
INFO[0000] Rotating certificates for etcd service       
INFO[0000] Rotating certificates for api-server service 
INFO[0000] Rotating certificates for controller-manager service 
INFO[0000] Rotating certificates for cloud-controller service 
INFO[0000] Rotating certificates for scheduler service  
INFO[0000] Rotating certificates for k3s-server service 
INFO[0000] Rotating dynamic listener certificate        
INFO[0000] Rotating certificates for k3s-controller service 
INFO[0000] Rotating certificates for auth-proxy service 
INFO[0000] Rotating certificates for kubelet service    
INFO[0000] Rotating certificates for kube-proxy service 
INFO[0000] Successfully backed up certificates for all services to path /var/lib/rancher/k3s/server/tls-1706041327, please restart k3s server or agent to rotate certificates 

CA以外のファイルが消えるのを確認。

/ # ls -l /var/lib/rancher/k3s/server/tls/
total 32
-rw-r--r-- 1 0 0  566 Jan 23 20:16 client-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-ca.key
-rw------- 1 0 0    0 Jan 23 20:22 dynamic-cert-regenerate
-rw-r--r-- 1 0 0 3815 Jan 23 20:16 dynamic-cert.json
drwxr-xr-x 2 0 0   86 Jan 23 20:22 etcd
-rw-r--r-- 1 0 0  591 Jan 23 20:16 request-header-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 request-header-ca.key
-rw-r--r-- 1 0 0  570 Jan 23 20:16 server-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 server-ca.key
-rw------- 1 0 0 1675 Jan 23 20:16 service.key
drwx------ 2 0 0   84 Jan 23 20:16 temporary-certs

K3sコンテナをリスタートする。

[zaki@cloud-dev2 k3s-compose]$ docker compose restart
[+] Restarting 2/2
 ✔ Container k3s-compose-agent-1   Started                                                    1.1s 
 ✔ Container k3s-compose-server-1  Started                                                    1.2s 
[zaki@cloud-dev2 k3s-compose]$ 

再作成されたのを確認。

[zaki@cloud-dev2 k3s-compose]$ docker compose exec server ls -l /var/lib/rancher/k3s/server/tls/
total 116
-rw-r--r-- 1 0 0 1169 Jan 23 20:25 client-admin.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-admin.key
-rw-r--r-- 1 0 0 1178 Jan 23 20:25 client-auth-proxy.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-auth-proxy.key
-rw-r--r-- 1 0 0  566 Jan 23 20:16 client-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 client-ca.key
-rw-r--r-- 1 0 0 1161 Jan 23 20:25 client-controller.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-controller.key
-rw-r--r-- 1 0 0 1157 Jan 23 20:25 client-k3s-cloud-controller.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-k3s-cloud-controller.key
-rw-r--r-- 1 0 0 1149 Jan 23 20:25 client-k3s-controller.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-k3s-controller.key
-rw-r--r-- 1 0 0 1177 Jan 23 20:25 client-kube-apiserver.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-kube-apiserver.key
-rw-r--r-- 1 0 0 1145 Jan 23 20:25 client-kube-proxy.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-kube-proxy.key
-rw------- 1 0 0  227 Jan 23 20:25 client-kubelet.key
-rw-r--r-- 1 0 0 1149 Jan 23 20:25 client-scheduler.crt
-rw------- 1 0 0  227 Jan 23 20:25 client-scheduler.key
-rw-r--r-- 1 0 0 3815 Jan 23 20:25 dynamic-cert.json
drwxr-xr-x 2 0 0 4096 Jan 23 20:25 etcd
-rw-r--r-- 1 0 0  591 Jan 23 20:16 request-header-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 request-header-ca.key
-rw-r--r-- 1 0 0  570 Jan 23 20:16 server-ca.crt
-rw------- 1 0 0  227 Jan 23 20:16 server-ca.key
-rw------- 1 0 0 1675 Jan 23 20:16 service.key
-rw-r--r-- 1 0 0 1376 Jan 23 20:25 serving-kube-apiserver.crt
-rw------- 1 0 0  227 Jan 23 20:25 serving-kube-apiserver.key
-rw------- 1 0 0  227 Jan 23 20:25 serving-kubelet.key
drwx------ 2 0 0   84 Jan 23 20:16 temporary-certs

期限のカスタム

Composeファイル内のserver環境変数に以下を追加。

    environment:
    - K3S_TOKEN=${K3S_TOKEN:?err}
    - K3S_KUBECONFIG_OUTPUT=/output/kubeconfig.yaml
    - K3S_KUBECONFIG_MODE=666
    - CATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYS=750        # これ

このComposeファイルでコンテナを起動し、期限を確認。

[zaki@cloud-dev2 k3s-compose]$ docker compose exec server cat /var/lib/rancher/k3s/server/tls/client-kube-apiserver.crt | openssl x509 -text -noout | grep Validity -A2
        Validity
            Not Before: Jan 23 20:54:48 2024 GMT
            Not After : Feb 11 20:54:48 2026 GMT

ちなみに環境変数の変更はrestart(stopstart)では反映されないので、初回起動時以外の変更はdownupk3s certificate rotateによる再作成が必要。


K3s自体には起動時の自動更新処理自体は備わってるけど実際にを運用で使うケースを考えると、オンプレでかつ一度構築したら様々な事情でメンテフリーで使いたい(運用に証明書更新を目的とした再起動も入れたくない)ということが多々あり、そうすると「証明書期限を可能な限り伸ばして初期構築したい」は自然な流れかなぁと思う。

メンテフリーというか、バージョン塩漬けというか。

ただしCATTLE_NEW_SIGNED_CERT_EXPIRATION_DAYSを使った期間の指定はK3s的には仕様に規定されてないはずなので、予告なく動きが変更される可能性もあるかも。