✍️

ツールごとのOPA/Regoの書き方

2023/11/16に公開

Regoとは

KubernetesやTerraformの静的解析で既存のルールでは足りないときや自分でカスタマイズしたいときにRegoというポリシー言語でコードを書く

https://www.openpolicyagent.org/docs/latest/policy-language/

Regoを利用できるツールの例

conftest

  • サンプルマニフェスト
ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: hoge
  labels:
    name: hoge
    istio.io/rev: stable
  • Namespaceにistioのrevisionが設定されているか検査する
istio.rego
package main

deny[msg] {
  input[i].contents.kind == "Namespace"
  labels := input[i].contents.metadata.labels
  not object.get(labels, "istio.io/rev", false)

  msg := "Isito not injected."
}

deny[msg] {
  input[i].contents.kind == "Namespace"
  labels := input[i].contents.metadata.labels
  object.get(labels, "istio.io/rev", false) != "stable"

  msg := "Revision is not stable."
}

trivy

ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: hoge
  labels:
    name: hoge
    istio.io/rev: stable
  • conftestのregoと内容は同じだが書き方が少しだけ違う
istio.rego
# METADATA
# title: Istio Injection
# description: Istio revision is not set.
# schemas:
#   - input: schema["kubernetes"]
# custom:
#   id: ID001
#   severity: HIGH
#   input:
#     combine: true
#     selector:
#     - type: kubernetes

package user.kubernetes.ID001

deny[res] {
  input[i].contents.kind == "Namespace"
  resource := input[i].contents

  labels := resource.metadata.labels
  not object.get(labels, "istio.io/rev", false)

  res := result.new("Isito not injected.", resource.kind)
}

deny[res] {
  input[i].contents.kind == "Namespace"
  resource := input[i].contents

  labels := resource.metadata.labels
  object.get(labels, "istio.io/rev", false) != "stable"

  res := result.new("Revision is not stable.", resource.kind)
}
  • METADATAが利用できる

  • result.new(msg, cause)が結果

    • msgは問題の発生を表す文字列
    • causeは問題が発生したプロパティ/オブジェクト
  • 実行

    • package user.kubernetes.ID001にしたのでuserを指定
    trivy conf --config-policy istio.rego --policy-namespaces user ns.yaml
    

kics

ns.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: hoge
  labels:
    name: hoge
    istio.io/rev: stable
  • trivyだとregoファイルに書いていたmetadataが外に切り出されている
queries/istio/metadata.json
{
  "id": "88add025-722a-4ec5-9b7d-e058779f24a9",
  "queryName": "Istio Injection",
  "severity": "HIGH",
  "category": "Build Process",
  "platform": "Kubernetes",
  "descriptionText": "Istio revision is not set."
}
  • trivyと同様に書き方が異なる
queries/istio/query.rego
package Cx

CxPolicy[result] {
    document := input.document[i]
    labels := document.metadata.labels

    not object.get(labels, "istio.io/rev", false)

    result := {
        "documentId": document.id,
        "resourceType": document.kind,
        "resourceName": document.metadata.name,
        "searchKey": sprintf("Namespace name: %s", [document.metadata.name]),
        "issueType": "MissingAttribute",
        "keyExpectedValue": "labels: istio.io/rev",
        "keyActualValue": "labels: ",
    }
}

CxPolicy[result] {
    document := input.document[i]
    labels := document.metadata.labels

    rev := object.get(labels, "istio.io/rev", false)
    rev != "stable"

    result := {
        "documentId": document.id,
        "resourceType": document.kind,
        "resourceName": document.metadata.name,
        "searchKey": sprintf("Namespace name: %s", [document.metadata.name]),
        "issueType": "IncorrectValue",
        "keyExpectedValue": "labels: istio.io/rev: stable",
        "keyActualValue": sprintf("labels: istio.io/rev: %s", [rev]),
    }
}
  • packageは必ずCx

  • denyではなくCxPolicy

  • 結果

    • searchKey: 引っ掛かったリソースを調べる情報。S3バケットならタグなど
    • issueType: IncorrectValue, MissingAttribute, RedundantAttribute
    • keyExpectedValue: 期待値
    • keyActualValue: 実際の値
  • 実行

    • デバッグ時や詳細を知りたいとき
      • -v scan
      • scan --log-levelのデフォルトがINFOなのでDEBUGやTRACEにする
    kics scan -t Kubernetes -q queries -p ns.yaml
    
  • ライブラリ

hoge.rego
package Cx

import data.generic.common as common_lib
import data.generic.k8s as k8sLib

CxPolicy[result] {
  document := input.document[i]
  metadata := document.metadata

  specInfo := k8sLib.getSpecInfo(document)
  ...

Discussion