外部認証
このタスクでは、actionフィールドの新しい値である`CUSTOM`を使用してIstio認可ポリシーを設定し、アクセス制御を外部認証システムに委譲する方法を示します。これにより、OPA認証、oauth2-proxy
、独自の外部認証サーバーなどとの統合に使用できます。
始める前に
このタスクを始める前に、以下の手順を実行してください。
Istio認証の概念をお読みください。
Istio のインストールには、Istio インストールガイドに従ってください。
テストワークロードのデプロイ
このタスクでは、
foo
名前空間でデプロイされる 2 つのワークロード、httpbin
とcurl
を使用します。両方のワークロードは、Envoy プロキシサイドカーと共に実行されます。次のコマンドを使用して、foo
名前空間とワークロードをデプロイします。$ 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
次のコマンドを使用して、
curl
がhttpbin
にアクセスできることを確認します。$ 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
外部オーサライザーのデプロイ
まず、外部オーサライザーをデプロイする必要があります。これには、メッシュ内のスタンドアロンポッドにサンプルの外部オーサライザーをデプロイするだけです。
サンプルの外部オーサライザーをデプロイするには、次のコマンドを実行します。
$ 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
サンプルの外部オーサライザーが稼働していることを確認します。
$ 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
を持つリクエストを許可するサンプルの外部オーサライザーを使用します。
次のコマンドでメッシュ構成を編集します。
$ kubectl edit configmap istio -n istio-system
エディターで、以下に示す拡張プロバイダーの定義を追加します。
次の内容は、同じサービス
ext-authz.foo.svc.cluster.local
を使用する2つの外部プロバイダーsample-ext-authz-grpc
とsample-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.
外部認証による有効化
これで、外部オーサライザーが承認ポリシーで使用できるようになりました。
次のコマンドで外部認証を有効にします。
次のコマンドは、
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
フィルタによって一時停止され、リクエストを許可または拒否するかどうかを決定するために、チェックリクエストが外部オーサライザーに送信されます。ヘッダー
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
ヘッダー
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" ], ...
パス
/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
サンプルの
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
サーバーに対して別の承認ポリシーを適用して、誰がアクセスできるかを制御できるようになりました。
クリーンアップ
構成から
foo
名前空間を削除します。$ kubectl delete namespace foo
メッシュ構成から拡張プロバイダーの定義を削除します。
パフォーマンスの期待値
パフォーマンスベンチマークを参照してください。