Slide 1

Slide 1 text

汎用ポリシー言語Rego + OPAと認可・検証事例の紹介 2024 / 05 / 17 Ubie株式会社 水谷正慶 Cloud Native Security Japan Kickoff meetup

Slide 2

Slide 2 text

2 本日の発表 ● 発表内容 ○ 汎用ポリシー言語Rego + OPAとは何か ○ Regoの利用事例の紹介 ■ 既存プロダクトでの利用(Gatekeeper for Kubernetes, Envoy) ■ Go SDKを使ったRego・OPAの活用 ● 本日は触れない内容 ○ Regoの詳細な文法 ○ Rego利用プロダクトの設定方法

Slide 3

Slide 3 text

3 自己紹介 ● 水谷正慶(Ph.D) ● 職域:セキュリティエンジニア & バックエンドエンジニア ● 経歴:Ubie (2021~), Cookpad(2017~), IBM(2011~) ● OPA/Regoとの関わり ○ 2021年末にOPA/Regoについての一人アドベントカレンダーを執 筆 https://adventar.org/calendars/6601 ○ そのきっかけでOPA Championを拝命 ○ セキュリティ・キャンプなどでPolicy as Codeに関する講義を担当 (2022~)

Slide 4

Slide 4 text

4 汎用ポリシー言語 Rego+OPAとは何か

Slide 5

Slide 5 text

5 Rego&OPAについて ● Rego https://www.openpolicyagent.org/docs/latest/ ○ 宣言型の汎用ポリシー記述言語 ○ Prolog・Datalogから着想を得て、改良された言語 ○ 認可・検証機能と相性が良いが、それ以外にも幅広く応用可能 ● OPA(Open Policy Agent)https://github.com/open-policy-agent/opa ○ Regoを動かすためのエンジン ○ コマンドラインツール & Go SDKとして提供されている

Slide 6

Slide 6 text

6 Regoの特徴 ● 宣言型言語である ○ 一般的なプログラミングに用いられる手続き型言語ではない ○ 繰り返しや変数の取り回しのパラダイムが手続き型と異なる ● (原則として)外部入出力は使わない ○ 判定に必要な引数(入力)と、判定結果となる返り値(出力)のみ ○ コアとなる判定のロジックのみに集中して記述する ● 入力と出力のスキーマは任意 ○ 構造データを自由に利用できるため、柔軟性が高く特定のプロダクト に依存しない

Slide 7

Slide 7 text

7 Regoの記述例 package my_policy allow { input.user == “alice” } allow { allowed_roles := [ “admin”, “developer”, ] input.role == allowed_roles[_] } 1つのブロックを「ルール」と呼 ぶ。これは “allow” という名 前のルール
 同じ名前のルールは複数あっ てもよい。ただし異なる評価 結果が重複してはいけない (undefined はOK) ブロック内の評価式 がすべて成立すれば “allow” に true が格 納される。成立しな ければ不定 (undefined) になる 必要に応じて変数の 定義が可能 [_] はリスト内のすべ ての要素を表す。1つで もこの式を満たす要素 があれば成立する

Slide 8

Slide 8 text

8 OPAによるRegoの評価例 { “user”: “bob”, “role”: “admin” } { “allow”: true } package my_policy allow { input.user == “alice” } allow { allowed_roles := [ “admin”, “developer”, ] input.role == allowed_roles[_] } ❌ 非成立
 ✅成立
 Regoルール Input Output undefined true

Slide 9

Slide 9 text

9 つまりOPAとは何か? ● 入力された構造データ(JSONなど)をRegoで評価し、結果の構造データを出力す るだけのエンジン ○ 入出力に使われる構造データのスキーマは自由であり、多様なデータをあつかえる ○ そのため特定のツールやプロダクトに依存せず利用可能 ○ 文法やエンジンがオープンかつ柔軟なので、独自のDSLを実装したりYAMLやJsonで条件 を記述させるより、低コスト・高信頼で実装可能 入力 (構造データ) ポリシー (Rego) 出力 (構造データ)

Slide 10

Slide 10 text

10 Policy as Code ● ポリシーをコードで表現・管理するアプローチ ○ 履歴・承認管理、テスト可能性、再現性、継続的デプロイなどの利点 ○ Rego & OPAと同じ文脈で語られることが多い ● 「Policy as Code == Rego」ではない ○ Policy as Code を実現する手段の一つがRego ○ Regoの利用目的の一つがPolicy as Codeの実現

Slide 11

Slide 11 text

