外部認証

このタスクでは、actionフィールドの新しい値である`CUSTOM`を使用してIstio認可ポリシーを設定し、アクセス制御を外部認証システムに委譲する方法を示します。これにより、OPA認証oauth2-proxy、独自の外部認証サーバーなどとの統合に使用できます。

始める前に

このタスクを始める前に、以下の手順を実行してください。

  • Istio認証の概念をお読みください。

  • Istio のインストールには、Istio インストールガイドに従ってください。

  • テストワークロードのデプロイ

    このタスクでは、foo 名前空間でデプロイされる 2 つのワークロード、httpbincurl を使用します。両方のワークロードは、Envoy プロキシサイドカーと共に実行されます。次のコマンドを使用して、foo 名前空間とワークロードをデプロイします。

    ZipZip
    $ kubectl create ns foo
    $ kubectl label ns foo istio-injection=enabled
    $ kubectl apply -f @samples/httpbin/httpbin.yaml@ -n foo
    $ kubectl apply -f @samples/curl/curl.yaml@ -n foo
    
  • 次のコマンドを使用して、curlhttpbin にアクセスできることを確認します。

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl http://httpbin.foo:8000/ip -s -o /dev/null -w "%{http_code}\n"
    200
    

外部オーサライザーのデプロイ

まず、外部オーサライザーをデプロイする必要があります。これには、メッシュ内のスタンドアロンポッドにサンプルの外部オーサライザーをデプロイするだけです。

  1. サンプルの外部オーサライザーをデプロイするには、次のコマンドを実行します。

    $ kubectl apply -n foo -f https://raw.githubusercontent.com/istio/istio/release-1.24/samples/extauthz/ext-authz.yaml
    service/ext-authz created
    deployment.apps/ext-authz created
    
  2. サンプルの外部オーサライザーが稼働していることを確認します。

    $ kubectl logs "$(kubectl get pod -l app=ext-authz -n foo -o jsonpath={.items..metadata.name})" -n foo -c ext-authz
    2021/01/07 22:55:47 Starting HTTP server at [::]:8000
    2021/01/07 22:55:47 Starting gRPC server at [::]:9000
    

あるいは、外部認証を必要とするアプリケーションの同じポッド内の別々のコンテナとして外部オーサライザーをデプロイすることも、メッシュの外側にデプロイすることもできます。いずれの場合も、サービスエントリリソースを作成してサービスをメッシュに登録し、プロキシからアクセスできるようにする必要があります。

以下は、外部認証を必要とするアプリケーションの同じポッド内の別々のコンテナにデプロイされた外部オーサライザーのサービスエントリの例です。

apiVersion: networking.istio.io/v1
kind: ServiceEntry
metadata:
  name: external-authz-grpc-local
spec:
  hosts:
  - "external-authz-grpc.local" # The service name to be used in the extension provider in the mesh config.
  endpoints:
  - address: "127.0.0.1"
  ports:
  - name: grpc
    number: 9191 # The port number to be used in the extension provider in the mesh config.
    protocol: GRPC
  resolution: STATIC

外部オーサライザーの定義

承認ポリシーでCUSTOMアクションを使用するには、メッシュで使用できる外部オーサライザーを定義する必要があります。これは現在、メッシュ構成の拡張プロバイダーで定義されています。

現在、サポートされている拡張プロバイダーの種類は、Envoy のext_authzプロバイダーのみです。外部オーサライザーは、対応する Envoy のext_authzチェックAPIを実装する必要があります。

