Skip to content
Published on

Traefik 応用 — ミドルウェア設計、TLS オプション、マルチプロバイダ

Authors

はじめに

Traefik を単なるリバースプロキシとして使っていると、ある時点で「認証をすべてのルートに共通でかけたい」「特定のクライアントだけ mTLS で受けたい」「新バージョンにトラフィックを 5 パーセントだけ流したい」といった要求が次々と出てきて、デフォルト設定だけでは限界に突き当たります。本記事では、その次の段階、つまり Traefik を本番で本格的に運用する際に必要となる応用機能を扱います。

実務で出会う典型的な状況はこうです。マイクロサービスが数十個に増えると、各サービスごとに認証ロジックを重複実装するのが非効率だと判断されます。同時にセキュリティチームは外部パートナー連携区間に相互 TLS 認証を求め、プラットフォームチームは無停止デプロイのためにカナリアとブルーグリーンを標準化したいと考えます。これらすべての要求が、Traefik のミドルウェア、TLSOption、サービスの応用機能、マルチプロバイダの組み合わせで解決されます。

2026 年現在、Kubernetes の Ingress API は事実上凍結状態です。新機能はもはや Ingress には追加されず、後継標準である Gateway API へと重心が移りつつあります。Traefik は IngressRoute という独自 CRD に加えて Gateway API プロバイダの両方をサポートするため、本記事で扱う応用パターンが Gateway API のフィルタおよびポリシーリソースとどう対応するのかも併せて確認します。

本記事の目的は単なる機能の列挙ではなく、各機能をいつどう組み合わせるべきかという設計の感覚を伝えることです。したがって概念説明のあとには、実際に適用可能な CRD YAML とコマンドを十分に盛り込みました。

ミドルウェアの基本概念

ミドルウェアは、リクエストがバックエンドサービスに到達する前、または応答がクライアントへ戻る前に割り込んで動作を変形する処理単位です。ヘッダ追加、認証、レート制限、パス書き換え、圧縮といった横断的関心事をルート定義から分離し、再利用可能にします。

Traefik ではミドルウェアを Kubernetes CRD である Middleware として宣言し、IngressRoute のルールから参照します。重要なのは、ミドルウェアが順番に適用されるチェーンであるという点です。チェーンの前方のミドルウェアがリクエストを拒否すると、後方のミドルウェアとバックエンドは呼び出されません。

リクエストの流れ

Client
  |
  v
[EntryPoint :websecure]
  |
  v
[Router マッチ]  --- Host(api.example.com) && PathPrefix(/v1)
  |
  v
[Middleware チェーン]
   1. rate-limit        (レート制限)
   2. forward-auth      (SSO 認証)
   3. strip-prefix      (パス書き換え)
   4. secure-headers    (セキュリティヘッダ)
  |
  v
[Service]  --- weighted / mirroring / sticky
  |
  v
Pod (バックエンド)

ミドルウェアを設計する際に最も重要な原則は順序です。認証よりレート制限を先に置くと、認証失敗のトラフィックもレート制限のカウントに含まれ、攻撃面を減らすのに役立ちます。逆にパス書き換えは認証の後に置くべきで、そうすればバックエンドが期待するパスで届きます。

ミドルウェアチェーンの設計パターン

複数のミドルウェアを毎回ルートごとに列挙するのはミスと重複を招きます。Traefik は chain タイプのミドルウェアを提供し、よく使う組み合わせを一つの名前でまとめられるようにします。

まず個別のミドルウェアを定義します。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: rate-limit
  namespace: edge
spec:
  rateLimit:
    average: 100
    burst: 50
    period: 1s
    sourceCriterion:
      ipStrategy:
        depth: 1
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: secure-headers
  namespace: edge
spec:
  headers:
    browserXssFilter: true
    contentTypeNosniff: true
    frameDeny: true
    stsSeconds: 31536000
    stsIncludeSubdomains: true
    stsPreload: true
    customResponseHeaders:
      X-Robots-Tag: "noindex, nofollow"