11 Policy as Code実現方法の比較(Rego v.s. SQL) データ処理の特性 Rego & OPA SQL (BigQueryなど) 大量データ ☔ 1件あたりの評価は100〜200μ秒(※1) 程度 だが、集計のためには大量のメモリが必要 ☀ 本来の用途なので有効に活用できる ルールの記述性・ テスト可能性 ☀ ルールをモジュラブルに記述できる。さらに 統合テスト・ユニットテストが記述可能 ☁ 一つのクエリに検証のための要素が密結合しがち。 テストもデータの用意などが大変 リアルタイム性 ☀ 遅延が小さいので都度認可判定するという ユースケースに向いている ☔ 本来の用途と異なり、数ms以下の遅延を期待する場 合は困難 複数データの結合 ☔ 大規模なデータの結合はパフォーマンスに影 響する ☀ 本来の用途なので有効に活用できる ※1:SDKを利用してApple M1 Max 2021年モデルで計測

Slide 12

Slide 12 text

12 Regoの利用事例の紹介

Slide 13

Slide 13 text

13 Gatekeeper for Kubernetes:リソース作成・変更の検証 ● Admission Controllers と連携してリソースを検証 ○ リソースの作成や変更をRegoによて検証できる ○ Dynamic Admission Controllersでより動的な検証が可能になる ○ ConstraintTemplateを使うことで検証の自由度が向上 https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes/ より

Slide 14

Slide 14 text

14 Gatekeeper のポリシー例 package kubernetes.admission import rego.v1 deny contains reason if { some container input_containers[container] not startswith(container.image, "hooli.com/") reason := "container image refers to illegal registry (must be hooli.com)" } input_containers contains container if { container := input.request.object.spec.containers[_] } input_containers contains container if { container := input.request.object.spec.template.spec.containers[_] } request.object.spec.containers と request.object.spec.template.spec.containres の両方からコンテナ情報を取得
 コンテナイメージ名のPrefixが hooli.com/ になっているかを検証
 検証に失敗した場合はメッ セージを残す
 Validating Webhook の検証

Slide 15

Slide 15 text

15 Envoy:L7 proxyにおけるトラフィックの認可制御 ● EnvoyのExternal Authorization APIを利用したOPAとの連携 ○ サイドカーとして各マイクロサービスへのAPI認可を制御 ○ リクエストに含まれるパスやメソッド、ヘッダの情報を自由に組み合わせ たルールが記述できる https://www.openpolicyagent.org/docs/latest/envoy-introduction/ より

Slide 16

Slide 16 text

16 Envoyのポリシー例 allow if { is_token_valid action_allowed } is_token_valid if { token.valid now := time.now_ns() / 1000000000 token.payload.nbf <= now now < token.payload.exp } action_allowed if { http.method == "GET" token.payload.role == "guest" glob.match("/people/*", ["/"], http.path) } action_allowed if { http.method == "POST" token.payload.role == "admin" glob.match("/people", ["/"], http.path) lower(input.parsed_body.firstname) != base64url.decode(token.payload.sub) } token := {"valid": valid, "payload": payload} if { [_, encoded] := split(http.headers.authorization, " ") [valid, _, payload] := io.jwt.decode_verify(encoded, {"secret": "secret"}) } (1) is_token_valid と action_allowed の両方が成立した場合通信を許可
 (2) JWTのnbf & expを検証
 (3) 「メソッドがGET」
 「JWTのroleが “guest”」
 「パスが “/people/*”」
 の全てに合致したらOK
 (4) 「メソッドがPOST」
 「JWTのroleが “admin”」
 「パスが “/people”」
 の全てに合致したらOK
 (5) JWTのデコードと検証


Slide 17

Slide 17 text

17 Go SDKを使ったRego・OPAの活用 ● ユーザー設定値の検査 ○ Conftest[1] : Kubernetesの設定ファイルを検査 ○ tfsec[2] : terraformの .tf ファイルの内容を検査 ● ワークフロー制御での利活用 ○ AlertChain[3] : SOAR (Security Orchestration, Automation and Response) におけるワークフローの記述 ○ Swarm[4] : セキュリティログのスキーマの検出や、データの変換・整形に利用 ● 通知の制御 ○ Octovy[5] : 脆弱性スキャンの結果の通知判断に利用 [1] https://www.conftest.dev/ [2] https://github.com/aquasecurity/tfsec [3] https://github.com/m-mizutani/alertchain [4] https://github.com/m-mizutani/swarm [5] https://github.com/m-mizutani/octovy

Slide 18

Slide 18 text

18 まとめ ● ポリシーの柔軟な記述が可能なRegoとそのエンジンであるOPA ○ KubernetesやEnvoyでリソース制御や通信の可否の制御など ○ 設定値の検証ツールで独自ルールを記述しやすくなる ○ ワークフローの制御にも活用可能 ● 利用するうえでの課題 ○ まだ普及しているとは言い難い状況で、世の中に参考事例が少ない ○ 手続き型プログラミングに慣れた人だと敷居が高い ○ SDKとして利用する場合、公式からはGoしかサポートされていない