CircleCIで実行バイナリをDLしてCI内でキャッシュしながら使う
CI内でどこかしらから実行バイナリをダウンロードして、それを使うことがあると思います。 最近では僕はOPAのテストをCircleCIで実行したかったので、OPAのバイナリをダウンロードしてテストを実行しました。
毎回バイナリをダウンロードしてCircleCIを回すのもコストが高いので、CircleCIのキャッシュを使う方法について共有します!
実行バイナリをDockerイメージに入れてそれをCIで使う方法もありますが、今回は実行バイナリをダウンロードして使う方法でやります!
キャッシュせずにやった場合
まずはキャッシュのことを考えずにCIしてみると、以下のような流れになります。(OPAの例です)
version: 2 jobs: build: docker: - image: circleci/buildpack-deps:jessie steps: - checkout - run: name: Download OPA command: | mkdir .bin wget -O .bin/opa https://github.com/open-policy-agent/opa/releases/download/v0.10.6/opa_linux_amd64 sudo chmod +x .bin/opa - run: name: Test Policies command: .bin/opa test .
- チェックアウト
- OPAのバイナリをGitHubからダウンロードして実行権限付与
- OPAのテスト実行
という感じです。特につまるところは無いと思います。
ただしこれにはパフォーマンスの問題があって、CIの度にOPAのバイナリをGitHubからダウンロードしてくるのが無駄です。 このバイナリもそんなにしょっちゅう変わるものでも無いので、ダウンロードが一度で済ませられたら良いですよね。
そこで使えるのがCircleCIのキャッシュです!
キャッシュして効率化
CircleCIには save_cache
と restore_cache
という便利な機能があります。
キーを基にCircleCI側にファイルやディレクトリなどをキャッシュしておくことができます。
よく見る場面としては、依存パッケージなどをキャッシュしておくときです。( node_modules
や vendors
ディレクトリなど)
ポイントとなるのがキャッシュの「キーの決め方」です。
まずはあまり工夫をせずにキャッシュの手順を加えて見ます。
version: 2 jobs: build: docker: - image: circleci/buildpack-deps:jessie steps: - checkout - restore_cache: key: opa-cache-0.10.6 - run: name: Download OPA command: | if [ ! -f .bin/opa ]; then mkdir .bin wget -O .bin/opa https://github.com/open-policy-agent/opa/releases/download/v0.10.6/opa_linux_amd64 sudo chmod +x .bin/opa fi - run: name: Test Policies command: .bin/opa test . - save_cache: key: opa-cache-0.10.6 paths: - .bin
- チェックアウト
- キャッシュがあれば復元
- OPAのバイナリが 無ければ GitHubからダウンロードして実行権限付与
- OPAのテスト実行
- OPAのバイナリをキャッシュしておく
restore_cache
と save_cache
のキーには opa-cache-0.10.6
を使っています。
これは、OPAのバイナリのバージョンが変わったらキャッシュもちゃんと変わるようにするためです。
ただし、これってバージョンが変わるたびに少なくとも3箇所( 0.10.6
となってる部分)は変えないといけないので微妙ですよね。
ということでどうにか1箇所に集約できないかと考えてしまいます。
ちょっとトリッキーなのが、環境変数が restore_cache
や save_cache
の key
に使えない点です(少なくとも僕の2019/04/19時点での認識では)。
- restore_cache: key: opa-cache-${OPA_VERSION}
上記のように書けるとすっきりしそうなんですけど、これができません。ではどうするかというと、以下のフォーラムでやり方を見つけることができました。
次のように書き換えることができます。
- run: name: Setup Environment Variables and Version file command: | echo 'export OPA_VERSION="0.10.6"' >> $BASH_ENV echo "${OPA_VERSION}" > _opa_version_ - restore_cache: key: opa-cache-{{ checksum "_opa_version_" }}
- 環境変数OPA_VERSIONにバージョン番号を入れて
- バージョン番号を
_opa_version_
というファイルにリダイレクト _opa_version_
のchecksum
をキャッシュのキーとして使う
言われてみれば「あ、その手があったか!」ってなります。
ということでこの方法で config.yml
を書き換えると下のようになります。
version: 2 jobs: build: docker: - image: circleci/buildpack-deps:jessie steps: - checkout - run: name: Setup Environment Variables and Version file command: | echo 'export OPA_VERSION="0.10.6"' >> $BASH_ENV echo "${OPA_VERSION}" > _opa_version_ - restore_cache: keys: - opa-cache-{{ checksum "_opa_version_" }} - run: name: Download OPA command: | if [ ! -f .bin/opa ]; then mkdir .bin wget -O .bin/opa https://github.com/open-policy-agent/opa/releases/download/v${OPA_VERSION}/opa_linux_amd64 sudo chmod +x .bin/opa fi - run: name: Test Policies command: .bin/opa test . - save_cache: key: opa-cache-{{ checksum "_opa_version_" }} paths: - .bin
こうすることで下の一行を変えるだけで、OPAの実行バイナリのバージョンを切り替えることができます!
echo 'export OPA_VERSION="0.10.6"' >> $BASH_ENV
今の所の僕の最適解はこのやり方になるのですが、他にも良い方法があればぜひとも知りたいところです!
おわりに
- 依存ライブラリ以外にもCircleCIの
save_cache
やresotre_cache
は使える - 一工夫すればキャッシュのキーに環境変数で設定したバージョン番号を指定できる
参考
ちょうど「CircleCI 福岡ミートアップ」が開催されて、その中でもキャッシュについて触れている発表があったようです!
OPAのテストをCircleCIに乗せる
OPAのPolicyをテストする方法について前回は紹介しました!
OPAのバイナリさえあれば opa test
コマンドで簡単にテストが実行できます。
ということでOPAのPolicyをCIに乗せて継続的にPolicyのテストができるようにしてみます。
どんなCIでもできますが、今回はCircleCIで試してみたのでその方法を共有します。
リポジトリの準備
Policyを書いたRegoファイルは 前回 のものをそのまま使います。
ファイルの構成は以下のとおり。これがGitHubのリポジトリに置いてあることを前提にします。
. ├── iam.rego └── iam_test.rego
ここに .circleci/config.yml
を作ります。
mkdir .circleci # エディタで編集 vim .circleci/config.yml
YAMLの中身は↓
version: 2 jobs: build: docker: - image: circleci/buildpack-deps:jessie steps: - checkout - run: name: Setup Environment Variables and Version file command: | echo 'export OPA_VERSION="0.10.6"' >> $BASH_ENV echo "${OPA_VERSION}" > _opa_version_ - restore_cache: keys: - opa-cache-{{ checksum "_opa_version_" }} - run: name: Download OPA command: | if [ ! -f .bin/opa ]; then mkdir .bin wget -O .bin/opa https://github.com/open-policy-agent/opa/releases/download/v${OPA_VERSION}/opa_linux_amd64 sudo chmod +x .bin/opa fi - run: name: Test Policies command: .bin/opa test . - save_cache: key: opa-cache-{{ checksum "_opa_version_" }} paths: - .bin
これで概ね準備完了です!
書いてる内容はちょっと多いのですがやってることは下の通り。
- リポジトリのチェックアウト
- ダウンロードするOPAのバージョンを環境変数に設定
- OPAバイナリのキャッシュがあれば復元する
- キャッシュが無ければダウンロードして実行権限与える
- Policyを
opa test
でテストする - 次から再利用できるようにOPAバイナリをキャッシュしておく
CircleCIを設定
以下の公式ドキュメントにもありますが、CircleCIに対象となるリポジトリを追加します。
下の画面に表示されている「Start building」をクリックすることでCIが走ります!
正常にCIが走ればテストも通るはずなので、下のように成功画面になります。
では、わざとテストが失敗するように下のように iam_test.rego
を編集しましょう!
新しいbranchで変更し、pushしてからPRを作ってみます。
pushした時点でCIが走るのでPRをGitHub上で作るころには失敗して、下のような画面になっているはずです!
このように、CIに乗せて継続的にPolicyのテストも実行していくことができます。
テスト好きにはたまらないですね!僕はテスト大好きなのでワクワクしてしまいます。
今回のコードは以下リポジトリに置いてますので、参考になればと思います!
おまけ
バッジもREADMEに載せてちょっと今風にしてみました!
CircleCIでプロジェクトの Settings に行って、Status Badgesに移動します。
すると、README.mdとかに貼り付けられるCircleCIのステータスバッジが下図のように記載されています。
これを貼り付ければ、下のようにCIの状況が表示されるバッジを載せることができます!
こういう取り組みも地味にモチベーションに繋がったりするので大事にしたいです。
OPAでテストコードを書く
OPAでPolicyをRegoで書いていくという話は何度かしてきました。
では、そのPolicyを正しく書けたかどうかはどう判断するのでしょう? これまで扱ってきた記事ではInputを用意してOutputをチェックしてました。 それって大変ですし、Policyが鬼のようにたくさんあった場合、デグレチェックも大変です。
そこで登場するのがOPAのテストコードの仕組みです!公式ドキュメントでも詳しい説明があります。
本記事では、以前とりあげたIAM Policyの例を使ってテストコードを書いてみます!
上の記事でテストしたパターンは以下の4つです。
- iam:ChangePasswordが 許可 されるパターン
- s3:ListAllMyBucketsが 許可 されるパターン
- s3:ListBucketが 許可 されるパターン
- s3:CreateBucketが 拒否 されるパターン
対象となるRegoは以下のとおり。
package aws allow { input.action == "iam:ChangePassword" } allow { input.action == "s3:ListAllMyBuckets" } allow { actions_match resources_match } actions_match { actions := ["s3:List.*","s3:Get.*"] action := actions[_] regex.globs_match(input.action, action) } resources_match { resources := ["arn:aws:s3:::confidential-data","arn:aws:s3:::confidential-data/.*"] resource := resources[_] regex.globs_match(input.resource, resource) }
テストの書き方
Regoのテストは(当たり前かもしれませんが)Regoで書きます!
ルールに test_
というprefixをつけることでOPAが暗黙的にテストだと認識します。
opa test
を実行した場合もOPAは test_
で始まるルールを集めてテストを実行するようになってます。
では、さっそくテストを順に書いていってみましょう!
テスト用のルールはどこに書いてもいいんですけど、 iam_test.rego
ファイルを作ってそこに書きましょう。
パッケージは同じにしておきたいので、先頭に package aws
を忘れずに書きます。
iam:ChangePasswordが許可されるパターン
まずは以下のルールをテストしてみましょう。
allow { input.action == "iam:ChangePassword" }
これはすごくシンプルで、 input.actionがiam:ChangePasswordだったら許可する
です。
そのままですね!テストも実はかなりそのまま書くことができます。
test_change_password_action_allowed { allow with input as { "action": "iam:ChangePassword" } }
test_
以降はわかりやすい、好きな名前をつけると良いです。
で、テストしたいルール(この場合 allow
)を書いて、
input
の中身を指定したいので with
と as
を使ってテストデータを注入します。
as
以降にはJSONをそのまま書くことができます!
OPAのバイナリを持ってる場合は以下のコマンドでテストが実行できます。
注意:iam.regoとiam_test.regoがあること
opa test .
VSCodeで拡張を入れている方は OPA: Test Workspace
というコマンドが用意されているので、
それを実行することで結果を見ることも可能です。
s3:ListAllMyBucketsが許可されるパターン
次にテストしたいパターンも上のパターン同様です(actionが違うだけ)。
allow { input.action = "s3:ListAllMyBuckets" }
先程と同様な考えでテストを書くと、次のようになります。
test_s3_ListAllMyBuckets_allowed { allow with input as { "action": "s3:ListAllMyBuckets" } }
s3:ListBucketが許可されるパターン
次にテストしたいのは以下のパターンです。
allow { actions_match resources_match } actions_match { actions = ["s3:List.*","s3:Get.*"] action = actions[_] regex.globs_match(input.action, action) } resources_match { resources = ["arn:aws:s3:::confidential-data","arn:aws:s3:::confidential-data/.*"] resource = resources[_] regex.globs_match(input.resource, resource) }
言葉で表すと arn:aws:s3:::confidential-data配下のresourceであればs3:List系あるいはs3:Get系のactionを許可
になります。
それでは input
を調整してテストしてみましょう!
test_s3_ListBucket_to_confidential_data_allowed { allow with input as { "action": "s3:ListBucket", "resource": "arn:aws:s3:::confidential-data/12345678" } }
これもわかりやすいですね!
s3:ListBucket
が arn:aws:s3:::confidential-data/12345678
に対して許可されることをテストしてます。
opa test .
してテストが通ることを確認しましょう。
s3:CreateBucketが拒否されるパターン
最後に拒否されるパターンも見てみましょう!
test_s3_CreateBucket_to_confidential_data_not_allowed { not allow with input as { "action": "s3:CreateBucket", "resource": "arn:aws:s3:::confidential-data/12345678" } }
action
を s3:CreateBucket
にしてみました。
ここのポイントは、 拒否されること
をテストするということです!
テストをよく見ると not allow
になっていることに気づきましたでしょうか?
そうです、 not
を使うことで拒否されることのテストができます。
テストを実行してみましょう!
$ opa test . data.aws.test_change_password_action_allowed: PASS (7.772µs) data.aws.test_s3_ListAllMyBuckets_allowed: PASS (1.735µs) data.aws.test_s3_ListBucket_to_confidential_data_allowed: PASS (1.239µs) data.aws.test_s3_CreateBucket_to_confidential_data_not_allowed: PASS (978ns) -------------------------------------------------------------------------------- PASS: 4/4
全部PASSしているはずです!
そして、VSCodeでやっている人は OPA: Toggle Workspace Coverage
を実行するとカバレッジも見れますよ!
おわりに
ということがわかりましたー。CIと組み合わせてPolicyのロバストネスを上げれそうですね!
2019-04-12追記:下の記事でCI連携についても書きました!
参考
OPAのVisual Studio Code拡張機能
機能
できるようになることは次のとおり(2019/03/31時点)
- 保存時に構文チェック(現時点でエディタ上で表示はされません)
- OPAのpackageを評価
- 選択中の箇所を評価
- 選択中の箇所を部分評価(Partial Evaluation)
- 選択中の箇所を評価して、トレースを表示
- 選択中の箇所を評価してプロファイルを表示
- ワークスペース内のテストを実行
- ワークスペース内のカバレッジ表示の切り替え
- 選択中の箇所のカバレッジ表示切り替え
準備するもの
OPAのバイナリを忘れずに!
opa
コマンドが叩けることが前提になっているので、ちゃんとインストールしておきましょう。
opa
に PATH
を通しておくか、VSCodeの設定で Opa: Path
にバイナリへのパスを直接指定することもできます!
お試し
拡張機能ページでGIFの紹介アニメーションがあるんですけど、サラッとどんどん進んじゃうので、一個一個見ていきます!
まず下の内容で simple.rego
を書きます。
package http.authz default allow = false allow { input.method = "GET" input.path = ["salary", user] input.user = user }
文でこのポリシーを言い表すと
- デフォルトは許可しない
- 下が全て満たされていれば許可
- 入力
method
が"GET"
- 入力
path
が["salary", user]
である - 入力
user
がpath
のuser
と一致
- 入力
となります。
ワークスペースのルートに input.json
を置くと、その中身をOPAの入力としてお試し実行することができます。
まずは次の内容にしてみましょう!
{ "user": "bob", "method": "GET", "path": ["salary", "bob"] }
bob
は bob
自身の salary
を見ようとしてるので、これは許可されるはずです!
Regoの allow
の部分をvscodeで選択して Cmd + Shift + p
して、OPA: Evaluate Selection
すると、この部分だけ評価できます。
やってみましょう。
期待通り、この結果は true
となります!
では、 path
を bob
から alice
に変えてみるとどうなるでしょ。
{ "user": "bob", "method": "GET", "path": ["salary", "alice"] }
もう一度 allow
を選択して実行すると次の通り。
自分の salary
じゃなくなってるので false
になってますね!
次はルールのBodyを選択して評価します!
input.method
は GET
なので結果は true
ですね!もう一行増やして実行してみましょう。
この結果の output.json
は、複数行関わった場合に何を基準にしているかちょっとわかっていないです。
予想としては ["salary", "alice"] = ["salary", user]
になって、 user
に alice
が代入されているのかなと思っています。そしてその結果が output.json
に出ているのではないかと。(この評価方法が正しいかは別途確認したいです)
では最終行も含めて実行してみましょう!
これは最終的に "bob" = "alice"
しようとして、 true
にならないので、結果は空っぽになります。
input.json
の path
を alice
から bob
に戻しましょう!
{ "user": "bob", "method": "GET", "path": ["salary", "bob"] }
そして実行してみると次のとおり!
何かしら戻り値が返ってくるのがわかります。
上でも返ってきたのと同様 user
に関するJSONなのですが、なぜこの値になるのかは現段階でちょっとわからないです。
allow
を選択して実行すると直感的な true
が返ってくるのですが。。。
おわりに
本記事ではOPAのVSCode拡張機能を紹介しました! 個人的にかなりうれしいのは下の2項目です。
- シンタックスハイライト
input.json
用意すれば即時実行できる
まだまだ使いこなせていないのでこの記事もわかったことが増え次第更新していきたいです!
IAM PolicyをOPAで書く
OPAシリーズです!今回は公式ドキュメントの「Comparison to Other Systems」について見てみます。 世の中に存在しているPolicyのパターンをOPAで実装した場合、どうなるかというのを教えてくれている項目です。
この中でも「Amazon Web Services IAM」をOPAで実装した場合について本記事では見ていきます。
IAM PolicyをOPAで書くとどうなるか
AWSのIAMではユーザーやロール、グループに加えてリソースに対してPolicyをアタッチできます。Policyは「 誰が
、 何に
、 何を
して 良いか
、あるいは ダメか
」を定義することができます。
次のIAMのPolicyを想定してみます。
{ "Version": "2012-10-17", "Statement": [ { "Sid": "FirstStatement", "Effect": "Allow", "Action": ["iam:ChangePassword"], "Resource": "*" }, { "Sid": "SecondStatement", "Effect": "Allow", "Action": "s3:ListAllMyBuckets", "Resource": "*" }, { "Sid": "ThirdStatement", "Effect": "Allow", "Action": [ "s3:List*", "s3:Get*" ], "Resource": [ "arn:aws:s3:::confidential-data", "arn:aws:s3:::confidential-data/*" ] } ] }
IAMに馴染みの無い人(僕もその一人)からするとわかりにくいと思うので、要点だけあげると
iam:ChangePassword
というアクションだったら許可s3:ListAllMyBuckets
というアクションだったら許可arn:aws:s3:::confidential-data
というリソース、あるいはその配下のリソースに対して、s3:List
から始まるアクション、s3:Get
から始まるアクションを許可
という内容になります。これをOPAのRegoで書くと次のようになります!
注意:一部、公式のコメントを日本語に差し替えています
package aws # FirstStatement allow { input.action == "iam:ChangePassword" } # SecondStatement allow { input.action == "s3:ListAllMyBuckets" } # ThirdStatement # ここではヘルパールールを定義することで見やすくしています # actions_matchとresources_matchがtrueの場合にallowがtrueになります allow { actions_match resources_match } # actions_matchはinput.actionがactionsに含まれていればtrueになる actions_match { actions := ["s3:List.*","s3:Get.*"] # actionはactionsを順番にイテレートする、という意味があります action := actions[_] # check if input.action matches an action regex.globs_match(input.action, action) } # resources_matchはinput.resourceがresourcesに含まれていればtrueになる resources_match { resources := ["arn:aws:s3:::confidential-data","arn:aws:s3:::confidential-data/.*"] # resourceはresourcesを順番にイテレートする、という意味があります resource := resources[_] # check if input.resource matches a resource regex.globs_match(input.resource, resource) }
Regoに対する慣れは必要ですが、上から読んでいってそれなりに意味を読み取れるのではないかと思います。 ただし、トリッキーな部分が少なくとも2個あります。
action := actions[_]
のようにイテレーションを宣言してる部分regex.globs_match(input.action, action)
でビルトインな関数を使っている部分
actions_match
のルールをもう少し噛み砕いてみます。
まず、このルールですけど action := actions[_]
と代入してる場所を無くしてみましょう。
個人的には action
が単数形なのがわかりにくくしている気がします。
関係のある箇所だけ見ると下のようになります。
actions_match { actions := ["s3:List.*","s3:Get.*"] regex.globs_match(input.action, actions[_]) }
だいぶすっきりしましたね!このルールを言葉で言うと
インプットで渡ってきた action
が actions
内の正規表現( s3:List.*
, s3:Get.*
) とマッチすれば許可する
という意味になります。 regex.globs_match
は公式ドキュメントにも記載がありますが、最初から使えるビルトインな関数です。
true if the intersection of regex-style globs glob1 and glob2 matches a non-empty set of non-empty strings.
ドキュメントにも書いてあるように、 第1引数と第2引数を総当たりして、正規表現がマッチするものがあれば true
になるという関数です。
試してみたところ、引数は string
でも stringの配列
でも正常に動作する様です。
Playgroundで検証
OPAにはPlaygroundがあるので、下のPlaygroundを使って検証してみましょう!
次のようなInputを渡してみましょう。
{ "action": "s3:ListBucket", "resource": "arn:aws:s3:::confidential-data/12345678" }
arn:aws:s3:::confidential-data
配下のリソースに対して s3:List
から始まるアクションを行っているので許可されるはずです。
Outputをみてみましょう。
{ "result": { "actions_match": true, "allow": true, "resources_match": true } }
true
の評価結果が全部返ってくるので actions_match
も resources_match
も返ってきますが、本命の allow
も true
になっているのがわかりますね!
では s3:ListBucket
を下のように s3:CreateBucket
にしたらどうなるでしょう?
{ "action": "s3:CreateBucket", "resource": "arn:aws:s3:::confidential-data/12345678" }
結果は下の通り。
{ "result": { "resources_match": true } }
リソースのルールは許可されているけど、アクションが拒否されたので allow
は返ってきませんでした!
想定した動きになってそうですね。
では、ちょっと戻りますけど、最初に登場する2つの allow
ルールについても検証してみましょう。
パスワード変更を想定して次のようなInputを渡します。
{ "action": "iam:ChangePassword", "resource": "arn:aws:iam::123456789012:user/JohnDoe" }
結果は下の通り。
{ "result": { "allow": true } }
iam:ChangePassword
であればなんでも許可なので allow
が true
になってます。
最後に s3:ListAllMyBuckets
についても見てみましょう。
{ "action": "s3:ListAllMyBuckets", "resource": "arn:aws:s3:::*" }
結果は下の通り。
{ "result": { "actions_match": true, "allow": true } }
allow
は true
で返ってきますね!また、 s3:List
から始まるアクションなので actions_match
も true
で返ってきているのがわかります。
おわりに
OPAではこのようにAWSのIAMに似たPolicy管理もしていくことができそうですね。ただし、シンプルなパターンを見ているだけなので、複雑なパターンでも検証してみたいところです。 今後のネタとしてその点についてはストックしておきます!
おまけ
regex.globs_match(glob1, glob2)
は第1引数( glob1
)と第2引数( glob2
)を総当たりして true
があれば true
になるみたいなので、Inputを下のようにすると
{ "action": ".*", "resource": ".*" }
正規表現としては なんでもOK
という意味になるので結果は下の通り。
{ "result": { "actions_match": true, "allow": true, "resources_match": true } }
この動きはどうなんだろう、と思いながらも使う側の問題なので制御自体はできそうです。
あるいは、ヘルパー関数は自分でも作れるので、例えば 第1引数の文字列が、第2引数の正規表現のいずれかにマッチする
かチェックする関数を作れば良さそうです。
この点についても今後深掘りしていけたらと思います!
参考
OPAでSSHとsudoの認可をする
前回 に引き続きOPAの勉強をしています。今回の記事では公式チュートリアルの「SSH and sudo Authorization」を見ます。
ゴール
このチュートリアルではLinux-PAM(Pluggable Authentication Modules)とOPAを連動させています。
Linux-PAM(以下PAM)について僕は今回初めて知ったのですが、Linuxに認証の仕組みをプラグインとして追加できるものという理解です。ここでOPAと連動できるPAMを使うことで、認証に加えてPolicyに沿った 認可 も行えるようにします。OPA公式のリポジトリにこのチュートリアル用のOPAのPAMがあります。
これを使って次のことを実現します!
さらに、次の登場人物がいると仮定します。
frontend-dev
はfrontend
ホストで動いているコードにコントリビュートしている開発者backend-dev
はbackend
ホストで動いているコードにコントリビュートしている開発者ops
は組織内のAdmin
図示すると次のようになります。
準備
まずは作業場所を用意します。
$ mkdir ssh_sudo_auth $ cd ssh_sudo_auth
上の図の状況を作り上げるために frontend
, backend
ホストとOPAを docker-compose
で立ち上げます。
# 好きなエディタで作ります
$ vim tutorial-docker-compose.yaml
yaml
の中身は次の通り。
version: '2' services: opa: image: openpolicyagent/opa:0.10.5 ports: - 8181:8181 # WARNING: OPA is NOT running with an authorization policy configured. This # means that clients can read and write policies in OPA. If you are # deploying OPA in an insecure environment, be sure to configure # authentication and authorization on the daemon. See the Security page for # details: https://www.openpolicyagent.org/docs/security.html. command: - "run" - "--server" - "--log-level=debug" frontend: image: openpolicyagent/demo-pam ports: - "2222:22" volumes: - ./frontend_host_id.json:/etc/host_identity.json backend: image: openpolicyagent/demo-pam ports: - "2223:22" volumes: - ./backend_host_id.json:/etc/host_identity.json
WARNING
の部分が何を言っているかというと、 OPA自身をPolicyで守っていないのでセキュアじゃないということです。
例えばREST APIは誰でも叩けてしまうので、OPAに対してリクエストを投げられる人がいれば、誰でもPolicyに変更を加えられちゃいます。
本来どうOPA自身を守るべきかという点については以下の公式ドキュメントにまとめられています。
それでは、このチュートリアル用のPAMがホストを識別するための host_id
情報を含んだ json
を作ります。
PAMはこのJSONを必要に応じて読み込んで、自分が frontend
なのか backend
なのか判別しています。
$ echo '{"host_id": "frontend"}' > frontend_host_id.json $ echo '{"host_id": "backend"}' > backend_host_id.json
準備が整ったので docker-compose
で立ち上げます!
$ docker-compose -f tutorial-docker-compose.yaml up
OPA連携するPAMの概要
このチュートリアルで用意されているPAMとOPAの関係は下図のようだと理解しました。
- PAMがOPAの
pull
エンドポイントに、システムから取得するファイル一覧を聞く - PAMがOPAの
sshd/authz
エンドポイントに、SSHしていいか聞く - PAMがOPAの
sudo/authz
エンドポイントに、sudoしていいか聞く
PAMの設定を実際に見てみましょう。 frontend
コンテナに入ります。
$ docker-compose -f tutorial-docker-compose.yaml exec frontend bash
sudo
のPAM設定を覗いてみます。
$ cat /etc/pam.d/sudo # Enable the Authz PAM module # At image build time, the value of HOST_UUID is replaced with the role of the image (for example, webapp) auth required /lib/security/pam_authz.so url=http://opa:8181 authz_endpoint=/v1/data/sudo/authz display_endpoint=/v1/data/display pull_endpoint=/v1/data/pull log_level=debug
auth required
に/lib/security/pam_authz.so
を指定している- OPAのエンドポイントを
url
でhttp://opa:8181
と指定している pull_endpoint
に/v1/data/pull
を指定しているauthz_endpoint
に/v1/data/sudo/authz
を指定している
のがわかります。 sudo
するときOPAに認可を問い合わせしている雰囲気がなんとなくですけど伝わってきますね!
注意:
display_endpoint
は本記事では扱わないので説明は割愛します。
Policyの作成
pull Policy作成
それでは、まずは pull
エンドポイント用のPolicyを作ります。PolicyはRegoで書きます。
$ cat >pull.rego <<EOF package pull # Which files should be loaded into the context? files = ["/etc/host_identity.json"] # Which environment variables should be loaded into the context? env_vars = [] EOF
上記を用意しておくことで、PAMが pull
エンドポイントに問い合わせをしたときに、どんなファイルを読み込む必要があるかを知ることができます。(この場合は /etc/host_identity.json
を読み込みます)
では、このPolicyをOPAにREST API経由で入れます。
$ curl -X PUT --data-binary @pull.rego \ localhost:8181/v1/policies/pull
sshdの認可Policy作成
では、SSHするときに適用する認可Policyを作りましょう。
$ cat >sshd_authz.rego <<EOF package sshd.authz import input.pull_responses import input.sysinfo import data.hosts # By default, users are not authorized. default allow = false # Allow access to any user that has the "admin" role. allow { data.roles["admin"][_] = input.sysinfo.pam_username } # Allow access to any user who contributed to the code running on the host. # # This rule gets the `host_id` value from the file `/etc/host_identity.json`. # It is available in the input under `pull_responses` because we # asked for it in our pull policy above. # # It then compares all the contributors for that host against the username # that is asking for authorization. allow { hosts[pull_responses.files["/etc/host_identity.json"].host_id].contributors[_] = sysinfo.pam_username } # If the user is not authorized, then include an error message in the response. errors["Request denied by administrative policy"] { not allow } EOF
ポイントとしては以下のとおり。
- 問い合わせしてきたユーザー(
sysinfo.pam_username
)がroles
のadmin
に含まれていれば許可 - 問い合わせしてきたユーザー(
sysinfo.pam_username
)がpull
で指定されたファイル/etc/host_identity.json
のホストにコントリビュートした人(contributors
)であれば許可 - 上に該当しなければエラーとする
input
に関連する情報はPAMがOPAにリクエストするときに渡ってくるデータです。次のようなJSONがリクエストに含まれてくるとイメージしてください。
{ "input": { "display_responses": { "last_name": "<ユーザーが入力した情報。本記事では扱いません。>", "secret": "<ユーザーが入力した情報。本記事では扱いません。>" }, "pull_responses": { "files": { "/etc/host_identity.json": { "host_id": "<JSON内に指定されたhost_id>" } }, "env_vars": {} }, "sysinfo": { "pam_username": "<PAMセッション内の値>", "pam_service": "<PAMセッション内の値>", "pam_req_username": "<PAMセッション内の値>", "pam_req_hostname": "<PAMセッション内の値>" } } }
また、Policy内に登場する roles
やcontributors
のデータはこの後OPAに投入します!
まずは上のPolicyをOPAに投入しましょう。
$ curl -X PUT --data-binary @sshd_authz.rego \ localhost:8181/v1/policies/sshd/authz
sudoの認可Policy作成
sudoするときのPolicyも同じ要領で作ります。
$ cat >sudo_authz.rego <<EOF package sudo.authz # By default, users are not authorized. default allow = false # Allow access to any user that has the "admin" role. allow { data.roles["admin"][_] = input.sysinfo.pam_username } # If the user is not authorized, then include an error message in the response. errors["Request denied by administrative policy"] { not allow } EOF
これはシンプルで、
- 問い合わせしてきたユーザー(
sysinfo.pam_username
)がroles
のadmin
に含まれていれば許可 - 上に該当しなければエラーとする
というPolicyになります。 これもOPAに投入します。
$ curl -X PUT --data-binary @sudo_authz.rego \ localhost:8181/v1/policies/sudo/authz
その他必要なデータの投入
roles
roles
のデータを投入します。これで ops
ユーザーだけが admin
となります。
$ curl -X PUT localhost:8181/v1/data/roles -d \ '{ "admin": ["ops"] }'
hosts
ホストの情報と、そのホストに対するコントリビューターの情報を投入します。
frontend
のコントリビューターとして frontend-dev
を入れて、 backend
のコントリビューターとして backend-dev
を定義します。
$ curl -X PUT localhost:8181/v1/data/hosts -d \ '{ "frontend": { "contributors": [ "frontend-dev" ] }, "backend": { "contributors": [ "backend-dev" ] } }'
実践!
準備ができました!では、Policyがちゃんと動作するか確認していきましょう。
opsユーザーとしてSSHとsudo
まずは ops
ユーザーでSSHとsudoをしてみましょう。 ops
は admin
なので成功するはずです。
ローカルのポート2222が frontend
のホストにつながるはずなので試してみます。
$ ssh -p 2222 ops@localhost \ -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null ops@f55968aabd47:~$
SSHはできました!では、sudoはどうでしょう?
$ sudo ls / OPA-PAM[49]: Session log level is set to debug OPA-PAM[49]: Commencing display cycle. OPA-PAM[49]: Initializing HTTP request GET /v1/data/display OPA-PAM[49]: HTTP request body: (null) OPA-PAM[49]: HTTP request complete, libcURL returned with 0. OPA-PAM[49]: HTTP response body: {} OPA-PAM[49]: Value of field 'result' does not have type object in JSON response. Please ensure that your endpoint flag '/v1/data/display' matches your package path. OPA-PAM[49]: Commencing pull cycle. OPA-PAM[49]: Initializing HTTP request GET /v1/data/pull OPA-PAM[49]: HTTP request body: (null) OPA-PAM[49]: HTTP request complete, libcURL returned with 0. OPA-PAM[49]: HTTP response body: {"result":{"env_vars":[],"files":["/etc/host_identity.json"]}} OPA-PAM[49]: Loaded JSON from file : { "host_id": "frontend" } OPA-PAM[49]: Collecting system information. OPA-PAM[49]: Loaded sysinfo pam_username: ops OPA-PAM[49]: Loaded sysinfo pam_service: sudo OPA-PAM[49]: Loaded sysinfo pam_req_username: ops OPA-PAM[49]: Loaded sysinfo pam_req_hostname: OPA-PAM[49]: Commencing authz cycle. OPA-PAM[49]: Initializing HTTP request POST /v1/data/sudo/authz OPA-PAM[49]: HTTP request body: {"input":{"display_responses":{},"pull_responses":{"files":{"/etc/host_identity.json":{"host_id":"frontend"}},"env_vars":{}},"sysinfo":{"pam_username":"ops","pam_service":"sudo","pam_req_username":"ops","pam_req_hostname":""}}} OPA-PAM[49]: HTTP request complete, libcURL returned with 0. OPA-PAM[49]: HTTP response body: {"result":{"allow":true,"errors":[]}} OPA-PAM[49]: Freeing allocated data. OPA-PAM[49]: Application invoked pam_sm_setcred. bd_build bin boot create_user.sh dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var OPA-PAM[49]: Application invoked pam_sm_setcred.
sudo ls /
してみると、ドカッとログがいっぱい出てきました!これはPAMが debug
モードで走ってるのでログがたくさん出るようになっているからです。
1行1行見ていくとなんとなく何をしているかというのがわかります。ポイントとしては次の行です。
Initializing HTTP request GET /v1/data/pull
Loaded sysinfo pam_username: ops
Initializing HTTP request POST /v1/data/sudo/authz
HTTP response body: {"result":{"allow":true,"errors":[]}}
上で用意したPolicyが動作しているのがわかりますね!最終的に allow: true
になっているので、 ls
の内容も出力されています。
bd_build bin boot create_user.sh dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
SSHから抜けておきましょう。
$ exit
adminじゃないユーザーでsudo
それでは、adminじゃないユーザーでsudoをしてみましょう。
frontend-dev
ユーザーとして frontend
ホストにSSHします。
$ ssh -p 2222 frontend-dev@localhost \ -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null frontend-dev@f55968aabd47:~$
SSHは成功します。ではsudoしたらどうなるでしょう?
$ sudo ls / OPA-PAM[81]: Session log level is set to debug OPA-PAM[81]: Commencing display cycle. OPA-PAM[81]: Initializing HTTP request GET /v1/data/display OPA-PAM[81]: HTTP request body: (null) OPA-PAM[81]: HTTP request complete, libcURL returned with 0. OPA-PAM[81]: HTTP response body: {} OPA-PAM[81]: Value of field 'result' does not have type object in JSON response. Please ensure that your endpoint flag '/v1/data/display' matches your package path. OPA-PAM[81]: Commencing pull cycle. OPA-PAM[81]: Initializing HTTP request GET /v1/data/pull OPA-PAM[81]: HTTP request body: (null) OPA-PAM[81]: HTTP request complete, libcURL returned with 0. OPA-PAM[81]: HTTP response body: {"result":{"env_vars":[],"files":["/etc/host_identity.json"]}} OPA-PAM[81]: Loaded JSON from file /etc/host_identity.json: { "host_id": "frontend" } OPA-PAM[81]: Collecting system information. OPA-PAM[81]: Loaded sysinfo pam_username: frontend-dev OPA-PAM[81]: Loaded sysinfo pam_service: sudo OPA-PAM[81]: Loaded sysinfo pam_req_username: frontend-dev OPA-PAM[81]: Loaded sysinfo pam_req_hostname: OPA-PAM[81]: Commencing authz cycle. OPA-PAM[81]: Initializing HTTP request POST /v1/data/sudo/authz OPA-PAM[81]: HTTP request body: {"input":{"display_responses":{},"pull_responses":{"files":{"/etc/host_identity.json":{"host_id":"frontend"}},"env_vars":{}},"sysinfo":{"pam_username":"frontend-dev","pam_service":"sudo","pam_req_username":"frontend-dev","pam_req_hostname":""}}} OPA-PAM[81]: HTTP request complete, libcURL returned with 0. OPA-PAM[81]: HTTP response body: {"result":{"allow":false,"errors":["Request denied by administrative policy"]}} OPA-PAM[81]: Received authz error log from OPA: Request denied by administrative policy OPA-PAM[81]: Freeing allocated data. Sorry, try again. OPA-PAM[81]: Application invoked pam_sm_authenticate. OPA-PAM[81]: Session log level is set to debug ... OPA-PAM[81]: HTTP response body: {"result":{"allow":false,"errors":["Request denied by administrative policy"]}} OPA-PAM[81]: Received authz error log from OPA: Request denied by administrative policy ... Sorry, try again. ... OPA-PAM[81]: Session log level is set to debug ... OPA-PAM[81]: HTTP response body: {"result":{"allow":false,"errors":["Request denied by administrative policy"]}} OPA-PAM[81]: Received authz error log from OPA: Request denied by administrative policy ... sudo: 3 incorrect password attempts
失敗しているのがわかります。エラー文言もPolicyで設定していた Request denied by administrative policy
になっています。
3回挑戦した結果、 sudo: 3 incorrect password attempts
と言ってあきらめています。
SSHから抜けておきます。
$ exit
コントリビューターじゃないホストへSSH
では、最後にコントリビュートしていないホストへのSSHがちゃんと拒否されるか確認しましょう。
frontend-dev
ユーザーとして backend
ホストにSSHしてみます。( backend
はローカルのポート2223で待ち受けています)
$ ssh -p 2223 frontend-dev@localhost \ -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null frontend-dev@localhost: Permission denied (keyboard-interactive).
ちゃんと拒否されます! docker-compose
のログをみると下のようにエラーを返しているのも確認できます。
opa_1 | time="2019-03-23T07:45:53Z" level=info msg="Sent response." client_addr="172.18.0.3:48434" req_id=39 req_method=POST req_path=/v1/data/sshd/authz resp_body="{\"result\":{\"allow\":false,\"errors\":[\"Request denied by administrative policy\"]}}" resp_bytes=79 resp_duration=2.7604 resp_status=200
準備したPolicyに対して、想定した動きになっているのが確認できました!
おわりに
本記事ではOPAとLinux-PAMを連携することで、SSHとsudoにOPAの認可Policyを適用する方法について見ました! 公式チュートリアルでは最後に 動的にユーザーの権限を昇格させる方法 について述べています。この記事では割愛していますが、ぜひそちらもチャレンジしてみてください。
「どんな人」が「何を」していいかというのをPolicyで制御できる
OPAの可能性の1つを学ぶことができました。
参考
Open Policy Agentを始めてみよう
Open Policy Agent(以下OPA)を最近ちょっとずつ耳にすることが多くなってきた気がします。KubeCon + CloudNativeCon 2017 - Austinで「How Netflix Is Solving Authorization Across Their Cloud」を見たときに「こんな感じに認可を外に出せたらなー」と思った記憶があります。 この記事ではOPAの簡単な紹介と、公式TutorialのGetting Startedを実践した記録を共有します!
OPAとは
OPAは汎用的なPolicy Engineと言われています。Policy Engineとは、定義されたルールに従って 判断を下すことができる専門家 です。Policyの中でも「やっていいかどうか(見ていいかどうか)」に関するものが「Access Policy」と呼ばれ、馴染みがあるかと思います。
例えばWebサービスでユーザーAの情報をログイン中のユーザーが見ていいかどうか、というのをDBに見に行くときに次のようなコード(ここではJavaScript)に見覚えがあるはずです。
// ログイン中のユーザーがAさんのマネージャーだったら参照してOK const canView = login.id === userA.manager_id; if (canView) { // DBからAさんの情報を取得する }
これはこれですぐに読めるコードですが、OPAを使った場合なら次のように書くことができます。
// Aさんの情報を参照していいかどうかチェックする const canView = opa.checkCanView(userA); if (canView) { // DBから取得する処理を記述する }
見ていいかどうかの 判断 がOPAにおまかせ(委譲)になった、というのが大きな違いです。こうすることでメインのアプリケーションでは「やっていいかどうか」を判断する必要が無くなり、OPAに 聞けばOK となります。
- 【注意その1】OPAは別のサービスとして立ち上がっているので、
opa.checkCanView
の中では、OPAへのHTTPリクエストが実行されていると思ってください。- 【注意その2】上の例はOPAを使ってREST APIリクエストの認可を行う例です。OPAが利用できる場面はこれだけではなく、Docker, SSH, sudo, Kafka, Kubernetes Admission Controlなど幅広いです。詳しくは 公式のTutorial を参照ください。
また、 login.id == userA.manager_id
というコードはとても繊細で、 id
だったり manager_id
が固定(ハードコード)されてしまっています。この条件が変わると、メインのアプリケーションのソースコードに影響が出てしまいます。ちょっと考えただけでも下の理由で壊れてしまいそうです。
manager_id
がmanager.id
のように構造が変わった- 「ユーザーAのマネージャー」という観点ではなく、「ログイン中のユーザーの部下」という観点に変わった
- 例:
login.subordinates.include(userA.id)
- 例:
- 「特定の時期に入社していること」が条件に加わった
login.id == userA.manager_id && userA.joined_at.year >= 2019
OPAにおまかせしていれば、ユーザーAというコンテキストが変わらない限りは、メインのアプリケーションへの影響はありません。変更はOPA側で吸収できます。
「やっていいかどうかは専門家におまかせ」で、メインのアプリケーションは本来届けたいと思っている価値の部分に集中できます。
それでは、OPAの仕組みがどうなっているのかというのを見てみましょう!
OPAのPolicy言語Rego
「ログイン中のユーザーが、あるユーザーのマネージャーだったら○○を許可する」というのはPolicyの一例です。OPAは定義されたPolicyに従って、リクエストに応じた判断を返してくれます。そして、Policyを書く際にRegoと呼ばれるOPA用の言語を使うというのが大きな特徴です。
Regoは、プログラミングを経験したことがある人ならなんとなーく読めるのではないでしょうか?長くなってしまうので詳しい説明についてはこの記事では割愛しますが、公式ドキュメントを一度は読んで見ることをおすすめします。
今のところRegoを書く際に最大限気をつけないといけないと思っていることは =
の挙動です。
x := 7 declares that x is a variable local to the rule. Furthermore, the compiler will stop you from writing two such assignments in the same rule, e.g. x := 7; x := 8 creates a compile-time error.
Neither of those are true with x = 7. The rule x = 7 will assign x to 7 if x is unbound, and it will compare x to 7 if x is already bound. Also, if there is a global variable named x, x = 7 will compare that global value of x to 7.
In short, use := if you want assignment inside a rule.
何を言っているのかと言うと、要は変数に値が入っているかどうかで =
は挙動が変わるということです。変数に何も入っていなければ 代入
になり、入っていれば 比較
になります。
OPAはRego用のREPLを用意しているので実際にやってみましょう。
$ docker run -it --rm openpolicyagent/opa run > x = 7 Rule 'x' defined in package repl. Type 'show' to see rules. > x = 7 true > x = 8 undefined
1回目は x
に何も値が入っていないので 代入
になります。2回目は x
に既に値が入っているので 比較
になって、結果が true
になっているのがわかります。3回目は直感的には false
になってほしいところですが、 undefined
になります。これも=
の特徴の様ですが、 true
になるかどうかを判定しにいくため、 true
と判定できないものに関しては undefined
になるというのが仕様みたいです。詳しくは公式ドキュメントの The Basics に記載してます。
この挙動は 直感的じゃない ので、別途代入には :=
という構文が増えて、比較には見慣れた ==
が使えるようになりました。
$ docker run -it --rm openpolicyagent/opa run > x := 7 Rule 'x' defined in package repl. Type 'show' to see rules. > x == 7 true > x == 8 false
こちらのほうがだいぶわかりやすいですね!
Get Startedチュートリアル
さて、Regoの構文の話をしてもPolicyが見えづらいと思うので、実際にPolicyを書きながら雰囲気をつかんでいきましょう。公式のチュートリアル「Get Started」をやってみます。
まずは下図のようにServer, Network, Port情報があったとします。
自分が保有しているサーバーが上の通りだとして、
パブリックネットワークでは暗号化された通信しか許可しない(HTTPはNG)
というPolicyを持ちたいとします。これをRegoで書いてみましょう。
まずは作業用のディレクトリを作ってその中に入ります。
mkdir opa cd opa
上図の情報をJSONにしたものを data.json
として用意します。
$ cat >data.json <<EOF { "servers": [ {"id": "s1", "name": "app", "protocols": ["https", "ssh"], "ports": ["p1", "p2", "p3"]}, {"id": "s2", "name": "db", "protocols": ["mysql"], "ports": ["p3"]}, {"id": "s3", "name": "cache", "protocols": ["memcache"], "ports": ["p3"]}, {"id": "s4", "name": "dev", "protocols": ["http"], "ports": ["p1", "p2"]} ], "networks": [ {"id": "n1", "public": false}, {"id": "n2", "public": false}, {"id": "n3", "public": true} ], "ports": [ {"id": "p1", "networks": ["n1"]}, {"id": "p2", "networks": ["n3"]}, {"id": "p3", "networks": ["n2"]} ] } EOF
上のデータに対してRegoファイルを以下のようにして作ります。
cat >example.rego <<EOF package opa.example import data.servers import data.networks import data.ports public_servers[s] { s := servers[_] s.ports[_] == ports[i].id ports[i].networks[_] == networks[j].id networks[j].public == true } violations[s] { s = servers[_] s.protocols[_] = "http" public_servers[s] } EOF
example.rego
というRegoをこれで書いたことになります。意味としてはざっくり以下のとおり。
opa.example
というパッケージを宣言data
として予め読み込んであるdata.servers
,data.networks
,data.ports
をimport
してルール(public_servers
,violations
)で使えるようにするpublic_servers[s]
ルールを宣言servers
の中でports
で宣言しているport
、networks
で宣言しているnetwork
を使っていて、network
がpublic
のものをpublic_servers
とする
violations[s]
を宣言servers
の中で、protocol
がhttp
かつpublic_servers
に含まれるserver
であればviolations
とする
これを実際 opa
に読み込ませてREPLで使ってみましょう。
docker run -it --rm -v $(pwd):/tmp/workspace -w /tmp/workspace openpolicyagent/opa run data.json example.rego
まず、データがあることを確認してみます。
> data.servers[_] +-------------------------------------------------------------------------------+ | data.servers[_] | +-------------------------------------------------------------------------------+ | {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} | | {"id":"s2","name":"db","ports":["p3"],"protocols":["mysql"]} | | {"id":"s3","name":"cache","ports":["p3"],"protocols":["memcache"]} | | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} | +-------------------------------------------------------------------------------+
先程用意したJSONの情報が読み込まれているのがわかりますね。では public_servers
はどうでしょう?
> data.opa.example.public_servers[_] +-------------------------------------------------------------------------------+ | data.opa.example.public_servers[_] | +-------------------------------------------------------------------------------+ | {"id":"s1","name":"app","ports":["p1","p2","p3"],"protocols":["https","ssh"]} | | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} | +-------------------------------------------------------------------------------+
public == true
なネットワーク(n3)の中のポート(p2)を使用している app
と dev
サーバーが返ってきているのがわかります。ちなみに [_]
の部分は _
に値が無いので 全部
という意味になります。(なので該当するサーバーが全部出てます)
では、 violations
も同様に見てみましょう。
> data.opa.example.violations[x] +-------------------------------------------------------------------+ | data.opa.example.violations[x] | +-------------------------------------------------------------------+ | {"id":"s4","name":"dev","ports":["p1","p2"],"protocols":["http"]} | +-------------------------------------------------------------------+
(結果はトリムしてますが、)先程の public_servers
の結果の中で、プロトコルが http
を使用している dev
サーバーが violation
として返ってきているのがわかります。
このように、宣言的にPolicyを書いていくことができるのがOPAの強みです!
余談:
data.opa.example.violations[_]
と書いた場合になぜかREPLではJSONが返ってきました。x
にすると表だったのですが、この違いがなぜ起きるのかはまだ原因がつかめていません。時間をみつけてOwnerに聞いてみます。
おわりに
今回はOPAの概要とPolicy言語Rego、そしてREPLについて紹介しました。まだ具体的にどう使うのかというのが見えづらい部分があると思いますがこの記事はいったんここまでとします!
- やっていいかどうかを判断してくれる
- 特定の条件を満たしているかどうかを教えてくれる
- PolicyをRegoという独自の言語で書くことができる
点についてはなんとなくイメージできたのではないでしょうか?次回はREPLではなく、実際に「やっていいかどうか」を判断してもらう使い方について紹介します!
参考
How Netflix Is Solving Authorization Across Their Cloud youtu.be
Intro: Open Policy Agent www.youtube.com