https://istio.io/latest/docs/concepts/security/
istioのsecurityコンセプトについてまとめられたドキュメントを見ていたら、ゼロトラストや多層防御などと同様にアプリケーションコードからセキュリティを分離するのもスコープとなっていました。認証プロセスをアプリケーションコードから分離するのに良さそうです。
istioを使ってアプリケーションコードを変更せずにアクセスコントロールができるかを見ていきます
実行環境はwsl2(ubuntu20.04)で構築したkubernetesクラスタで行います
主要ソフトウェアとしては以下のバージョンを利用しています
minikube version: v1.15.1
kubernetes v1.20.0
istio v1.8.1
oauth2-proxyを利用したOpenID Providerとの連携
一例として、workloadへのアクセスについてOIDC認証することを強制してみます。
ありがたいことにJetstackからoauth2-proxyの設定例が公開されているので参考にします。
ここでやることは以下です
・RequestAuthenticationによってワークロードのリクエスト認証方法をGoogle OAuth applicationと定義
・AuthorizationPolicyによってワークロードへのアクセスにAuthorizationヘッダを含むことを強制
・oauth2-proxyがreverse proxyとして振る舞うことでGoogle OAuth applicationに認証を移譲
証明書を用意
cert-managerでletsencryptから証明書を取得してくれるはずなんですが、私の環境では以下のエラーによりアクセス出来ませんでした。とりあえずローカルで起動するように"*.127.0.0.1.nip.io"のドメインに対する証明書を作ってお茶を濁します。
curl: (35) OpenSSL SSL_connect: SSL_ERROR_SYSCALL in connection to :443
SSL通信のためワイルドカード証明書を作成しistio-systemにデプロイします
#!/usr/bin/env bash
mkcert "*.127.0.0.1.nip.io"
mv _wildcard.127.0.0.1.nip.io-key.pem server.key
mv _wildcard.127.0.0.1.nip.io.pem server.crt
kubectl create -n istio-system secret generic wildcard-clusterlocal-credential --from-file=key=server.key --from-file=cert=server.crt
Google OAuth applicationの作成
Google OAuth applicationはkubeflowのドキュメントを参考にしながら作成します
https://www.kubeflow.org/docs/gke/deploy/oauth-setup/
各種設定
ブログではコマンドラインで実行できるようになっているのでターゲットのドメインとして
$NGINX_DOMAIN=istio-oicd-auth.127.0.0.1.nip.ioと設定します
oauth2-proxyのconfigurationについては以下で定義されています
https://oauth2-proxy.github.io/oauth2-proxy/docs/configuration/overview/
trusted-ipなどの設定も可能なのでinternalな利用も出来そうですね
RequestAuthentication,AuthorizationPolicyが実際どう使われているかですが、以下のように指定プロバイダからの署名であることの検証とリクエストプリンシパルの検証として定義されています
apiVersion: security.istio.io/v1beta1
kind: RequestAuthentication
metadata:
name: istio-ingressgateway
namespace: istio-system
spec:
selector:
matchLabels:
app: istio-ingressgateway
jwtRules:
- issuer: $OIDC_ISSUER_URL
jwksUri: $OIDC_JWKS_URI
---
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
name: istio-ingressgateway
namespace: istio-system
spec:
selector:
matchLabels:
app: istio-ingressgateway
rules:
- from:
- source:
requestPrincipals: ["*"]
デプロイ
ブラウザからhttps://istio-oicd-auth.127.0.0.1.nip.ioにアクセスするとプロバイダの認証画面に遷移するようになります。ブログでも言及されているようにEnvoyFilterはingressgatewayではなくsidecarを選択することも可能で認証を必要としないアプリケーションとの共存ももちろん可能です。
opa-envoy-pluginを利用したサイドカーパターン
次の例として、Envoy&OPAポリシーでexternal-authorizationを実装するパターンを検証してみます。
ネットワークホップなしにpolicyを評価できるのが魅力的です。
リポジトリ内にistioでのquick startが用意されているのでこれをベースにして進めます
進めていくとadmissionwebhookが用意されinjectionを指定したnamespace内のpodに対してsidecarがデプロイされます。https://github.com/open-policy-agent/opa-envoy-plugin/blob/master/examples/istio/quick_start.yaml#L313
configmapに定義したpolicyに基づいて認可を行います
https://github.com/open-policy-agent/opa-envoy-plugin/blob/master/examples/istio/quick_start.yaml#L393
aliceはguestとして定義されているので/api/v1/productsにaliceとしてリクエストをすると403を返却します
istioが採用しているEnvoyのExternalAuthorizationを使いsidecarとしてデプロイされたOPAがアクセス制御することでアプリケーションから認可を分離できました
❯ curl --user alice:password -i http://127.0.0.1/api/produtspage
HTTP/1.1 200 OK
content-type: text/html; charset=utf-8
content-length: 3769
server: istio-envoy
date: Sun, 20 Dec 2020 05:18:54 GMT
x-envoy-upstream-service-time: 35
❯ curl --user alice:password -i http://127.0.0.1/api/v1/products
HTTP/1.1 403 Forbidden
date: Sun, 20 Dec 2020 05:26:12 GMT
server: istio-envoy
content-length: 0
x-envoy-upstream-service-time: 5
ここまででquick startは終わりですが、例えばOPAで用意されているJWTの検証を使いリクエストpathの許可制御を行うことが出来ます。
valid_id_token = true {
[id_valid, id_header, id_payload] := io.jwt.decode_verify(input.user, {"secret": "secret"})
id_valid == true
}
default allow = {
"allowed": false,
"reason": "deny"
}
allow = true {
input.attributes.request.http.path = "/unprotected"
}
else = true {
input.attributes.request.http.path = "/protected"
valid_id_token
}
最後に
istioを軸にして認証プロセスをアプリケーションコードから分離することが出来ました。
個人的にこれまでトラフィック制御としてistioを利用することが多かったですがこういったアクセス制御の面も充実していて拡張性が高いソフトウェアだと再認識できました。また1.5のリリースで発表されたようにistioの拡張性はenvoyに統合されwasmサポートとともにさらなる進化を迎えそうな予感がしますね。これからが非常に楽しみです。