zaki work log

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

[K3s] opensslで用意した自前のサーバー証明書でTraefik Ingress Controllerを運用する設定

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

設定方法

結論としては、自前の証明書を使ってTraefikを運用するには以下の通り。

  • 証明書を持つTLS Secretを作成
  • 作成したTLS Secretをオプション指定したHelmChartのパラメタを作成

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 the helm command as an additional value file.

https://docs.k3s.io/helm#customizing-packaged-components-with-helmchartconfig

docs.k3s.io

zaki-hmkc.hatenablog.com

そして必要な設定は、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

github.com

github.com

これで再デプロイされた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として名前解決されます。

参考