Slide 1

Slide 1 text

©2021 VMware, Inc. Open Policy Agent (OPA) と Kubernetes Policy Mar. 12, 2021 CTO, North Asia (Japan, Korea and Greater China) Motonori Shindo / motonori_shindo

Slide 2

Slide 2 text

3 ©2021 VMware, Inc. そもそもポリシーって何︖ 何かしらによって課される制約に対して、どうある べきかを規定するもの • 法律、条例 • ビジネスルール • アプリケーション要求 • 地域的制約 • セキュリティ的要件 • … Photo by Scott Graham on Unsplash

Slide 3

Slide 3 text

4 ©2021 VMware, Inc. 多くのシステムには個別にポリシーが存在している Policy Policy Policy Policy Policy Policy Policy Policy

Slide 4

Slide 4 text

5 ©2021 VMware, Inc. Open Policy Agent (OPA) とは Domain Agnositc な Policy Engine OPA は Policy Decision だけを⾏い、Policy Enforcement には関与しない Rego という Datalog Inspired な宣⾔的 Policy ⾔ 語を持つ オープンソース 2021.02 に CNCF を卒業 利⽤⽅法 Library (Go)、REST API、Wasm Source: https://www.openpolicyagent.org/docs/latest/

Slide 5

Slide 5 text

6 ©2021 VMware, Inc. Rego Primer by Example Network, Server, App Toplogies { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } JSON web app db p1 p2 p3 p4 Net1 (public) Net2 (private) https ssh tomcat mysql Internet

Slide 6

Slide 6 text

7 ©2021 VMware, Inc. Rego Primer by Example (1) Complete Rules, References, Arrays, Logical AND, Assignments, Anonymous Variable, Packages package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output

Slide 7

Slide 7 text

8 ©2021 VMware, Inc. Rego Primer by Example (1) Complete Rules package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output Complete Rule: = { } が true であれば = になる。 ”= true” は省略可能。

Slide 8

Slide 8 text

9 ©2021 VMware, Inc. Rego Primer by Example (1) References package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output “input” は予約されたグローバル変数。

Slide 9

Slide 9 text

10 ©2021 VMware, Inc. References Rego Primer by Example (1) package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output “.” でつなぐ事により、JSON の階層デ ータにアクセスすることができる。

Slide 10

Slide 10 text

11 ©2021 VMware, Inc. Rego Primer by Example (1) Arrays, Anonymous variables package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output [ ] は配列を表す。‘_’ は無名変数。後に参 照する必要がなければ無名変数を使うこと ができる。

Slide 11

Slide 11 text

12 ©2021 VMware, Inc. Rego Primer by Example (1) Logical AND package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output 中の複数⾏の は、Logical AND として解釈される。” ; ” と書いても同様。

Slide 12

Slide 12 text

13 ©2021 VMware, Inc. Rego Primer by Example (1) Assignments package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output ”:=” は assginment(代⼊) operator。Rego の変数 は immutable なので、同じ変数に⼆度 ”:=” で代⼊す ることはできない。

Slide 13

Slide 13 text

14 ©2021 VMware, Inc. Rego Primer by Example (1) Packages package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output Package は Rego のルールに名前空間 を作り出す。Data API で呼び出される 場合も、この名前空間が使われる。

Slide 14

Slide 14 text

15 ©2021 VMware, Inc. Rego Primer by Example (1) Output package example.rules any_public_networks = true { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "any_public_networks": true } Policy Input Output 最終的に Output が返ってくる。

Slide 15

Slide 15 text

16 ©2021 VMware, Inc. Rego Primer by Example (2) Partial Rules package example.rules public_network[net.id] { net := input.networks[_] net.public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "public_network": [ "net1" ] } Policy Input Output が [ ] を持っている場合は、Partial Rule と呼ばれ、複数の値をセットするのに 使われる。

Slide 16

Slide 16 text

17 ©2021 VMware, Inc. Rego Primer by Example (3) Logical OR package example.rules shell_accessible[server.id] { server := input.servers[_] server.proto[_] == "telnet" } shell_accessible[server.id] { server := input.servers[_] server.proto[_] == "ssh" } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "shell_accessible": [ "web" ] } Policy Input Output 同じ を持つルールが複数ある場合 は、それらは Logical OR と解釈される。

Slide 17

Slide 17 text

18 ©2021 VMware, Inc. Rego Primer by Example (4) Iterations, Joins package example.rules public_ports[id] { some i, j id := input.ports[i].id input.ports[i].network == input.networks[j].id input.networks[j].public } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "public_ports": [ "p1" ] } Policy Input Output Rego では に “some” で宣⾔ した変数を埋め込むことで暗黙的にループ が形成される