---
apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: strip-api-prefix
  namespace: edge
spec:
  stripPrefix:
    prefixes:
      - /v1

これらを chain でまとめます。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: api-edge-chain
  namespace: edge
spec:
  chain:
    middlewares:
      - name: rate-limit
      - name: forward-auth-sso
      - name: strip-api-prefix
      - name: secure-headers

ルートでは chain を一つ参照するだけで済みます。

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: api-route
  namespace: edge
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`api.example.com`) && PathPrefix(`/v1`)
      kind: Rule
      middlewares:
        - name: api-edge-chain
      services:
        - name: api-backend
          port: 8080
  tls:
    secretName: api-example-com-tls

この構造の利点は明確です。共通ポリシーを一箇所で管理するため、セキュリティヘッダのポリシーを変えるとすべてのルートに即座に反映されます。またルート定義が簡潔になり、可読性とレビュー効率が上がります。

forwardAuth による SSO の実装

中央の認証サーバに認証判断を委譲するパターンが forwardAuth です。Traefik はリクエストをバックエンドへ送る前に指定した認証サーバへ複製を送り、認証サーバが 2xx を返せば通過させ、それ以外は認証サーバの応答をそのままクライアントに返します。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: forward-auth-sso
  namespace: edge
spec:
  forwardAuth:
    address: http://auth-service.auth.svc.cluster.local:4180/oauth2/auth
    trustForwardHeader: true
    authResponseHeaders:
      - X-Auth-Request-User
      - X-Auth-Request-Email
      - X-Auth-Request-Groups
    authRequestHeaders:
      - Cookie
      - Authorization

ここで authResponseHeaders が核心です。認証サーバが検証結果としてユーザ情報をヘッダに載せて返すと、Traefik はそのヘッダだけを抽出してバックエンドへ伝えます。バックエンドはトークン検証を自分で行わずに、信頼できるユーザ識別情報を受け取れます。

oauth2-proxy のような認証ゲートウェイと組み合わせると OIDC ベースの SSO が完成します。認証されていないリクエストを oauth2-proxy がログインページへリダイレクトできるよう、別途 sign-in パスをルーティングしておく必要があります。

forwardAuth の動作順序

Client --(1) リクエスト--> Traefik
Traefik --(2) /oauth2/auth 検証--> auth-service
auth-service --(3a) 200 OK + ユーザヘッダ--> Traefik --(4) バックエンド呼び出し--> Backend
auth-service --(3b) 401/302--> Traefik --(5) 同じ応答を返す--> Client

trustForwardHeader を true にすると X-Forwarded-* ヘッダを信頼しますが、これは Traefik の前段を信頼できる場合にのみ有効にすべきです。外部に直接公開されたエントリポイントなら、クライアントが偽造したヘッダをそのまま信じてしまうため注意が必要です。

TLSOption による精緻な TLS 制御

デフォルトの TLS 設定だけでは不十分な場合が多くあります。あるルートは TLS 1.3 のみ許可し、別のルートはレガシークライアントのために幅広い cipher suite を開ける必要があるかもしれません。TLSOption CRD がこの細かな制御を担います。

apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: modern-tls
  namespace: edge
spec:
  minVersion: VersionTLS13
  sniStrict: true
  alpnProtocols:
    - h2
    - http/1.1
---
apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: intermediate-tls
  namespace: edge
spec:
  minVersion: VersionTLS12
  cipherSuites:
    - TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
    - TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
    - TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
    - TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
  curvePreferences:
    - CurveP521
    - CurveP384

minVersion で最小 TLS バージョンを強制し、sniStrict を true にすると SNI のない接続を拒否します。注意すべきは、TLS 1.3 では cipherSuites の設定が無視されるという点です。cipher suite の制御は TLS 1.2 区間でのみ意味があるため、最新のセキュリティポリシーを適用するには minVersion を VersionTLS13 に上げるほうが単純で安全です。

