IT分野でいうOPA(Open Policy Agent、読み方は「オーパ」)は、「このユーザーにこの操作を許可してよいか」といった判断をアプリケーションの業務ロジックから切り離し、共通のポリシーで評価するためのオープンソースのポリシーエンジンです。JSONの入力を受け取り、Regoという言語で書いたルールで評価して、許可・拒否などの結果をJSONで返します。商業施設のOPA(オーパ)とは無関係です。
2026年10月29日時点の最新版はv1.21.4(2026年10月23日公開)です。この記事では、v1.21.4とconftest 0.71.1を手元で実行した出力をもとに、OPAの仕組み、REST APIでの問い合わせ方、Kubernetes・Terraform・APIでの使い方、KyvernoやCedarとの違い、導入で失敗しやすい点を解説します。
まとめ:OPAの要点
- OPAは判断(ポリシー決定)だけを担います。結果に従ってリクエストを止めるのは、OPAに問い合わせたアプリやプロキシの役目です。
- CNCFのGraduatedプロジェクトで、ライセンスはApache 2.0です。2025年8月20日に開発者のApple参加が発表されましたが、プロジェクトの運営体制とライセンスは変わっていません。
opa run --serverの既定の待受はlocalhost:8181です。コンテナで動かして外から問い合わせるなら--addr 0.0.0.0:8181を指定します。- REST APIは、存在しないルールを問い合わせてもHTTP 200を返し、
resultキーを省きます。呼び出し側は「resultが無い=拒否」と実装しておく必要があります。 - Kubernetesのマニフェスト検査だけが目的なら、OPA本体ではなくGatekeeperやKyverno、Kubernetes標準のValidatingAdmissionPolicyから選びます。OPA本体が活きるのは、APIの認可・IaCの検査・Kubernetesなど複数の場所で同じルールを使い回したいときです。
OPAの意味と役割:認可の判断をアプリから切り離すエンジン
判断するOPAと、止めるアプリの分担
OPAの公式ドキュメントは、OPAの役割を「ポリシーの判断(decision-making)を適用(enforcement)から切り離すこと」と説明しています(OPA公式ドキュメント)。アクセス制御の用語でいえば、OPAは判断を下すPDP(Policy Decision Point)で、APIサーバーやプロキシ、KubernetesのAPIサーバーが判断に従ってリクエストを通す・止めるPEP(Policy Enforcement Point)です。
この分担があるため、OPAは認可の対象を選びません。HTTPのメソッドとユーザー属性でも、Kubernetesのマニフェストでも、Terraformの実行計画でも、JSONにできれば同じエンジンで判定できます。逆に、OPAを入れただけではリクエストは止まりません。呼び出し側に「OPAに聞いて、拒否なら止める」処理を書く作業が必ず残ります。
Policy as Codeとアクセス制御モデルの関係
ルールをRegoのファイルとして書き、Gitで履歴を管理し、テストを通してから配布する運用はPolicy as Codeと呼ばれます。OPAはそのための実行エンジンで、どのアクセス制御モデルを採るかは書くルール次第です。ロールに権限を割り当てるRBAC(ロールベースアクセス制御)も、部署・時間帯・リソースの属性で判定するABAC(属性ベースアクセス制御)も、同じRegoで表現できます。
OPAの開発元・ライセンス・最新版
OPAはStyra社の創業者らが開発し、CNCFのプロジェクトページによると、2018年3月29日にCNCFへ受け入れられ、2019年4月2日にIncubating、2021年1月29日にGraduatedへ移りました(CNCF:Open Policy Agent)。ライセンスはApache 2.0で、商用利用を含めて無料で使えます。公式リポジトリのADOPTERS.mdには、Netflix、Goldman Sachs、Cloudflare、Atlassian、Mercariなどが利用組織として載っています。
2025年8月20日、OPAの開発者3人(Teemu Koponen・Tim Hinrichs・Torin Sandall)とStyraのチームの多くがAppleに参加したことが、OPA公式ブログで告知されました(Note from Teemu, Tim, and Torin to the Open Policy Agent community)。同じ告知で、OPAはCNCFのGraduatedプロジェクトのままで、運営体制とライセンスに変更はないと明記されています。Styraの商用版だったEOPA、バンドル配布の管理基盤OPA Control Plane、RegoのリンターRegalなどは、CNCFのOPAのGitHub組織へ移す手続きが始まりました。2026年10月時点で、OPA Control PlaneとRegalは同組織で開発が続いており、EOPAのリポジトリはアーカイブされています。
リリースは月1回のペースです。直近のマイナー版と主な変更は次のとおりです(OPAのGitHubリリース)。
| 版 | 公開日 | 主な変更 |
|---|---|---|
| v1.0.0 | 2024-12-20 | Rego v1が既定・ルール本体にifが必須 |
| v1.19.0 | 2026-07-30 | Compile APIのSQLインジェクション修正・Wasm実行系をwazeroへ |
| v1.20.0 | 2026-08-27 | Regoにand・orキーワードを追加 |
| v1.21.0 | 2026-09-24 | ルールの索引を改善・YAMLを1.2のcoreスキーマで解釈 |
| v1.21.3 | 2026-10-19 | bundle署名検証を迂回できる問題2件を修正 |
| v1.21.4 | 2026-10-23 | 2026年10月29日時点の最新パッチ |
v1.21.0のYAMLの解釈変更は破壊的変更として告知されています。YAMLでデータを渡している環境では、yesやonのような値の扱いが変わっていないかを、アップグレード前にテストで確かめてください。bundleの署名検証を使っている環境は、v1.21.3より前の版を使い続けないでください(GHSA-g2pf-6v5v-qcc8)。and・orは、v1.21.4でもimport future.keywords.andとimport future.keywords.orを書かないとパースエラーになりました。
OPAを動かす:評価・テスト・REST APIでの判定
OPAは単一のバイナリで配布されており、GitHubのリリースからOSに合うファイル(macOSのIntel機ならopa_darwin_amd64)を取得して実行権限を付ければ動きます。以下の出力は、macOS上のv1.21.4で実行した結果です。
opa evalによるポリシーの評価
次のポリシーは、管理者にはすべての操作を許可し、一般ユーザーには自分の部署の文書の参照だけを許可します。default allow := falseで、どの条件にも当てはまらないときは拒否にしています。
package authz
default allow := false
# 管理者はすべての操作を許可
allow if input.user.role == "admin"
# 一般ユーザーは自分の部署の文書だけ参照できる
allow if {
input.method == "GET"
input.user.dept == input.resource.dept
}
同じallowを2回定義すると、どちらかが成り立てばtrueになります(OR条件)。1つのルール本体に並べた条件は、すべて成り立つ必要があります(AND条件)。上のポリシーをauthz.regoとして保存し、同じディレクトリのinput.jsonに次のJSONを書いて評価します。
$ cat input.json
{"method":"GET","user":{"role":"member","dept":"sales"},"resource":{"dept":"sales"}}
$ opa eval -d authz.rego -i input.json "data.authz.allow" --format pretty
true
OPA 1.0より前の書き方(allow { ... }のようにifの無いルール)は、v1.21.4ではrego_parse_error(ルール本体の前にifが必要という内容)で読み込みに失敗します。古い記事のコードを試すときは、opa fmt --v0-v1で書き換えるか、--v0-compatibleを付けて実行します。--v0-compatibleを付けたopa runはサーバーの既定の待受も全インターフェースに戻るので、移行の一時しのぎにとどめます。構文の詳細と移行手順はRegoの書き方とOPA 1.0(Rego v1)への移行ガイドで解説しています。
opa testによる許可・拒否のテスト
Regoのテストは、名前がtest_で始まるルールとして書きます。次のコードをauthz_test.regoとしてauthz.regoと同じディレクトリに保存します。with input asで入力を差し替えて、許可されるべきケースと拒否されるべきケースを両方固定しておくのが基本です。
package authz_test
import data.authz
test_admin_can_delete if {
authz.allow with input as {"method": "DELETE", "user": {"role": "admin"}, "resource": {"dept": "sales"}}
}
test_other_dept_denied if {
not authz.allow with input as {"method": "GET", "user": {"role": "member", "dept": "dev"}, "resource": {"dept": "sales"}}
}
$ opa test . -v
data.authz_test.test_admin_can_delete: PASS (487.317µs)
data.authz_test.test_other_dept_denied: PASS (535.143µs)
--------------------------------------------------------------------------------
PASS: 2/2
テストが1件でも失敗すると終了コードが0以外になるので、CIにopa testを入れておけば、ルールの変更で既存の許可・拒否が崩れたときにマージ前に止められます。
OPAサーバーの起動とREST APIによる認可判定
アプリから判定を問い合わせる場合は、OPAをサーバーとして常駐させます。--set decision_logs.console=trueは、判定ごとの記録(decision log)を標準出力に出す設定です。
$ opa run --server --set decision_logs.console=true authz.rego
$ curl -s -X POST localhost:8181/v1/data/authz/allow \
-d '{"input":{"method":"GET","user":{"role":"member","dept":"sales"},"resource":{"dept":"sales"}}}'
{"decision_id":"655ee476-1ce9-42b6-a806-b261d290ffa5","result":true}
$ curl -s -X POST localhost:8181/v1/data/authz/allow \
-d '{"input":{"method":"DELETE","user":{"role":"member","dept":"sales"},"resource":{"dept":"sales"}}}'
{"decision_id":"ec647f8f-6a27-44b0-bae4-a0787e98a834","result":false}
URLの/v1/data/authz/allowは、パッケージauthzのルールallowに対応します。decision_idは判定ログを有効にしたときだけ付く値で、ログ側の同じIDと突き合わせれば「いつ・どの入力で・何を返したか」を後から追えます。今回のログには、評価にかかった時間(timer_rego_query_eval_ns)が1回目で141,345ナノ秒、2回目で61,347ナノ秒と記録されていました。ルール2本の小さなポリシーを同じマシン内で呼んだ値なので、本番の見積もりには自分のポリシーとデータで測り直してください。
起動時のログは"addrs":["localhost:8181"]でした。OPA 1.0以降、既定の待受はlocalhostで、セキュリティガイドにも「同じマシンの外のサービスに公開しないため」と書かれています(OPA:Security)。Dockerのコンテナで動かしてほかのコンテナから問い合わせるなら、--addr 0.0.0.0:8181を明示しないと接続できません。外に開ける場合は、同じガイドにあるTLSと認証(--authentication・--authorization、どちらも既定はoff)を合わせて設定します。
未定義の判定におけるresultの省略
ルール名をalowと打ち間違えて問い合わせると、エラーではなく次の応答が返りました。
$ curl -s -X POST localhost:8181/v1/data/authz/alow -d '{"input":{}}'
{"decision_id":"5408abac-a889-4229-ba2d-aaeb39f6415f"}
REST APIのリファレンスにも、パスが未定義の文書を指す場合はHTTP 200を返し、レスポンスにresultを含めないと書かれています(OPA:REST API)。呼び出し側が「HTTP 200なら許可」や「resultがfalseのときだけ拒否」と書いていると、パスの誤りやポリシーの配布漏れがそのまま全許可になります。判定は「resultがtrueのときだけ許可し、それ以外はすべて拒否」と実装し、ポリシー側にもdefault allow := falseを置いて二重に守ります。
OPAの組み込み方と配置の選び方
ここでは、OPAの主な組み込み方としてREST API・Goライブラリ・WebAssembly(Wasm)の3つを比較します。公式ドキュメントの比較をまとめると次のようになります(OPA:Integrating OPA)。
| 方式 | 評価の速さ | 使える言語 | OPAの更新 |
|---|---|---|---|
| REST API | 速い | 任意 | OPAだけ入れ替える |
| Goライブラリ | より速い | Goのみ | サービス全体を再ビルド |
| Wasm | 最も速い | Wasmを実行できる言語 | サービス側の更新はまれ |
同じページは、REST APIが最も一般的で、多くはサイドカーとして配置されると説明しています。迷ったらREST APIのサイドカーから始め、Goのサービスで通信のぶんの遅延も削りたくなったらライブラリへ移ります。
配置は、アプリと同じホストやPodに置くサイドカー型と、OPAを1か所に集める集中型に分かれます。公式の配置ガイドは、サイドカー型を「判定がローカルで済むので遅延が非常に小さく、ネットワークが切れても動く」、集中型を「ネットワークを経由するぶん遅延が大きく、冗長化しないと単一障害点になる一方、大量のデータを共有でき資源の総量は少ない」と整理しています(OPA:Deploying OPA)。リクエストごとに認可を問うAPIはサイドカー型、CIのジョブやバッチのように呼び出し頻度が低い用途は集中型が向きます。
サイドカー型でOPAが増えると、ポリシーとデータを各OPAへどう届けるかが課題になります。OPAには、ポリシーとデータを1つのバンドルにまとめてHTTPサーバーやOCIレジストリから定期取得するbundle機能、判定ログを外部へ送るdecision log機能、各OPAの状態を報告するstatus機能が用意されています。判定ログにはリクエストの入力がそのまま残るため、トークンや個人情報を含む入力を扱うなら、data.system.log.maskのルールで該当フィールドを消すか置き換える設定が必要です(OPA:Decision Logs)。
OPAの主な用途
Kubernetes:Gatekeeperによるマニフェスト検査
KubernetesでOPAを使う場合の標準はOPA Gatekeeperです(2026年10月時点の最新安定版はv3.23.1)。KubernetesのAPIサーバーがリソースを受け付ける段階(アドミッション)でポリシーを評価し、違反するマニフェストを拒否します。ルールはConstraintTemplate(Regoで判定を書くテンプレート)とConstraint(どのリソースにどのパラメータで適用するか)の2つのリソースで定義します。
注意点は、GatekeeperのRegoは既定で今もv0構文だということです。Gatekeeper 3.19以降は、ConstraintTemplateのsourceにversion: "v1"を書いたときだけRego v1を使えます(Gatekeeper:Constraint Templates)。OPA本体の記事を見てv1構文のRegoをそのまま貼ると、versionの指定が無い限り読み込めません。
targets:
- target: admission.k8s.gatekeeper.sh
code:
- engine: Rego
source:
version: "v1"
rego: |
package k8srequiredlabels
violation contains {"msg": msg} if {
provided := {label | input.review.object.metadata.labels[label]}
required := {label | label := input.parameters.labels[_]}
missing := required - provided
count(missing) > 0
msg := sprintf("必須ラベルがありません: %v", [missing])
}
掲載したYAMLはConstraintTemplateのspec.targets以下の抜粋で、単独では適用できません。このRego部分を、appラベルだけを持つマニフェストと、必須ラベルapp・ownerの組み合わせでopa evalにかけると、[{"msg":"必須ラベルがありません: {\"owner\"}"}]が返りました。Gatekeeperは3.18から、ConstraintTemplateにKubernetes標準のCEL式を書く方式も安定版として扱っています。Kubernetesの全体像はKubernetes(クバネティス)とは?仕組みとDockerとの違いを参照してください。
IaC:conftestによるTerraform実行計画の検査
conftestはOPAのGitHub組織で開発されている、設定ファイルをRegoで検査するコマンドです(最新版は0.71.1、同梱のOPAは1.21.1)。Terraformなら、terraform show -jsonで書き出した実行計画(plan)のJSONを入力にします。次のポリシーは、S3のパブリックアクセスブロックでblock_public_aclsをfalseにする変更を拒否します。
package main
# パブリックアクセスブロックを無効にするS3設定を拒否する
deny contains msg if {
rc := input.resource_changes[_]
rc.type == "aws_s3_bucket_public_access_block"
rc.change.after.block_public_acls == false
msg := sprintf("%s: block_public_acls を false にできません", [rc.address])
}
$ conftest test plan.json
FAIL - plan.json - main - aws_s3_bucket_public_access_block.logs: block_public_acls を false にできません
1 test, 0 passed, 0 warnings, 1 failure, 0 exceptions
conftestはpolicyディレクトリのmainパッケージにあるdeny・violation・warnのルールを読み、既定ではdeny・violationによる失敗があると終了コード1を返します。warnによる警告だけなら終了コード0です。警告でもCIを失敗させるには--fail-on-warnを指定します。CIでterraform planの直後に置けば、危険な設定をapplyの前に止められます。なお、Terraform 1.17.0ではHCLでポリシーを書くTerraform Policyが一般提供になりました。Regoを覚えずにTerraform内で完結させたい場合の比較はTerraform Policyとは:HCLで書くポリシーの手順とSentinel・OPAとの違いで整理しています。
APIとサービスメッシュ:Envoyの外部認可とOPAの連携
APIの認可では、EnvoyやEnvoyを使うIstioの外部認可(ext_authz)の問い合わせ先としてOPAを置く構成が一般的です。OPAプロジェクトは、Envoyの外部認可APIをOPAに組み込んだopa-envoy-pluginを配布しており、2026年10月時点の最新版はv1.21.2-envoy-3です(opa-envoy-plugin)。Istioでは、AuthorizationPolicyのCUSTOMアクションで外部の認可サービスへ判定を委ねます。ALLOW・DENYとの評価順はIstio AuthorizationPolicyの書き方で解説しています。
この構成なら、パス・メソッド・JWTの中身による認可をアプリのコードから外せます。ただし「この請求書の担当者か」のようにアプリのデータベースを見ないと決まらない認可は、プロキシの段階では材料が足りません。そうした判定はアプリから属性を渡してOPAに問い合わせます。
OPAと他のポリシーエンジンの比較
| 手法 | 適用範囲 | ルールの書き方 |
|---|---|---|
| OPA | JSONにできるもの全般 | Rego |
| OPA Gatekeeper | Kubernetesのアドミッション | Rego(既定v0)・CEL |
| Kyverno | Kubernetesのアドミッション | YAML・CEL |
| ValidatingAdmissionPolicy | Kubernetes標準(1.30でGA) | CEL |
| Cedar(Amazon Verified Permissions) | アプリの認可 | Cedar |
| クラウドのIAM | 各クラウドのリソース | JSONなどの宣言 |
KubernetesのValidatingAdmissionPolicyは、1.30で安定版になり既定で有効です(Kubernetes:Validating Admission Policy)。APIサーバーの中でCELを評価するので、Webhookのサーバーを別に運用せずに済み、ラベルの必須化やレジストリの制限程度ならこれで足ります。YAMLで書けてリソースの生成・書き換えもしたい場合はKyvernoが候補になり、2026年10月20日にv1.19.2が公開されています。
アプリの中の細かい認可を、マネージドサービスに任せたい場合は、AWSのCedar言語を使うAmazon Verified Permissionsが比較対象になります。OPAとの大きな違いは、Cedarが認可(誰が・何に・何をできるか)専用の言語なのに対し、RegoはKubernetesのマニフェストやTerraformの実行計画のような任意のJSONも検査できる点です。認可だけでなく構成の検査も同じ言語で書きたいならOPAが向いています。
OPAを採用すべきでない場面と運用の落とし穴
次の条件に当てはまるなら、OPAの導入は見送るか範囲を絞ったほうが運用は軽くなります。
- 対象がKubernetesのマニフェストだけ:ValidatingAdmissionPolicyかKyverno、既存のRego資産があるならGatekeeperで足ります。OPAのサーバーを自前で動かす理由がありません。
- 認可が1つのアプリの中で閉じている:Webフレームワーク標準の認可機能や言語別の認可ライブラリなら、プロセスの外に問い合わせる遅延も、OPAの監視も要りません。複数のサービスで同じルールを守らせたくなった時点がOPAへの移行の目安です。
- Regoを読める人がチームに1人もいない:Regoは集合と論理式で書く宣言型の言語で、手続き型の言語と読み方が違います。ポリシーをレビューできる人がいないと、Policy as Codeの利点であるレビューとテストが形だけになります。
導入する場合は、最初に3点を決めておくと事故が減ります。1つ目は前述の「resultがtrueのときだけ許可」の実装、2つ目はOPAに接続できないときの扱い(拒否に倒すか許可に倒すか)、3つ目は判定ログに残してよい入力の範囲です。2つ目はAPIの可用性とセキュリティのどちらを優先するかの判断で、GatekeeperでもfailurePolicyとして同じ選択を迫られます。
OPAの導入とあわせて、ログイン・トークン発行・権限管理を含む認証・認可の基盤全体を設計し直す場合は、認証基盤・ID管理システム開発の支援内容もあわせてご覧ください。
よくある質問
OPAは何の略で、どう読みますか?
IT分野のOPAはOpen Policy Agentの略で、公式ドキュメントの発音表記は「oh-pa」(オーパ)です。同じ表記の商業施設「OPA(オーパ)」や同名の企業とは無関係です。
OPAとGatekeeperの違いは何ですか?
OPAはどのシステムからでも使える汎用のポリシーエンジンで、GatekeeperはOPAをKubernetesのアドミッション制御に特化させたコントローラーです。GatekeeperはポリシーをKubernetesのリソースとして配布・再利用でき、既存リソースの違反を洗い出す監査機能も持っています。
OPAは無料ですか?Appleに買収されたのですか?
OPAはApache 2.0ライセンスのオープンソースで、商用利用を含めて無料です。2025年8月にAppleに参加したのはOPAの開発者とStyraのチームで、OPAそのものはCNCFのプロジェクトのままです。その後も月1回のリリースが続いています。
OPAを使うにはRegoを覚える必要がありますか?
自分でポリシーを書くなら必要です。Kubernetesの用途だけなら、Gatekeeperの公開ライブラリにある既成のConstraintTemplateを使えば、Regoをほとんど書かずに始められます。基本構文はRegoの書き方ガイドで確認できます。
OPAとRBACはどう使い分けますか?
RBACはロールに権限を割り当てるアクセス制御の考え方で、OPAはRBACを含むさまざまなルールを評価する実行エンジンです。ロールだけで判定できるならフレームワークやクラウドのRBAC機能で足ります。ロールに加えて部署・時間帯・リソースの所有者のような属性を条件にしたくなったとき、そのルールをRegoで書いてOPAに判定させます。