Apache APISIX 与 Open Policy Agent 集成以丰富其生态系统

API7.ai

December 24, 2021

Ecosystem

Open Policy Agent (OPA) は、オープンソースの軽量な汎用ポリシーエンジンであり、ソフトウェア内の組み込みポリシー機能モジュールを置き換え、ユーザーがサービスとポリシーエンジンを分離するのを支援します。OPAの充実したエコシステムのおかげで、ユーザーはプログラムライブラリやHTTP APIなどの他のサービスとOPAを簡単に統合できます。

以下の図に示すように、OPAはまずポリシー言語Regoを使用してポリシーを記述し、その後JSONを使用してポリシーデータを保存します。その後、ユーザーはクエリリクエストを送信できます。OPAはクエリリクエストを受信すると、ポリシー、データ、およびユーザー入力を組み合わせてポリシー決定を生成し、その決定をサービスに送信します。

OPA Workflow

プラグイン紹介

Apache APISIXは、opaプラグインを提供しており、ユーザーがOPAが提供するポリシー機能をApache APISIXに簡単に導入し、柔軟な認証とアクセス制御機能を実現できます。

ルートにopaプラグインを設定すると、Apache APISIXはリクエスト情報や接続情報などをJSONデータに組み立て、レスポンスリクエストを処理する際にポリシー決定APIアドレスに送信します。OPAにデプロイされたポリシーがApache APISIXが設定したデータ仕様に準拠している限り、リクエストの許可、リクエストの拒否、カスタムステータスコード、カスタムレスポンスヘッダーなどの機能を実現できます。

この記事では、HTTP APIを例にopaプラグインを紹介し、Apache APISIXとOPAを統合してバックエンドサービスの認証認可を分離する方法について詳しく説明します。

使用方法

テスト環境の構築

  1. Dockerを使用してOPAサービスを構築します。

    # DockerでOPAを実行
    docker run -d --name opa -p 8181:8181 openpolicyagent/opa:0.35.0 run -s
    
  2. exampleポリシーを作成します。

    # ポリシーを作成
    curl -XPUT 'localhost:8181/v1/policies/example' \
    --header 'Content-Type: text/plain' \
    --data-raw 'package example
    
    import input.request
    import data.users
    
    default allow = false
    
    allow {
        # リクエストヘッダーにtest-headerがあり、その値がonly-for-testである
        request.headers["test-header"] == "only-for-test"
        # リクエストメソッドがGETである
        request.method == "GET"
        # リクエストパスが/getで始まる
        startswith(request.path, "/get")
        # GETパラメータtestが存在し、その値がabcdでない
        request.query["test"] != "abcd"
        # GETパラメータuserが存在する
        request.query["user"]
    }
    
    reason = users[request.query["user"]].reason {
        not allow
        request.query["user"]
    }
    
    headers = users[request.query["user"]].headers {
        not allow
        request.query["user"]
    }
    
    status_code = users[request.query["user"]].status_code {
        not allow
        request.query["user"]
    }'
    
  3. usersデータを作成します。

    # テストユーザーデータを作成
    curl -XPUT 'localhost:8181/v1/data/users' \
    --header 'Content-Type: application/json' \
    --data-raw '{
        "alice": {
            "headers": {
                "Location": "http://example.com/auth"
            },
            "status_code": 302
        },
        "bob": {
            "headers": {
                "test": "abcd",
                "abce": "test"
            }
        },
        "carla": {
            "reason": "Give you a string reason"
        },
        "dylon": {
            "headers": {
                "Content-Type": "application/json"
            },
            "reason": {
                "code": 40001,
                "desc": "Give you a object reason"
            }
        }
    }'
    

ルートを作成し、プラグインを有効化

以下のコマンドを実行してルートを作成し、opaプラグインを有効にします。

curl -XPUT 'http://127.0.0.1:9080/apisix/admin/routes/r1' \
--header 'X-API-KEY: <api-key>' \
--header 'Content-Type: application/json' \
--data-raw '{
    "uri": "/*",
    "methods": [
        "GET",
        "POST",
        "PUT",
        "DELETE"
    ],
    "plugins": {
        "opa": {
            "host": "http://127.0.0.1:8181",
            "policy": "example"
        }
    },
    "upstream": {
        "nodes": {
            "httpbin.org:80": 1
        },
        "type": "roundrobin"
    }
}'

リクエストのテスト

次に、以下のコマンドを実行してopaプラグインにリクエストを送信し、プラグインの動作状態をテストします。

# リクエストを許可
curl -XGET '127.0.0.1:9080/get?test=none&user=dylon' \
    --header 'test-header: only-for-test'
{
    "args": {
        "test": "abcd1",
        "user": "dylon"
    },
    "headers": {
        "Test-Header": "only-for-test",
        "with": "more"
    },
    "origin": "127.0.0.1",
    "url": "http://127.0.0.1/get?test=abcd1&user=dylon"
}

# リクエストを拒否し、ステータスコードとレスポンスヘッダーを書き換える
curl -XGET '127.0.0.1:9080/get?test=abcd&user=alice' \
    --header 'test-header: only-for-test'

HTTP/1.1 302 Moved Temporarily
Date: Mon, 20 Dec 2021 09:37:35 GMT
Content-Type: text/html
Content-Length: 142
Connection: keep-alive
Location: http://example.com/auth
Server: APISIX/2.11.0

# リクエストを拒否し、カスタムレスポンスヘッダーを返す
curl -XGET '127.0.0.1:9080/get?test=abcd&user=bob' \
    --header 'test-header: only-for-test'

HTTP/1.1 403 Forbidden
Date: Mon, 20 Dec 2021 09:38:27 GMT
Content-Type: text/html; charset=utf-8
Content-Length: 150
Connection: keep-alive
abce: test
test: abcd
Server: APISIX/2.11.0

# リクエストを拒否し、カスタムレスポンス(文字列)を返す
curl -XGET '127.0.0.1:9080/get?test=abcd&user=carla' \
    --header 'test-header: only-for-test'

HTTP/1.1 403 Forbidden
Date: Mon, 20 Dec 2021 09:38:58 GMT
Content-Type: text/plain; charset=utf-8
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.11.0

Give you a string reason

# リクエストを拒否し、カスタムレスポンス(JSON)を返す
curl -XGET '127.0.0.1:9080/get?test=abcd&user=dylon' \
    --header 'test-header: only-for-test'

HTTP/1.1 403 Forbidden
Date: Mon, 20 Dec 2021 09:42:12 GMT
Content-Type: application/json
Transfer-Encoding: chunked
Connection: keep-alive
Server: APISIX/2.11.0

{"code":40001,"desc":"Give you a object reason"}

プラグインの無効化

Apache APISIXの動的な性質により、ルート上のOPAプラグインは、ルート設定からopaプラグイン関連の設定を削除して保存するだけで無効にできます。

まとめ

この記事では、Apache APISIXとOpen Policy Agentを連携するための詳細な手順を説明しました。この記事が、Apache APISIXでOpen Policy Agentを使用する際の理解を深め、その後の実践操作を容易にすることを願っています。

Apache APISIXは、自身の高性能を維持することに加えて、オープンソースエコシステムの構築にも非常に重視しています。現在、Apache APISIXには10以上の認証認可関連のプラグインがあり、業界の主要な認証認可サービスとの連携をサポートしています。

他の認証認可サービスとの連携が必要な場合は、Apache APISIXのGitHubにアクセスし、issueを通じて提案を残してください。または、Apache APISIXのメーリングリストに登録して、メールでアイデアを伝えてください。

Tags: