プラットフォームでポリシーを適用できますか? プラットフォーム L7 ポリシー機能でチームを加速

ポリシーはあなたの得意分野ですか?おそらくそうではないでしょうが、正しく行う必要があります。IstioとOPAで一度設定すれば、チームは最も重要なことに集中できます。

2024年10月14日 | 執筆者: Antonio Berben - Solo.io、Charlie Egan - Styra

共有コンピューティングプラットフォームは、テナントチームにリソースと共有機能を提供することで、すべてをゼロから構築する必要をなくします。テナントからのすべての要求のバランスをとるのが難しい場合もありますが、プラットフォームチームは次の質問をすることが重要です。テナントに提供できる最も価値のある機能は何でしょうか?

多くの場合、作業はアプリケーションチームに直接割り当てられて実装されますが、一度実装してすべてのチームにサービスとして提供するのが最適な機能もあります。ほとんどのプラットフォームチームが実現できる機能の1つは、レイヤー7アプリケーション認証ポリシーのための標準的で応答性の高いシステムを提供することです。ポリシーをコードとして記述することで、チームは認証の決定をアプリケーションレイヤーから軽量でパフォーマンスの高い分離されたシステムに移行できます。難しいように聞こえるかもしれませんが、適切なツールを使用すれば、そうである必要はありません。

ここでは、IstioとOpen Policy Agent(OPA)を使用して、プラットフォームでレイヤー7ポリシーを適用する方法について詳しく説明します。簡単な例を使用して、開始方法を説明します。この組み合わせが、ビジネスのあらゆる場所にいるアプリケーションチームにポリシーを迅速かつ透過的に提供すると同時に、セキュリティチームが監査とコンプライアンスに必要なデータを提供するための確実な選択肢であることがわかります。

試してみる

Istioと統合すると、OPAを使用してマイクロサービスのきめ細かいアクセス制御ポリシーを適用できます。このガイドでは、単純なマイクロサービスアプリケーションのアクセス制御ポリシーを適用する方法を示します。

前提条件

Istioをインストールし、OPAを有効にするようにメッシュオプションを設定します

$ istioctl install -y -f - <<'EOF'
apiVersion: install.istio.io/v1alpha1
kind: IstioOperator
spec:
  meshConfig:
    accessLogFile: /dev/stdout
    accessLogFormat: |
      [OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%"
    extensionProviders:
    - name: "opa.local"
      envoyExtAuthzGrpc:
        service: "opa.opa.svc.cluster.local"
        port: "9191"
EOF

設定では、OPAスタンドアロンインストールを指すextensionProvidersセクションを定義していることに注意してください。

サンプルアプリケーションをデプロイします。Httpbinは、HTTPリクエストをテストするために使用できるよく知られたアプリケーションであり、リクエストとレスポンスの属性をどのように操作できるかをすぐに示すのに役立ちます。

$ kubectl create ns my-app
$ kubectl label namespace my-app istio-injection=enabled

$ kubectl apply -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/httpbin/httpbin.yaml -n my-app

OPAをデプロイします。使用するデフォルトのRegoルールを含むconfigMapが想定されているため、失敗します。このconfigMapは、この例の後半でデプロイされます。

$ kubectl create ns opa
$ kubectl label namespace opa istio-injection=enabled

$ kubectl apply -f - <<EOF
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: opa
  name: opa
  namespace: opa
spec:
  replicas: 1
  selector:
    matchLabels:
      app: opa
  template:
    metadata:
      labels:
        app: opa
    spec:
      containers:
      - image: openpolicyagent/opa:0.61.0-envoy
        name: opa
        args:
          - "run"
          - "--server"
          - "--disable-telemetry"
          - "--config-file=/config/config.yaml"
          - "--log-level=debug" # Uncomment this line to enable debug logs
          - "--diagnostic-addr=0.0.0.0:8282"
          - "/policy/policy.rego" # Default policy
        volumeMounts:
          - mountPath: "/config"
            name: opa-config
          - mountPath: "/policy"
            name: opa-policy
      volumes:
        - name: opa-config
          configMap:
            name: opa-config
        - name: opa-policy
          configMap:
            name: opa-policy
---
apiVersion: v1
kind: ConfigMap
metadata:
  name: opa-config
  namespace: opa
data:
  config.yaml: |
    # Here the OPA configuration you can find in the offcial documention
    decision_logs:
      console: true
    plugins:
      envoy_ext_authz_grpc:
        addr: ":9191"
        path: mypackage/mysubpackage/myrule # Default path for grpc plugin
    # Here you can add your own configuration with services and bundles
---
apiVersion: v1
kind: Service
metadata:
  name: opa
  namespace: opa
  labels:
    app: opa
spec:
  ports:
    - port: 9191
      protocol: TCP
      name: grpc
  selector:
    app: opa
---
EOF

OPAによって保護されるサービスを定義するために、AuthorizationPolicyをデプロイします。

$ kubectl apply -f - <<EOF
apiVersion: security.istio.io/v1
kind: AuthorizationPolicy
metadata:
  name: my-opa-authz
  namespace: istio-system # This enforce the policy on all the mesh being istio-system the mesh config namespace
spec:
  selector:
    matchLabels:
      ext-authz: enabled
  action: CUSTOM
  provider:
    name: "opa.local"
  rules: [{}] # Empty rules, it will apply to selectors with ext-authz: enabled label
EOF

ポリシーを適用するためにアプリにラベルを付けましょう

$ kubectl patch deploy httpbin -n my-app --type=merge -p='{
  "spec": {
    "template": {
      "metadata": {
        "labels": {
          "ext-authz": "enabled"
        }
      }
    }
  }
}'

