1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

Emissary を利用した Envoy での JWT SVID ベースのアクセス制御

Last updated at Posted at 2020-10-30

はじめに

数ヶ月前に SPIFFE Slack で GitHub の方が Emissary という OSS を公開したと アナウンス されていて、Emissary を利用することで何ができるのかが気になったので調べてみました。なお、この記事では SPIFFE/SPIRE についての説明は割愛します。

Emissary とは

image

Emissary は Envoy へのリクエストに含まれる JWT SVID を元にしたアクセス制御機能と、Envoy から別サービスへのリクエストに JWT SVID を追加する機能を持つソフトウェアとなっており、Envoy の認可処理を別システムに任せる仕組みである External Authorization の Server として機能します。Server は TCP または UDS での起動がサポートされています。

External Authorizaion の Client としては Network Filter(L4)と HTTP Filter(L7)の ext_authz フィルターが存在しますが、Emissary は HTTP Filter の Server として機能するものになっています。基本的には External Authorization での利用となりますが Lua スクリプトで利用する方法もサンプルコードでは紹介されています。

Emissary には Ingress モードと Egress モードの2つのモードがあり、Envoy の ext_authz フィルターから Emissary へのリクエストに含まれる x-emissary-mode ヘッダーによりモードが決定されます。モードは サンプルの Envoy 設定 のように extensions.filters.http.ext_authz.v3.AuthorizationRequestheaders_to_add で固定して設定するのが良いかと思います。

Ingress モード

Ingress モードは HTTP リクエストの x-emissary-auth ヘッダーで渡ってきた JWT SVID のデジタル署名を検証して、aud に自身の SPIFFE ID がセットされているかをチェックした後で、HTTP メソッドや HTTP パスなどのリクエスト情報を元にアクセス制御(認可)を行う機能になっています。

アクセス制御の定義は Emissary 起動時に EMISSARY_INGRESS_MAP という環境変数に JSON 形式で設定する必要があります。以下の定義例は spiffe://domain.test/app という SPIFFE ID を持つ通信相手に対して特定のエンドポイントへのアクセスを許可するものとなります。

$ EMISSARY_INGRESS_MAP='{"spiffe://domain.test/app": [{"path":"/put","methods":["PUT"]},{"path":"/p","methods":["PATCH"]},{"path":"/g","methods":["GET"]}]}'

$ echo $EMISSARY_INGRESS_MAP | jq .
{
  "spiffe://domain.test/app": [
    {
      "path": "/put",
      "methods": [
        "PUT"
      ]
    },
    {
      "path": "/p",
      "methods": [
        "PATCH"
      ]
    },
    {
      "path": "/g",
      "methods": [
        "GET"
      ]
    }
  ]
}

Egress モード

Egress モードは SPIRE Agent の Workload API を介して JWT SVID を取得して HTTP リクエストの x-emissary-auth ヘッダーに JWT SVID をセットする機能になっています。

JWT SVID の aud(JWT を受け取るシステム、つまり通信相手)に含める SPIFFE ID もしくは任意の文字列は、Emissary 起動時に EMISSARY_EGRESS_MAP という環境変数に JSON 形式で設定する必要があります。以下の定義例はホスト名が app.domain.test へのリクエストに audspiffe://domain.test/app が定義された JWT SVID をセットするものとなります。

$ EMISSARY_EGRESS_MAP='{"app.domain.test": "spiffe://domain.test/app"}'

$ echo $EMISSARY_EGRESS_MAP | jq .
{
  "app.domain.test": "spiffe://domain.test/app"
}

全体像を整理する

サービス間の通信は Emissary が設定された Envoy を介して行われることを前提としています。

リクエストを受けたとき

  1. リクエストを受けた Envoy が x-emissary-mode:ingress というヘッダーをセットして HTTP Filter の ext_authz フィルターを介して Emissary に問い合わせを行う
  2. Emissary はリクエストの x-emissary-auth ヘッダーで定義された JWT SVID を検証した後で EMISSARY_INGRESS_MAP で許可されているリクエストであるかを判定して Envoy にレスポンスする
  3. Envoy は判断結果に応じてレスポンスを制御する

リクエストを送るとき

  1. Envoy と同居しているサービスから自身の Envoy にリクエストが送られる
  2. リクエストを受けた Envoy は x-emissary-mode:egress というヘッダーをセットして HTTP Filter の ext_authz フィルターを介して Emissary にリクエストする
  3. Emmisary は同居しているサービスからのリクエストに含まれる Host ヘッダーと EMISSARY_EGRESS_MAP で定義された情報を照合して JWT SVID の aud に含める SPIFFE ID を判定した上で JWT SVID を Workload API から発行する
  4. 元リクエストの x-emissary-auth ヘッダーに JWT SVID を乗せて別サービスにリクエストを投げる

手元で動かすには?

Kubernetes で Emissary を動かす Example が提供されているので、spire-examples を参考に SPIRE がインストールされた Kubernetes クラスタを用意すれば手元でも試すことができます。

所感

Envoy で JWT SVID を利用してアクセス制御をしたいユースケースで非常に役立ちそうな印象を受けましたが、サービス数が多い環境では EMISSARY_INGRESS_MAPEMISSARY_EGRESS_MAP を動的に変更できるような仕組みがないと厳しいかな思ったので、本番導入を検討する際には機能改善をするかアイデアを拝借して新しいものを作るのが良いかなと思いました。

あとは、External Authorization の Server で Egress リクエストにヘッダーを追加するという使い方が斬新だなと思いました。リクエストに応じて都度 JWT SVID を発行してセットするということをやろうとするとこのようなアプローチしかなかったのか、これが一般的なのかはナレッジ不足なので詳しい方いらっしゃったらコッソリ教えてもらいたいです。

最後に小ネタですが HiNative での投稿 によると Envoy と Emissary は同義語みたいです。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?