コンテンツにスキップ

Open Policy Agent

はじめに

本サイトにつきまして、【IT技術の知見】はじめに - 俺の技術ノート をご認識のほど宜しくお願いいたします。


01. Open Policy Agent の仕組み

アーキテクチャ

Open Policy Agent は、Open Policy Agent、rego ファイル、DB、といったコンポーネントから構成される。

open-policy-agent_architecture


Open Policy Agent

Open Policy Agent は、rego ファイルの認可スコープに基づいて許否を決定し、boolean 値を返す。

返却された boolean 値に基づいて、クライアント側 (例:アプリケーション、kube-apiserver) で認可結果を適用する。

open-policy-agent


rego ファイル

認可スコープのロジックを定義する。


DB

資格情報を json 形式、認可スコープ定義を rego 形式で、保管する。


02. ユースケース

アプリケーションの認可マイクロサービスとして

▼ アプリケーションの認可マイクロサービスとは

アプリケーションの認可スコープ定義の責務を認可マイクロサービスとして切り分ける。

アプリケーションは Open Policy Agent にリクエストを送信し、Open Policy Agent は認可スコープに基づいて許否を決定して boolean 値を返す。

返却された boolean 値に基づいて、アプリケーションは認可結果を適用する。

▼ 資格情報の作成

  1. 資格情報を json 形式で作成する。

    ここでは、各アカウントが一般社員または管理職のいずれかであるかを定義している。

    // subordinates.json ファイル
    {"alice": ["bob"], "bob": [], "charlie": ["david"], "david": []}
    
  2. アプリケーションは、Open Policy Agent に資格情報を送信し、資格情報を DB に作成する。

    $ curl \
        -X PUT \
        -H 'Content-Type:application/json' \
        --data-binary @subordinates.json \
        127.0.0.1:8181/v1/data/subordinates
    

▼ 認可スコープの定義

  1. 認可スコープ定義のロジックを rego 形式で作成する。

    package httpapi.authz
    
    # 社員の資格情報をインポートする。
    import data.subordinates as subord
    
    default allow = false
    
    # 一般社員は、自身の給与のみで参照権限を持つ (trueを返却する) 。
    allow {
      some username
      input.method == "GET"
      input.path = ["finance", "salary", username]
      input.user == username
    }
    
    # 管理職は、自身と部下社員の給与で参照権限を持つ (trueを返却する) 。
    allow {
      some username
      input.method == "GET"
      input.path = ["finance", "salary", username]
      subord[input.user][_] == username
    }
    
  2. アプリケーションは、Open Policy Agent に rego ファイルを送信し、認可スコープ定義を DB に作成する。

    $ curl \
        -X PUT \
        -H 'Content-Type: text/plain'\
        --data-binary @httpapi_authz.rego \
        127.0.0.1:8181/v1/policies/httpapi_authz
    

▼ 認可スコープのリクエスト

  1. 認可スコープを取得するためのリクエストを json 形式で作成する。

    実際は、aliceというアカウントがデータを参照できるかどうかを取得するために、アプリケーションがリクエストを作成する。

    // request.json ファイル
    {
      // リクエストの内容
      "input": {
          // GET メソッド
          "method": "GET",
          // パス
          "path": ["finance", "salary", "alice"],
          // アカウント名
          "user": "alice",
        },
    }
    
  2. アプリケーションは、alice アカウントの参照権限の有無を Open Policy Agent にリクエストを送信する。

    Open Policy Agentは、アプリケーションにtrueを返却する。

    $ curl \
        -X POST \
        -H 'Content-Type:application/json' \
        --data-binary @request.json \
        127.0.0.1:8181/v1/data/httpapi/authz/allow | jq .
    
    {
      "result": "true"
    }
    


Kubernetes の Gatekeeper として

▼ Gatekeeper とは

これは認可ツールではなく、マニフェストのコード規約違反を検知する PaC ツールである。

内部的に Open Policy Agent を使用して、Kubernetes のマニフェストを検証する。

kube-apiserver の validating-admission ステップ時に、Gatekeeper の webhook サーバーに AdmissionReview のリクエストが送信され、Gatekeeper の持つ Open Policy Agent の処理を発火させる。

そのため、GitOps の CD パイプライン上にバリデーションを実行できる。

kubernetes_open-policy-agent

▼ gatekeeper-validating-webhook-configuration

Pod の作成/更新時に webhook サーバーへリクエストを送信できるようにする。そのため、ValidatingWebhookConfiguration で ValidatingWebhook アドオンを設定する。

.webhooks.failurePolicy キーで設定しているとおり、webhook サーバーのコールに失敗した場合は、無視して kube-apiserver の処理を続ける。

そのため、Gatekeeper が起動に失敗しても、Pod が中止されることはない。

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingWebhookConfiguration
metadata:
  name: gatekeeper-validating-webhook-configuration
  labels:
    gatekeeper.sh/system: "yes"
webhooks:
  # webhook 名は完全修飾ドメイン名にする。
  - name: validation.gatekeeper.sh
    admissionReviewVersions: ["v1", "v1beta1"]
    clientConfig:
      # webhook サーバーを Cluster 内部に自作する場合は、webhook サーバーに証明書バンドルを登録する。
      caBundle: Ci0tLS0tQk...
      # Gatekeeper の Webhook Service の宛先情報を登録する。
      service:
        name: gatekeeper-webhook-service
        namespace: gatekeeper-system
        # エンドポイント
        path: /v1/admit
        port: 443
    failurePolicy: Ignore
    matchPolicy: Exact
    namespaceSelector:
      matchExpressions:
        - key: admission.gatekeeper.sh/ignore
          operator: DoesNotExist
    objectSelector: {}
    # validating-admission ステップ発火条件を登録する。
    rules:
      - apiGroups: ["*"]
        apiVersions: ["*"]
        operations: ["CREATE", "UPDATE"]
        resources: ["*"]
        scope: "*"
    sideEffects: None
    timeoutSeconds: 3
    # webhook 名は完全修飾ドメイン名にする。
  - name: check-ignore-label.gatekeeper.sh
    admissionReviewVersions: ["v1", "v1beta1"]
    clientConfig:
      # webhook サーバーを Cluster 内部に自作する場合は、webhook サーバーに証明書バンドルを登録する。
      caBundle: Ci0tLS0tQk...
      # Gatekeeper の Webhook Service の宛先情報を登録する。
      service:
        name: gatekeeper-webhook-service
        namespace: gatekeeper-system
        # エンドポイント
        path: /v1/admitlabel
        port: 443
    # webhook サーバーのコールに失敗した場合の処理を設定する。
    failurePolicy: Fail
    matchPolicy: Exact
    namespaceSelector: {}
    objectSelector: {}
    # validating-admission ステップ発火条件を登録する。
    rules:
      - apiGroups: [""]
        apiVersions: ["*"]
        operations: ["CREATE", "UPDATE"]
        resources: ["namespaces"]
        scope: "*"
    sideEffects: None
    timeoutSeconds: 3