注目のOpen Policy Agent、その概要とKubernetesでの活用事例
はじめに
Kubernetesコミュニティにおいて、ここ数年で国内外の企業がOpen Policy Agentを導入しているという話を耳にすることが多くなってきました。今回は、KubernetesをはじめとしたCloud Native技術には触れているが、Open Policy Agentにはまだ触れたことがない方向けに、その概要や仕組み、Kubernetesでの活用事例などを紹介します。
なお、ここで扱うOpen Policy Agentの情報は執筆時点(2020年4月26日現在)での最新バージョン
Open Policy Agentとは
Open Policy Agent(以下、OPA)はオープンソースの軽量かつ汎用的なポリシーエンジンです。ポリシーをコードとして管理する(Policy as Code)ためのポリシー言語Regoと、アプリケーションからの問い合わせに対してポリシーの評価を行うAPIが提供されています。OPAは"oh-pa"、Regoは"ray-go"と発音します。
OPAをサイドカー、デーモン、ライブラリなどでアプリケーションに統合し、アプリケーション内のリクエスト制御をAPIを介してOPAに問い合わせた評価結果から判定するアーキテクチャにすることで、アプリケーションからポリシー制御の機能を切り離すことが可能になります。
OPAによりポリシー制御を分離することで、アプリケーションのソースコード変更、再デプロイを必要としないポリシー管理や、様々なアプリケーションにおけるポリシー制御を統一された仕組みで実現できます。
OPAの公式ページでは、マイクロサービスにおけるサービス間の認可、問い合わせ元の権限に基づいたデータフィルタリング、KubernetesのAdmission Controlを使ったリソースのバリデーション、SSHとsudoでのアクセス制御、Terraformの設定ファイルのバリデーションなど、様々なユースケースが紹介されています。
OPAの仕組み
ハイレベルアーキテクチャ
OPAはDocumentの集合であるData、Ruleの集合であるPolicyと、それらを操作するAPIで構成され、APIを介して受け取った問い合わせを元にDataをPolicyで評価した結果を返すアーキテクチャとなっています。
DataとPoilicyについて
DataはJSON形式のデータで、アプリケーションまたはユーザーがData APIを介して外部からロードする静的データのBase Documentと、RuleによるData(Base Document、Virtual Document)の評価結果であるVirtual Documentの2種類で構成されています。問い合わせ時の入力データもBase Documentに分類されますが、文脈によってInput DocumentやQuery Inputと表現されます。
PolicyはRegoを使用して定義されたRuleから構成されています。単一または複数のRuleを定義したRegoファイルをPolicy Moduleとして、Policy APIを介してOPAに追加できます。
チュートリアル
OPAでHTTP APIを認可することを想定したチュートリアルを紹介します。
仕様
HTTP APIに給与を参照するエンドポイント
- 社員は自身の給与を参照できる
- マネージャーは部下の給与も参照できる
また、OPAへの問い合わせ時には、入力データとして以下の情報を渡すこととします。
- HTTPメソッド
- エンドポイント
- リクエスト元のユーザー
チュートリアルの実施
OPAをServerモードで起動します。今回はAPIも操作したいので、ローカルにDockerで起動したOPAを利用していきますが、公式から提供されているRego Playgroundを使ってWeb上で試すことも可能です。
$ docker run --rm -d --name opa -p 8181:8181 openpolicyagent/opa:0.19.1 run --server
起動時点でまだData, Policyは存在しないため、まずはData API経由で社員情報をBase DocumentとしてOPAに追加します。今回はaliceの部下がbob、charlieの部下がdavidと定義しています。
$ cat subordinates.json { "alice": ["bob"], "bob": [], "charlie": ["david"], "david": [] } $ curl -X PUT -H 'Content-Type:application/json' --data-binary @subordinates.json \ localhost:8181/v1/data/subordinates
追加したBase DocumentはData APIを介して参照できます。Data APIを実行してみるとレスポンスの
$ curl -s localhost:8181/v1/data/subordinates | jq . { "result": { "alice": [ "bob" ], "bob": [], "charlie": [ "david" ], "david": [] } }
次に、Policy Moduleを作成します。このPolicy Module内では
また、パッケージ名とRule名から評価結果であるVirtual Documentの参照先が決定されます。今回は
$ cat httpapi_authz.rego package httpapi.authz # 社員情報をインポート import data.subordinates as subord default allow = false # 社員は自身の給与を参照できる allow { some username input.method == "GET" input.path = ["finance", "salary", username] input.user == username } # マネージャーは部下の給与も参照できる allow { some username input.method == "GET" input.path = ["finance", "salary", username] subord[input.user][_] == username }
作成したPolicy ModuleをPolicy APIを介してOPAに追加します。
$ curl -X PUT -H 'Content-Type: text/plain' --data-binary @httpapi_authz.rego \ localhost:8181/v1/policies/httpapi_authz
これでData, Policyの準備が完了したので、OPAに問い合わせのテストを行っていきます。以下はaliceがalice(自分)の給与を参照できるか問い合わせた例です。自分の給与は参照できるポリシーなので
$ cat alice_to_alice.json { "input": { "method": "GET", "path": ["finance", "salary", "alice"], "user": "alice" } } $ curl -s -X POST -H 'Content-Type:application/json' --data-binary @alice_to_alice.json \ localhost:8181/v1/data/httpapi/authz/allow | jq . { "result": true }
次は、aliceがbobの給与を参照できるか問い合わせた例です。マネージャーは部下の給与も参照できるポリシーなので
$ cat alice_to_bob.json { "input": { "method": "GET", "path": ["finance", "salary", "bob"], "user": "alice" } } $ curl -s -X POST -H 'Content-Type:application/json' --data-binary @alice_to_bob.json \ localhost:8181/v1/data/httpapi/authz/allow | jq . { "result": true }
最後は、aliceがdavidの給与を参照できるか問い合わせた例です。マネージャーは部下の給与も参照できるポリシーですが、davidはcharlieの部下でaliceの部下ではないので
$ cat alice_to_david.json { "input": { "method": "GET", "path": ["finance", "salary", "david"], "user": "alice" } } $ curl -s -X POST -H 'Content-Type:application/json' --data-binary @alice_to_david.json \ localhost:8181/v1/data/httpapi/authz/allow | jq . { "result": false }
今回は手動で入力データを作成してOPAに問い合わせを行いましたが、HTTP APIの内部でリクエスト情報から入力データを自動生成してOPAに問い合わせを行い、得られた評価結果からリクエスト制御を行うことでHTTP APIの認可をOPAに切り出すことができます。
また、このチュートリアルではHTTP APIの認可として簡単なポリシーを例にしましたが、RegoにはBuild-inされている関数が多数あるので、JWTを検証してClaimsの値を使用したり、TLSクライアント証明書の情報を使用したりなど、要件に応じてポリシーを作り込んでいくことも可能です。
KubernetesでのOPA活用事例
ここでは、KubernetesにおけるOPAの活用事例を紹介します。
Kubernetesリソースのバリデーション
Kubernetesクラスタに意図しないリソースが適用されることを避けるために、GatekeeperをAdmission Controller Webhookとして導入することで、kube-apiserverでOPAによるCRDベースのポリシーに基づいたリソースのバリデーションを行うことができます。これはPodSecurityPolicyの代替にもなります。
ほかにもkube-apiserverでのバリデーションではなく、マニフェストをバージョン管理しているリポジトリにCIでconftestを導入することで、マニフェストに対してOPAによるポリシーに基づいたユニットテストを実行することもできます。
RBACの拡張
Kubernetes Authorization via Open Policy Agentで紹介されていた事例です。社内にKubernetesクラスタを提供しているチームでは利用者の拡大によりRBACによる認可の管理に課題を抱えており、RBACのようなホワイトリスト形式での認可に加えOPAによるブラックリスト形式の認可を採用したという話です。アーキテクチャとしてはWebhook ModuleとAdmission Controller WebhookにGatekeeperを採用しているようです。
ワークロードから実行された悪意ある処理の検知
Secure Kubernetes using eBPF & Open Policy Agentで紹介されていた事例です。Kubernetesクラスタにデプロイされているワークロードから実行された悪意のある処理を検知するためにeBPFを使用してシステムコールを解析し、ポリシー違反がないかをOPAに問い合わせるアーキテクチャを採用しているようです。
Service Meshにおけるサービス間の認可
Data PlaneとしてEnvoyを採用しているService Meshであれば、EnvoyのExternal Authorization(認可の処理を別システムに任せるための機能)としてOPAを統合することで、リクエスト情報を元にサービス間の認可をOPAで行うことができます。また、Istioを使用しているケースでは公式で提供されているプラグインが利用できます。
おわりに
今回はOPAに触れたことがない方向けに、その概要や仕組み、Kubernetesでの活用事例などを紹介しました。OPAはCNCFのIncubatingプロジェクト(2020年4月26日現在)としてホストされており、開発も活発でコミュニティも拡大していて、国内外で採用事例が増えてきています。現在抱えている課題をOPAで解決できそうであれば、導入を検討してみるのも良いでしょう。
【参考資料】
・https://www.openpolicyagent.org
・https://github.com/open-policy-agent/opa
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- CNDO 2021、Open Policy Agentを使ったポリシーアズコードの紹介
- KubernetesネイティブなポリシーエンジンKyverno
- KubeCon EU 2020から脆弱性スキャンのTrivyとOPAを紹介
- CNDT 2022、ChatworkのSREがコンテナセキュリティのための新しいツールを紹介
- KubeCon Europe前日のプレカンファレンスKubeSec Enterprise Summit
- Keycloakと認可プロダクトを利用したマイクロサービスにおける認証認可の実現
- CNDT2020シリーズ:CAのインフラエンジニアが解説するKubernetesネイティブなCI/CD
- AWSが公開したオープンソースのポリシー記述言語Cedarを紹介
- 「Cloud Native Trail Map」の10ステップを紐解く(ステップ6~7)
- Oracle Cloud Hangout Cafe Season4 #3「CI/CD 最新事情」(2021年6月9日開催)