Envoy入門(その2)外部認証
はじめに
マイクロサービスやWeb API界隈では、サービス間のネットワークの制御をライブラリではなく、プロキシのコンテナをサイドカーとして使うのだとか。そのデファクトスタンダード的な立ち位置なのが Envoy さん。
ですが、日本語の新しい情報が少ないので、だったら本家のサイトを見ながら、自力でなんとかするしかないね。という記事です。
第一回目は、Transport layer security (TLS) でした。
第二回目は、External authorization (ext_authz) filter です。
やってみた
環境構築
今回は省略します。必要な方は、(その1) をご覧ください。
External authorization (ext_authz) filter
External Authorization 自体の説明とそこからさらにArchitecture overviewのリンクもありますが、今回も、ま、やってみよう!ということで。
このサンプルが目指しているのは、x-current-useer
ヘッダーに認証サーバから得られたユーザー名を付けて、内側のサーバーを呼び出すことになります。
Step 1
ということで、今回も Step 1 では、必要なものを全部作って、動かします。書いてあるとおりにやると(若干出力が異なりますが)動きます。
$ docker-compose pull
Pulling ext_authz-http-service ... done
Pulling ext_authz-grpc-service ... done
Pulling ext_authz-opa-service ... done
Pulling upstream-service ... done
Pulling front-envoy ... done
$ docker-compose up --build -d
Creating network "ext_authz_default" with the default driver
Creating ext_authz_ext_authz-http-service_1 ... done
Creating ext_authz_ext_authz-opa-service_1 ... done
Creating ext_authz_upstream-service_1 ... done
Creating ext_authz_ext_authz-grpc-service_1 ... done
Creating ext_authz_front-envoy_1 ... done
$ docker-compose ps
Name Command State Ports
-----------------------------------------------------------------------------------------------------------------------
ext_authz_ext_authz-grpc-service_1 /app/server -users /etc/us ... Up
ext_authz_ext_authz-http-service_1 docker-entrypoint.sh /bin/ ... Up
ext_authz_ext_authz-opa-service_1 ./opa_envoy_linux_amd64 ru ... Up
ext_authz_front-envoy_1 /docker-entrypoint.sh /bin ... Up 10000/tcp, 0.0.0.0:8000->8000/tcp, :::8000->8000/tcp
ext_authz_upstream-service_1 python3 /code/service.py Up (healthy)
今回も docker-compose.yaml
で登場人物を見ていきましょう。
今回、Envoy は一つで ext_authz_front-envoy_1
。内側のサービスも一つで ext_authz_upstream-service_1
。認証サーバが三つで、環境変数 FRONT_ENVOY_YAML
を切り替えてテストしていくことになります。
Step 4 ??
なぜか Step 1 の次は Step 4 です。まぁ、2、3、4 で、三つの認証サーバを試そうね♡。という予定だったのかもしれません。ということで、それぞれ見ていきます。
ext_authz-http-service
最初は、docker-compose.yaml にハードコードしてある config/http-service.yaml
から。
listener
は、8000 番ポートで待っていて、filter_chains
の最初の filter
の目的地は、upstream-service
cluster で、これは upstream-service
の 8080 番ポートです。
ただ、http_filters
は、いつもの Router
の前に ExtAuthz
が刺さっていて、これが ext_authz-http-service
cluster と通信してレスポンスの x-current-user
ヘッダーをチェックしています。
ですから、書かれているとおり、単純に GET すると失敗します。
$ curl -v localhost:8000/service
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> GET /service HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.3.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< date: Wed, 30 Oct 2024 10:26:09 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host localhost left intact
なんで localhost:8000/service
なのか?は、upstream-service
が待っているからです。
認証サーバも確認しておきましょう。
authorization
ヘッダーの Bearer
の次の token が users.json
にあれば、user
を x-current-useer
ヘッダーとして、200 OK を返し、無ければ、403 Forbidden を返していることがわかります。(require って JSON も食べられるんですね。)
で、users.json
は、こんな感じ
なので、成功パターンは、以下のようになります。
$ curl -v -H "Authorization: Bearer token1" localhost:8000/service
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> GET /service HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.3.0
> Accept: */*
> Authorization: Bearer token1
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 30
< date: Wed, 30 Oct 2024 10:43:16 GMT
< server: envoy
< x-envoy-upstream-service-time: 0
<
* Connection #0 to host localhost left intact
Hello user1 from behind Envoy!
ext_authz-grpc-service
こちらも、まずは、Envoy の Yaml ファイルから
ExtAuthz
で、envoy_grpc
として呼び出しているので、チェックなどは ext_authz-grpc-service
側へ行っているようです。
ここを作るのは、Envoy入門(笑)の範囲を超えていますが、軽く見ておくと
のように、ガッツリ Envoy さんの API を使って実装されていますが、authorization
ヘッダーのチェックと成功時の x-current-user
ヘッダーの付与など ext_authz-http-service
と同じ実装になっているようです。
ということで、落として、設定を変えて立ち上げます。(出力は省略します。)
$ docker-compose down
$ FRONT_ENVOY_YAML=config/grpc-service/v3.yaml docker-compose up --build -d
以下は、失敗し
$ curl -v localhost:8000/service
以下は、成功します。
$ curl -v -H "Authorization: Bearer token1" localhost:8000/service
ext_authz-opa-service
3つ目の最後は、Open Policy Agent の OPA です。最初に見た docker-compose.yaml
のこの部分で定義しています。
ここで使用している Dockerfile は、以下のもので
この policy.rego をコピーしています。
詳細は、OPA のドキュメントを読む必要がありますが、rego というのは、prolog 系の宣言型の言語ということなので、上から順に実行されるというよりも、入力と宣言されたルールが適合したものが出力になるみたいな感じのようです。
上の例では、HTTP メソッドが GET
なら、許可されて、x-current-user
ヘッダに OPA
が入ることになります。これを受けた動きを定義するのが、以下の Envoy の Yaml ファイルです。
と言っても、期待している x-current-user
ヘッダーの設定が終わっているので、通すだけです。ただし、HTTP メソッドが GET
でないとエラーになります。当然、リクエストの URL も指定できますので、API 単位での認可が実現できる仕組みになります。
動かします。(出力は省略します。)
$ docker-compose down
$ FRONT_ENVOY_YAML=config/opa-service/v3.yaml docker-compose up --build -d
テストします。GET
であれば成功します。
$ curl localhost:8000/service --verbose
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> GET /service HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.3.0
> Accept: */*
>
< HTTP/1.1 200 OK
< content-type: text/plain; charset=utf-8
< content-length: 28
< date: Sat, 02 Nov 2024 05:25:34 GMT
< server: envoy
< x-envoy-upstream-service-time: 0
<
* Connection #0 to host localhost left intact
Hello OPA from behind Envoy!
POST
は失敗します。
$ curl -X POST localhost:8000/service --verbose
* Trying [::1]:8000...
* Connected to localhost (::1) port 8000
> POST /service HTTP/1.1
> Host: localhost:8000
> User-Agent: curl/8.3.0
> Accept: */*
>
< HTTP/1.1 403 Forbidden
< date: Sat, 02 Nov 2024 05:27:21 GMT
< server: envoy
< content-length: 0
<
* Connection #0 to host localhost left intact
おわりに
外部認証についても、サンプルとマニュアルを見ながらであれば、設定できそうですね。
それでは、第3回があれば(笑)、第3回で。
Discussion