API GatewayとOpen Policy Agent (OPA)を使用したRBAC

Bobur Umurzokov

Bobur Umurzokov

May 15, 2023

Technology

さまざまなアクセス制御モデルと実装方法が利用可能であるにもかかわらず、バックエンドサービスAPIの認可システムを構築することは依然として難しい課題です。しかし、最終的な目標は、適切な個人が関連するリソースに適切にアクセスできるようにすることです。この記事では、オープンソースのAPIゲートウェイであるApache APISIXOpen Policy Agent(OPA)を使用して、APIに**ロールベースのアクセス制御(RBAC)**認可モデルを有効にする方法について説明します。

学習目標

この記事を通じて、以下のことを学びます:

  • RBACとは何か、そしてどのように機能するのか?
  • OPAとは何か、そしてどのように機能するのか?
  • OPAとApache APISIXを使用してRBACを実装する方法
  • OPAでポリシーを定義し、登録する方法
  • アップストリーム、ルートを作成し、OPAプラグインを有効にする方法
  • ユーザーのロールと権限をJWTトークンのペイロードまたはコンシューマーデータから解析する方法

RBACとは?

ロールベースのアクセス制御(RBAC)と属性ベースのアクセス制御(ABAC)は、コンピュータシステム内でリソースへのアクセスを管理し、制御するために使用される2つの一般的なアクセス制御モデルです。RBACは、組織内のユーザーのロールに基づいて権限を割り当てます。RBACでは、ユーザーの機能や責任に基づいてロールが定義され、それらのロールに権限が割り当てられます。ユーザーは1つ以上のロールに割り当てられ、それらのロールに関連する権限を継承します。APIのコンテキストでは、例えば、開発者ロールはAPIリソースを作成および更新する権限を持ち、エンドユーザーロールはAPIリソースを読み取りまたは実行する権限のみを持つかもしれません。

基本的に、RBACはユーザーのロールに基づいて権限を割り当て、ABACはユーザーとリソースに関連する属性に基づいて権限を割り当てます。

RBACでは、ポリシーはユーザーに割り当てられたロール、許可されたアクション、およびそれらのアクションを実行できるリソースの組み合わせによって定義されます。

OPAとは?

OPA(Open Policy Agent)は、分散システム全体でポリシーを強制するためのポリシーエンジンおよびツールセットです。これにより、単一のポイントからポリシーを定義、管理、および強制することができます。ポリシーをコードとして定義することで、OPAはポリシーのレビュー、編集、ロールバックを容易にし、効率的なポリシー管理を可能にします。

Open Policy Agentの例

OPAは、Regoと呼ばれる宣言型言語を提供し、スタック全体でポリシーを作成および強制することができます。OPAにポリシー決定を要求すると、.regoファイルで提供されたルールとデータを使用してクエリを評価し、応答を生成します。クエリ結果はポリシー決定として返されます。OPAはすべてのポリシーと必要なデータをメモリ内キャッシュに保存します。その結果、OPAは迅速に結果を返します。以下は、簡単なOPA Regoファイルの例です:

package example

default allow = false

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

この例では、"example"というパッケージがあり、"allow"というルールを定義しています。"allow"ルールは、入力メソッドが"GET"で、リクエストされたパスが/api/resourceであり、ユーザーのロールが"admin"である場合にリクエストを許可します。これらの条件が満たされると、"allow"ルールは"true"と評価され、リクエストが進行します。

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

APIゲートウェイは、APIとAPIコンシューマーを構成および管理するための集中型の場所を提供します。これは、各サービスが内部で認証ロジックを実装することを避けることで、集中型認証ゲートウェイとして使用できます。一方、OPAは認可レイヤーを追加し、コードからポリシーを分離することで、認可に明確な利点をもたらします。この組み合わせにより、APIリソースに対する権限をロールに追加できます。ユーザーは1つ以上のユーザーロールに関連付けられるかもしれません。各ユーザーロールは、RBACリソース(URIパスで定義)に対する一連の権限(GET、PUT、DELETE)を定義します。次のセクションでは、これら2つを使用してRBACを実現する方法を学びます。

Apache APISIX APIゲートウェイとOpen Policy Agent

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

Apache APISIXでは、ルートプラグインを構成してAPIの動作を定義できます。APISIXのopaプラグインを使用して、リクエストをOPAに転送し、意思決定を行うことでRBACポリシーを強制できます。その後、OPAはユーザーのロールと権限に基づいてリアルタイムで認可決定を行います。

Conference APIがあると仮定します。このAPIでは、イベントセッション、トピック、スピーカー情報を取得/編集できます。スピーカーは自分のセッションとトピックのみを読み取ることができ、管理者はさらにセッションとトピックを追加/編集できます。または、参加者はスピーカーのセッションに関するフィードバックを/speaker/speakerId/session/feedbackにPOSTリクエストで送信し、スピーカーは同じURIのGETメソッドをリクエストすることでフィードバックを確認できます。以下の図は、このシナリオ全体を示しています:

