この記事は Z Lab Advent Calendar 2020 の7日目の記事となります。
はじめに
この記事では Envoy でサポートされている External Authorization を紹介していきます。ここからの情報は v1.16.0 時点での v3 API の仕様を元にまとめたものとなっています。
External Authorization とは
External Authorization は Envoy が受信したリクエストが認可されているかを gRPC または HTTP で外部のシステムに問い合わせて判定する機能です。これを利用することで Envoy での認可処理を外部のシステム(以下、認可サービス)に委譲することが可能になります。
External Authorization は Network Filter(L3/L4)や HTTP Filter(L7)として設定する仕様になっていて、Network Filter でリクエストが許可されていない判定がされた場合はコネクションがクローズされ、HTTP Filter でリクエストが許可されていない判定がされた場合は 403 Forbidden がレスポンスされます。
なお、External Authorization で使用する ext_authz
フィルターはフィルターチェーンの最初のフィルターとして設定することが推奨されています。これはリクエストが許可されているかを判定した後で他のフィルターによるリクエスト処理を行うほうが効率が良いためです。
認可サービスが受け取る情報
以下のように Network Filter では gRPC サーバーとして稼働する認可サービスを設定でき、HTTP Filter では gRPC または HTTP サーバーとして稼働する認可サービスのどちらかを設定できる仕様になっており、認可サービスがどのように稼働しているかで受け取る情報のフォーマットが異なります。
種別 | gRPC な認可サービス | HTTP な認可サービス |
---|---|---|
Network Filter | ○ | ✕ |
HTTP Filter | ○ | ○ |
gRPC な認可サービスが受け取る情報は、Protocol Buffers で定義されたメッセージである service.auth.v3.CheckRequest として定義されます。CheckRequest には、送信元の IP や X.509証明書、HTTPメソッドやHTTPヘッダーなど、受信リクエストのコンテキストが含まれています。そして、HTTP な認可サービスでは HTTP Filter での受信リクエストをそのまま受け取る仕様になっています。
認可サービスではこれらの情報を元に認可判定の処理を実装することになります。
ext_authz フィルターの設定例
ここからの設定例は、以下のように 127.0.0.1:10003
で稼働させている gRPC な認可サービスが Cluster として定義されている前提となります。
clusters:
- name: ext-authz
type: static
http2_protocol_options: {}
load_assignment:
cluster_name: ext-authz
endpoints:
- lb_endpoints:
- endpoint:
address:
socket_address:
address: 127.0.0.1
port_value: 10003
Network Filter
以下が設定例です。フィルターの設定(extensions.filters.network.ext_authz.v3.ExtAuthz)で認可サービスが停止している場合に受信リクエストを許可するか否かや、認可サービスへのリクエストに受信リクエストのX.509証明書を含めるかどうかなどの制御が可能です。
filters:
- name: envoy.filters.network.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.network.ext_authz.v3.ExtAuthz
stat_prefix: ext_authz
grpc_service:
envoy_grpc:
cluster_name: ext-authz
failure_mode_allow: false
include_peer_certificate: true
HTTP Filter
以下が設定例です。フィルターの設定(extensions.filters.http.ext_authz.v3.ExtAuthz)で Network Filter と同様の制御に加えて、前述の通り gRPC ではなく HTTP な認可サービスを指定できたり、認可サービスに受信リクエストのHTTPリクエストボディを送信するかどうかなど HTTP に関する制御が可能です。
http_filters:
- name: envoy.filters.http.ext_authz
typed_config:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthz
stat_prefix: ext_authz
grpc_service:
envoy_grpc:
cluster_name: ext-authz
# Only one of grpc_service, http_service may be set.
# http_service:
# ...
failure_mode_allow: false
include_peer_certificate: true
with_request_body:
max_request_bytes: 8192
allow_partial_message: true
HTTP Filter では HTTP connection manager のルーティング設定で、特定パスへのリクエストにおいて External Authorization を無効にすることもできます。以下は /static
プレフィックスを持つパスへのリクエストに対して External Authorization を無効にする設定例です。
route_config:
name: local_route
virtual_hosts:
- name: local_service
domains: ["*"]
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
check_settings:
context_extensions:
virtual_host: local_service
routes:
- match: { prefix: "/static" }
route: { cluster: some_service }
typed_per_filter_config:
envoy.filters.http.ext_authz:
"@type": type.googleapis.com/envoy.extensions.filters.http.ext_authz.v3.ExtAuthzPerRoute
disabled: true
- match: { prefix: "/" }
route: { cluster: some_service }
認可サービスの開発方法
gRPC サービスとして実装する場合
Authorization サービス が実装された gRPC サーバーを開発すれば完了です。1
// A generic interface for performing authorization check on incoming
// requests to a networked service.
service Authorization {
// Performs authorization check based on the attributes associated with the
// incoming request, and returns status `OK` or not `OK`.
rpc Check(CheckRequest) returns (CheckResponse) {
}
}
Check メソッド内で、service.auth.v3.CheckRequest に含まれる受信リクエストのコンテキストを元に認可処理を実行して、service.auth.v3.CheckResponse で判定結果をフィルターに返す必要があります。
CheckResponse では単純に判定結果を返すだけではなく、HTTP Filter での利用で NG 判定をした際の HTTP レスポンスをデフォルトの 403 Forbidden ではなく任意のものに変更することや、OK 判定をした際に後続のフィルターチェーンでの利用を想定して、受信リクエストの HTTP ヘッダーを 追加/更新/削除 することも可能になっています。
GitHub で gRPC な実装が OSS で公開してくれている方がいるので、開発時に参考にすると良いかと思います。
- https://github.com/salrashid123/envoy_external_authz
- https://github.com/nokamoto/envoy-external-authz
HTTP サービスとして実装する場合
extensions.filters.http.ext_authz.v3.HttpService に記載されているとおり、認可判定が OK なら 200 レスポンスを返し、NG なら 200 以外のレスポンスを返す HTTP サーバーを開発すれば完了です。
gRPC サービスとして実装する際には OK 判定の場合に CheckResponse で受信リクエストを加工することができましたが、HTTP サービスとして実装する場合には、先述の HttpService の設定オプションである extensions.filters.http.ext_authz.v3.AuthorizationResponse を利用することで HTTP ヘッダーを加工すること(追加/更新のみ)が可能です。
GitHub で HTTP な実装が OSS で公開してくれている方がいるので、開発時に参考にすると良いかと思います。
おわりに
今回は、Envoy での認可処理を外部のシステムに委譲することが可能になる External Authorization という機能を紹介しました。RBAC Filter など Envoy にビルトインされている認可機構ではなく、独自の認可機構を利用したい場合には External Authorization を活用すると良いでしょう。2 3
-
Open Policy Agent(OPA)を gRPC な認可サービスとして利用することが可能です。詳細は Z Lab Advent Calendar 2019 で公開した Envoy + SPIRE + OPA で Service Mesh を構築する を参照ください。 ↩
-
一例として Consul Connect で構築された Service Mesh では External Authorization を利用して独自の認可機構が提供されている。 ↩
-
Istio や Kuma のように xDS サーバーを配置して RBAC Filter を動的に設定することもアプローチとしては有用。 ↩