DUICUO

API GatewayとOPAを使用してRBACを実装する方法

現在、適切なユーザーが適切なリソースにアクセスできるようにするには、システムに適切なアクセス制御方式を適用する必要があります。しかし、様々な既知の実装モデルのバックエンドサービス向けにAPI認可システムを構築することは、しばしば大きな課題となります。この記事では、オープンソースAPIゲートウェイであるApache APISIX( https://apisix.apache.org/とOpen Policy Agent(OPA、 https://www.openpolicyagent.org/docs/latest/ )を使用して、APIにロールベースアクセス制御(RBAC)認可モデルを適用する方法について説明します。

RBAC とは何ですか?

ロールベースアクセス制御(RBAC、https://en.wikipedia.org/wiki/Role-based_access_control)と属性ベースアクセス制御(ABAC、https://en.wikipedia.org/wiki/Attribute-based_access_control)は、コンピュータシステム内のリソースへの権限とアクセスを管理するために最も一般的に使用される2つのアクセス制御モデルです。通常、RBACは組織内の役割、機能、責任に基づいてユーザーに権限を割り当てます。

つまり、RBACでは、ユーザーの機能や責任に基づいてロールが定義され、対応する権限がこれらのロールに割り当てられます。もちろん、実際の運用では、ユーザーに1つ以上のロールを割り当て、それらのロールに関連付けられた権限を継承させることがよくあります。たとえば、APIのコンテキストでは、開発者ロールにはAPIリソースの作成と更新の権限が与えられますが、エンドユーザーロールにはAPIリソースの読み取りまたは実行の権限しか与えられません。さらに、RBACでは、ユーザーに割り当てられたロール、ユーザーが実行を許可されている操作、それらの操作を実行するために必要なリソースなど、複数の要素の組み合わせによってポリシーが定義されます。RBACがユーザーロールに基づいて権限を割り当てるのに対し、ABACはユーザーとリソースに関連付けられた属性に基づいて権限を割り当てます。

OPAとは何ですか?

OPAは、ポリシーエンジンとツールスイートとして、分散システム全体にわたるポリシー適用のための統一的なアプローチを提供します。開発者は、単一のエンドポイントからポリシーを一元的に定義、管理、実装できます。ポリシーをコードとして定義することで、ポリシーの確認、編集、ロールバックを容易にし、効率的なポリシー管理を実現します。

上の図に示すように、OPAはRego(https://www.openpolicyagent.org/docs/latest/policy-language/)と呼ばれる宣言型言語を提供しています。これにより、テクノロジースタック全体にわたって様々なポリシーを作成・実装できます。OPAにポリシー決定を要求すると、OPAはファイルで指定されたルールとデータを使用してクエリを評価し、応答を生成し、クエリ結果をポリシー決定として返します。OPAは必要なポリシーとデータをすべて内部キャッシュに保存するため、結果を迅速に返すことができます。以下はOPA Regoファイルの簡単な例です。

 package example default allow = false allow { input.method == "GET" input.path =="/api/resource" input.user.role == "admin" }

上記のコードスニペットに示すように、「example」というパッケージには「allow」というルールが定義されています。このルールは、入力メソッドが「GET」、リクエストされたパスが/api/resource、ユーザーロールが「admin」の場合にリクエストを許可することを指定します。つまり、これらの条件がすべて満たされている場合、「allow」ルールは「true」と評価され、リクエストが続行されます。

OPA と API ゲートウェイを RBAC に使用できるのはなぜですか?

APIゲートウェイは、APIとそのユーザーを一元的に設定・管理するための場所を提供します。集中型の認証ゲートウェイであるため、個々のサービスが独自の認証ロジックを内部的に実装する必要がなくなります。一方、OPAは、認可のための独立した認可レイヤーを作成することで、ポリシーとコードを分離します。この組み合わせにより、APIリソースへの権限をロールに追加し、各ユーザーロールに対して、URIパスで定義されたRBACリソースに対する一連の権限(GET、PUT、DELETE)を定義できます。次のセクションでは、両方を使用してRBACを実装する方法を学びます。

OPAとApache APISIXを使用してRBACを実装する方法

Apache APISIXでは、ルート(https://apisix.apache.org/docs/apisix/terminology/route/)とプラグイン(https://apisix.apache.org/docs/apisix/terminology/plugin/)を設定することで、APIの動作を定義できます。具体的には、APISIXのOPAプラグイン(https://apisix.apache.org/docs/apisix/plugins/opa/)を使用して、リクエストをOPAに転送し、OPAで承認を行うことで、RBACポリシーを適用できます。つまり、OPAはユーザーのロールと権限に基づいて、リアルタイムで認可の判断を行います。

イベントのセッション、トピック、講演者情報を取得/編集できる Conference API (https://conferenceapi.azurewebsites.net/) があるとします。承認の観点から、講演者は自身のセッションとトピックの参照のみが可能で、管理者はセッションとトピックの追加/編集が可能です。さらに、参加者は講演者のセッションに関するフィードバックを、パス `/speaker/speakerId/session/feedback` への POST リクエストで送信できます。講演者は、同じ URI に対して GET リクエストを送信することでのみ、このフィードバックを参照できます。次の図は、このシナリオ全体を示しています。

1. API ユーザーは、認証情報 (Authorization ヘッダー内の JWT トークンなど、https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization) を使用して、API ゲートウェイ上のルートをリクエストします。

2. API ゲートウェイは、JWT ヘッダーを含むユーザー データを OPA エンジンに送信します。

3. OPA は、.rego ファイルで指定されたポリシー (ロールや権限など) を使用して、ユーザーがリソースにアクセスする権限を持っているかどうかを評価します。

4. OPA が「許可」を決定した場合、要求は上流の会議サービスに転送されます。

次に、APISIX をインストールして構成し、OPA でさまざまなポリシーを定義します。

前提条件

  • コンテナ化された etcd と APISIX をインストールするには、Docker (https://docs.docker.com/get-docker/) を使用します。
  • APISIX Admin APIへのリクエスト送信にはCurl(https://curl.se/)を使用します。また、Postman(https://www.postman.com/)などのツールを使用してAPIを操作することもできます。

ステップ1: Apache APISIXをインストールする

APISIX は次のスクリプトを使用して簡単にインストールし、すぐに起動できます。

 curl -sL https://run.api7.ai/apisix/quickstart | sh

ステップ2: バックエンドサービス(アップストリーム)を構成する

Conference APIのバックエンドサービスにリクエストをルーティングするには、Admin API (https://apisix.apache.org/docs/apisix/admin-api/) 経由でApache APISIXにアップストリームサーバーを追加して設定する必要があります。以下のコードをご覧ください。

 curl http://127.0.0.1:9180/apisix/admin/upstreams/1 -X PUT -d ' { "name":"Conferences API upstream", "desc":"Register Conferences API as the upstream", "type":"roundrobin", "scheme":"https", "nodes":{ "conferenceapi.azurewebsites.net:443":1 } }'

ステップ3: APIユーザーを作成する

次に、 Apache APISIXユーザー名Jackを使用してユーザー(つまり、新しいスピーカー)を作成し、指定されたキーを使用してjwt-auth ( https://apisix.apache.org/docs/apisix/plugins/jwt-auth/ ) プラグインを設定します。これにより、ユーザーはJSON Web Token ( JWT https://jwt.io/ ) を使用して認証できるようになります。以下のコードをご覧ください。

 curl http://127.0.0.1:9180/apisix/admin/consumers -X PUT -d ' { "username": "jack", "plugins": { "jwt-auth": { "key": "user-key", "secret": "my-secret-key" } } }'

ステップ4: JWTトークンを生成するためのパブリックエンドポイントを作成する

また、トークンの生成と発行にpublic-api ( https://apisix.apache.org/docs/apisix/plugins/public-api/ ) プラグインを使用する新しいルートを設定する必要があります。この場合、 API Gateway はIDプロバイダー サーバーとして機能し、ユーザーJackのキーによって作成および検証されたトークンを検証します。もちろん、ID プロバイダーには、 Google ( https://developers.google.com/identity )、Okta (https://www.okta.com/)、Keycloak ( https://www.keycloak.org/ )、 Ory Hydra ( https://www.ory.sh/hydra/ )などのサードパーティ サービスを使用できます。次のコードを参照してください。

 curl http://127.0.0.1:9180/apisix/admin/routes/jas -X PUT -d ' { "uri": "/apisix/plugin/jwt/sign", "plugins": { "public-api": {} } }'

ステップ5: APIユーザーの新しいJWTトークンをリクエストする

これで、作成したパブリックエンドポイントを使用して、APIゲートウェイからJackの新しいトークンを取得できます。次のcurlコマンドは、Jackの認証情報を使用して新しいトークンを生成し、ペイロードでロールと権限を割り当てます。

curl -G --data-urlencode 'payload={"role":"speaker","permission":"read"}' http://127.0.0.1:9080/apisix/plugin/jwt/sign?key=user-key -i

上記のコマンドを実行すると、新しいトークンがレスポンスとして返されます。このトークンは、後で新しいAPIゲートウェイエンドポイントにアクセスするために使用できるように、とりあえずどこかに保存しておきます。

ステップ6: 新しいプラグイン設定を作成する

このステップでは、3つのAPISIXプラグイン(proxy-rewrite( https://apisix.apache.org/docs/apisix/plugins/proxy-rewrite/ )、jwt-auth https://apisix.apache.org/docs/apisix/plugins/jwt-auth/ )、 OPA https://apisix.apache.org/docs/apisix/plugins/opa/ ))を設定します。以下のコードをご覧ください。

 curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PUT -d ' { "plugins":{ "jwt-auth":{ }, "proxy-rewrite":{ "host":"conferenceapi.azurewebsites.net" } } }'
  • プロキシ書き換えプラグインは、すべてのリクエストを conferenceapi.azurewebsites.net ホストにプロキシするように構成されています。
  • OPA認証プラグインは、 http://localhost:8181/v1/data/rbacExampleで実行されているOPAポリシーエンジンを使用するように設定されています。さらに、APISIXはユーザー関連のすべての情報をOPAに送信します。OPA設定セクションに.regoファイルを追加する必要があります。

ステップ7: 会議セッションのルートを作成する

最後のステップは、Conferences API のスピーカー セッション用の新しいルートを作成することです。

 curl "http://127.0.0.1:9180/apisix/admin/routes/1" -X PUT -d ' { "name":"Conferences API speaker sessions route", "desc":"Create a new route in APISIX for the Conferences API speaker sessions", "methods": ["GET", "POST"], "uris": ["/speaker/*/topics","/speaker/*/sessions"], "upstream_id":"1", "plugin_config_id":1 }'

ペイロードには、名前、説明、メソッド、URI、アップストリームID、プラグイン設定IDなどのルーティング情報が含まれます。この例では、ルートは2つの異なるURI(/speaker/topicsと/speaker/sessions)に対するGETリクエストとPOSTリクエストを処理するように設定されています。「upstream_id」フィールドは、このルートからのリクエストを処理するアップストリームサービスのIDを指定し、「plugin_config_id」フィールドは、このルートで使用されるプラグイン設定のIDを指定します。

ステップ8: OPAなしでセットアップをテストする

これまで、APISIXを設定して、受信したリクエストをConference APIの様々なエンドポイントに誘導し、承認されたAPIユーザーのみがアクセスできるようにしてきました。そのため、APIユーザーはエンドポイントにアクセスするたびに、Conferenceバックエンドサービスからデータを取得するためのJWTトークンを提供する必要があります。エンドポイントをクリックすると、このトークンが認証されていることを確認できます。ここでリクエストしているドメインアドレスは、Conferenceサービス自体ではなく、カスタムAPIゲートウェイです。

 curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'

ステップ9: OPAサービスを実行する

次に、Dockerを使用してOPAサービスを実行し、そのAPIを使用してポリシー定義をアップロードします。このAPIは、受信リクエストごとに認可ポリシーを評価するために使用できます。

 docker run -d --network=apisix-quickstart-net --name opa -p 8181:8181 openpolicyagent/opa:latest run -s

上記のDockerコマンドは、最新バージョンのOPAイメージを使用してコンテナを起動します。既存のAPISIXネットワークapisix-quickstart-net上にOPAという名前の新しいコンテナを作成し、ポート8181を公開します。そのため、APISIXは[http://opa: 8181 ]というアドレスを使用して、OPAに直接ポリシー検査リクエストを送信できます。OPAとAPISIXは同じDockerネットワーク上で実行されている必要があることに注意してください。

ステップ10: 戦略の定義と登録

OPA側の次のステップは、APIリソースへのアクセスを制御するためのポリシーを定義することです。これらのポリシーでは、アクセスに必要な属性(例:どのユーザーがどのロールを持っているか)と、それらの属性に基づいて許可または拒否される権限(例:どのロールがどの権限を持っているか)を定義する必要があります。例えば、以下の設定では、OPAに「user_roles」テーブルをチェックして「Jack」というロールを見つけるように指示しています。この情報は、APISIXによって内部的に「input.consumer.username」によって送信されます。次に、JWTペイロードを読み取り、「token.payload.permission」を抽出することで、ユーザーの権限を検証します。以下のコメントでこれらの手順を明確に説明しています。

 curl -X PUT '127.0.0.1:8181/v1/policies/rbacExample' \ -H 'Content-Type: text/plain' \ -d 'package rbacExample # Assigning user rolesuser_roles := { "jack": ["speaker"], "bobur":["admin"] } # Role permission assignments role_permissions := { "speaker": [{"permission": "read"}], "admin": [{"permission": "read"}, {"permission": "write"}] } # Helper JWT Functions bearer_token := t { t := input.request.headers.authorization } # Decode the authorization token to get a role and permission token = {"payload": payload} { [_, payload, _] := io.jwt.decode(bearer_token) } # Logic that implements RBAC default allow = falseallow { # Lookup the list of roles for the user roles := user_roles[input.consumer.username] # For each role in that list r := roles[_] # Lookup the permissions list for role r permissions := role_permissions[r] # For each permission p := permissions[_] # Check if the permission granted to r matches the users request p == {"permission": token.payload.permission} }'

ステップ11: OPAプラグインを使用して既存のプラグイン構成を更新する

OPAサービスでポリシーを定義したら、ルートがOPAプラグインを使用できるように、既存のプラグイン設定を更新する必要があります。以下のコードスニペットに示すように、OPAプラグインの`policy`プロパティでこれを指定する必要があります。

 curl "http://127.0.0.1:9180/apisix/admin/plugin_configs/1" -X PATCH -d ' { "plugins":{ "opa":{ "host":"http://opa:8181", "policy":"rbacExample", "with_consumer":true } } }'

ステップ12: OPAテスト設定を使用する

この時点で、OPAポリシーを使用してすべての設定をテストできます。以下のcurlコマンドを実行してAPIゲートウェイエンドポイントにアクセスすると、認証プロセス中にまずJWTトークンがチェックされ、次に認可プロセス中にユーザーとJWTトークンのデータがOPAに送信され、ロールと権限が検証されます。当然のことながら、JWTトークンがない、または認可されたロールがないリクエストは拒否されます。

 curl -i http://127.0.0.1:9080/speaker/1/topics -H 'Authorization: {API_CONSUMER_TOKEN}'

まとめ

この記事では、シンプルなカスタムポリシーロジックを定義することで、APIユーザーのロールと権限に基づいてAPIリソースへのアクセスを許可または拒否する方法を説明します。また、APISIXから送信されるJWTトークンペイロード内のポリシーファイル、またはユーザーのオブジェクトからAPIユーザー関連情報を抽出する方法も示し、OPAとApache APISIXのRBAC効果を実現します。

翻訳者紹介

51CTOのコミュニティエディターであるJulian Chenは、ITプロジェクトの実装において10年以上の経験を有しています。社内外のリソースとリスクの管理に長けており、ネットワークと情報セキュリティに関する知識と経験の普及に注力しています。

原題: API Gateway と Open Policy Agent (OPA) を使用した RBAC、著者: Bobur Umurzokov

リンク: https://dzone.com/articles/rbac-with-api-gateway-and-open-policy-agentopa