ルートで TLSOption を参照する方法は次のとおりです。

apiVersion: traefik.io/v1alpha1
kind: IngressRoute
metadata:
  name: secure-route
  namespace: edge
spec:
  entryPoints:
    - websecure
  routes:
    - match: Host(`secure.example.com`)
      kind: Rule
      services:
        - name: secure-backend
          port: 8443
  tls:
    secretName: secure-example-com-tls
    options:
      name: modern-tls
      namespace: edge

mTLS と TLSStore

相互 TLS 認証は、サーバだけでなくクライアントにも証明書の提示を求めます。外部パートナー連携、サービス間のセキュア通信、ゼロトラスト環境でよく使われます。TLSOption の clientAuth セクションで設定します。

apiVersion: traefik.io/v1alpha1
kind: TLSOption
metadata:
  name: mtls-required
  namespace: edge
spec:
  minVersion: VersionTLS13
  clientAuth:
    secretNames:
      - partner-ca-bundle
    clientAuthType: RequireAndVerifyClientCert

clientAuthType の値によって動作が大きく変わります。下の表にまとめます。

clientAuthTypeクライアント証明書要求検証実施証明書がない場合
NoClientCertしないしない通過
RequestClientCert要求のみしない通過
RequireAnyClientCert強制しない拒否
VerifyClientCertIfGiven任意提示されれば検証通過
RequireAndVerifyClientCert強制常に検証拒否

本番で真の mTLS を望むなら RequireAndVerifyClientCert を選ぶべきです。RequestClientCert は検証を行わないため、単に証明書を収集する用途にのみ適します。

TLSStore はデフォルト証明書を定義するリソースです。SNI にマッチする証明書がないときに使うフォールバック証明書を指定します。クラスタ全体のデフォルトは名前が default の TLSStore で設定します。

apiVersion: traefik.io/v1alpha1
kind: TLSStore
metadata:
  name: default
  namespace: edge
spec:
  defaultCertificate:
    secretName: wildcard-example-com-tls

この設定がないと、マッチしない SNI リクエストに対して Traefik が自己署名のデフォルト証明書を提示し、ブラウザの警告を引き起こすことがあります。

サービスの応用機能

Traefik のサービスは単なるバックエンド指定以上を表現できます。重み付けラウンドロビン、ミラーリング、スティッキーセッションが代表例です。

重み付けルーティング (weighted)

複数のバックエンドに比率を決めてトラフィックを分配します。カナリアデプロイの基盤になります。

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-weighted
  namespace: edge
spec:
  weighted:
    services:
      - name: api-stable
        port: 8080
        weight: 90
      - name: api-canary
        port: 8080
        weight: 10

stable に 90、canary に 10 の重みを与えると、おおよそ 10 パーセントのトラフィックが新バージョンへ流れます。IngressRoute ではこの TraefikService を通常のサービスのように参照します。

ミラーリング (mirroring)

実トラフィックの複製を別のバックエンドへ複写して送ります。応答は無視されるため、クライアントに影響を与えずに新バージョンを実トラフィックでテストできます。

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-mirrored
  namespace: edge
spec:
  mirroring:
    name: api-stable
    port: 8080
    mirrors:
      - name: api-shadow
        port: 8080
        percent: 20

上の設定はトラフィックの 20 パーセントを api-shadow へ複写します。シャドートラフィックテストは、新バージョンが実際の負荷パターンでどう動くかを安全に検証する強力なツールです。

スティッキーセッション (sticky)

同じクライアントを同じバックエンドへ固定します。セッション状態をメモリに持つレガシーアプリケーションに必要です。

apiVersion: traefik.io/v1alpha1
kind: TraefikService
metadata:
  name: api-sticky
  namespace: edge
