NGINX Ingress Controllerの柔軟なアプリケーション制御、具体的なユースケースと設定方法を理解する
- 各リソースのデプロイに失敗する
・「kubectl describe <resource type> <resource name> (-n <namespace>)」コマンドの結果を確認し、リソースのデプロイでエラーが発生していないか確認してください
・「kubectl logs <pod name> (-n <namespace>)」コマンドの結果を確認し、PODのログメッセージを確認し、エラーが発生していないか確認してください - リソースは正しく作成できたが設定が反映されない
・1.の内容を参考にコマンドを実行してください
・特にリソースが正しく生成された場合でも、NGINXのコンフィグロードでエラーとなる場合があります。その場合には、「kubectl logs」コマンドでコンフィグロードに失敗する理由が表示されておりますのでそちらでご確認ください
・一度設定の反映に成功し、その後リソースの内容変更によりエラーが発生した場合には、リソースの変更前の設定で動作いたします。注意深くログを確認し、問題箇所を特定してください - 期待した疎通ができない
・「kubectl logs <NGINX Ingress Controller pod> -n nginx-ingress」コマンドの結果を確認し、Access LogとError Logの内容を確認してください
・NGINX Ingress Controllerで通信を受け付けた場合には以下のようにログが出力されます。ログが出力されること、ログの内容をもとに期待した通信となっているか確認してください
10.244.0.1 - - [24/Aug/2021:14:30:51 +0000] "GET / HTTP/1.1" 500 178 "-" "curl/7.68.0" "-" 10.244.0.1 - - [25/Aug/2021:05:47:17 +0000] "POST / HTTP/1.1" 200 246 "-" "curl/7.68.0" "-" 10.244.0.1 - - [25/Aug/2021:05:47:09 +0000] "POST / HTTP/1.1" 200 246 "-" "curl/7.68.0" "-"
- 設定の内容を確認したい
・NGINX Ingress Controllerは本書で紹介のVirtualServer / VirtualServerRoute / PolicyやIngressにより設定を行うと、それらの内容からNGINXの設定ファイルに変換し、その内容を反映しています
・「kubectl exec -it <NGINX Ingress Controller pod> -n nginx-ingress」 -- bash」コマンドを実行し、NGINX Ingress ControllerのShellを操作することが可能です
・以下フォルダの各ファイルが適切な設定となっているかご確認ください。意図したファイルが生成されていない場合にはリソースの作成に失敗している可能性がありますのでログをご確認ください。
/etc/nginx/conf.d/ HTTP/HTTPSの主な設定が格納されます。複数のVirtualServerをデプロイした場合には複数のファイルが生成されます。
■生成されるファイル名
vs_<namespace>_<object name>.conf/etc/nginx/stream-conf.d/ TCP/UDPの主な設定が格納されます。複数のTransportServerをデプロイした場合には複数のファイルが生成されます。合わせて必要となるGlobalConfigurationの作成も完了していることを確認してください。
■生成されるファイル名
ts_<namespace>_<object name>.conf/etc/nginx/secrets/ 証明書・鍵のファイルが格納されます。複数のSecretをデプロイした場合には複数のファイルが生成されます。参照先のオブジェクトの生成が成功した際に、本ファイルが生成されます。
■生成されるファイル名
<namespace>-<object name>/etc/nginx/waf/nac-policies/ WAFのセキュリティポリシーが格納されます。複数のAPPolicyをデプロイした場合には複数のファイルが生成されます。
■生成されるファイル名
<namespace>_<object name>/etc/nginx/waf/nac-logconfs/ WAFのログポリシーが格納されます。複数のAPLogConfをデプロイした場合には複数のファイルが生成されます。ログポリシーの参照先となるWAFセキュリティポリシーの生成が成功した際に、本ファイルが生成されます。
■生成されるファイル名
<namespace>_<object name>/etc/nginx/waf/nac-usersigs/ WAFのユーザ定義Signatureが格納されます。複数のAPUserSigをデプロイした場合には複数のファイルが生成されます。ログポリシーの参照先となるWAFセキュリティポリシーの生成が成功した際に、本ファイルが生成されます。
■生成されるファイル名
<namespace>_<object name>/etc/nginx/oidc/ OIDCで参照するファイルが格納されています。
高度な攻撃からWebアプリケーションを守る
NGINX App Protect
最後に外部からの攻撃に備え、Kubernetesで動作するアプリケーション、ひいてはプラットフォーム全体をセキュアに保つ方法について見てみましょう。昨今、WebアプリケーションはHTTP・HTTPSをベースにWebページやファイルだけでなく様々なデータをやり取りする仕組みとして活用されるようになりました。インターネット上にWebアプリケーションを公開することで大変便利な機能を提供すること可能ですが、それは同時に正常なユーザに扮した悪意あるユーザを招き入れることになります。我々はサービスを公開すると同時に高いセキュリティを効率的に実現することが当たり前に求められる状況となっているのです。
それでは実際の環境での手順を見てみましょう。対象となるGitHubのフォルダへ移動します。ブラウザでGitHub上の手順ご覧になられる場合にはWAFを参照してください。
# cd ~/kubernetes-ingress/examples-of-custom-resources/waf/
防御対象となるWebアプリケーションと、WAFのセキュリティログを転送するSyslogサーバをデプロイします。
# kubectl apply -f webapp.yaml # kubectl apply -f syslog.yaml
WAFで指定するSignature定義とセキュリティログポリシーをデプロイします。
# kubectl apply -f ap-apple-uds.yaml # kubectl apply -f ap-dataguard-alarm-policy.yaml
WAFに適用するセキュリティポリシーを変更します。Syslogの転送先としてIPアドレスを指定します。8行目をお客様の環境に合わせて適切に指定してください。
# kubectl get svc | grep syslog syslog-svc ClusterIP 10.105.40.208514/TCP 48m # vi waf.yaml ** 省略 ** waf: ** 省略 ** logDest: "syslog:server=10.105.40.208:514" # kubectl apply -f ap-logconf.yaml
WAFのポリシー、VirtualServerをデプロイします。
# kubectl apply -f waf.yaml # kubectl apply -f virtual-server.yaml
作成したアプリケーションに対し疎通を確認したいと思います。WAFはすべての通信を対象にログを出力するように設定しております。Syslogサーバのログを画面に出力しながら、Curlコマンドで通信をテストしたいと思います。
Syslogサーバのログを以下コマンドでターミナルに継続して出力する状態とします。4行目をお客様の環境に合わせて適切に指定してください。
# kubectl get pods | grep syslog syslog-76c65fd9b9-frthw 1/1 Running 0 19h # kubectl exec -it syslog-76c65fd9b9-frthw -- tail -f /var/log/messages
正常な通信を確認するため、「http://webapp.example.com/」を宛先に指定しCurlコマンドを実行します。
# curl "http://webapp.example.com/" Server address: 10.244.0.17:8080 Server name: webapp-7c6d448df9-k2cfd Date: 25/Aug/2021:05:32:55 +0000 URI: / Request ID: a81af16a33b381d758e1dc1eb18bdcd0
正しく接続することができました。Syslogサーバに接続しているターミナルには以下が出力されます。
ログメッセージを見ると、通信をブロックせず転送(PASSED)していることが確認できます。NGINX App ProtectはBot Signatureの機能をもっておりますので、curlコマンドであることを“人によるブラウザの通信ではなくBot Clientである”という形で検知をしておりますが、即座に驚異であると判断される設定となっておりませんので適切な通信としてWebアプリケーションへ転送が行われております。
次にクロスサイトスクリプティング(XSS)を想定した接続をします。Security Policyで攻撃を検知した場合には通信を拒否する設定となっておりますので、正しくブロックされることを確認します。今回はURLに「<script>」を追加し、通信を確認します。「http://webapp.example.com/<script>」を宛先に指定しCurlコマンドを実行します。
# curl "http://webapp.example.com/<script>" <html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is: 5115561320371363623<br><br><a href='javascript:history.back();'>[Go Back]</a></body></html>
正しく通信がブロックされました。デフォルトのブロックページにSupport IDが出力されております。またSyslogサーバに接続しているターミナルには以下が出力されます。ブロックページのSupport IDとして表示された文字列がログのsupport_idの内容と一致しており、通信とログメッセージを一意に紐付けることができます。
ログメッセージを見ると、URLに不正な文字列が含まれており、XSS script tag(URI)などのSignatureで検知、通信をブロック(REJECTED)していることが確認できます。
参考の情報ですが、Curlコマンドの「<script>」を「?a=a?%27+OR+1=1--」などの文字列に入れ替えると、SQL Injectionのブロックを見ることができますのでご確認ください。
次にUser Defined Signatureで指定した内容が正しく動作しているか確認します。Webアプリケーションに”apple”という文字を送信します。
# curl -X POST -d "apple" "http://webapp.example.com/"Request Rejected The requested URL was rejected. Please consult with your administrator.
Your support ID is: 5115561320371365663
[Go Back]r
正しく通信がブロックされました。Syslogサーバに接続しているターミナルには以下が出力されます。
ログメッセージを見ると、該当のログメッセージが、User Defined Signatureの“Apple_medium_acc”というSignature Nameで検知されブロック(REJECTED)されていることが確認できます。
現在の状態は以下の図となります(図22)。
今回の実装ではdefault namespaceに、WAFに必要となるリソースを実装しています。SyslogサーバはNGINX Ingress Controllerから内部ネットワークを通じて接続するため、外部への公開は行っていません。WebアプリケーションはWAFのセキュリティポリシーを参照し、VirtualServerの公開設定を行っています。これにより外部からWebアプリケーション宛のアクセスはセキュリティポリシーの内容に従って制御される構成となります。
それでは、最後に作成したアプリケーションを削除します。
# kubectl delete -f webapp.yaml # kubectl delete -f syslog.yaml # kubectl delete -f ap-apple-uds.yaml # kubectl delete -f ap-dataguard-alarm-policy.yaml # kubectl delete -f waf.yaml # kubectl delete -f virtual-server.yaml
おわりに
本稿ではNGINX Plusが提供する様々な機能を用いて柔軟かつ簡単にアプリケーションに合わせた外部へのサービス公開方法を紹介しました。本番環境でアプリケーションを展開するためにはコンテナでアプリケーションを動作させるだけでなく、高い安定性やセキュリティを実現する必要があり、更に多様化するアプリケーションのニーズに合わせた通信制御が必要となります。
ぜひともこの記事を参考にNGINX Ingress Controllerの柔軟なアプリケーション制御を試していただけますと幸いです。
【補足】
事象の切り分け方法
各パートでKubernetes環境にデプロイしたアプリケーションの動作が期待した通りとならない場合の切り分けについてご紹介します。
OIDC KeycloakのAPI操作について
OIDCで利用するKeycloakですがGUIを利用せずAPIで設定することが可能です。KeyacloakのAPIを利用した設定方法はGitHub上のKeycloak Setupに記載されています。
APIではJSON形式のデータをやり取りします。予めjqコマンドをインストールしてください。
まずKeycloakにアクセスするため、クライアント端末のhostsファイルで適切にアクセスできるよう設定してください。
# grep host virtual-server-idp.yaml virtual-server-idp.yaml: host: keycloak.example.com
APIより操作するためAccess Tokenを取得します。Access Tokenの有効期限は大変短く設定されております(60秒)。ご注意ください。
APIで利用するAccess Tokenの取得 # TOKEN=`curl -sS -k --data "username=admin&password=admin&grant_type=password&client_id=admin-cli" https://keycloak.example.com/auth/realms/master/protocol/openid-connect/token | jq -r .access_token` nginx-userというユーザ名を持つユーザの作成 # curl -sS -k -X POST -d '{ "username": "nginx-user", "enabled": true, "credentials":[{"type": "password", "value": "test", "temporary": false}]}' -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/admin/realms/master/users nginx-userの設定追加及びSECRETの取得 # SECRET=`curl -sS -k -X POST -d '{ "clientId": "nginx-plus", "redirectUris": ["https://webapp.example.com/_codexch"] }' -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/realms/master/clients-registrations/default | jq -r .secret` 取得したSECRETの確認 # echo $SECRET ***SECRET***
Keycloakの設定作業で正しく値が取得できない場合、以下コマンドを参考に切り分けを行い、適切にSECRETの取得が完了することを確認してください。
指定の管理者ユーザ名、パスワードを用いて正しくTokenが取得できることを確認してください。APIではこちらのAccessTokenを利用します。 # curl -sS -k --data "username=admin&password=admin&grant_type=password&client_id=admin-cli" https://keycloak.example.com/auth/realms/master/protocol/openid-connect/token | jq . { "access_token": "***ACCESS TOKEN***", "expires_in": 60, "refresh_expires_in": 1800, "refresh_token": "***REFRESH TOKEN***", "token_type": "Bearer", "not-before-policy": 0, "session_state": "f1cc69a8-2239-424a-9e48-f73ab29ef931", "scope": "profile email" } ユーザ情報を取得。管理者及び新規に作成する「nginx-plus」というユーザが表示されることを確認してください # curl -sS -k -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/admin/realms/master/users | jq . [ { "id": "3d0cf453-5d72-494f-9e8f-f3c3aa8e279b", "createdTimestamp": 1629247264332, "username": "admin", "enabled": true, "totp": false, "emailVerified": false, "disableableCredentialTypes": [], "requiredActions": [], "notBefore": 0, "access": { "manageGroupMembership": true, "view": true, "mapRoles": true, "impersonate": true, "manage": true } }, { "id": "4fd6d403-db91-4d84-9235-84a418a5b8ef", "createdTimestamp": 1629704326796, "username": "nginx-user", "enabled": true, "totp": false, "emailVerified": false, "disableableCredentialTypes": [], "requiredActions": [], "notBefore": 0, "access": { "manageGroupMembership": true, "view": true, "mapRoles": true, "impersonate": true, "manage": true } } ] 登録されたクライアント情報を確認し、必要となる情報を確認してください。NGINX OIDCの設定で規定した動作とならない場合にはこちらに表示されるパラメータを参照し、確認を行ってください。 # curl -sS -k -X GET -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/realms/master/clients-registrations/default/nginx-plus | jq . { "id": "32f41851-f0f7-4259-af19-68e11b5bb10c", "clientId": "nginx-plus", "surrogateAuthRequired": false, "enabled": true, "alwaysDisplayInConsole": false, "clientAuthenticatorType": "client-secret", "secret": "***SECRET***", "redirectUris": [ "https://webapp.example.com:443/_codexch" ], "webOrigins": [ "https://webapp.example.com:443" ], "notBefore": 0, "bearerOnly": false, "consentRequired": false, "standardFlowEnabled": true, "implicitFlowEnabled": false, "directAccessGrantsEnabled": false, "serviceAccountsEnabled": false, "publicClient": false, "frontchannelLogout": false, "protocol": "openid-connect", "attributes": {}, "authenticationFlowBindingOverrides": {}, "fullScopeAllowed": true, "nodeReRegistrationTimeout": -1, "defaultClientScopes": [ "web-origins", "role_list", "profile", "roles", "email" ], "optionalClientScopes": [ "address", "phone", "offline_access", "microprofile-jwt" ] }
Keycloakで誤った情報、意図しない情報でオブジェクトを作成した場合には以下コマンドを参考に削除してください。
・ユーザの削除 # curl -sS -k -X DELETE -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/admin/realms/master/users/ | jq . ・クライアントの削除 # curl -sS -k -X DELETE -H "Content-Type:application/json" -H "Authorization: bearer ${TOKEN}" https://keycloak.example.com/auth/realms/master/clients-registrations/default/ | jq .
連載バックナンバー
Think ITメルマガ会員登録受付中
全文検索エンジンによるおすすめ記事
- クラウドネイティブな環境でKeycloakによるシングルサインオンを実現
- KubernetesのDiscovery&LBリソース(その2)
- Oracle Cloud Hangout Cafe Season7 #1「Kubnernetes 超入門」(2023年6月7日開催)
- Oracle Cloud Hangout Cafe Season5 #3「Kubernetes のセキュリティ」(2022年3月9日開催)
- KubernetesのConfig&Storageリソース(その1)
- Kubernetesの基礎
- コンテナ上のマイクロサービスの認証強化 ~StrimziとKeycloak~
- コンテナ上のマイクロサービスの認証強化 ~IstioとKeycloak~
- コンテナ上のマイクロサービスの認証強化 ~QuarkusとKeycloak~
- kustomizeやSecretを利用してJavaアプリケーションをデプロイする