K3sインストールで標準でついてくるTraefik Ingress Controllerは、デプロイしたPodへ簡単にHTTPSアクセスを実現できるので大変便利です。
デフォルトではダミーの証明書が使われるので、プライベート環境などであれば証明書エラーを無視して運用するということもできますが、場合によっては組織で用意した証明書を使いたい場合もあるかもしれません。
Traefikの証明書に関してはLet's Encryptで発行される証明書を使う方法はいくつもヒットしますが、自前の証明書を使う方法は探し方が悪いのかヒットしないので、検証してみました。

設定方法
結論としては、自前の証明書を使ってTraefikを運用するには以下の通り。
K3s環境におけるTraefikのパラメタを更新するには、HelmChartConfigリソースを作成すればOK
TLS Secretの作成
kubectl create secret tls host-certs -n kube-system \ --cert=server.crt \ --key=server.key
HelmChartのカスタム
K3sにおいてHelmChartリソースを使ってデプロイされているチャートは、HelmChartConfigで設定を上書きします。
To allow overriding values for packaged components that are deployed as HelmCharts (such as Traefik), K3s supports customizing deployments via a HelmChartConfig resources. The HelmChartConfig resource must match the name and namespace of its corresponding HelmChart, and it supports providing additional
valuesContent, which is passed to thehelmcommand as an additional value file.https://docs.k3s.io/helm#customizing-packaged-components-with-helmchartconfig
そして必要な設定は、Traefikのドキュメントを見ても正直読み取れず、HelmチャートのExampleからトライアンドエラーで試した結果、以下のマニフェストを適用すればOKでした。(host-certsは作成した証明書のSecretリソース名)
--- apiVersion: helm.cattle.io/v1 kind: HelmChartConfig metadata: name: traefik namespace: kube-system spec: valuesContent: | tlsStore: default: defaultCertificate: secretName: host-certs
これで再デプロイされたTraefikのPod経由でwebアクセスすれば、自前で用意した証明書が使用されていることを確認できます。