Slide 18

Slide 18 text

19 ©2021 VMware, Inc. Comprehensions Rego Primer by Example (5) package example.rules public_ports[port] { port := {p | p = input.ports[_] ; n = input.networks[_] ; p.network == n.id ; n.public } } { "servers": [ { "id": "web", "proto": ["https", "ssh"], "ports": ["p1", "p2"]}, { "id": "app", "proto": ["tomcat"], "ports": ["p3"]}, { "id": "db", "proto": ["mysql"], "ports": ["p4"]} ], "networks": [ {"id": "net1", "public": true}, {"id": "net2", "public": false} ], "ports": [ {"id": "p1", "network": "net1"}, {"id": "p2", "network": "net2"}, {"id": "p3", "network": "net2"}, {"id": "p4", "network": "net2"} ] } { "public_ports": [ [ { "id": "p1", "network": "net1" } ] ] } Policy Input Output 数学の集合と同様、内包表現 { 出⼒要素 | 条件 } も可能

Slide 19

Slide 19 text

22 ©2021 VMware, Inc. Rego 組み込み関数 https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions ⽐較 • ==, !=, <, <=, >, >= 数 • +, -, *, /, %, round(), abs(), etc. 集約 • count(), sum(), max(), min(), product(), sort(), etc. 配列 • concat(), slice() 集合 • get(), remove(), union(), filter(), etc. ⽂字列 • concat(), contains(), startwith(), endswith(), etc. 正規表現 • match(), is_valid(), split(), find_n(), etc. グロブ • match(), quote_meta() ビット処理 • or(), and(), negate(), xor(), lsh(), rsh() 変換 • to_number() 型 • is_number(), is_string(), is_boolean(), etc. エンコード • encode(), decode(), marshal(), unmarshal(), etc.

Slide 20

Slide 20 text

23 ©2021 VMware, Inc. Rego 組み込み関数(続き) https://www.openpolicyagent.org/docs/latest/policy-reference/#built-in-functions トークン署名 • encode_sign_raw(), encode_sign() トークン検証 • verify_rs256(), verify_rs384(), etc. 時刻 • date(), clock(), weekday(), add_date(), etc. 暗号 • md5(), sha1(), sha256(), parse_certficates(), etc. グラフ • walk(), reachable() HTTP • send() ネットワーク • cidr_contain(), cidr_intersects(), etc. UUID • rfc4122() セマンティック・バージョン • is_valid(), compare() Rego • parse_module() OPA • runtime() デバッグ • trace()

Slide 21

Slide 21 text

24 ©2021 VMware, Inc. Rego Playground https://play.openpolicyagent.org/

Slide 22

Slide 22 text

26 ©2021 VMware, Inc. OPA エコシステム

Slide 23

Slide 23 text

27 ©2021 VMware, Inc. Kubernetes と OPA のインテグレーション - Gatekeeper Kubernetes API Server と OPA の 間のブリッジとして 動作 API Server が Gatekeeper の Webhook をトリ ガー 課したい制約を Rego で記述 Source: https://kubernetes.io/blog/2019/08/06/opa-gatekeeper-policy-and-governance-for-kubernetes/

Slide 24

Slide 24 text

28 ©2021 VMware, Inc. Policy Template と Policy Instance Resource apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: k8srequiredlabels spec: crd: spec: names: kind: K8sRequiredLabels validation: # Schema for the `parameters` field openAPIV3Schema: properties: labels: type: array items: string targets: - target: admission.k8s.gatekeeper.sh rego: | package k8srequiredlabels violation[{"msg": msg, "details": {"missing_labels": missing}}] { provided := {label | input.review.object.metadata.labels[label]} required := {label | label := input.parameters.labels[_]} missing := required - provided count(missing) > 0 msg := sprintf("you must provide labels: %v", [missing]) } apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sRequiredLabels metadata: name: ns-must-have-gk spec: match: kinds: - apiGroups: [""] kinds: ["Namespace"] parameters: labels: ["gatekeeper"]

Slide 25

Slide 25 text

29 ©2021 VMware, Inc. Tanzu Mission Control で提供されている Policy Template

Slide 26

Slide 26 text

30 ©2021 VMware, Inc. Policy の例 (1) – tmc-block-nodeport-service パラメータ無しのケース

Slide 27

Slide 27 text

