この文書は自動機械翻訳技術を使用して翻訳されています。 正確な翻訳を提供するように努めておりますが、翻訳された内容の完全性、正確性、信頼性については一切保証いたしません。 相違がある場合は、元の英語版 英語 が優先され、正式なテキストとなります。

これは未公開の文書です Admission Controller 1.34-dev.

コンテキスト認識 CELポリシー

SUSE Security Admission Controllerの`cel-policy`は、コンテキスト認識機能をサポートしています。ポリシーは、クラスター情報を読み取り、ポリシー評価をトリガーしたリソース以外の既存リソースに基づいて意思決定を行う能力を持っています。

これを実現するために、ポリシーに含まれているhttps://github.com/kubewarden/cel-policy?tab=readme-ov-file#host-capabilities[Admission Controllerのホスト機能のためのCEL拡張機能ライブラリ]を使用できます。

例:一意のIngress

Ingressの作成または更新時に、Ingressが一意であることを確認し、ホストが最大で1つのIngressルールを持つようにするポリシーを書きましょう。

そのために、ポリシーがコンテキスト認識であることを宣言します。他のIngressリソースを読み取るために必要な詳細な権限も宣言します。これは`spec.contextAwareResources`(1)で実現されます。通常通り、`kwctl`を使用して出発点を得ることができます:

$ kwctl scaffold manifest -t ClusterAdmissionPolicy \
  registry://ghcr.io/kubewarden/policies/cel-policy:v1.0.0` \
  --allow-context-aware

その後、私たちのIngressリソースに関連するように編集できます:

./cel-policy-ingress.yaml
apiVersion: policies.kubewarden.io/v1
kind: ClusterAdmissionPolicy
metadata:
  name: "unique-ingress"
  annotations:
    io.kubewarden.policy.category: Best practices
    io.kubewarden.policy.severity: low
spec:
  module: ghcr.io/kubewarden/policies/cel-policy:v1.0.0
  failurePolicy: Fail
  rules:
    - apiGroups: ["networking.k8s.io"]
      apiVersions: ["v1"]
      resources: ["ingresses"]
      operations: ["CREATE", "UPDATE"]
  contextAwareResources: # (1)
    - apiVersion: networking.k8s.io/v1
      kind: Ingress

今、クラスター内の既存のIngressを取得するCELコードを書く必要があります。そのために、[Admission Controller CEL拡張機能ライブラリ](https://github.com/kubewarden/cel-policy?tab=readme-ov-file#host-capabilities)を使用します。 特に、グループバージョンKindをクエリすることを可能にする`kw.k8s`ホスト機能です。CEL関数の利用可能なドキュメントは[こちら](https://pkg.go.dev/github.com/kubewarden/cel-policy/internal/cel/library)で確認できます。

ライブラリは、アップストリームのKubernetes CEL拡張と同様にビルダーパターンを使用しており、CEL関数メソッドを呼び出すと、特定の関数メソッドを持つCELオブジェクトが返されます。これにより、私たちのCELコードの範囲と戻り値を確実にすることが簡素化されます。

この場合、kw.k8s.apiVersion("v1").kind("Ingress")`を使用します。ここでは、`apiVersion()`ライブラリの`kw.k8s`関数を呼び出し、<ClientBuilder>`オブジェクトを返します。このオブジェクトには、すべてのリソースのリストを返す`<ClientBuilder>.kind()`メソッドがあります。これは`items`という配列に格納されています。

これにより、クラスター内のIngressのリストを変数に保存します。

variables:
  - name: knownIngresses
    expression: kw.k8s.apiVersion("networking.k8s.io/v1").kind("Ingress").list().items

次に、これらのIngressからホストのリストを構築します。Ingressごとに複数のホストが存在する可能性があるため、この式は配列の配列を保持します(これはCEL言語の現在の制限です):

variables:
  - name: knownHosts
    expression: |
      variables.knownIngresses.map(i, i.spec.rules.map(r, r.host))

しかし、これでは更新操作を正しく処理できません。そのためには、現在のオブジェクトを削除し、残りのIngressからホストを抽出する必要があります。 これを、`filter()`内の現在のオブジェクトに対して`object`を使用して実行できます。 これにより、更新操作が正しくチェックされます。これは、ポリシーが監査スキャナーに対しても結果を正しく報告することを意味します。このように見えます:

variables:
  - name: knownHosts
    expression: |
      variables.knownIngresses
      .filter(i, (i.metadata.name != object.metadata.name) && (i.metadata.namespace != object.metadata.namespace))
      .map(i, i.spec.rules.map(r, r.host))

現在のIngressリクエストにおけるホストのリストも必要です。これを比較します:

variables:
  - name: desiredHosts
    expression: |
      object.spec.rules.map(r, r.host)

これらの2つの変数を使用して、既知のホストと希望するホストの間で集合の交差を行い、もしあれば拒否します:

validations:
  - expression: |
      !variables.knownHost.exists_one(hosts, sets.intersects(hosts, variables.desiredHosts))
    message: "Cannot reuse a host across multiple ingresses"

すべてをまとめると、ポリシーは次のようになります:

apiVersion: policies.kubewarden.io/v1
kind:ClusterAdmissionPolicy
metadata:
  name: "unique-ingress"
  annotations:
    io.kubewarden.policy.category:Best practices
    io.kubewarden.policy.severity: low
spec:
  module: ghcr.io/kubewarden/policies/cel-policy:v1.0.0
  failurePolicy:Fail
  rules:
    - apiGroups: ["networking.k8s.io"]
      apiVersions: ["v1"]
      resources: ["ingresses"]
      operations: ["CREATE", "UPDATE"]
  contextAwareResources:
    - apiVersion: networking.k8s.io/v1
      kind:Ingress
  settings:
    variables:
      - name: knownIngresses
        expression: |
          kw.k8s.apiVersion("networking.k8s.io/v1").kind("Ingress").list().items
      - name: knownHosts
        expression: |
          variables.knownIngresses
          .filter(i, (i.metadata.name != object.metadata.name) && (i.metadata.namespace != object.metadata.namespace))
          .map(i, i.spec.rules.map(r, r.host))
      - name: desiredHosts
        expression: |
          object.spec.rules.map(r, r.host)
    validations:
      - expression: |
          !variables.knownHosts.exists_one(hosts, sets.intersects(hosts, variables.desiredHosts))
        message:"複数のIngress間でホストを再利用することはできません"

Deploying the policy

As normal, we can deploy our policy by instantiating its manifest:

$ kubectl apply -f ./cel-policy-example.yaml

これで、Ingressesをインスタンス化することでテストできます。最初のものは成功します。なぜなら、そのホストをターゲットにする他のものがないからです。

$ kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-host-foobar-1
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/bar"
        backend:
          service:
            name: service1
            port:
              number: 80
EOF

しかし、2番目のものは拒否されます。

$ kubectl apply -f - <<EOF
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: ingress-host-foobar-2
spec:
  rules:
  - host: "foo.bar.com"
    http:
      paths:
      - pathType: Prefix
        path: "/foo"
        backend:
          service:
            name: service2
            port:
              number: 80
EOF
Error from server: error when creating "STDIN":
  admission webhook "clusterwide-unique-ingress.kubewarden.admission" denied the request:
  Cannot reuse a host across multiple ingresses