Appendix
デフォルトの設定
Traefikは標準でインストールされますが、仕組みとしてはHelmChartでデプロイされます。
$ kubectl get helmchart -n kube-system
NAME JOB CHART TARGETNAMESPACE VERSION REPO HELMVERSION BOOTSTRAP
traefik helm-install-traefik https://%{KUBERNETES_API}%/static/charts/traefik-27.0.201+up27.0.2.tgz
traefik-crd helm-install-traefik-crd https://%{KUBERNETES_API}%/static/charts/traefik-crd-27.0.201+up27.0.2.tgz
これは、ノードOSの/var/lib/rancher/k3s/server/manifests/traefik.yamlにあるマニフェストからデプロイされていて、内容は以下。
--- apiVersion: helm.cattle.io/v1 kind: HelmChart metadata: name: traefik-crd namespace: kube-system spec: chart: https://%{KUBERNETES_API}%/static/charts/traefik-crd-27.0.201+up27.0.2.tgz --- apiVersion: helm.cattle.io/v1 kind: HelmChart metadata: name: traefik namespace: kube-system spec: chart: https://%{KUBERNETES_API}%/static/charts/traefik-27.0.201+up27.0.2.tgz set: global.systemDefaultRegistry: "" valuesContent: |- deployment: podAnnotations: prometheus.io/port: "8082" prometheus.io/scrape: "true" providers: kubernetesIngress: publishedService: enabled: true priorityClassName: "system-cluster-critical" image: repository: "rancher/mirrored-library-traefik" tag: "2.11.10" tolerations: - key: "CriticalAddonsOnly" operator: "Exists" - key: "node-role.kubernetes.io/control-plane" operator: "Exists" effect: "NoSchedule" - key: "node-role.kubernetes.io/master" operator: "Exists" effect: "NoSchedule" service: ipFamilyPolicy: "PreferDualStack"
ただしHelmChartリソースや上記のマニフェストファイルを修正してもリブートなどによって復元されるため、変更についてはHelmChartConfigを使う必要があります。
Traefikのpodはどこから証明書設定を読んでいるの? (未解決)
わからん…
podなどの定義情報を読んでもConfigMapやSecretを参照してる箇所がなくて不思議。
ちなみに作成した証明書情報はCRDのtlsstoresとしてリソース作成されるが、これをどこから読んでいるかがわからなかった。
$ kubectl get tlsstores.traefik.io -n kube-system default -o yaml
apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
annotations:
meta.helm.sh/release-name: traefik
meta.helm.sh/release-namespace: kube-system
creationTimestamp: "2024-12-31T08:59:23Z"
generation: 1
labels:
app.kubernetes.io/instance: traefik-kube-system
app.kubernetes.io/managed-by: Helm
app.kubernetes.io/name: traefik
helm.sh/chart: traefik-27.0.201_up27.0.2
name: default
namespace: kube-system
resourceVersion: "71480"
uid: 1d2ff4ed-8fe0-47fd-ab3d-d3ff211b8e60
spec:
defaultCertificate:
secretName: host-certs
カスタムコントローラーとか動いてないよねぇ。。
CAとサーバー証明書の作成 (openssl)
とりあえずコマンド実行ログ。
そういえば「genrsaコマンドはdeprecated」という情報を少し前に見たけど、ちょっと調べてみた感じだと「一度は廃止になったけど取り消された」みたいです。
CA秘密鍵と証明書
zaki@cloud-dev2:~/local/openssl/private$ openssl genrsa > ca.key zaki@cloud-dev2:~/local/openssl/private$ openssl req -new -x509 -days 3650 -key ca.key -subj "/CN=nijigasaki.example.org/" -out ca.crt zaki@cloud-dev2:~/local/openssl/private$ ls ca.crt ca.key
クライアントPCはca.crtファイルを証明書ストアに登録しておく。
作成したCAで署名したSANを設定したサーバー証明書
現在CNの値はChromeブラウザなどは無視されるようになっているので、SAN(Subject Alternative Name)を-addextを使って指定します。
以下はserver.example.orgと*.server.example.orgというワイルドカードDNS指定の場合。
(特にIngressのL7ロードバランスを使ってHTTPS接続を構築する場合は、Dnsmasqのワイルドカード設定と併用するとかなり楽)
zaki@cloud-dev2:~/local/openssl/private$ openssl req -newkey rsa -nodes -sha256 -keyout server.key -x509 -days 365 -out server.crt -subj '/CN=server.example.org/' -addext "subjectAltName=DNS:server.example.org,DNS:*.server.example.org" -CA ca.crt -CAkey ca.key .+++++++++++++++++++++++++++++++++++++++*..+...+...+......+.............+............. [snip] zaki@cloud-dev2:~/local/openssl/private$ zaki@cloud-dev2:~/local/openssl/private$ ls -l 合計 16 -rw-r--r--. 1 zaki zaki 1139 12月 30 20:01 ca.crt -rw-------. 1 zaki zaki 1700 12月 30 20:00 ca.key -rw-r--r--. 1 zaki zaki 1220 1月 2 17:36 server.crt -rw-------. 1 zaki zaki 1704 1月 2 17:36 server.key
サーバー証明書のTLS Secret作成
zaki@cloud-dev2:~/local/openssl/private$ kubectl create secret tls host-cert -n kube-system \ --cert=server.crt \ --key=server.key secret/host-cert created
genrsaじゃなくgenpkeyを使う場合は
openssl genpkey -algorithm rsa -pkeyopt rsa_keygen_bits:4096 > ca.key
オプション多いからめんどいね。
DnsmasqのワイルドカードDNS設定
address=/server.example.org/192.168.0.100
これを設定しておくと、*.server.example.orgは全て192.168.0.100として名前解決されます。