spec:
  weighted:
    services:
      - name: api-stable
        port: 8080
        weight: 1
    sticky:
      cookie:
        name: traefik_backend
        secure: true
        httpOnly: true
        sameSite: strict

可能であればアプリケーションをステートレスに設計し、スティッキーセッションへの依存をなくすほうが、拡張性と回復力の面で有利です。

マルチプロバイダの組み合わせ

Traefik は複数の設定ソースを同時に読み込むマルチプロバイダアーキテクチャを持ちます。Kubernetes CRD、Kubernetes Ingress、ファイル、Consul、Gateway API などを一緒に有効化できます。

providers:
  kubernetesCRD:
    allowCrossNamespace: false
    allowExternalNameServices: false
  kubernetesGateway: {}
  file:
    directory: /etc/traefik/dynamic
    watch: true

各プロバイダは固有のネームスペース接頭辞を持つルータとサービスを生成します。一つのクラスタで CRD ベースのルーティングを主に使いつつ、クラスタ外のレガシーサービスはファイルプロバイダで静的に定義し、新標準導入のために Gateway API を段階的に併用する、という戦略が可能です。

プロバイダ主な用途動的更新備考
kubernetesCRDIngressRoute など Traefik ネイティブあり最も機能が豊富
kubernetesGatewayGateway API 標準あり将来の標準、移植性が高い
kubernetesIngress標準 Ingress 互換ありAPI 凍結、新機能なし
fileクラスタ外の静的定義あり (watch)レガシー連携に有用

マルチプロバイダを使うとき注意すべきはルータの優先順位です。異なるプロバイダが重なるホストを定義すると、優先順位ルールに従って衝突が起こり得るため、ホストとパスのネームスペースをプロバイダごとに明確に分離するのがよいでしょう。

Gateway API との対応関係

2026 年の方向性を考えると、Gateway API への移行経路を知っておくことが重要です。Traefik の応用ミドルウェア機能の多くが Gateway API のフィルタとポリシーリソースに対応します。

Traefik の機能Gateway API の対応
Middleware (ヘッダ操作)HTTPRoute の RequestHeaderModifier フィルタ
Middleware (パス書き換え)HTTPRoute の URLRewrite フィルタ
Middleware (リダイレクト)HTTPRoute の RequestRedirect フィルタ
weighted TraefikServiceHTTPRoute backendRefs の weight
TLSOptionGateway listener の TLS 設定とポリシー
forwardAuthExtAuth 関連の拡張 (実装ごとに異なる)

注意すべきは、Gateway API のコア仕様がまだ forwardAuth やレート制限のようなすべての応用ミドルウェアを標準として網羅していない点です。こうした機能は実装ごとの拡張や ExtensionRef フィルタで扱われ、Traefik も一部を ExtensionRef で自社の Middleware を接続する方式でサポートします。したがって完全な移植性を望むならコアフィルタで表現できる機能のみを使い、応用機能は実装依存を受け入れる折衷が必要です。

Yaegi プラグイン

Traefik は Yaegi という Go インタプリタを内蔵しており、コンパイルなしに Go で書いたミドルウェアプラグインを動的にロードできます。社内ポリシーをコードで実装する必要があるときに有用です。

静的設定でプラグインを宣言します。

experimental:
  plugins:
    customHeader:
      moduleName: github.com/example/traefik-custom-header
      version: v0.2.0

プラグインの核となる骨格は次のような Go コードです。

package customheader

import (
	"context"
	"net/http"
)

type Config struct {
	HeaderName  string `json:"headerName,omitempty"`
	HeaderValue string `json:"headerValue,omitempty"`
}

func CreateConfig() *Config {
	return &Config{}
}

type CustomHeader struct {
	next        http.Handler
	name        string
	headerName  string
	headerValue string
}

func New(ctx context.Context, next http.Handler, config *Config, name string) (http.Handler, error) {
	return &CustomHeader{
		next:        next,
		name:        name,
		headerName:  config.HeaderName,
		headerValue: config.HeaderValue,
	}, nil
}