このタスクでは、ヘッダーx-ext-authz: allowを持つリクエストを許可するサンプルの外部オーサライザーを使用します。

  1. 次のコマンドでメッシュ構成を編集します。

    $ kubectl edit configmap istio -n istio-system
    
  2. エディターで、以下に示す拡張プロバイダーの定義を追加します。

    次の内容は、同じサービスext-authz.foo.svc.cluster.localを使用する2つの外部プロバイダーsample-ext-authz-grpcsample-ext-authz-httpを定義しています。このサービスは、Envoyのext_authzフィルタで定義されているHTTPとgRPCの両方のチェックAPIを実装しています。このサービスは次のステップでデプロイします。

    data:
      mesh: |-
        # Add the following content to define the external authorizers.
        extensionProviders:
        - name: "sample-ext-authz-grpc"
          envoyExtAuthzGrpc:
            service: "ext-authz.foo.svc.cluster.local"
            port: "9000"
        - name: "sample-ext-authz-http"
          envoyExtAuthzHttp:
            service: "ext-authz.foo.svc.cluster.local"
            port: "8000"
            includeRequestHeadersInCheck: ["x-ext-authz"]
    

    あるいは、外部オーサライザーに送信するヘッダー、アプリケーションバックエンドに送信するヘッダー、エラー発生時の返すステータスなど、ext_authzフィルタの動作を制御するために拡張プロバイダーを変更することもできます。たとえば、以下はoauth2-proxyで使用できる拡張プロバイダーを定義しています。

    data:
      mesh: |-
        extensionProviders:
        - name: "oauth2-proxy"
          envoyExtAuthzHttp:
            service: "oauth2-proxy.foo.svc.cluster.local"
            port: "4180" # The default port used by oauth2-proxy.
            includeRequestHeadersInCheck: ["authorization", "cookie"] # headers sent to the oauth2-proxy in the check request.
            headersToUpstreamOnAllow: ["authorization", "path", "x-auth-request-user", "x-auth-request-email", "x-auth-request-access-token"] # headers sent to backend application when request is allowed.
            headersToDownstreamOnAllow: ["set-cookie"] # headers sent back to the client when request is allowed.
            headersToDownstreamOnDeny: ["content-type", "set-cookie"] # headers sent back to the client when request is denied.
    

外部認証による有効化