このリソースでは、Istio設定で設定したOPA extensionProviderを定義していることに注意してください

[...]
  provider:
    name: "opa.local"
[...]

仕組み

AuthorizationPolicyを適用すると、Istioコントロールプレーン(istiod)は、ポリシーで選択されたサービスのサイドカープロキシ(Envoy)に必要な設定を送信します。次に、EnvoyはリクエストをOPAサーバーに送信して、リクエストが許可されているかどうかを確認します。

Istio and OPA

Envoyプロキシは、チェーンでフィルターを設定することによって機能します。これらのフィルターの1つはext_authzであり、特定のメッセージを持つ外部認証サービスを実装します。正しいprotobufを実装するサーバーは、Envoyプロキシに接続して認証の決定を提供できます。OPAはそのようなサーバーの1つです。

Filters

以前、OPAサーバーをインストールしたときは、サーバーのEnvoyバージョンを使用しました。このイメージでは、ext_authz protobufサービスを実装するgRPCプラグインを設定できます。

[...]
      containers:
      - image: openpolicyagent/opa:0.61.0-envoy # This is the OPA image version which brings the Envoy plugin
        name: opa
[...]

設定では、Envoyプラグインとリッスンするポートを有効にしています

[...]
    decision_logs:
      console: true
    plugins:
      envoy_ext_authz_grpc:
        addr: ":9191" # This is the port where the envoy plugin will listen
        path: mypackage/mysubpackage/myrule # Default path for grpc plugin
    # Here you can add your own configuration with services and bundles
[...]

Envoyの認証サービスのドキュメントを確認すると、メッセージにこれらの属性があることがわかります

OkHttpResponse
{
  "status": {...},
  "denied_response": {...},
  "ok_response": {
      "headers": [],
      "headers_to_remove": [],
      "dynamic_metadata": {...},
      "response_headers_to_add": [],
      "query_parameters_to_set": [],
      "query_parameters_to_remove": []
    },
  "dynamic_metadata": {...}
}

これは、authzサーバーからのレスポンスに基づいて、Envoyがヘッダー、クエリパラメーターを追加または削除し、レスポンスステータスを変更できることを意味します。OPAドキュメントに記載されているように、OPAもこれを行うことができます。

テスト

簡単な使用方法(認証)をテストしてから、より高度なルールを作成して、OPAを使用してリクエストとレスポンスを変更する方法を示しましょう。

httpbinサンプルアプリケーションにcurlコマンドを実行するアプリをデプロイします

$ kubectl -n my-app run --image=curlimages/curl curl -- /bin/sleep 100d

最初のRegoルールを適用し、OPAデプロイを再起動します

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: opa-policy
  namespace: opa
data:
  policy.rego: |
    package mypackage.mysubpackage

    import rego.v1

    default myrule := false

    myrule if {
      input.attributes.request.http.headers["x-force-authorized"] == "enabled"
    }

    myrule if {
      input.attributes.request.http.headers["x-force-authorized"] == "true"
    }
EOF
$ kubectl rollout restart deployment -n opa

簡単なシナリオは、値がenabledまたはtrueのヘッダーx-force-authorizedが含まれている場合にリクエストを許可することです。ヘッダーが存在しないか、値が異なる場合、リクエストは拒否されます。

Regoルールを作成するには、複数の方法があります。この場合、2つの異なるルールを作成しました。順番に実行され、すべての条件を満たす最初のルールが使用されます。

簡単なルール

次のリクエストは403を返します

$ kubectl exec -n my-app curl -c curl  -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get

次のリクエストは200と本文を返します

$ kubectl exec -n my-app curl -c curl  -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-authorized: enabled"

高度な操作

次に、より高度なルールです。2番目のRegoルールを適用し、OPAデプロイを再起動します

$ kubectl apply -f - <<EOF
apiVersion: v1
kind: ConfigMap
metadata:
  name: opa-policy
  namespace: opa
