Service Mesh (ASM) は Open Policy Agent (OPA) プラグインと統合されています。 OPA を使用してアクセス制御ポリシーを定義し、アプリケーションにきめ細かいアクセス制御を実装できます。 ASM では、コントロールプレーンで OPA ポリシーを定義し、そのポリシーをデータプレーンのクラスターにプッシュできます。 このトピックでは、ASM で OPA ポリシーを定義して、アプリケーションにきめ細かいアクセス制御を実装する方法について説明します。 たとえば、リクエスト URL やリクエストヘッダーのトークンに基づいてリクエストを許可またはブロックする OPA ポリシーを定義できます。 また、このトピックでは、OPA ポリシーを使用してアクセス制御を実装するサンプルシナリオも提供します。
前提条件
バージョン 1.9.7 以降の ASM インスタンスが作成されていること。 詳細については、「ASM インスタンスの作成」をご参照ください。
クラスターが ASM インスタンスに追加されていること。 詳細については、「ASM インスタンスへのクラスターの追加」をご参照ください。
デフォルトの名前空間で自動サイドカープロキシインジェクションが有効になっていること。 詳細については、「グローバル名前空間の管理」トピックの「自動サイドカープロキシインジェクションの有効化」セクションをご参照ください。
背景情報
OPA は Cloud Native Computing Foundation (CNCF) の卒業プロジェクトです。 ポリシーエンジンとして、OPA を使用してアプリケーションにきめ細かいアクセス制御を実装できます。 OPA は、マイクロサービスとともにスタンドアロンサービスとしてデプロイできます。 アプリケーションを保護するには、アプリケーションのマイクロサービスへの各リクエストが処理される前に承認されていることを確認します。 承認を確認するために、マイクロサービスは OPA に API 呼び出しを行い、リクエストが承認されているかどうかを確認します。
ステップ 1:OPA プラグインと OPA のインジェクション範囲を制御する機能を有効にする
ASM コンソール にログインします。 左側のナビゲーションペインで、 を選択します。
[メッシュ管理] ページで、ASM インスタンスの名前をクリックします。 左側のナビゲーションペインで、 を選択します。
[OPA ポリシー] ページで、[open Policy Agent (OPA) プラグインを有効にする] と [OPA インジェクション範囲制御を有効にする] を選択し、[OPA を有効にする] をクリックします。 [注記] メッセージで、[OK] をクリックします。
手順 2:OPA ポリシーを作成する
ASM インスタンスのコントロールプレーンで OPA ポリシーを作成すると、OPA ポリシーはデータプレーンのクラスターにプッシュされます。 その後、クラスターの Pod 内の OPA は、OPA ポリシーを使用してきめ細かいアクセス制御を実装します。 次の方法を使用して OPA ポリシーを作成できます。
方法 1: ASM コンソールで OPA ポリシーを作成する
ASM コンソール にログインします。 左側のナビゲーションペインで、 を選択します。
[メッシュ管理] ページで、ASM インスタンスの名前をクリックします。 左側のナビゲーションペインで、 を選択します。
[OPA ポリシー] ページで、[作成] をクリックし、[名前空間] ドロップダウンリストから default を選択し、[名前] を bookinfo-opa に設定し、[一致ラベルを追加] をクリックします。 [一致ラベル] セクションで、[名前] を version に、[値] を v1 に設定します。 次の内容を Rego ルールコードエディターにコピーし、[作成] をクリックします。
方法 2: kubectl を使用して OPA ポリシーを作成する
次の内容を含む opa.yaml ファイルを作成します。
説明Pod の OPA ポリシーを定義する場合は、1 つの OPA ポリシーにのみ
default allowフィールドが含まれていることを確認してください。 複数の OPA ポリシーが Pod に適用され、各 OPA ポリシーにdefault allowフィールドが定義されている場合、複数のdefault allowフィールドが原因で、OPA ポリシーは動的に更新されません。OPA ポリシーを定義する際は、ラベルを使用して OPA ポリシーの有効範囲を指定することをお勧めします。 無効な Rego ポリシーは、サービスにアクセスできなくします。
OPA はビジネスコンテナと同じ Pod に存在し、ポート 15081 と 9191 を使用します。
デフォルトでは、OPA ポリシーの
default allowフィールドは false に設定されています。default allowフィールドを再度設定しないでください。 そうしないと、競合が発生します。 次の表に、opa.yaml ファイルのパラメーターをいくつか示します。
パラメーター
説明
specRego で記述されたポリシーの内容。 Rego 構文の詳細については、「ポリシー言語」をご参照ください。
workloadSelector指定された名前空間における OPA ポリシーの有効範囲。 デフォルトでは、OPA ポリシーは名前空間内のすべての Pod に適用されます。 このパラメーターを設定すると、OPA ポリシーは指定されたラベルを持つ Pod にのみ適用されます。
user_rolesユーザーに割り当てられたロール。 この例では、guest ロールは guest1 ユーザーに割り当てられ、admin ロールは admin1 ユーザーに割り当てられます。
role_perms各ロールの権限。 この例では、guest ロールには /productpage を含む URL を使用してアプリケーションにアクセスする権限が付与され、admin ロールには /productpage または /api/v1/products を含む URL を使用してアプリケーションにアクセスする権限が付与されます。
次のコマンドを実行して、OPA ポリシーを作成します。
kubectl を使用して ASM インスタンスに接続する方法の詳細については、「コントロールプレーンで kubectl を使用して Istio リソースにアクセスする」をご参照ください。
kubectl apply -f opa.yaml
ステップ 3: OPA を挿入する
ASMインスタンスにサンプル アプリケーション Bookinfo をデプロイし、OPAが Bookinfo アプリケーションの各ポッドに挿入されているかどうかを確認します。
ASM インスタンスに Bookinfo アプリケーションをデプロイします。詳細については、「ASM インスタンスへのアプリケーションのデプロイ」をご参照ください。
イングレスゲートウェイ、Istio ゲートウェイ、および仮想サービスを作成します。詳細については、「Istio リソースを使用してサービスの異なるバージョンにトラフィックをルーティングする」をご参照ください。
Bookinfo アプリケーションの各アプリケーションのポッドに OPA が挿入されているかどうかを確認します。
ACK コンソール にログインします。左側のナビゲーションペインで、[クラスター] をクリックします。
[クラスター] ページで、管理するクラスターを見つけ、その名前をクリックします。左側のペインで、 を選択します。
[ポッド] ページで、既定[名前空間] ドロップダウンリストから を選択し、目的のアプリケーションのポッド名をクリックします。
[コンテナー] タブで、istio-proxy という名前のサイドカープロキシと opa-istio という名前の OPA が各コンテナーに挿入されていることがわかります。各アプリケーションのコンテナーを順番にチェックして、サイドカープロキシと OPA が各コンテナーに挿入されていることを確認します。
手順 4:OPA ポリシーによってアクセス制御が想定どおりに実装されていることを確認する
/productpage を含む URL を使用してアプリケーションにアクセスするには、次のコマンドを実行します。
curl -X GET http://<IP address of the ingress gateway>/productpage --user guest1:password -I予期される出力:
HTTP/1.1 200 OK/api/v1/productsを含む URL を使用してアプリケーションにアクセスするには、次のコマンドを実行します。curl -X GET http://<IP address of the ingress gateway>/api/v1/products --user guest1:password -I予期される出力:
HTTP/1.1 403 Forbidden予期される結果は、guest ロールが guest1 ユーザーに割り当てられており、guest1 ユーザーは
/productpageを含む URL を使用してアプリケーションにアクセスする権限を持っているが、/api/v1/productsを含む URL を使用してはアクセスできないことを示しています。/productpageを含む URL を使用してアプリケーションにアクセスするには、次のコマンドを実行します。curl -X GET http://{{IP address of the ingress gateway}}/productpage --user admin1:password -I予期される出力:
HTTP/1.1 200 OK/api/v1/productsを含む URL を使用してアプリケーションにアクセスするには、次のコマンドを実行します。curl -X GET http://<IP address of the ingress gateway>/api/v1/products --user admin1:password -I予期される出力:
HTTP/1.1 200 OK予期される結果は、admin ロールが admin1 ユーザーに割り当てられており、admin1 ユーザーは
/productpageまたは/api/v1/productsを含む URL を使用してアプリケーションにアクセスする権限を持っていることを示しています。上記の結果は、定義された OPA ポリシーが期待どおりにアクセス制御を実装していることを示しています。
手順 5:OPA ポリシーを動的に更新する
次のコマンドを実行して、OPA ポリシーの編集 UI に移動します。
kubectl edit asmopapolicy bookinfo-opa -n default コマンド出力で、OPA ポリシーを編集して、guest1 ユーザーに guest ロールと admin ロールの両方を割り当てます。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: bookinfo-opa
namespace: default
spec:
policy: |
package istio.authz
import input.attributes.request.http as http_request
allow {
// ユーザーのロール
roles_for_user[r]
// 必要なロール
required_roles[r]
}
roles_for_user[r] {
// ユーザー名からロールを取得
r := user_roles[user_name][_]
}
required_roles[r] {
// ロールから権限を取得
perm := role_perms[r][_]
// メソッドとパスが一致するか確認
perm.method = http_request.method
perm.path = http_request.path
}
user_name = parsed {
// Authorization ヘッダーからユーザー名を取得
[_, encoded] := split(http_request.headers.authorization, " ")
[parsed, _] := split(base64url.decode(encoded), ":")
}
user_roles = {
// ユーザーとロールのマッピング
"guest1": ["guest", "admin"],
"admin1": ["admin"]
}
role_perms = {
// ロールと権限のマッピング
"guest": [
{"method": "GET", "path": "/productpage"},
],
"admin": [
{"method": "GET", "path": "/productpage"},
{"method": "GET", "path": "/api/v1/products"},
],
}user_roles: ユーザーに割り当てられたロール。この例では、guestロールとadminロールがguest1ユーザーに割り当てられ、adminロールがadmin1ユーザーに割り当てられています。
role_perms: 各ロールの権限。この例では、guestロールには /productpage を含む URL を使用してアプリケーションにアクセスする権限が付与され、adminロールには /productpage または /api/v1/products を含む URL を使用してアプリケーションにアクセスする権限が付与されます。
手順 6:OPA ポリシーが動的に更新されることを確認する
/productpage を含む URL を使用してアプリケーションにアクセスするために、次のコマンドを実行します。
curl -X GET http://<IP address of the ingress gateway>/productpage --user guest1:password -I期待される出力:
HTTP/1.1 200 OK/api/v1/products を含む URL を使用してアプリケーションにアクセスするために、次のコマンドを実行します。
curl -X GET http://<IP address of the ingress gateway>/api/v1/products --user guest1:password -I期待される出力:
HTTP/1.1 200 OKOPA ポリシーが更新される前は、guest1 ユーザーは /productpage を含む URL を使用してアプリケーションにアクセスできますが、/api/v1/products を含む URL を使用することはできません。 OPA ポリシーが更新された後、guest1 ユーザーは /productpage または /api/v1/products を含む URL を使用してアプリケーションにアクセスできます。 これらの結果は、OPA ポリシーが動的に更新されたことを示しています。
サンプルシナリオ
シナリオ 1:JWT を確認してリクエストを認証する
アプリケーションがリクエストを受信すると、OPA はリクエストのヘッダーにある JSON Web Token(JWT)を確認します。アプリケーションへのアクセス要求は、JWT が指定された要件を満たしている場合にのみ許可されます。
次の OPA ポリシーは、リクエストが GET メソッドを使用し、リクエストに含まれる JWT の Role フィールドが guest に設定され、userGroup フィールドが visitor に設定されている場合にのみ、Productpage アプリケーションへのアクセス要求が許可されることを定義しています。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-jwt
namespace: default
spec:
policy: |
package istio.authz
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
// 証明書 'B41BD5F462719C6D6118E673A2389' を設定します
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == "guest"
claims.userGroup == "visitor"
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}input.attributes.request.http.method: リクエストメソッド。この例では、値は
GETに設定されています。input.parsed_path[0]: アクセスされるアプリケーション。
claims.Role: JWT の
Roleフィールドの期待値。この例では、値はguestに設定されています。claims.userGroup: JWT の
userGroupフィールドの期待値。この例では、値はvisitorに設定されています。
JWT ツールを使用して、Role フィールドや userGroup フィールドなどのリクエスト情報を JWT 文字列にエンコードできます。
Productpage アプリケーションにアクセスするには、次のコマンドを実行します。
curl --location --request GET 'http://{イングレスゲートウェイの IP アドレス}/productpage' \
--header 'Authorization: Bearer
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3Vlc3QxIiwiUm9sZSI6Imd1ZXN0IiwidXNlckdyb3VwIjoidmlzaXRvciJ9.44OnUFZwOzSWzC7hyVfcle-uYk8byv7q_BBxS10AEWc'期待される出力:
200ステータスコード 200 が返されます。これは、Role フィールドが guest に設定され、userGroup フィールドが visitor に設定された JWT を含む GET リクエストを使用して Productpage アプリケーションにアクセスできることを示します。無効な JWT を使用した場合、またはリクエストに JWT が含まれていない場合は、エラーコード 403 が返されます。これは、Productpage アプリケーションへのアクセス要求が拒否されたことを示します。
シナリオ 2:HTTP リクエストボディと JWT を確認してリクエストを認証する
OPA ポリシーを使用して、リクエストボディの username パラメーターの値がリクエストの JWT の Role フィールドの値と同じである場合にのみ、HTTP リクエストを許可できます。
次の OPA ポリシーは、リクエストが GET メソッドを使用し、リクエストボディの username パラメーターの値がリクエストの JWT の Role フィールドの値と同じであり、JWT の userGroup フィールドが manager に設定されている場合にのみ、Productpage アプリケーションへのアクセス要求が許可されることを定義しています。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-body
namespace: default
spec:
policy: |
package istio.authz
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == input.parsed_body.username
claims.userGroup == "manager"
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}input.attributes.request.http.method: リクエストメソッド。この例では、値は
GETに設定されています。input.parsed_path[0]: アクセスされるアプリケーション。
claims.Role: JWT の Role フィールドの期待値。この例では、値は
input.parsed_body.usernameに設定されています。これは、リクエストボディのusernameパラメーターの値がリクエストの JWT の Role フィールドの値と同じである必要があることを示します。claims.userGroup: JWT の
userGroupフィールドの期待値。この例では、値はmanagerに設定されています。
JWT ツールを使用して、Role フィールドや userGroup フィールドなどのリクエスト情報を JWT 文字列にエンコードできます。
Productpage アプリケーションにアクセスするには、次のコマンドを実行します。
curl --location --request GET 'http://{イングレスゲートウェイの IP アドレス}/productpage' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiZ3Vlc3QxIiwiUm9sZSI6ImFkbWluIiwidXNlckdyb3VwIjoibWFuYWdlciJ9.pAUvTeONHF-i5Ps-EUYYXk-hnaz-j-ZgP_wXJZMBiR0' \
--header 'Content-Type: application/json' \
--header 'Cookie: session=eyJ1c2VyIjoiYWRtaW4ifQ.YRz90g.GT34_5BqlFTwGqabZk_qGZzxYQ0' \
--data-raw '{
"username":"admin",
"password":"12****"
期待される出力:
200ステータスコード 200 が返されます。これは、Role フィールドの値がリクエストボディの username パラメーターの値と同じであり、userGroup フィールドが manager に設定された JWT を含む GET リクエストを使用して Productpage アプリケーションにアクセスできることを示します。無効な JWT を使用した場合、またはリクエストに JWT が含まれていない場合は、エラーコード 403 が返されます。これは、Productpage アプリケーションへのアクセス要求が拒否されたことを示します。
シナリオ 3:より多くのコンテキスト情報を確認してリクエストを認証する
シナリオ 2 で確認された情報に加えて、OPA ポリシーを使用してより多くのコンテキスト情報を確認できます。この例では、JWT の username フィールドの値は、bookinfo_managers フィールドで指定された値の範囲内にある必要があります。
次の OPA ポリシーは、リクエストが GET メソッドを使用し、リクエストボディの username パラメーターの値がリクエストの JWT の Role フィールドの値と同じであり、JWT の username フィールドの値が bookinfo_managers フィールドで指定された値の範囲内であり、JWT の userGroup フィールドが manager に設定されている場合にのみ、Productpage アプリケーションへのアクセス要求が許可されることを定義しています。
apiVersion: istio.alibabacloud.com/v1beta1
kind: ASMOPAPolicy
metadata:
name: policy-range
namespace: default
spec:
policy: |
package istio.authz
bookinfo_managers = [{"name": "user1"}, {"name": "user2"}, {"name": "user3"}] // bookinfo_managers を定義します
allow {
input.attributes.request.http.method == "GET"
input.parsed_path[0] == "productpage"
io.jwt.verify_hs256(bearer_token, "B41BD5F462719C6D6118E673A2389")
claims.Role == input.parsed_body.username
claims.userGroup == "manager"
claims.username == bookinfo_managers[_].name
}
claims := payload {
[_, payload, _] := io.jwt.decode(bearer_token)
}
bearer_token := t {
v := input.attributes.request.http.headers.authorization
startswith(v, "Bearer ")
t := substring(v, count("Bearer "), -1)
}input.attributes.request.http.method: リクエストメソッド。この例では、値は
GETに設定されています。input.parsed_path[0]: アクセスされるアプリケーション。
claims.Role: JWT の Role フィールドの期待値。この例では、値は
input.parsed_body.usernameに設定されています。これは、リクエストボディの username パラメーターの値がリクエストの JWT の Role フィールドの値と同じである必要があることを示します。claims.userGroup: JWT の
userGroupフィールドの期待値。この例では、値はmanagerに設定されています。claims.username: JWT の
usernameフィールドの期待値。この例では、値はbookinfo_managers[_].nameに設定されています。これは、JWT の username フィールドの値がbookinfo_managersフィールドで指定された値の範囲内にある必要があることを示します。
JWT ツールを使用して、リクエスト情報を JWT 文字列にエンコードできます。
Productpage アプリケーションにアクセスするには、次のコマンドを実行します。
curl --location --request GET 'http://{イングレスゲートウェイの IP アドレス}/productpage' \
--header 'Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIxIiwiUm9sZSI6ImFkbWluIiwidXNlckdyb3VwIjoibWFuYWdlciJ9.2X0Fmb96jBexLcVm_55t8ZY6XveSxUAsQ1j3ar5dI_g' \
--header 'Content-Type: application/json' \
--header 'Cookie: session=eyJ1c2VyIjoiYWRtaW4ifQ.YRz90g.GT34_5BqlFTwGqabZk_qGZzxYQ0' \
--data-raw '{
"username":"admin",
"password":"12****"
}'期待される出力:
200Role フィールドの値がリクエストボディの username パラメーターの値と同じであり、JWT の username フィールドの値が bookinfo_managers フィールドで指定された値の範囲内であり、userGroup フィールドが manager に設定された JWT を含む GET リクエストを使用して、Productpage アプリケーションにアクセスできることを示します。無効な JWT を使用した場合、またはリクエストに JWT が含まれていない場合は、エラーコード 403 が返されます。これは、Productpage アプリケーションへのアクセス要求が拒否されたことを示します。
FAQ
ポッドがOPAポリシーを使用しているかどうかを確認するにはどうすればよいですか?
OPAは、ビジネスコンテナと同じポッドにサイドカーモードでデプロイされます。 ポッドがOPAポリシーを使用しているかどうかを確認するには、ポッドに接続して次のコマンドを実行します。
curl 127.0.0.1:15081/v1/policiesRegoで記述されたポリシーを確認するにはどうすればよいですか?
OPAの公式Webサイトには、Regoで記述されたポリシーを確認するために使用できるオンラインツールが用意されています。
参考資料
ConfigMap を使用して OPA ポリシーを定義する場合は、次のトピックをご覧ください。