これで、外部オーサライザーが承認ポリシーで使用できるようになりました。

  1. 次のコマンドで外部認証を有効にします。

    次のコマンドは、httpbinワークロードにCUSTOMアクション値を持つ承認ポリシーを適用します。このポリシーは、sample-ext-authz-grpcで定義された外部オーサライザーを使用して、パス/headersへのリクエストに対して外部認証を有効にします。

    $ kubectl apply -n foo -f - <<EOF
    apiVersion: security.istio.io/v1
    kind: AuthorizationPolicy
    metadata:
      name: ext-authz
    spec:
      selector:
        matchLabels:
          app: httpbin
      action: CUSTOM
      provider:
        # The provider name must match the extension provider defined in the mesh config.
        # You can also replace this with sample-ext-authz-http to test the other external authorizer definition.
        name: sample-ext-authz-grpc
      rules:
      # The rules specify when to trigger the external authorizer.
      - to:
        - operation:
            paths: ["/headers"]
    EOF
    

    実行時に、httpbinワークロードのパス/headersへのリクエストはext_authzフィルタによって一時停止され、リクエストを許可または拒否するかどうかを決定するために、チェックリクエストが外部オーサライザーに送信されます。

  2. ヘッダーx-ext-authz: denyを持つパス/headersへのリクエストがサンプルのext_authzサーバーによって拒否されることを確認します。

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -H "x-ext-authz: deny" -s
    denied by ext_authz for not found header `x-ext-authz: allow` in the request
    
  3. ヘッダーx-ext-authz: allowを持つパス/headersへのリクエストがサンプルのext_authzサーバーによって許可されることを確認します。

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/headers" -H "x-ext-authz: allow" -s | jq '.headers'
    ...
      "X-Ext-Authz-Check-Result": [
        "allowed"
      ],
    ...
    
  4. パス/ipへのリクエストが許可され、外部認証がトリガーされないことを確認します。

    $ kubectl exec "$(kubectl get pod -l app=curl -n foo -o jsonpath={.items..metadata.name})" -c curl -n foo -- curl "http://httpbin.foo:8000/ip" -s -o /dev/null -w "%{http_code}\n"
    200
    
  5. サンプルのext_authzサーバーのログを確認して、2回呼び出されたことを確認します(2つのリクエストに対して)。最初のものは許可され、2番目は拒否されました。

    $ kubectl logs "$(kubectl get pod -l app=ext-authz -n foo -o jsonpath={.items..metadata.name})" -n foo -c ext-authz
    2021/01/07 22:55:47 Starting HTTP server at [::]:8000
    2021/01/07 22:55:47 Starting gRPC server at [::]:9000
    2021/01/08 03:25:00 [gRPCv3][denied]: httpbin.foo:8000/headers, attributes: source:{address:{socket_address:{address:"10.44.0.22"  port_value:52088}}  principal:"spiffe://cluster.local/ns/foo/sa/curl"}  destination:{address:{socket_address:{address:"10.44.3.30"  port_value:80}}  principal:"spiffe://cluster.local/ns/foo/sa/httpbin"}  request:{time:{seconds:1610076306  nanos:473835000}  http:{id:"13869142855783664817"  method:"GET"  headers:{key:":authority"  value:"httpbin.foo:8000"}  headers:{key:":method"  value:"GET"}  headers:{key:":path"  value:"/headers"}  headers:{key:"accept"  value:"*/*"}  headers:{key:"content-length"  value:"0"}  headers:{key:"user-agent"  value:"curl/7.74.0-DEV"}  headers:{key:"x-b3-sampled"  value:"1"}  headers:{key:"x-b3-spanid"  value:"377ba0cdc2334270"}  headers:{key:"x-b3-traceid"  value:"635187cb20d92f62377ba0cdc2334270"}  headers:{key:"x-envoy-attempt-count"  value:"1"}  headers:{key:"x-ext-authz"  value:"deny"}  headers:{key:"x-forwarded-client-cert"  value:"By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=dd14782fa2f439724d271dbed846ef843ff40d3932b615da650d028db655fc8d;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/curl"}  headers:{key:"x-forwarded-proto"  value:"http"}  headers:{key:"x-request-id"  value:"9609691a-4e9b-9545-ac71-3889bc2dffb0"}  path:"/headers"  host:"httpbin.foo:8000"  protocol:"HTTP/1.1"}}  metadata_context:{}
    2021/01/08 03:25:06 [gRPCv3][allowed]: httpbin.foo:8000/headers, attributes: source:{address:{socket_address:{address:"10.44.0.22"  port_value:52184}}  principal:"spiffe://cluster.local/ns/foo/sa/curl"}  destination:{address:{socket_address:{address:"10.44.3.30"  port_value:80}}  principal:"spiffe://cluster.local/ns/foo/sa/httpbin"}  request:{time:{seconds:1610076300  nanos:925912000}  http:{id:"17995949296433813435"  method:"GET"  headers:{key:":authority"  value:"httpbin.foo:8000"}  headers:{key:":method"  value:"GET"}  headers:{key:":path"  value:"/headers"}  headers:{key:"accept"  value:"*/*"}  headers:{key:"content-length"  value:"0"}  headers:{key:"user-agent"  value:"curl/7.74.0-DEV"}  headers:{key:"x-b3-sampled"  value:"1"}  headers:{key:"x-b3-spanid"  value:"a66b5470e922fa80"}  headers:{key:"x-b3-traceid"  value:"300c2f2b90a618c8a66b5470e922fa80"}  headers:{key:"x-envoy-attempt-count"  value:"1"}  headers:{key:"x-ext-authz"  value:"allow"}  headers:{key:"x-forwarded-client-cert"  value:"By=spiffe://cluster.local/ns/foo/sa/httpbin;Hash=dd14782fa2f439724d271dbed846ef843ff40d3932b615da650d028db655fc8d;Subject=\"\";URI=spiffe://cluster.local/ns/foo/sa/curl"}  headers:{key:"x-forwarded-proto"  value:"http"}  headers:{key:"x-request-id"  value:"2b62daf1-00b9-97d9-91b8-ba6194ef58a4"}  path:"/headers"  host:"httpbin.foo:8000"  protocol:"HTTP/1.1"}}  metadata_context:{}
    

    ログから、ext-authzフィルタとサンプルのext_authzサーバー間の接続でmTLSが有効になっていることもわかります。これは、ソースプリンシパルにspiffe://cluster.local/ns/foo/sa/curlの値が入力されているためです。

    これで、サンプルのext_authzサーバーに対して別の承認ポリシーを適用して、誰がアクセスできるかを制御できるようになりました。

クリーンアップ

  1. 構成からfoo名前空間を削除します。

    $ kubectl delete namespace foo
    
  2. メッシュ構成から拡張プロバイダーの定義を削除します。

パフォーマンスの期待値

パフォーマンスベンチマークを参照してください。

この情報は役に立ちましたか?
改善のための提案はありますか?

ご意見ありがとうございます!