data:
  policy.rego: |
    package mypackage.mysubpackage

    import rego.v1

    request_headers := input.attributes.request.http.headers

    force_unauthenticated if request_headers["x-force-unauthenticated"] == "enabled"

    default allow := false

    allow if {
      not force_unauthenticated
      request_headers["x-force-authorized"] == "true"
    }

    default status_code := 403

    status_code := 200 if allow

    status_code := 401 if force_unauthenticated

    default body := "Unauthorized Request"

    body := "Authentication Failed" if force_unauthenticated

    myrule := {
      "body": body,
      "http_status": status_code,
      "allowed": allow,
      "headers": {"x-validated-by": "my-security-checkpoint"},
      "response_headers_to_add": {"x-add-custom-response-header": "added"},
      "request_headers_to_remove": ["x-force-authorized"],
      "dynamic_metadata": {"my-new-metadata": "my-new-value"},
    }
EOF
$ kubectl rollout restart deployment -n opa

そのルールでは、次のことがわかります

myrule["allowed"] := allow # Notice that `allowed` is mandatory when returning an object, like here `myrule`
myrule["headers"] := headers
myrule["response_headers_to_add"] := response_headers_to_add
myrule["request_headers_to_remove"] := request_headers_to_remove
myrule["body"] := body
myrule["http_status"] := status_code

これらは、OPAサーバーからEnvoyプロキシに返される値です。Envoyはこれらの値を使用して、リクエストとレスポンスを変更します。

true/falseだけでなくJSONオブジェクトを返す場合は、allowedが必要であることに注意してください。これはOPAのドキュメントにあります。

返される本文の変更

新しい機能をテストしてみましょう

$ kubectl exec -n my-app curl -c curl  -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get

これで、レスポンス本文を変更できます。403の場合、Regoルールの本文は「Unauthorized Request」に変更されます。前のコマンドでは、次のものが表示されます

Unauthorized Request
http_code=403

返される本文とステータスコードの変更

ヘッダーx-force-authorized: enabledでリクエストを実行すると、本文「Authentication Failed」とエラー「401」が表示されます

$ kubectl exec -n my-app curl -c curl  -- curl -s -w "\nhttp_code=%{http_code}" httpbin:8000/get -H "x-force-unauthenticated: enabled"

リクエストにヘッダーを追加する

有効なリクエストを実行すると、新しいヘッダーx-validated-by: my-security-checkpointと削除されたヘッダーx-force-authorizedを含むエコー本文が表示されます

$ kubectl exec -n my-app curl -c curl  -- curl -s httpbin:8000/get -H "x-force-authorized: true"

レスポンスにヘッダーを追加する

同じリクエストを実行しますが、ヘッダーのみを表示すると、Authzチェック中に追加されたレスポンスヘッダーx-add-custom-response-header: addedが見つかります

$ kubectl exec -n my-app curl -c curl  -- curl -s -I httpbin:8000/get -H "x-force-authorized: true"

フィルター間でデータを共有する

最後に、dynamic_metadataを使用して、後続のEnvoyフィルターにデータを渡すことができます。これは、チェーン内の別のext_authzフィルターにデータを渡したい場合、またはアプリケーションログに印刷したい場合に役立ちます。

Metadata

そのためには、以前に設定したアクセスログの形式を確認してください

[...]
    accessLogFormat: |
      [OPA DEMO] my-new-dynamic-metadata: "%DYNAMIC_METADATA(envoy.filters.http.ext_authz)%"
[...]

DYNAMIC_METADATAは、メタデータオブジェクトにアクセスするための予約キーワードです。残りは、アクセスしたいフィルターの名前です。この場合、名前envoy.filters.http.ext_authzはIstioによって自動的に作成されます。Envoy設定をダンプすることで、これを確認できます

$ istioctl pc all deploy/httpbin -n my-app -oyaml | grep envoy.filters.http.ext_authz

フィルターの設定が表示されます。

動的メタデータをテストしてみましょう。詳細ルールでは、新しいメタデータエントリ:{"my-new-metadata": "my-new-value"}を作成しています。

リクエストを実行し、アプリケーションのログを確認します

$ kubectl exec -n my-app curl -c curl  -- curl -s -I httpbin:8000/get -H "x-force-authorized: true"
$ kubectl logs -n my-app deploy/httpbin -c istio-proxy --tail 1

出力に、OPA Regoルールによって設定された新しい属性が表示されます

[...]
 my-new-dynamic-metadata: "{"my-new-metadata":"my-new-value","decision_id":"8a6d5359-142c-4431-96cd-d683801e889f","ext_authz_duration":7}"
[...]

結論

このガイドでは、IstioとOPAを統合して、単純なマイクロサービスアプリケーションのポリシーを適用する方法を示しました。また、Regoを使用してリクエストとレスポンスの属性を変更する方法も示しました。これは、すべてのアプリケーションチームが使用できるプラットフォーム全体のポリシーシステムを構築するための基礎となる例です。

この記事を共有する