Apache APISIXとOpen Policy Agent

  1. APIコンシューマーは、認証ヘッダーにJWTトークンなどの資格情報を付けてAPIゲートウェイにルートをリクエストします。
  2. APIゲートウェイは、JWTヘッダーとコンシューマーデータをOPAエンジンに送信します。
  3. OPAは、.regoファイルで指定したポリシー(ロールと権限)を使用して、コンシューマーがリソースにアクセスする権利を持っているかどうかを評価します。
  4. OPAの決定が許可された場合、リクエストはアップストリームのConferenceサービスに転送されます。

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

前提条件

  • Dockerは、コンテナ化されたetcdとAPISIXをインストールするために使用されます。
  • curlは、APISIX Admin APIにリクエストを送信するために使用されます。Postmanなどのツールを使用してAPIとやり取りすることもできます。

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

APISIXは、以下のクイックスタートスクリプトを使用して簡単にインストールおよび起動できます:

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

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

Conference APIのバックエンドサービスにリクエストをルーティングするために、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プラグインが設定されます。これにより、コンシューマーはJSON Web Token(JWT)を使用して認証できるようになります。

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プラグインを使用してトークンを生成および署名する新しいルートを設定する必要があります。このシナリオでは、APIゲートウェイは、コンシューマーjackのキーを使用してトークンを作成および検証するIDプロバイダーサーバーとして機能します。IDプロバイダーは、GoogleOktaKeycloakOry 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トークンを要求する

これで、作成した公開エンドポイントからスピーカー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: 新しいプラグイン構成を作成する

このステップでは、APISIXの3つのプラグイン、proxy-rewritejwt-authopaプラグインを構成します。

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

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

最後のステップは、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などの情報が含まれています。この場合、ルートは、/speaker/topicsおよび/speaker/sessionsの2つの異なるURIに対するGETおよびPOSTリクエストを処理するように構成されています。"upstream_id"フィールドは、このルートに対する着信リクエストを処理するアップストリームサービスのIDを指定し、"plugin_config_id"フィールドは、このルートに使用するプラグイン構成のIDを指定します。

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

これまでに、APISIXがConference APIエンドポイントに着信リクエストを転送し、認可されたAPIコンシューマーのみがアクセスできるようにするために必要なすべての構成を設定しました。これで、APIコンシューマーがエンドポイントにアクセスするたびに、JWTトークンを提供してConferenceバックエンドサービスからデータを取得する必要があります。以下のコマンドを実行して、エンドポイントにアクセスし、リクエストしているドメインアドレスが実際のConferenceサービスではなく、カスタムAPIゲートウェイであることを確認できます:

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

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

他の2つのステップは、Dockerを使用してOPAサービスを実行し、その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](http://opa:8181)というアドレスを使用して直接OPAにポリシーチェックリクエストを送信できます。OPAとAPISIXは同じDockerネットワークで実行する必要があります。

ステップ10: ポリシーを定義し、登録する

OPA側の2番目のステップは、APIリソースへのアクセスを制御するために使用されるポリシーを定義することです。これらのポリシーは、アクセスに必要な属性(どのユーザーがどのロールを持っているか)と、それらの属性に基づいて許可または拒否される権限(どのロールがどの権限を持っているか)を定義する必要があります。例えば、以下の構成では、jackのロールをuser_rolesテーブルで確認するようにOPAに指示しています。この情報は、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

# ユーザーロールを割り当てる

user_roles := {
    "jack": ["speaker"],
    "bobur":["admin"]
}

# ロールの権限を割り当てる
role_permissions := {
    "speaker": [{"permission": "read"}],
    "admin":   [{"permission": "read"}, {"permission": "write"}]
}

# JWTヘルパー関数
bearer_token := t {
 t := input.request.headers.authorization
}

# 認証トークンをデコードしてロールと権限を取得
token = {"payload": payload} {
 [_, payload, _] := io.jwt.decode(bearer_token)
}

# RBACを実装するロジック
default allow = false

allow {
    # ユーザーのロールリストを検索
    roles := user_roles[input.consumer.username]

    # そのリスト内の各ロールに対して
    r := roles[_]

    # ロールrの権限リストを検索
    permissions := role_permissions[r]

    # 各権限に対して
    p := permissions[_]

    # rに付与された権限がユーザーのリクエストと一致するか確認
    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}'

結論

この記事では、OPAとApache APISIXを使用してRBACを実装する方法を学びました。APIコンシューマーのロールと権限に基づいてAPIリソースへのアクセスを許可/拒否するための簡単なカスタムポリシーロジックを定義しました。また、このチュートリアルでは、APISIXから送信されたJWTトークンのペイロードまたはコンシューマーオブジェクトからAPIコンシューマー関連情報をポリシーファイルで抽出する方法も示しました。

関連リソース

推奨コンテンツ

Tags: