Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Open Policy Agent / Gatekeeper 勉強会

Open Policy Agent / Gatekeeper 勉強会

A8df61f7319d2c1b4359657b3204ca85?s=128

Akihiro Ikezoe

August 26, 2019
Tweet

Transcript

  1. Open Policy Agent Gatekeeper 勉強会 2019/08/26 (2019/11/24改訂) 池添 明宏

  2. 目次 • OpenPolicyAgent とは • Document • Rego • Gatekeeper

    とは • ポリシーの作り方 • 機能紹介 2
  3. Open Policy Agent とは • 軽量な汎用ポリシーエンジン • ポリシーに基づいて入力データの検証をおこなう • 特徴

    • 宣言的な記述でポリシーの読み書きがしやすい • コンパイルし直すことなくポリシーの変更が可能 3
  4. 何に使えるの? • 例えば Kubernetes において、RBAC よりも柔軟なルールを適用でき る。 • 例: •

    Pod に resource フィールドの指定を強制する • 利用可能なコンテナレジストリを制限する • 特定のユーザーに一部のリソースへのアクセスを制限する 4
  5. Data & Policy 5 • Data (Document) • サービスやユーザーが用意するデータ •

    JSON 形式 • Policy (Rule) • データに適用する検証ルール • Rego という独自言語で定義 出展: https://www.openpolicyagent.org/docs/latest/how-does-opa-work/
  6. Document の種別 • Base Documents • サービスやユーザーが用意した データ • Virtual

    Documents • Rule を適用した結果データ • Virtual Documents にさらに Rule を適用することも可能 6 出展: https://www.openpolicyagent.org/docs/latest/how-does-opa-work/
  7. Documents の構造 • data • 検証に利用する静的なデータ • Rule を適用した結果データ •

    input • 検証対象の入力データ 7 出展: https://www.openpolicyagent.org/docs/latest/how-does-opa-work/
  8. Rule から Document へのアクセス • import で Document にアクセス できる

    • 例えば opa.examples.allow とい うルールを適用した結果の Virtual Document は、data.opa.example s.allow に入っている。 8 import data.opa.examples.allow import data.servers rule { server := servers[_] input.server == server allow[server] }
  9. Rules • Rego という独自言語でデータの検証ルールを記述する • Rego • Datalog という Prolog

    系言語をインスパイア • 宣言的な記述が可能 • The Rego Playground で試してみよう • https://play.openpolicyagent.org/ 9
  10. Rego による Rule の定義 10 # BODYの条件を満たした場合 # HEADの値を返す rule_name

    <HEAD> { <BODY> } # 条件を満たさなかった場合 # undefinedを返す # 配列の場合は空配列を返す # HEADの省略は =trueと同じ rule1 { input.x < input.limit } # 固定値を返す rule2 = "abc" { input.x < input.limit } # 変数を返すことも可能 rule3 = x { x := input.x x < input.limit }
  11. AND 条件と OR 条件 11 # 羅列するとAND条件 rule_name <HEAD> {

    <CONDITION1> <CONDITION2> <CONDITION3> } # OR条件は同一名のルールを # 複数個用意する rule_name <HEAD1> { <CONDITION1> } rule_name <HEAD2> { <CONDITION2> }
  12. Go 言語と比較してみる 12 # Rego default allow = false allow

    = true { input.size != 0 input.size < data.limit } # Go func allow() bool { if input.size == 0 { return false } if input.size >= data.limit { return false } return true }
  13. else • 同一名の複数ルールは適用順序が考 慮されない。矛盾したルールは記述 することができない。 • 順序を考慮する場合は else キーワー ドを利用する。

    13 rule_name <HEAD1> { <CONDITION1> } else <HEAD2> { <CONDITION2> }
  14. 繰り返し 14 containers = { "ap": { "image": "ubuntu:18.04" },

    "db": { "image": "mysql:latest" }, } # latestイメージを抽出してkeyを返す rule[key] = image { image := containers[key].image endswith(image, ":latest") } numbers = [1, 2, 3, 4, 5] # 偶数だけを抽出 rule[x] { x := numbers[_] x % 2 == 0 }
  15. For Any, For All 15 # For All # 条件を逆にして否定する

    positive { not isNegative } isNegative { x := numbers[_] x < 0 } # 内包表記 positives { negative := [x | x := numbers[_]; x < 0] count(negatives) == 0 } # For Any numbers = [1, 2, 3, 4, 5] # 1つでも条件に一致すればtrue rule { x := numbers[_] x >= 0 }
  16. Go 言語と比較してみる 16 # Rego images = [ "quay.io/cybozu/ubuntu", "docker.io/nginx",

    "localhost/nginx" ] repos = [ "quay.io", "docker.io" ] rule[img] { img := images[_] rep := repos[_] startswith(img, rep) } # Go func rule() []string { var result []string for _, img := range images { for _, rep := range repos { if strings.HasPrefix(img, rep) { result = append(result, img) } } } return result }
  17. 関数定義 • ルールと似たような記法で関数 を定義することができる。 • 関数は引数を受け取ることがで きる。 • ルールのように Virtual

    Document は生成されない 17 starts_with_foo(s) { startswith(s, "foo") } remove_prefix_foo(s) = r { starts_with_foo(s) r := substring(s, 3, -1) }
  18. 組み込み関数 • 集計: count(), sum(), sort(), all(), any() • 文字列:

    contains(), startswidt(), endswith(), replace(), split(), sprintf(), substring() • 正規表現: re_match(), regex.split() • 型判定: is_number(), is_string(), is_set() • エンコーディング: json.marshal()/unmarshal(), yaml.marshal()/unmarshal() • そのほか Token Signing/Verification, 時間, HTTP通信 などなど 18
  19. 集合演算 • 集合を扱うことができる • 和集合、差集合、積集合などの 計算が可能 • 空集合は {} ではなく

    set() 19 resources = { "pod", "service", "deployment", "node" } apps = { "pod", "deployment" } hello[x] { # 差集合の計算 x := resources - apps }
  20. コマンドラインツール • バイナリダウンロード • https://github.com/open-policy-agent/opa/releases • 文法チェック • opa check

    hoge.rego • 実行 • opa eval --input input.json --data hoge.rego –format pretty "data.query" • フォーマット • opa fmt -w hoge.rego 20
  21. テスト • with xxx as yyy 構文を利用して、 テスト対象のルールに任意の入力 を与えることができる。 •

    下記のコマンドでテスト実行 • opa test -v . 21 package k8s.test_admission import data.k8s.admission test_deny { input1 := { "review": { "namespace": "kube-system" }, "parameters": { "systemNamespaces": [ "kube-system", "kube-public" ] } } # k8s.admission.violation というルールをテスト res := admission.violation with input as input1 count(res) > 0 }
  22. Gatekeeper • Open Policy Agent を Kubernetes で使えるようにしたもの • Kubernetes

    の Admission Controller Webhook による呼び 出し • ポリシーを Custom Resource と して定義可能 • 共通処理をライブラリとして利用 可能 22 出展: vhttps://kubernetes.io/blog/2019/08/06/opa- gatekeeper-policy-and-governance-for-kubernetes/
  23. Admission Controller Webhook • Kubernetes の拡張機能のひとつ • Kubernetes のリソースを作成、更新、削除するタイミング で外部の

    Webhook API を呼び出す • Validating: リソースが条件を満たしているかチェック • Mutating: リソースの一部を書き換える • Gatekeeper v3 では未対応 (対応中) 23
  24. Gatekeeper の歴史 • Gatekeeper v1 • OpenPolicyAgent + kube-mgmt •

    Gatekeeper v2 • OpenPolicyAgent + kube-mgmt + kube-policy-controller • Gatekeeper v3 • kubebuilder を利用して全面刷新 24
  25. 動かしてみよう • https://github.com/zoetrope/gatekee per-demo • kind をセットアップ • kind create

    cluster --name gatekeeper --config cluster.yaml • kubectl apply -f https://raw.githubuse rcontent.com/open-policy-agent/gate keeper/master/deploy/gatekeeper.ya ml 25 # cluster.yaml kind: Cluster apiVersion: kind.sigs.k8s.io/v1alpha3 kubeadmConfigPatches: - | apiVersion: kubeadm.k8s.io/v1beta2 kind: ClusterConfiguration metadata: name: config apiServer: extraArgs: "enable-admission-plugins": "ValidatingAdmissionWebhook" nodes: - role: control-plane - role: worker
  26. 動かしてみよう • 特定のレジストリ以外のコンテナイメージの利用を禁止するポリ シーを用意する • ポリシーを記述した ConstraintTemplate カスタムリソースを作成 • ポリシーの適用対象とパラメータを記述した

    Constraint カスタムリ ソースを作成 26
  27. カスタムリソース • ConstraintTemplate • Gatekeeper が Custom Resource Definition を用意している

    • Constraint の名前とスキーマを定義 • ポリシーを Rego で記述 • Constraint • ConstraintTemplate から動的に作られる Custom Resource Definition • ポリシー適用対象リソースとパラメータを指定 27
  28. ConstraintTemplate 28 apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: k8sallowedrepos spec:

    crd: spec: names: kind: K8sAllowedRepos # Constraint の Custom Resource 名 validation: # `parameters` フィールドのスキーマ openAPIV3Schema: properties: repos: type: array items: type: string targets: # 後述
  29. ConstraintTemplate 29 apiVersion: templates.gatekeeper.sh/v1beta1 kind: ConstraintTemplate metadata: name: k8sallowedrepos spec:

    crd: # 省略 targets: - target: admission.k8s.gatekeeper.sh # 固定 rego: | package k8sallowedrepos violation[{"msg": msg}] { container := input.review.object.spec.containers[_] satisfied := [good | repo = input.parameters.repos[_]; good = startswith(container.image, repo)] not any(satisfied) msg := sprintf("container <%v> has an invalid image repo <%v>", [container.name, container.image]) }
  30. Constraint 30 apiVersion: constraints.gatekeeper.sh/v1beta1 kind: K8sAllowedRepos metadata: name: repo-is-cybozu spec:

    match: # 対象リソースを指定 kinds: - apiGroups: [""] kinds: ["Pod"] parameters: # パラメータの指定。rego から input.parameters としてアクセス可能 repos: - "quay.io/cybozu/"
  31. 入力データ 31 { "review": { # AdmissionReview "uid": "705ab4f5-6393-11e8-b7cc-42010a800002", "kind":

    {"group":"apps","version":"v1","kind":"Pod"}, "resource": {"group":"apps","version":"v1","resource":"pods"}, "operation": "CREATE", "userInfo": { "username": "admin", "groups": ["system:authenticated","system:masters"], }, "object": { "apiVersion":"v1","kind":"Pod",... }, }, # Constraintで指定したパラメータ "parameters": {"repos": ["quay.io/cybozu",...]} }
  32. 検証してみよう $ kubectl apply -f nginx.yaml Error from server ([denied

    by repo-is-cybozu] container <nginx> has an invalid image repo <nginx:latest>): error when creating "nginx.yaml": admission webhook "validation.gatekeeper.sh" denied the request: [denied by repo-is-cybozu] container <nginx> has an invalid image repo <nginx:latest> 上記のようなメッセージが表示されたら成功 32 # nginx.yaml apiVersion: v1 kind: Pod metadata: labels: run: nginx name: nginx namespace: default spec: containers: - name: nginx image: nginx:latest
  33. データの同期 • Kubernetes クラスタの特定リ ソースを Rego から利用できるよ うにする。 • client-go/informer

    により結果を キャッシュ。 • Audit 機能や namespaceSelector を利用するときにも必要。 33 apiVersion: config.gatekeeper.sh/v1alpha1 kind: Config metadata: name: config namespace: "gatekeeper-system" spec: sync: syncOnly: - group: "" version: "v1" kind: "Namespace" - group: "" version: "v1" kind: "Pod"
  34. 同期データの利用方法 • Cluster-scoped Resource • data.inventory.cluster[<groupVe rsion>][<kind>][<name>] • Namespaced Resource

    • data.inventory.namespace[<na mespace>][<groupVersion>][<k ind>][<name>] 34 allPods[name] = pod { # 全namespaceのPodを取得 pod := data.inventory .namespace[_][_]["Pod"][name] }
  35. Audit • ValidatingAdmissionWebhook はリソースの作成、更新、削除など のタイミングで実行される • Audit 機能を利用すると、定期的にポリシーのチェックが実施される • 検証対象のリソースは同期設定しておく必要ある

    • Constraint リソースの Status に検証結果が保存される 35
  36. トレース • トレース機能を利用すると、検証に 利用した入力データや検証結果がロ グに出力される • 大量にログが出力されるので、デ バッグ用途での利用を推奨 • 正直トレースの読み方がわからない

    36 apiVersion: config.gatekeeper.sh/v1alpha1 kind: Config metadata: name: config namespace: "gatekeeper-system" spec: validation: traces: - user: "kubernetes-admin" kind: group: "" version: "v1" kind: "Pod" dump: "All"
  37. Rego のテスト • ConstraintTemplate の中に Rego が埋め込まれてるのでテストしにくい。 • ConstraintTemplate のベースファイルに

    Rego を埋め込む Kustomize プラグイ ンをつくってみた。 • https://github.com/zoetrope/ConstraintTemplateGenerator 37
  38. おしまい(所感) • 最初はとっつきにくいかもしれないけど、慣れればそれなりに書き やすい。 • デバッグしにくいので、トレース周りは改善してほしい。 • argo-cd と相性悪いのをなんとかしたい。 •

    パフォーマンスがよくないかも? • https://github.com/open-policy-agent/gatekeeper/issues/266 38