func (c *CustomHeader) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
	req.Header.Set(c.headerName, c.headerValue)
	c.next.ServeHTTP(rw, req)
}

宣言したプラグインは Middleware CRD で plugin タイプとして参照します。

apiVersion: traefik.io/v1alpha1
kind: Middleware
metadata:
  name: add-custom-header
  namespace: edge
spec:
  plugin:
    customHeader:
      headerName: X-Internal-Trace
      headerValue: edge-gateway

Yaegi はインタプリタであるため、コンパイルされた Go より実行速度が遅くなります。ホットパスで重い演算を行うプラグインは遅延に影響を与え得るため、性能が重要なロジックは別のサイドカーや正式にビルドしたミドルウェアへ分離することを推奨します。

カナリアとブルーグリーンデプロイ

前述の weighted サービスを活用すると、カナリアデプロイを容易に実装できます。核心は重みを段階的に調整することです。

# ステップ1: カナリアに 5 パーセントだけ分配
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":95},{"name":"api-canary","port":8080,"weight":5}]}}}'

# メトリクス観察後に問題なければ 25 パーセントへ増やす
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":75},{"name":"api-canary","port":8080,"weight":25}]}}}'

# 最終切り替え: 100 パーセント
kubectl patch traefikservice api-weighted -n edge --type merge \
  -p '{"spec":{"weighted":{"services":[{"name":"api-stable","port":8080,"weight":0},{"name":"api-canary","port":8080,"weight":100}]}}}'

ブルーグリーンは二つの環境をあらかじめ起動しておき、ルータのサービス参照を一度に切り替える方式です。重み 0 と 100 を即座に入れ替えるか、ルートのサービス名を blue から green に変えればよいです。

カナリアの進行曲線

トラフィック比率
100% |                          ____ green/canary
     |                      ___/
 50% |              ______/
     |        _____/
  5% |  _____/
   0% |_/_______________________________ 時間
       T0   T1   T2   T3   T4   T5
       各ステップでメトリクス/エラー率のゲート通過を確認

各ステップの間には必ずメトリクスゲートを置くべきです。エラー率、遅延、飽和度といった指標が閾値を超えたら自動または手動でロールバックする手順を定めておくことが、安全な運用の基本です。

可観測性: メトリクスとトレーシング

応用ルーティングを運用するには、何がどこへ流れているかを可視化する必要があります。Traefik は Prometheus メトリクスと OpenTelemetry トレーシングを標準でサポートします。

metrics:
  prometheus:
    addEntryPointsLabels: true
    addRoutersLabels: true
    addServicesLabels: true
    buckets:
      - 0.1
      - 0.3
      - 1.2
      - 5.0
tracing:
  otlp:
    http:
      endpoint: http://otel-collector.observability.svc.cluster.local:4318/v1/traces

addServicesLabels を有効にすると、サービスごとにリクエスト数と遅延を分けて見られるため、カナリアデプロイ時に stable と canary の性能を直接比較できます。ただしラベルのカーディナリティが高くなると Prometheus の負荷が増すため、ルータが非常に多い環境では addRoutersLabels を慎重に有効化すべきです。

トレーシングは forwardAuth のような外部呼び出しが割り込んだリクエスト経路でボトルネックを見つけるのに特に有用です。認証サーバの応答が遅いのか、バックエンドが遅いのかをスパン単位で区別して見られます。

本番の落とし穴

実運用でよく出会う落とし穴をまとめます。

第一に、ミドルウェアのネームスペース参照漏れです。ミドルウェアを別のネームスペースから参照する際、名前だけでは見つけられません。プロバイダ接頭辞形式である 名前-at-ネームスペース-at-プロバイダ の規則に従うか、同じネームスペースに置く必要があります。クロスネームスペース参照は allowCrossNamespace の設定にも依存します。

