はじめに
この記事では Istio からワークロードのアクセス制御を行うための機能として提供されている Authorization Policy の仕組みや使い方を紹介していきます。
ここでいうアクセス制御というのはリクエスト元のワークロードのアクセス権限を確認した上でリクエストを 許可/拒否 する認可処理のことを指しています。
Istio における認可モデルの変遷
Istio 1.4 からアクセス制御を行うための認可モデルが Alpha 版として提供されていた RBAC Policy から、Beta 版としてシンプルさと柔軟性に重点を置いた Authorization Policy に変更されました。1
改善点としては RBAC Policy では ClusterRbacConfig
, ServiceRole
, ServiceRoleBinding
というカスタムリソースを定義する必要があったのが、Authorization Policy では AuthorizationPolicy
というカスタムリソースを定義するだけで済むようになり管理方法がシンプルになったことや、Ingress/Egress Gateway に対してポリシーが適用できるようになったことで Service Mesh 内だけではなく Service Mesh に出入りするトラフィックに対してもアクセス制御を行えるようになったことなどが挙げられています。2
なお、最新バージョン Istio 1.6 では istio/istio#22060 で RBAC Policy を廃止する作業が進められているため、RBAC Policy を利用していたユーザーが 1.6 にバージョンアップする際には Istio Blog で公開されている Introducing the Istio v1beta1 Authorization Policy を参考にして Authorization Policy への移行を行う必要があります。
Authorization Policy の仕組み
先述の通り Authorization Policy では AuthorizationPolicy
リソースでポリシーを定義することでワークロードのアクセス制御を行うことができます。また、アクセス制御のスコープを Service Mesh 全体、特定 Namespace、特定 Workload など設定によって柔軟に定義することが可能です。
アーキテクチャとしては Control Plane である istiod が AuthorizationPolicy
リソースで定義されたポリシーを Envoy の RBAC Filter の設定に変換3 して LDS 経由で Data Plane である istio-proxy(Envoy)に反映する仕組みとなっており、ポリシーのチェックは istio-proxy 内部で実行されるためパフォーマンスへの影響もありません。
引用: https://istio.io/latest/docs/concepts/security/#authorization-architecture
AuthorizationPolicy リソースについて
以下は AuthorizationPolicy
リソースの Spec についての説明を記載した図となります。
上記のポリシーは ec-system
ネームスペース内で app: backend
というラベルを持つワークロード(Pod)に対して、cluster.local/ns/ec-system/sa/web
という Workload Identity4 を持つワークロードから、8080
ポートの /data
エンドポイントを GET
するリクエストを ALLOW(許可)
するものとなります。
基本的にポリシーは同じネームスペース内のワークロードに対して適用されますが、以下のように Istio の Root Namespace
にポリシーを作成することで全てのネームスペース内のワークロードに対してポリシーを適用することができます。Root Namespace
は MeshConfig で設定可能となっていて、デフォルトでは istio-system
が使用されます。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: all-ns-policy
namespace: istio-system
spec:
...
action について
Authorization Policy ではリクエストを許可する(ALLOW
)ポリシーとリクエストを拒否する(DENY
)ポリシーがサポートされており、どちらのポリシーにするかを action で指定する仕様になっています。
# リクエストを許可するポリシー
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: allow-policy
namespace: foo
spec:
action: ALLOW
selector:
...
rules:
...
# リクエストを拒否するポリシー
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: deny-policy
namespace: foo
spec:
action: DENY
selector:
...
rules:
...
ワークロードに両方のポリシーにマッチした場合には DENY
ポリシーが先に評価される仕様なので注意してください。また、action のデフォルト値は ALLOW
のため未定義の場合には ALLOW
ポリシーとなりますが、わかりやすさの観点で明示的に定義することが推奨されています。
selector について
selector はポリシーを適用するワークロード(Pod)をラベルベースで指定するもので Gateway, Sidecar, EnvoyFilter の selector と同様の仕組みが利用されています。以下は foo
ネームスペース内の app: bar
というラベルを持つワークロードに適用されるポリシーの例となります。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: bar-policy
namespace: foo
spec:
action: ALLOW
selector:
matchLabels:
app: bar
rules:
...
selector を定義することでワークロードを指定できますが、以下のように selector が未定義の場合には同じネームスペース内の全てのワークロードにポリシーが適用される仕様です。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: foo-ns-policy
namespace: foo
spec:
action: ALLOW
rules:
...
rules について
rules では from
でリクエスト元を指定、to
でリクエスト内容を指定、when
でその他の条件を指定してポリシーのルールを定義することで、ルールにマッチしたリクエストに対して action で指定した判定を適用することができます。更に from
, to
, when
の文字列型のフィールドではワイルドカード(*) を利用したマッチもサポートされています。5
以下は cluster.local/ns/foo/sa/hoge
という Workload Identity4 を持つワークロードから 8080
ポートを GET
するリクエストに対してのルールを定義したポリシーとなります。
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: bar-policy
namespace: foo
spec:
action: ALLOW
selector:
matchLabels:
app: bar
rules:
- from:
- source:
principals: ["cluster.local/ns/foo/sa/hoge"]
to:
- operation:
methods: ["GET"]
when:
- key: destination.port
values: ["8080"]
from について
from では以下のような値を元にリクエスト元を指定することができます。6
- Principal(X.509 証明書に含まれる SPIFFE ID)
- JWT の iss と sub
- Namespace
- IP や CIDR
to について
to では以下のような値を元にリクエスト内容を指定することができます。
- ホスト名
- ポート
- HTTP メソッド
- URL パス
when について
when で以下のような値をその他の条件として指定することができます。6
- HTTP リクエストヘッダー
- Principal(X.509 証明書に含まれる SPIFFE ID)
- JWT クレーム
- Namespace
- IP や CIDR
from
や to
で指定可能な値と重複しているフィールドが多く存在しているので、ルールをわかりやすくするためにも when
でしか指定できない条件のみを定義するのが良いかもしれません。
チュートリアル
Istio 1.6.5 をインストールした Kuberntes クラスタを作成します。
kind create cluster --name istio-dev --image kindest/node:v1.18.0
istioctl manifest apply --set profile=demo
セットアップが完了したかどうかを確認します。
$ istioctl version
client version: 1.6.5
control plane version: 1.6.5
data plane version: 1.6.5 (3 proxies)
サンプルアプリとして Bookinfo をデプロイします。
kubectl create ns bookinfo
kubectl label namespace bookinfo istio-injection=enabled
kubectl apply -n bookinfo -f https://raw.githubusercontent.com/istio/istio/1.6.5/samples/bookinfo/platform/kube/bookinfo.yaml
あとは Authorization for HTTP traffic を実施するだけです。チュートリアルの中で AuthorizationPolicy
リソースの Spec を変更してみたり、ポリシーの適用後に Envoy に設定された RBAC Filter を Admin Interface から参照してみたりすると理解が深まるかもしれません。
さいごに
今回は Istio からワークロードのアクセス制御を行うための機能として提供されている Authorization Policy の仕組みや使い方を紹介しました。認可モデルの変遷でも言及したとおり、以前の RBAC Policy と比べて Authorization Policy はシンプルで使いやすいモデルになっています。
TCP トラフィックに対するアクセス制御、HTTP トラフィックに対するアクセス制御、Ingress Gateway 上でのアクセス制御など、各ユースケースに応じた Authorization Policy の利用方法が 公式ドキュメント で提供されているので、導入を検討している方はそちらを参考に作業を進めていくのが良いかと思います。
Authorization Policy がこのまま GA になるのか、servicemeshinterface/smi-adapter-istio を利用して SMI ベースでアクセス制御を行うのが推奨されていくのか、など気になることもあるので、引き続き Istio の Workload Identity とアクセス制御周りの動向に注目していきたいと思います。
参考資料
- https://istiobyexample-ja.github.io/istiobyexample/authorization/
- https://istio.io/latest/docs/concepts/security/#authorization-architecture
-
Authorization Policy での改善点とデザインゴールで Istio Configuration Model について言及されていたが肝心の ドキュメント は非公開となっているのでこちらでは割愛。 ↩
-
Istio では SPIFFE がサポートされており mTLS が有効になっていると全てのワークロードに Workload Identity となる SPIFFE ID(
spiffe://<Trust Domain>/ns/<Namespace>/sa/<Service Account>
)が SAN URI にセットされた X.509 証明書が配布される。Istio では SPIFFE ID をワークロードの Namespace と Service Account から構成するため、foo
という Namespace にbar
という Service Account を使用してデプロイされた Pod にはspiffe://cluster.local/ns/foo/sa/bar
という SPIFFE ID を含む X.509 証明書が配布される仕様となっている。 ↩ ↩2 -
マッチ方法として Exact, Prefix, Suffix, Presence がサポートされている。詳細は こちら を参照 ↩
-
指定可能なフィールドの中には Principal や Namespace など mTLS 有効化が必須なものがあります。Security の観点から mTLS による相互認証とワークロード間の通信の暗号化はとても重要なので、それらのフィールドを使わない要件だったとしても基本的には mTLS を有効化した上でアクセス制御を行うのが良いかと思います。 ↩ ↩2