envoy設定のポイント

envoyは高機能なネットワークプロキシで、サポートするプロトコルが豊富、動的に構成変更が可能といった特長があります。 高機能プロキシ共通の課題としてconfigが複雑であるため、構成を理解することが適切な設定に必要となります。

導入着手の際、重要なリソースには以下のようなものがあります。

  • dockerの envoy公式イメージを利用するのが手軽。
    • 起動configとして、/etc/envoy/envoy.yamlを参照する。このファイルの記述が適切であれば動作する。誤りがあると起動ログにエラー表示して終了。
  • 情報源は、 公式ドキュメントを最重視すべき
    • そもそも情報は少ない。また、configフォーマットがバージョン間で頻繁に変更されており、非公式な情報の書き方がdeprecatedになっているケースが多い。
    • 公式ドキュメントの情報量は多く、全体的に通読した方がconfigの構成を理解しやすい

configのトップレベル要素は以下のような構成になっています。

  • adminオブジェクトは必須要素だが、基礎的な使い方の範囲ではさほどカスタマイズする必要はない。 Simple Configurationの記述と同じ内容でも動作する。
  • static_resources内のlistenersclustersを目的に沿って適切に設定する。
    • xDS規約に沿ってファイルを分割した方が良い。バックエンドホストが増えるとconfigが長くなる
    • YAMLには空白行や行頭#コメントを書けるので、見やすさのための区切りを入れると良い

Listener

Listenerは、流入するDownstreamリクエストをポート単位で定義するオブジェクトです。
主要な内部オブジェクトとして Filterを定義し、L7プロキシの典型的な用途ではホスト名やパスに基づいてルーティングやリダイレクトなどを処理します。

  • listeners.filter_chains.filters[]に個別のフィルタを定義
    • HTTP connection managerが汎用のL7プロキシ向けオブジェクト
      • route_config.virtual_hosts[].routes[] がルーティング定義
      • stat_prefixは必須
      • http_filtersは必須。指定しない場合、起動するがレスポンスなしでタイムアウトする動作になる
  • Basic認証JWT認証TCPプロキシはListenerに設定
  • アクセスIPはデフォルトでは別のプロキシから X-FORWARDED-FORを受け取る想定になっている。HTTP connection manager設定にuse_remote_address: trueを指定するとピアのIPを参照する挙動となり、アクセスログのREQ(X-FORWARDED-FOR)にもこの値が入る

また、以下の点は注意が必要です。

  • VirtualHostdomainsは対象とするドメイン文字列を設定するフィールドで、ワイルドカードも指定できます。開発環境などで非ウェルノウンポートを使う場合、この文字列にポート番号まで含めないとマッチしません(例:"*.example.com:8443")。
    • domainsの定義が意図と異なる場合にもenvoyは特に問題なく起動します。VirtualHostにマッチする定義がなかった場合、リクエストはUpstreamホストに到達せず、分かりづらい挙動になります。

SSL証明書

SSLを使用する場合、 listeners.filter_chains.transport_socketDownstreamTlsContextを設定します。以下のように証明書と秘密鍵のpathを指定すると動作します。

"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
  tls_certificates:
  - certificate_chain:
      filename: "/etc/envoy/fullchain.pem"
    private_key:
      filename: "/etc/envoy/privkey.pem"

tls_certificatesはArrayを指定できますが、clientとの通信に設定できる証明書は1つに限られ、複数ドメインの証明書を指定するとエラーとなり起動しません。 基本的に証明書を複数指定したい場合には、listenerを分割する必要があります。

SNI

SNIによるマルチドメイン収容は、API v3で追加されました。

Listenerフィルターenvoy.filters.listener.tls_inspectorを追加すると、ドメイン名ごとに設定を分割できます。
なおv1.23頃からtyped_configは必須になりました。

listener_filters:
- name: "envoy.filters.listener.tls_inspector"
  typed_config:
    "@type": type.googleapis.com/envoy.extensions.filters.listener.tls_inspector.v3.TlsInspector
filter_chains:
- filter_chain_match:
    server_names: ["example.com", "www.example.com"]
  filters:
    # listener config
- filter_chain_match:
    server_names: "api.example.com"
  filters:
    # listener config

これにより、transport_socketセクションもドメインごとに設定でき、SSL証明書の使い分けが可能になります。

server_namesにはワイルドカードも使えますが、サブドメインに限定される挙動であるため書き方には注意が必要です。

Cluster

Clusterは、Upstreamのサービス群を定義するオブジェクトです。configのネストは深いものの、基本的にはバックエンドのアドレスを登録することで動作します。

  • 上流のホストは、cluster.typeの設定と連動して、IPアドレス(デフォルト)またはホスト名で指定する。typeの種類は、 Supported service discovery typesを読んで理解しておく。
    • kubernetesのサービスホストで名前解決することも可能

なお、存在しないホストを登録した場合にも特にエラーなく起動します。ネットワークが不安定な場合にも起動への影響はなさそうです。

クライアント証明書認証

mTLSなどに用いるクライアント証明書認証は、 common_tls_contextvalidation_contextオブジェクトとして指定します。

"@type": type.googleapis.com/envoy.extensions.transport_sockets.tls.v3.DownstreamTlsContext
common_tls_context:
  validation_context:
    trusted_ca:
      filename: "/etc/envoy/ca.crt"
require_client_certificate: true

この設定例では、検証方法は認証局との信頼チェーンのみです(require_client_certificateの記述位置がcommon_tls_contextの外であることに注意)。追加の検証方法は、 validation_contextにオプションが定義されています。

config中の設定ポジションは以下のとおりです。

  • DownStreamはlisteners[].filter_chains[].transport_socket.typed_config
  • UpStreamはclusters[].transport_socket.typed_config

なお、trusted_caに指定する認証局の証明書ファイルは期限も含めて有効なファイルを使用する必要があります。無効なものを指定した場合、クライアント側に要求も表示されず分かりづらい挙動となるため、開発環境などで注意が必要です。

⁋ 2020/08/16↻ 2024/11/07
中馬崇尋
Chuma Takahiro