第二に、TLS 1.3 で cipherSuites が無視される問題です。セキュリティスキャナが特定の cipher を指摘して cipherSuites を調整しても変化がなければ、十中八九、接続が TLS 1.3 でネゴシエートされているためです。

第三に、forwardAuth のヘッダ偽造リスクです。trustForwardHeader を有効にしたままエントリポイントを外部に直接公開すると、クライアントが X-Forwarded-User のようなヘッダを偽造できます。信頼境界の外では、認証関連ヘッダを必ず除去するミドルウェアを前段に置くべきです。

第四に、スティッキーセッションと無停止デプロイの衝突です。スティッキークッキーが特定の Pod に固定されていると、その Pod がローリングアップデートで終了する際にセッションが切れます。セッションドレイニングと graceful shutdown を併せて設計する必要があります。

第五に、ミラーリングの副作用です。ミラー対象が書き込み処理を行うバックエンドなら、データが二重に変更され得ます。ミラーは必ず読み取り専用か隔離された環境であるべきです。

トラブルシューティング

問題が起きたときの確認順序を定めておくと診断が速くなります。

# ルータ、ミドルウェア、サービスが正常に登録されたか API で確認
kubectl port-forward -n edge deploy/traefik 8080:8080
curl -s http://localhost:8080/api/http/routers | jq '.[] | {name, status, rule}'
curl -s http://localhost:8080/api/http/middlewares | jq '.[] | {name, status}'

# CRD の適用状態とイベントを確認
kubectl describe ingressroute api-route -n edge
kubectl get events -n edge --sort-by=.lastTimestamp

# TLS ハンドシェイクと証明書を確認
openssl s_client -connect secure.example.com:443 -servername secure.example.com </dev/null 2>/dev/null | openssl x509 -noout -dates -subject

# mTLS クライアント証明書で呼び出しテスト
curl -v --cert client.crt --key client.key https://secure.example.com/health

ダッシュボード API の status フィールドが enabled でなければ、設定に誤りがあるということです。ミドルウェアが warning 状態なら、参照対象がないかネームスペースが合っていない場合がほとんどです。

ログレベルを一時的に DEBUG へ上げると、ルータのマッチとミドルウェアの適用過程を詳細に追跡できます。ただし本番ではログ量が急増するため、診断後は必ず元に戻してください。

おわりに: 本番チェックリスト

Traefik の応用機能は強力ですが、誤って組み合わせるとデバッグの難しい障害につながります。以下のチェックリストでデプロイ前の点検を推奨します。

  • ミドルウェアチェーンの順序が意図どおりか (レート制限 → 認証 → 書き換え → セキュリティヘッダ)
  • forwardAuth の trustForwardHeader が信頼境界の中でのみ有効になっているか
  • 外部公開エントリポイントで認証関連ヘッダを整理するミドルウェアが前段にあるか
  • TLSOption の minVersion がセキュリティポリシーに合わせて設定されているか (TLS 1.3 推奨)
  • mTLS 区間は RequireAndVerifyClientCert で検証まで行うか
  • TLSStore に default 証明書が指定され、マッチ失敗時の警告を防いでいるか
  • カナリアの各ステップでメトリクスゲートとロールバック手順が定義されているか
  • ミラー対象が読み取り専用か隔離されていてデータの二重変更がないか
  • スティッキーセッション使用時に graceful shutdown とセッションドレイニングが設計されているか
  • Prometheus のラベルカーディナリティが管理可能なレベルか
  • Yaegi プラグインがホットパスの遅延を引き起こさないか
  • マルチプロバイダ間でホスト/パスの衝突がないか
  • Gateway API 移行時にコアフィルタで代替可能な機能と依存機能を区別したか

これらの項目を習慣のように点検すれば、Traefik を単なるプロキシから信頼できる本番ゲートウェイへと引き上げられます。次のステップとして、cert-manager との自動証明書発行連携、そして Gateway API ベースのルーティングへの段階的移行を検討することをお勧めします。

参考資料