Envoy Gateway TLS とセキュリティ: HTTPS を設定し、API を裸で走らせない

8 min read

HTTP が動いたら、そこはまだウォームアップです。あなたの service が石器時代に住んでいない限り、本番ではほぼ確実に HTTPS が必要です。この記事では、Envoy Gateway における TLS の基本を整理し、API を平文で走らせないための土台を固めます。

Gateway で TLS はどう動くのか

最も一般的なやり方は、Gateway listener で TLS を terminate することです。

意味:

  • client は HTTPS で接続する
  • Gateway が handshake と証明書を処理する
  • その後のトラフィックは内部では平文のまま Service へ転送される

基本的な HTTPS listener はこうです。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg
spec:
  gatewayClassName: eg
  listeners:
    - name: http
      protocol: HTTP
      port: 80
    - name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
          - kind: Secret
            group: ""
            name: example-cert

ここで大事なのは 2 点です。

  • protocol: HTTPS
  • tls.mode: Terminate

つまり、TLS はここで終端し、Gateway が証明書を扱うということです。

HTTPS + HTTPRouteTLSRoute の違い

ここは重要です。TLSRoute を見ると、多くの人がすぐこう思います。

「HTTPS listener があれば足りるのでは? なぜ別に TLSRoute が必要なの?」

違いは Gateway が TLS を terminate するかどうか にあります。

シナリオ よくある組み合わせ 説明
Gateway が TLS を terminate し、その後 HTTP ルールを適用する HTTPS listener + HTTPRoute 最も一般的な web サイト / API パターン
Gateway は復号せず、SNI だけで暗号化トラフィックを route する TLS listener + TLSRoute TLS passthrough パターン

つまり:

  • Gateway で pathheadermethod を見たいなら、まず TLS を terminate してから HTTPRoute を使う必要がある
  • end-to-end 暗号化を保ち、Gateway で復号したくないなら TLSRoute が正しい道筋

どちらが「より上級」かではありません。大事なのは、自分が欲しいのが「TLS を terminate して L7 routing したい」のか、「暗号化を保ったまま SNI ベース routing したい」のかを理解することです。

証明書 Secret を用意する

テスト環境なら self-signed certificate で十分です。公式例でもこの形を使っています。

openssl req -x509 -sha256 -nodes -days 365 -newkey rsa:2048   -subj '/O=example Inc./CN=example.com'   -keyout example.com.key   -out example.com.crt
 
openssl req -out www.example.com.csr -newkey rsa:2048 -nodes   -keyout www.example.com.key   -subj "/CN=www.example.com/O=example organization"
 
openssl x509 -req -days 365 -CA example.com.crt -CAkey example.com.key   -set_serial 0 -in www.example.com.csr -out www.example.com.crt

cert/key を Kubernetes Secret として保存します。

kubectl create secret tls example-cert   --key=www.example.com.key   --cert=www.example.com.crt

その後 Gateway から certificateRefs 経由でこの Secret を参照できます。

⚠️ self-signed cert はテスト用に限ります。本番では cert-manager など既存の証明書管理フローを使ってください。デモ構成をそのまま本番へ持ち込むのは、カップ麺だけで生活するようなもので、たまにならいいけれど長期運用には向きません。

既存 Gateway に HTTPS listener を追加する

前の記事の eg Gateway がすでにあるなら、そのまま patch できます。

kubectl patch gateway eg --type=json --patch '
  - op: add
    path: /spec/listeners/-
    value:
      name: https
      protocol: HTTPS
      port: 443
      tls:
        mode: Terminate
        certificateRefs:
        - kind: Secret
          group: ""
          name: example-cert
'

status を確認します。

kubectl get gateway/eg -o yaml

listener が正常そうなら HTTPS を試します。

curl -v -HHost:www.example.com   --resolve "www.example.com:443:${GATEWAY_HOST}"   --cacert example.com.crt   https://www.example.com/get

このコマンドはローカル検証にかなり便利です。特に DNS がまだ張られていない段階で役立ちます。

1 つの Gateway で複数 HTTPS domain を扱う

複数 domain がある場合、同じ Gateway に複数の HTTPS listener を追加します。 たとえば foo.example.com を足すなら:

listeners:
  - name: https-main
    protocol: HTTPS
    port: 443
    hostname: www.example.com
    tls:
      mode: Terminate
      certificateRefs:
        - name: example-cert
  - name: https-foo
    protocol: HTTPS
    port: 443
    hostname: foo.example.com
    tls:
      mode: Terminate
      certificateRefs:
        - name: foo-cert

対応する HTTPRoute 側の hostname も正しく更新するのを忘れないでください。listener が待ち受けていても、route が送信先を知らなければトラフィックは流れません。

TLSRoute のメンタルモデル

TLS passthrough では、TLSRoute を次のように捉えるとわかりやすいです。

💡 TLSRouteapiVersion は、インストールしている Gateway API CRD の version によって異なる場合があります。以下は概念説明用の例です。適用前に cluster に入っている version を確認してください。

apiVersion: gateway.networking.k8s.io/v1
kind: Gateway
metadata:
  name: eg-tls
spec:
  gatewayClassName: eg
  listeners:
    - name: tls
      protocol: TLS
      port: 443
---
apiVersion: gateway.networking.k8s.io/v1alpha2
kind: TLSRoute
metadata:
  name: passthrough-route
spec:
  parentRefs:
    - name: eg-tls
  hostnames:
    - "db.example.com"
  rules:
    - backendRefs:
        - name: db-service
          port: 5432

このパターンのよくある用途:

  • SNI hostname だけで route する
  • 暗号化を backend まで保つ
  • Gateway では HTTP path/header を解釈しない

つまり TLSRouteHTTPRoute の違いは名前だけではなく、扱っているトラフィック層がまったく違います。

Cross-Namespace の証明書参照

公式ドキュメントが強調している便利な機能があります。 Gateway は他 namespace の certificate Secret を参照できますが、そのためには ReferenceGrant が必要です。

たとえば platform team が envoy-gateway-system に証明書を集約し、application 側の Gateway が default にある場合、明示的な許可なしに namespace をまたいで参照することはできません。

例:

apiVersion: gateway.networking.k8s.io/v1beta1
kind: ReferenceGrant
metadata:
  name: allow-default-to-read-cert
  namespace: envoy-gateway-system
spec:
  from:
    - group: gateway.networking.k8s.io
      kind: Gateway
      namespace: default
  to:
    - group: ""
      kind: Secret

意味:

  • default namespace の Gateway
  • envoy-gateway-system にある Secret を参照することを許可する

ReferenceGrant がなければ、この cross-namespace reference は invalid として扱われます。Gateway API が意地悪なのではなく、防御のための仕組みです。

一文まとめ

Envoy Gateway の TLS は、響きほど謎ではありません。certificate Secret を用意し、HTTPS listener を設定し、route と hostname を揃える。基本はこれでほぼ完了です。

💡 初心者が最もやりがちな失敗は、TLS が難しすぎることではありません。hostnamecertificateRefsHTTPRoute hostnames の 3 つが揃っていないことです。どれか 1 つずれると、トラフィックはすぐ不機嫌になります。

次の一歩

インストール、概念、routing、TLS を一通り見たので、次の記事ではそれらを本番で爆発しにくい実践知にまとめます。 👉 ベストプラクティス