はじめに
K3sのデフォルトIngressコントローラであるTraefikを使って、クライアントからバックエンドサービスまで完全なTLS通信(End-to-End TLS)を実現する方法を解説します。この設定により、様々なサービスをセキュアに公開し、データ盗難や中間者攻撃を防ぐことができます。本記事では、TraefikのServersTransportリソースを使った設定手順を、実際のYAML例とともにステップごとに説明します。
ServersTransportリソースの作成
まず、バックエンドサービスへのTLS接続を定義するために、ServersTransportというカスタムリソースを作成します。これはTraefikのCRD(Custom Resource Definition)を使って実現されます。TraefikはデフォルトでバックエンドサービスにHTTPで接続しますが、ServersTransportリソースを使うことでバックエンドへの接続をHTTPS(TLS)に変更でき、End-to-Endの暗号化を実現できます。以下は、my-namespaceというネームスペースのサービスを例にしたYAML例です:
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
  name: my-transport
  namespace: my-namespace
spec:
  serverName: "*.my-namespace.svc.cluster.local"
  rootCAsSecrets: 
    - my-ca-secret
  insecureSkipVerify: false
- serverName:
 バックエンドサービスのSNI(Server Name Indication)を指定します。ここではワイルドカードを使ってmy-namespaceのサービスを対象にしています。
- rootCAsSecrets: バックエンドサービスのルートCA秘密鍵を指定します。これは独自の証明書(例: 自前で生成したもの)を使う場合に必要です。ここでmy-ca-secretというSecretを参照しています。
- insecureSkipVerify:false:
 に設定すると、証明書の検証が行われます。これがデフォルトのセキュアな設定です。
このリソースを作成したら、kubectl apply -f filename.yaml で適用します。
IngressやServiceへのAnnotation設定
次に、Ingressリソースや関連するServiceにTraefik固有のAnnotationを追加して、ServersTransportを適用します。Serviceに以下のAnnotationを追加します:
annotations:
  traefik.ingress.kubernetes.io/service.serversscheme: https
  traefik.ingress.kubernetes.io/service.serverstransport: my-namespace-my-transport@kubernetescrd
- service.serversscheme:https:
 バックエンドへの通信をHTTPSに指定します。これでEnd-to-End TLSが有効になります。
- traefik.ingress.kubernetes.io/service.serverstransport: my-namespace-my-transport@kubernetescrd:
 先ほど作成したServersTransportのリソース名を参照します。@kubernetescrdはTraefikがCRDを参照することを示します。my-namespaceがネームスペースで、my-transportがServersTransportのnameに相当します。
これにより、Traefikはバックエンドサービスに対してTLSで接続し、証明書の検証を行います。
独自証明書 vs Let's Encryptの場合の違い
バックエンドサービスの証明書によって、ServersTransportの設定が変わります。
独自証明書の場合
自前で生成した証明書(例: OpenSSLで作成したもの)を使う場合、Traefikがその証明書を信頼するためにルートCAを指定する必要があります。上記のYAML例のように、rootCAsSecretsにSecret名を追加します。このSecretには、ルートCAの証明書ファイル(例: ca.crt)が含まれているはずです。これを省略すると、証明書の検証に失敗し、接続エラーが発生します。
Let's Encryptの場合
Let's Encryptの証明書は信頼された公開CA(例: ISRG Root X1)によって発行されるため、Traefikがデフォルトでこれを信頼します。したがって、rootCAsSecretsの指定は不要です。以下は設定例です:
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
  name: my-transport
  namespace: my-namespace
spec:
  serverName: "*.my-namespace.svc.cluster.local"
  insecureSkipVerify: false
これでTraefikが自動的に証明書を検証します。
insecureSkipVerifyをtrueに設定して簡略化
証明書の検証をスキップしたい場合(例: 開発環境や一時的なテスト時)、insecureSkipVerify: trueに設定します。これにより、serverNameの指定が不要になります。YAML例は以下のようになります:
apiVersion: traefik.io/v1alpha1
kind: ServersTransport
metadata:
  name: my-transport
  namespace: my-namespace
spec:
  insecureSkipVerify: true
この設定は便利ですが、セキュリティリスクがあるため、本番環境では避けましょう。Man-in-the-Middle攻撃の可能性が増します。
注意点とトラブルシューティング
- Traefikのバージョン:
 K3sのデフォルトTraefikはv2.x系です。ServersTransportはTraefik v2.5以降でサポートされているので、バージョンを確認してください。
- Secretの作成:rootCAsSecretsで参照するSecretは、KustomizeとSmallstepで内部ドメイン証明書を完全自動化:GitOpsで実現するワイルドカード証明書と動的caBundleの革新的運用を参照ください。
- エラー対応:
 接続エラーが発生したら、TraefikのPodログをチェック(kubectl logs -n kube-system -l app=traefik)。証明書関連のエラーが出力されるはずです。
- ベストプラクティス:
 常にinsecureSkipVerify: falseを使い、適切なCAを指定してセキュアに保ちましょう。Cert-Managerと連携して自動証明書管理を検討するのもおすすめです。こちらはcert-managerを使った固定IPサーバーのSSL/TLS証明書自動化の実践ガイドを参照ください。
まとめ
K3sのTraefikでEnd-to-End TLSを設定するのは、ServersTransportとAnnotationの組み合わせで実現可能です。独自証明書ではrootCAsSecretsを追加、Let's Encryptでは不要、そしてinsecureSkipVerifyで簡略化できる点を覚えておきましょう。この設定で、MinIOや他のサービスを安全に露出できます。ご質問があればコメントください! KubernetesのTLS設定で困った経験があれば共有しましょう。