31 ©2021 VMware, Inc. Policy の例 (1) – tmc-block-nodeport-service パラメータ無しのケース apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: tmc-block-nodeport-service spec: crd: spec: names: kind: tmc-block-nodeport-service targets: - target: admission.k8s.gatekeeper.sh rego: | package tmcblocknodeportsvc violation[{"msg": msg}] { input.review.kind.kind == "Service" input.review.kind.group == "" input.review.object.spec.type == "NodePort" msg := "service of type NodePort is forbidden" } { "review": { "object": { "apiVersion": "v1", "kind": "Service", "spec": { "type": "NodePort" }, }, "kind": { "kind": "Service", "group": "", "version": "v1" }, } } フルバージョンの Admission Review Request は ここ ConstraintTemplate Admission Review Request (関連部分のみ)

Slide 28

Slide 28 text

32 ©2021 VMware, Inc. Policy の例 (2) – tmc-require-labels パラメータ有りのケース

Slide 29

Slide 29 text

34 ©2021 VMware, Inc. Policy の例 (2) – tmc-require-labels パラメータ有りのケース apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: tmc-require-labels : targets: - target: admission.k8s.gatekeeper.sh rego: | package tmcrequirelabels violation[{"msg": msg, "details": {"missing_labels": missing}}] { provided := {label | input.review.object.metadata.labels[label]} required := {label | label := input.parameters.labels[_].key} missing := required - provided count(missing) > 0 msg := sprintf("You must provide labels with keys: %v", [missing]) } violation[{"msg": msg}] { value := input.review.object.metadata.labels[key] expected := input.parameters.labels[_] expected.key == key expected.value != "" expected.value != value msg := sprintf("Label <%v: %v> must match the value: %v", [key, value, expected.value]) } { "parameters": { "labels": [ { "value": "production", "key": "env" }, ] }, "review": { "object": { "apiVersion": "apps/v1", "kind": "Deployment", "metadata": { "labels": { "app": "nginx", "env": "production" }, }, }, } } Admission Review Request (関連部分のみ) ConstraintTemplate フルバージョンの Admission Review Request は ここ

Slide 30

Slide 30 text

35 ©2021 VMware, Inc. VMware HANDS-ON LABS / HOL-2132-01-MAP https://labs.hol.vmware.com/HOL/catalogs/catalog/1212

Slide 31

Slide 31 text

37 Confidential │ ©2020 VMware, Inc. 練習問題

Slide 32

Slide 32 text

38 ©2021 VMware, Inc. securityContext の privileged が true な Pod の作成を許可しないポリシーを書きなさい。 練習問題 (1) apiVersion: v1 kind: Pod metadata: name: nginx-non-privileged labels: app: nginx spec: containers: - name: nginx image: nginx securityContext: privileged: false apiVersion: v1 kind: Pod metadata: name: nginx-privileged labels: app: nginx spec: containers: - name: nginx image: nginx securityContext: privileged: true 許可されるケース 許可されないケース フルバージョンの Admission Review Request は ここ フルバージョンの Admission Review Request は ここ

Slide 33

Slide 33 text

40 ©2021 VMware, Inc. containerPort が「min < ポート番号 < max」の範囲となる Pod だけ作成を許可するポリシー を書きなさい。ただし、min、max はパラメータとして渡すこととする。 練習問題 (2) apiVersion: v1 kind: Pod metadata: name: nginx-port-8080 labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 8080 apiVersion: v1 kind: Pod metadata: name: nginx-port-80 labels: app: nginx spec: containers: - name: nginx image: nginx ports: - containerPort: 80 許可されるケース 許可されないケース フルバージョンの Admission Review Request は ここ フルバージョンの Admission Review Request は ここ

Slide 34

Slide 34 text

42 ©2021 VMware, Inc. Namespace 名に特定の⽂字列(例えば “slave” など)が含まれている namespace の作成を許 可しないポリシーを書きなさい。ただし、拒否する⽂字列は複数与えることができるものとする 。 ヒント︓ 組み込み関数の ”contains()” を使ってみよう 練習問題 (3) % kubectl create namespace ns-good % kubectl create namespace ns-slave 許可されるケース 許可されないケース フルバージョンの Admission Review Request は ここ フルバージョンの Admission Review Request は ここ

Slide 35

Slide 35 text

58 ©2021 VMware, Inc. Open Policy Agent 本家のサイト • https://www.openpolicyagent.org/ • https://github.com/open-policy-agent Tanzu Mission Control で学ぶ Open Policy Agent Part 1 〜 4 by VMware 星野さん • https://blog.lespaulstudioplus.info/posts/tmc-demanabu-opa/ OPA Deep Dive, Kubecon NA 2019 • https://www.youtube.com/watch?v=Uj2N9S58GLU TGIK 119 Gatekeeper and OPA • https://www.youtube.com/watch?v=ZJgaGJm9NJE Styra • https://www.styra.com/ • https://academy.styra.com/ 参考リンク

Slide 36

Slide 36 text

©2021 VMware, Inc. Thank You