NICによるWebアプリの通信制御

この章では、実際にデプロイしたNGINX Ingress Controllerを使い、様々なサンプルアプリケーションを動作させ、その設定方法や動きを確認いただきます。 設定例は NGINX Inc GitHubの examples/custom-resources/ に管理されております

シンプルなWebアプリケーションのデプロイ

シンプルなWebアプリケーションをデプロイします。 Kubernetes環境で、Webアプリケーションをデプロイします。そのアプリケーションに対し通信制御を行うVirtualServer、及びHTTPSに必要な証明書をデプロイします。

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/basic-configuration

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/basic-configuration/
kubectl create -f cafe.yaml
kubectl create -f cafe-secret.yaml
kubectl create -f cafe-virtual-server.yaml

リソースを確認

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get pod
実行結果サンプル
1NAME                       READY   STATUS    RESTARTS  AGE
2coffee-7c86d7d67c-wjxss    1/1     Running   0         1m
3coffee-7c86d7d67c-8jm9z    1/1     Running   0         1m
4tea-5c457db9-dc4cs         1/1     Running   0         1m
kubectl get deployment
実行結果サンプル
1NAME     READY   UP-TO-DATE   AVAILABLE   AGE
2coffee   2/2     2            2           1m
3tea      1/1     1            1           1m
kubectl get secret  | grep cafe-secret
実行結果サンプル
1cafe-secret           kubernetes.io/tls                     2      1m
kubectl get vs
実行結果サンプル
1NAME   STATE   HOST               IP    PORTS   AGE
2cafe   Valid   cafe.example.com                 94s

動作確認

curlコマンドでリクエストを送信します。作成したWebアプリケーションから応答があることを確認します。 /coffee/tea というURLに応じて異なるアプリケーションに転送されていることが確認できます

curl -H "Host:cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.25:8080
2Server name: coffee-7c86d7d67c-wjxss
3Date: 17/Jan/2022:00:14:03 +0000
4URI: /coffee
5Request ID: 069567120c306da6f92e16e5d73e5040
curl -H "Host:cafe.example.com" http://localhost/tea
実行結果サンプル
1Server address: 192.168.127.20:8080
2Server name: tea-5c457db9-dc4cs
3Date: 17/Jan/2022:00:14:08 +0000
4URI: /tea
5Request ID: 6fd58877d9e85903300df7ceb0f81eb2

同様に、HTTPSの接続を確認します。

curl -kv -H "Host:cafe.example.com" https://localhost/coffee
実行結果サンプル
 1*   Trying 127.0.0.1:443...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 443 (#0)
 4* ALPN, offering h2
 5* ALPN, offering http/1.1
 6* successfully set certificate verify locations:
 7*   CAfile: /etc/ssl/certs/ca-certificates.crt
 8  CApath: /etc/ssl/certs
 9* TLSv1.3 (OUT), TLS handshake, Client hello (1):
10* TLSv1.3 (IN), TLS handshake, Server hello (2):
11* TLSv1.2 (IN), TLS handshake, Certificate (11):
12* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
13* TLSv1.2 (IN), TLS handshake, Server finished (14):
14* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
15* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
16* TLSv1.2 (OUT), TLS handshake, Finished (20):
17* TLSv1.2 (IN), TLS handshake, Finished (20):
18* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
19* ALPN, server accepted to use http/1.1
20* Server certificate:
21*  subject: CN=NGINXIngressController
22*  start date: Sep 12 18:03:35 2018 GMT
23*  expire date: Sep 11 18:03:35 2023 GMT
24*  issuer: CN=NGINXIngressController
25*  SSL certificate verify result: self signed certificate (18), continuing anyway.
26> GET /coffee HTTP/1.1
27> Host:cafe.example.com
28> User-Agent: curl/7.68.0
29> Accept: */*
30>
31* Mark bundle as not supporting multiuse
32< HTTP/1.1 200 OK
33< Server: nginx/1.21.3
34< Date: Mon, 17 Jan 2022 00:14:34 GMT
35< Content-Type: text/plain
36< Content-Length: 164
37< Connection: keep-alive
38< Expires: Mon, 17 Jan 2022 00:14:33 GMT
39< Cache-Control: no-cache
40<
41Server address: 192.168.127.26:8080
42Server name: coffee-7c86d7d67c-8jm9z
43Date: 17/Jan/2022:00:14:34 +0000
44URI: /coffee
45Request ID: 3af5bd62d9756c934b4c731d0cadfcb1
46* Connection #0 to host localhost left intact
curl -kv -H "Host:cafe.example.com" https://localhost/tea
実行結果サンプル
 1*   Trying 127.0.0.1:443...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 443 (#0)
 4* ALPN, offering h2
 5* ALPN, offering http/1.1
 6* successfully set certificate verify locations:
 7*   CAfile: /etc/ssl/certs/ca-certificates.crt
 8  CApath: /etc/ssl/certs
 9* TLSv1.3 (OUT), TLS handshake, Client hello (1):
10* TLSv1.3 (IN), TLS handshake, Server hello (2):
11* TLSv1.2 (IN), TLS handshake, Certificate (11):
12* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
13* TLSv1.2 (IN), TLS handshake, Server finished (14):
14* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
15* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
16* TLSv1.2 (OUT), TLS handshake, Finished (20):
17* TLSv1.2 (IN), TLS handshake, Finished (20):
18* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
19* ALPN, server accepted to use http/1.1
20* Server certificate:
21*  subject: CN=NGINXIngressController
22*  start date: Sep 12 18:03:35 2018 GMT
23*  expire date: Sep 11 18:03:35 2023 GMT
24*  issuer: CN=NGINXIngressController
25*  SSL certificate verify result: self signed certificate (18), continuing anyway.
26> GET /tea HTTP/1.1
27> Host:cafe.example.com
28> User-Agent: curl/7.68.0
29> Accept: */*
30>
31* Mark bundle as not supporting multiuse
32< HTTP/1.1 200 OK
33< Server: nginx/1.21.3
34< Date: Mon, 17 Jan 2022 00:14:39 GMT
35< Content-Type: text/plain
36< Content-Length: 156
37< Connection: keep-alive
38< Expires: Mon, 17 Jan 2022 00:14:38 GMT
39< Cache-Control: no-cache
40<
41Server address: 192.168.127.20:8080
42Server name: tea-5c457db9-dc4cs
43Date: 17/Jan/2022:00:14:39 +0000
44URI: /tea
45Request ID: af1466d1fc1b7481cb82352885f9cbc2

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/basic-configuration/
kubectl delete -f cafe-secret.yaml
kubectl delete -f cafe-virtual-server.yaml
kubectl delete -f cafe.yaml

複数アプリケーション・チームを想定した VS / VSR 設定

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/cross-namespace-configuration

NGINX Ingress Controller はCRDを用い、Virtual Server / Virtual Server Router / Policy といったリソースを使うことで、権限と設定範囲を適切に管理することが可能です。 ここでは、通信を待ち受けるため cafe namespace に、VirtualServer をデプロイします。そして tea / coffee namespace に アプリケーションと、アプリケーション宛に通信を転送するための VirtualServerRoute をデプロイします。

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/cross-namespace-configuration
kubectl create -f namespaces.yaml
kubectl create -f tea.yaml
kubectl create -f coffee.yaml
kubectl create -f tea-virtual-server-route.yaml
kubectl create -f coffee-virtual-server-route.yaml
kubectl create -f cafe-secret.yaml
kubectl create -f cafe-virtual-server.yaml

リソースを確認

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get ns --sort-by=.metadata.creationTimestamp
実行結果サンプル
1NAME               STATUS   AGE
2**省略**
3coffee             Active   75s
4cafe               Active   75s
5tea                Active   75s
kubectl get vsr -A
実行結果サンプル
1NAMESPACE   NAME     STATE   HOST               IP    PORTS   AGE
2coffee      coffee   Valid   cafe.example.com                 89s
3tea         tea      Valid   cafe.example.com                 93s
kubectl get vs -A
実行結果サンプル
1NAMESPACE   NAME   STATE   HOST               IP    PORTS   AGE
2cafe        cafe   Valid   cafe.example.com                 85s
kubectl get secret -A | grep cafe
実行結果サンプル
1cafe               cafe-secret                                      kubernetes.io/tls                     2      101s
2cafe               default-token-94nrl                              kubernetes.io/service-account-token   3      2m3s
kubectl get secret -A | grep cafe-secret
実行結果サンプル
1NAME                  TYPE                                  DATA   AGE
2cafe-secret           kubernetes.io/tls                     2      2m5s
kubectl get pod -o wide -A|grep -e coffee -e tea
実行結果サンプル
1coffee             coffee-7c86d7d67c-pq5w2                    1/1     Running   0                88s   192.168.127.22   ip-10-1-1-9   <none>           <none>
2tea                tea-5c457db9-h5sm9                         1/1     Running   0                14m   192.168.127.24   ip-10-1-1-9   <none>           <none>

動作確認

curlコマンドでリクエストを送信します。作成したWebアプリケーションから応答があることを確認します。 /coffee/tea というURLに応じて異なるアプリケーションに転送されていることが確認できます

curl -H "Host: cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.22:8080
2Server name: coffee-7c86d7d67c-pq5w2
3Date: 17/Jan/2022:05:44:25 +0000
4URI: /coffee
5Request ID: 1414627aac091b5a7897bac37d046cea
curl -H "Host: cafe.example.com" http://localhost/tea
実行結果サンプル
1Server address: 192.168.127.24:8080
2Server name: tea-5c457db9-h5sm9
3Date: 17/Jan/2022:05:44:29 +0000
4URI: /tea
5Request ID: 698ab29da633f24a9bf5384c1499b056

同様にHTTPSの接続を確認します。HTTPSの結果は /tea にアクセスした結果のみ掲載します。 /coffee の結果も合わせて確認ください。

curl -vk -H "Host: cafe.example.com" https://localhost/tea
実行結果サンプル
 1*   Trying 127.0.0.1:443...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 443 (#0)
 4* ALPN, offering h2
 5* ALPN, offering http/1.1
 6* successfully set certificate verify locations:
 7*   CAfile: /etc/ssl/certs/ca-certificates.crt
 8  CApath: /etc/ssl/certs
 9* TLSv1.3 (OUT), TLS handshake, Client hello (1):
10* TLSv1.3 (IN), TLS handshake, Server hello (2):
11* TLSv1.2 (IN), TLS handshake, Certificate (11):
12* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
13* TLSv1.2 (IN), TLS handshake, Server finished (14):
14* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
15* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
16* TLSv1.2 (OUT), TLS handshake, Finished (20):
17* TLSv1.2 (IN), TLS handshake, Finished (20):
18* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
19* ALPN, server accepted to use http/1.1
20* Server certificate:
21*  subject: CN=NGINXIngressController
22*  start date: Sep 12 18:03:35 2018 GMT
23*  expire date: Sep 11 18:03:35 2023 GMT
24*  issuer: CN=NGINXIngressController
25*  SSL certificate verify result: self signed certificate (18), continuing anyway.
26> GET /tea HTTP/1.1
27> Host: cafe.example.com
28> User-Agent: curl/7.68.0
29> Accept: */*
30>
31* Mark bundle as not supporting multiuse
32< HTTP/1.1 200 OK
33< Server: nginx/1.21.3
34< Date: Mon, 17 Jan 2022 05:44:42 GMT
35< Content-Type: text/plain
36< Content-Length: 156
37< Connection: keep-alive
38< Expires: Mon, 17 Jan 2022 05:44:41 GMT
39< Cache-Control: no-cache
40<
41Server address: 192.168.127.24:8080
42Server name: tea-5c457db9-h5sm9
43Date: 17/Jan/2022:05:44:42 +0000
44URI: /tea
45Request ID: 8ec25fd33d381df7261fda9f9da66558
46* Connection #0 to host localhost left intact

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/cross-namespace-configuration
kubectl delete -f tea-virtual-server-route.yaml
kubectl delete -f cafe-virtual-server.yaml
kubectl delete -f coffee-virtual-server-route.yaml
kubectl delete -f cafe-secret.yaml
kubectl delete -f tea.yaml
kubectl delete -f coffee.yaml
kubectl delete -f namespaces.yaml

通信内容による条件分岐・サービスへの転送

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/advanced-routing

通信の内容に応じて転送するサービスを制御するサンプルです。
ユーザの属性毎に転送するサービスを変更したり、開発中アプリケーションに対するリクエストを識別し通信を制御したりする場合などに利用します

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/advanced-routing
kubectl create -f cafe.yaml
kubectl create -f cafe-virtual-server.yaml

リソースを確認

ポイントとなるファイルの内容を確認します。

cafe-virtual-server.yaml で通信制御の条件を指定しています。条件は matches というパラメータで指定します。このサンプルの条件は以下の内容です。

  • path: /tea

    • リクエストのHTTPメソッド($request_method)が、POSTの場合、 tea-post へ転送する。 それ以外は tea へ転送する。

  • path: /coffee

    • cookie の version の値が v2 の場合、 coffee-v2 へ転送する。それ以外は coffee-v1 へ転送する。

それぞれの記述内容を以下で確認してください。

cafe-virtual-server.yaml
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: cafe
 5spec:
 6  host: cafe.example.com
 7  upstreams:
 8  - name: tea-post
 9    service: tea-post-svc
10    port: 80
11  - name: tea
12    service: tea-svc
13    port: 80
14  - name: coffee-v1
15    service: coffee-v1-svc
16    port: 80
17  - name: coffee-v2
18    service: coffee-v2-svc
19    port: 80
20  routes:
21  - path: /tea
22    matches:
23    - conditions:
24      - variable: $request_method
25        value: POST
26      action:
27        pass: tea-post
28    action:
29      pass: tea
30  - path: /coffee
31    matches:
32    - conditions:
33      - cookie: version
34        value: v2
35      action:
36        pass: coffee-v2
37    action:
38      pass: coffee-v1

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get deployment
実行結果サンプル
1NAME        READY   UP-TO-DATE   AVAILABLE   AGE
2coffee-v1   1/1     1            1           16s
3coffee-v2   1/1     1            1           15s
4tea         1/1     1            1           15s
5tea-post    1/1     1            1           15s
kubectl get pod -o wide
実行結果サンプル
1NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
2coffee-v1-6b78998db9-8cv49   1/1     Running   0          26s   192.168.127.23   ip-10-1-1-9   <none>           <none>
3coffee-v2-748cbbb49f-mbxpr   1/1     Running   0          26s   192.168.127.27   ip-10-1-1-9   <none>           <none>
4tea-5c457db9-dcswc           1/1     Running   0          26s   192.168.127.33   ip-10-1-1-9   <none>           <none>
5tea-post-7db8cd8bf-m5gbz     1/1     Running   0          26s   192.168.127.32   ip-10-1-1-9   <none>           <none>
kubectl get vs
実行結果サンプル
1NAME   STATE   HOST               IP    PORTS   AGE
2cafe   Valid   cafe.example.com                 28s

動作確認

先程、設定ファイルから確認した条件を再度記載します。

  • path: /tea

    • リクエストのHTTPメソッド($request_method)が、POSTの場合、 tea-post へ転送する。 それ以外は tea へ転送する。

  • path: /coffee

    • cookie の version の値が v2 の場合、 coffee-v2 へ転送する。それ以外は coffee-v1 へ転送する。

Curlコマンドで動作を確認します。

/tea 宛でHTTPメソッドを指定しない(GET)の場合の動作は以下の通りです

curl -H "Host: cafe.example.com" http://localhost/tea
実行結果サンプル
1Server address: 192.168.127.33:8080
2Server name: tea-5c457db9-dcswc
3Date: 17/Jan/2022:09:00:56 +0000
4URI: /tea
5Request ID: 00e9eb4d61f7afdb8c5656da94d15b98

/tea 宛でHTTP POSTメソッドを指定した場合の動作は以下の通りです。

curl -H "Host: cafe.example.com" http://localhost/tea -X POST
実行結果サンプル
1Server address: 192.168.127.32:8080
2Server name: tea-post-7db8cd8bf-m5gbz
3Date: 17/Jan/2022:09:01:02 +0000
4URI: /tea
5Request ID: 4deeb82434a6f799ffc894a229ac361a

/coffee 宛でCookieの値を指定しない場合の動作は以下の通りです。

curl -H "Host: cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.23:8080
2Server name: coffee-v1-6b78998db9-8cv49
3Date: 17/Jan/2022:09:01:25 +0000
4URI: /coffee
5Request ID: 8d182c9c060d5a4d4dec226292ac2820

/coffee 宛でCookieに”version=v2”と指定した場合の動作は以下の通りです。

curl -H "Host: cafe.example.com" http://localhost/coffee --cookie "version=v2"
実行結果サンプル
1Server address: 192.168.127.27:8080
2Server name: coffee-v2-748cbbb49f-mbxpr
3Date: 17/Jan/2022:09:01:35 +0000
4URI: /coffee
5Request ID: befacc5e7ca56a1a09e5982315c74fa0

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/advanced-routing
kubectl delete  -f cafe-virtual-server.yaml
kubectl delete  -f cafe.yaml

割合を指定した分散 (Traffic Split)

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/traffic-splitting

割合を指定し、トラフィックを分散することができます。

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/traffic-splitting
kubectl create -f cafe.yaml
kubectl create -f cafe-virtual-server.yaml

Virtual Serverの内容を確認します。 cafe-virtual-server.yaml/coffeesplits を指定し、更に weight でサービスへ転送する割合を指定しています。

リソースを確認

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get deployment
実行結果サンプル
1NAME        READY   UP-TO-DATE   AVAILABLE   AGE
2coffee-v1   2/2     2            2           19s
3coffee-v2   2/2     2            2           19s
kubectl get pod -o wide
実行結果サンプル
1NAME                         READY   STATUS    RESTARTS   AGE   IP               NODE          NOMINATED NODE   READINESS GATES
2coffee-v1-6b78998db9-h4jkb   1/1     Running   0          25s   192.168.127.47   ip-10-1-1-9   <none>           <none>
3coffee-v1-6b78998db9-nn42z   1/1     Running   0          25s   192.168.127.44   ip-10-1-1-9   <none>           <none>
4coffee-v2-748cbbb49f-llpb6   1/1     Running   0          25s   192.168.127.45   ip-10-1-1-9   <none>           <none>
5coffee-v2-748cbbb49f-vrpzx   1/1     Running   0          25s   192.168.127.46   ip-10-1-1-9   <none>           <none>
kubectl get vs
実行結果サンプル
1NAME   STATE   HOST               IP    PORTS   AGE
2cafe   Valid   cafe.example.com                 26s

動作確認

Curlコマンドで複数回リクエストを送ると、 coffee-v1coffee-v2 のそれぞれに転送されていることが確認できます。

curl -s -H "Host: cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.44:8080
2Server name: coffee-v1-6b78998db9-nn42z
3Date: 17/Jan/2022:12:26:49 +0000
4URI: /coffee
5Request ID: c127f0f724eb1b3becd57603b6d603ea
curl -s -H "Host: cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.45:8080
2Server name: coffee-v2-748cbbb49f-llpb6
3Date: 17/Jan/2022:12:26:37 +0000
4URI: /coffee
5Request ID: 357237a3fea498b6efd90c929d526e64

以下コマンドを参考に複数回Curlを実行し、その結果をファイルに記録します。記録の内容より coffee-v1coffee-v2 転送した数を確認できます。 分散する割合は少しばらつきが発生しますが、参考として分散した数の結果を確認してください。

## cd ~/kubernetes-ingress/examples/custom-resources/traffic-splitting
> split.txt ;\
for i in {1..20}; \
do curl -s -H "Host: cafe.example.com" http://localhost/coffee | grep "Server name" >> split.txt ; \
done ; \
echo -n "v1:" ; grep v1 split.txt  | wc -l ; echo -n "v2:"  ; grep v2 split.txt  | wc -l
実行結果サンプル
1v1:18
2v2:2

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/traffic-splitting
kubectl delete -f cafe-virtual-server.yaml
kubectl delete -f cafe.yaml
rm split.txt

IPアドレスによる通信の制御 (Access Control)

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/access-control

Policyにより通信制御を行う方法を確認します。リクエストの送信元IPアドレスに応じて通信の許可・拒否を行う方法を確認します。

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/access-control
kubectl apply -f webapp.yaml
kubectl apply -f access-control-policy-deny.yaml
kubectl apply -f virtual-server.yaml

リソースを確認

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get pod
実行結果サンプル
1NAME                     READY   STATUS    RESTARTS   AGE
2webapp-64d444885-j4q7z   1/1     Running   0          2m7s
kubectl get deployment
実行結果サンプル
1NAME     READY   UP-TO-DATE   AVAILABLE   AGE
2webapp   1/1     1            1           2m13s
kubectl get vs
実行結果サンプル
1NAME     STATE   HOST                 IP    PORTS   AGE
2webapp   Valid   webapp.example.com                 2m8s
kubectl get policy
実行結果サンプル
1NAME            STATE   AGE
2webapp-policy   Valid   2m18s

VirtualServerに webapp-policy が割り当てられていることが確認できます。

kubectl describe vs
実行結果サンプル
 1Name:         webapp
 2Namespace:    default
 3Labels:       <none>
 4Annotations:  <none>
 5API Version:  k8s.nginx.org/v1
 6Kind:         VirtualServer
 7
 8** 省略 **
 9
10Spec:
11  Host:  webapp.example.com
12  Policies:
13    Name:  webapp-policy
14  Routes:
15    Action:
16      Pass:  webapp
17    Path:    /
18  Upstreams:
19    Name:     webapp
20    Port:     80
21    Service:  webapp-svc
22Status:
23  External Endpoints:
24    Ip:
25    Ports:
26  Message:  Configuration for default/webapp was added or updated
27  Reason:   AddedOrUpdated
28  State:    Valid
コマンドを実行しPolicyの内容を確認します。Policyの内容が Spec に記載されています。
kubectl describe policy
実行結果サンプル
 1Name:         webapp-policy
 2Namespace:    default
 3Labels:       <none>
 4Annotations:  <none>
 5API Version:  k8s.nginx.org/v1
 6Kind:         Policy
 7
 8** 省略 **
 9
10Spec:
11  Access Control:
12    Deny:
13      10.0.0.0/8
14Status:
15  Message:  Policy default/webapp-policy was added or updated
16  Reason:   AddedOrUpdated
17  State:    Valid
18Events:
19  Type    Reason          Age                  From                      Message
20  ----    ------          ----                 ----                      -------
21  Normal  AddedOrUpdated  61s (x3 over 2m31s)  nginx-ingress-controller  Policy default/webapp-policy was added or updated

動作確認

curlコマンドで動作を確認します。以下のように通信が 拒否 されていることが確認できます

curl -H "Host:webapp.example.com" http://localhost/
実行結果サンプル
1<html>
2<head><title>403 Forbidden</title></head>
3<body>
4<center><h1>403 Forbidden</h1></center>
5<hr><center>nginx/1.21.3</center>
6</body>
7</html>

webapp-policy の内容を変更します

## cd ~/kubernetes-ingress/examples/custom-resources/access-control
kubectl apply -f access-control-policy-allow.yaml

コマンドを実行しPolicyの内容を確認します。Policyの内容が Spec に記載されています。

kubectl describe policy
実行結果サンプル
 1Name:         webapp-policy
 2Namespace:    default
 3Labels:       <none>
 4Annotations:  <none>
 5API Version:  k8s.nginx.org/v1
 6Kind:         Policy
 7
 8** 省略 **
 9
10Spec:
11  Access Control:
12    Allow:
13      10.0.0.0/8
14Status:
15  Message:  Policy default/webapp-policy was added or updated
16  Reason:   AddedOrUpdated
17  State:    Valid

curlコマンドで動作を確認します。以下のように通信が 許可 されていることが確認できます

curl -H "Host:webapp.example.com" http://localhost/
実行結果サンプル
1Server address: 192.168.127.48:8080
2Server name: webapp-64d444885-j4q7z
3Date: 17/Jan/2022:12:48:51 +0000
4URI: /
5Request ID: 752997339b21d94210fc911cb41f7216

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/access-control
kubectl delete -f access-control-policy-allow.yaml
kubectl delete -f virtual-server.yaml
kubectl delete -f webapp.yaml

URL Path の 変換 (Rewrite)

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/rewrites

Rewrite を用いて、URL Path を書換えることが可能です。

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/rewrites
cat << EOF > rewrite-virtual-server.yaml
apiVersion: k8s.nginx.org/v1
kind: VirtualServer
metadata:
  name: cafe
spec:
  host: cafe.example.com
  upstreams:
  - name: tea
    service: tea-svc
    port: 80
  - name: coffee
    service: coffee-svc
    port: 80
  routes:
  - path: /tea/
    action:
      proxy:
        upstream: tea
        rewritePath: /
  - path: /coffee
    action:
      proxy:
        upstream: coffee
        rewritePath: /beans
  - path: ~ /(\w+)/(.+\.(?:gif|jpg|png)$)
    action:
      proxy:
        upstream: tea
        rewritePath: /service/\$1/image/\$2
EOF

kubectl apply -f ../basic-configuration/cafe.yaml
kubectl apply -f rewrite-virtual-server.yaml

リソースを確認

Virtual Serverの定義内容を確認します。route に 3つのPathを定義し、rewritePath でURLの書換えを行います。該当のPathでそれぞれのサービスに適したPathの書換えルールを定義します。

書換えのルールを表にまとめます。

Path

一致タイプ

Rewrite

結果

/tea/

完全一致

/

/tea/abc -> /abc

/coffee

完全一致

/beans

/coffee/def/ghi -> /beans/def/ghi

~ /(w+)/(.+.(?:gif|jpg|png)$)

正規表現

/service/$1/image/$2

/cafe/top.jpg -> /service/cafe/image/top.jpg

正規表現のルールは、以下サイトを利用し確認いただけます
PCRE をプルダウンより選択し、上部に 正規表現のルール 、下部に 評価する文字列 を入力し、結果を確認できます

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get pod
実行結果サンプル
1NAME                      READY   STATUS    RESTARTS   AGE
2coffee-7c86d7d67c-ws2t8   1/1     Running   0          39m
3coffee-7c86d7d67c-zt5tr   1/1     Running   0          39m
4tea-5c457db9-ksljs        1/1     Running   0          39m
kubectl get deployment
実行結果サンプル
1NAME     READY   UP-TO-DATE   AVAILABLE   AGE
2coffee   2/2     2            2           39m
3tea      1/1     1            1           39m
kubectl get vs
実行結果サンプル
1NAME   STATE   HOST               IP    PORTS   AGE
2cafe   Valid   cafe.example.com                 39m

動作確認

先程定義を確認したとおり、URLが書換えられていることが確認できます。

curl -H "Host:cafe.example.com" http://localhost/tea/
実行結果サンプル
1Server address: 192.168.127.40:8080
2Server name: tea-5c457db9-ksljs
3Date: 17/Jan/2022:14:22:46 +0000
4URI: /
5Request ID: 2576a16546e7d17467e04da2ab794109
curl -H "Host:cafe.example.com" http://localhost/tea/abc
実行結果サンプル
1Server address: 192.168.127.40:8080
2Server name: tea-5c457db9-ksljs
3Date: 17/Jan/2022:14:22:14 +0000
4URI: /abc
5Request ID: 5ce49a600fb24a40340ba6edad91ffb2
curl -H "Host:cafe.example.com" http://localhost/coffee
実行結果サンプル
1Server address: 192.168.127.39:8080
2Server name: coffee-7c86d7d67c-zt5tr
3Date: 17/Jan/2022:14:22:40 +0000
4URI: /beans
5Request ID: 9b15d10a624faee145b875b8f83460e3
curl -H "Host:cafe.example.com" http://localhost/coffee/def/ghi
実行結果サンプル
1Server address: 192.168.127.39:8080
2Server name: coffee-7c86d7d67c-zt5tr
3Date: 17/Jan/2022:14:22:27 +0000
4URI: /beans/def/ghi
5Request ID: f70d98547c615a145b2a40ddfe5884a4
curl -H "Host:cafe.example.com" http://localhost/cafe/top.jpg
実行結果サンプル
1Server address: 192.168.127.40:8080
2Server name: tea-5c457db9-ksljs
3Date: 17/Jan/2022:14:23:02 +0000
4URI: /service/cafe/image/top.jpg
5Request ID: 38c3cf24e3f5e0cdfe451b0d646c0e1d

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/rewrites
kubectl delete -f ../basic-configuration/cafe.yaml
kubectl delete -f rewrite-virtual-server.yaml

Ingress Controller で JWT Validation のデプロイ

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/jwt

NGINX Ingress Controller で JWT の Validation を行い、通信制御を行うことが可能です。

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/jwt/
kubectl apply -f webapp.yaml
kubectl apply -f jwk-secret.yaml
kubectl apply -f jwt.yaml
kubectl apply -f virtual-server.yaml

リソースを確認

利用するファイルの内容を確認します

まず、JWK(Json Web Key)としてVirtual ServerのPolicy内で指定するsecretの内容を確認します

jwk-secret.yaml
1apiVersion: v1
2kind: Secret
3metadata:
4  name: jwk-secret
5type: nginx.org/jwk
6data:
7  jwk: eyJrZXlzIjoKICAgIFt7CiAgICAgICAgImsiOiJabUZ1ZEdGemRHbGphbmQwIiwKICAgICAgICAia3R5Ijoib2N0IiwKICAgICAgICAia2lkIjoiMDAwMSIKICAgIH1dCn0K

jwk というKeyに対し、 として文字列が指定されていることが確認できます。 文字列の内容をbase64デコードします

# echo -n <jwk に指定された文字列> | base64 -d
echo -n "eyJrZXlzIjoKICAgIFt7CiAgICAgICAgImsiOiJabUZ1ZEdGemRHbGphbmQwIiwKICAgICAgICAia3R5Ijoib2N0IiwKICAgICAgICAia2lkIjoiMDAwMSIKICAgIH1dCn0K" | base64 -d

出力結果が以下となります。

jwk を base64 デコードした結果
1{"keys":
2    [{
3        "k":"ZmFudGFzdGljand0",
4        "kty":"oct",
5        "kid":"0001"
6    }]
7}

各パラメータ内容は以下の通り

Parameter

意味

Link

k

k (key value) パラメータは, kty octで利用する base64url encodeされたKey文字列をもつ

JSON Web Algorithms (JWA) 6.4.1 “k”

kty

kty (key type) パラメータは, RSA や EC といった暗号アルゴリズムファミリーを示す

JSON Web Key (JWK) 4.1 “kty”

kid

kid (key ID) パラメータは特定の鍵を識別するために用いられる

JSON Web Key (JWK) 4.5 “kid”

kty “oct” で利用する Keyの内容をBase64デコードした結果は以下の通り

echo -n "ZmFudGFzdGljand0" | base64 -d
実行結果サンプル
1fantasticjwt

この結果により、このサンプルでは fantasticjwt という文字列がKeyとして使用されていることが確認できます。

今回サンプルリクエストに利用するJWTがこの文字列で署名されたものであるか確認します。 token.jwt の内容を表示します。

token.jwt
1eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEifQ.eyJuYW1lIjoiUXVvdGF0aW9uIFN5c3RlbSIsInN1YiI6InF1b3RlcyIsImlzcyI6Ik15IEFQSSBHYXRld2F5In0.ggVOHYnVFB8GVPE-VOIo3jD71gTkLffAY0hQOGXPL2I
JWT.io を開き、 AlgorithmHS256 であることを確認します。
VERIFY SIGNATURE 欄の your-256-bit-secret に先程 jwk の内容をデコードして確認した文字列 fantasticjwt を入力してください。
画面左側 Eocoded 欄に、 token.jwt の内容を貼り付け、左下の表示が Signature Verified となることを確認してください。
この結果より、クライアントリクエストで利用するJWTは、検証可能なものであることが確認できます。またこのJWTに含まれる情報が右側に表示されますので合わせて確認ください。
../../_images/jwtio_verify.jpg

その他、NGINX Plus / JWT に関する詳細は Blog:Authenticating API Clients with JWT and NGINX Plus を参照してください

VirtualServerで利用するPolicyについて確認します。まずVirtualServerの内容は以下です

virtual-server.yaml
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  policies:
 8  - name: jwt-policy
 9  upstreams:
10  - name: webapp
11    service: webapp-svc
12    port: 80
13  routes:
14  - path: /
15    action:
16      pass: webapp

hostに対し jwt-policy というポリシーが適用されていることが確認できます。 では次に、Policyの内容を確認します

jwt.yaml
1apiVersion: k8s.nginx.org/v1
2kind: Policy
3metadata:
4  name: jwt-policy
5spec:
6  jwt:
7    realm: MyProductAPI
8    secret: jwk-secret
9    token: $http_token
先程VirtualServerの内容で確認したように、 jwt-policy という名前のPolicyとなります。
specにPolicyの設定が記述されています。secretに先程作成した jwt-secret が指定されており、
tokenとして参照する内容は、 token というhttp headerの値とするため、 $http_token を指定しています。

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get deployment
実行結果サンプル
1NAME     READY   UP-TO-DATE   AVAILABLE   AGE
2webapp   1/1     1            1           23s
kubectl get secret | grep jwk
実行結果サンプル
1jwk-secret            nginx.org/jwk                         1      40s
kubectl get policy
実行結果サンプル
1NAME         STATE   AGE
2jwt-policy   Valid   38s
kubectl get vs
実行結果サンプル
1NAME     STATE   HOST                 IP    PORTS   AGE
2webapp   Valid   webapp.example.com                 35s

動作確認

Policyが適用されたVirtualServerにJWTをHeaderに付与していないため、通信に対し 401 Authorization required が応答されていることを確認します

curl -H "Host:webapp.example.com" http://localhost/
実行結果サンプル
1<html>
2<head><title>401 Authorization Required</title></head>
3<body>
4<center><h1>401 Authorization Required</h1></center>
5<hr><center>nginx/1.23.4</center>
6</body>
7</html>

curlコマンドで動作を確認します。以下のように通信が 許可 されていることが確認できます

curl -H "Host:webapp.example.com" http://localhost/ -H "Token: `cat token.jwt`"
実行結果サンプル
1Server address: 192.168.127.57:8080
2Server name: webapp-64d444885-r5fnt
3Date: 18/Jan/2022:12:49:59 +0000
4URI: /
5Request ID: 86182122eec0392769b4d86d64653419

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/jwt/
kubectl delete -f virtual-server.yaml
kubectl delete -f jwt.yaml
kubectl delete -f jwk-secret.yaml
kubectl delete -f webapp.yaml

Ingress Controller で OIDC RPのデプロイ

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/oidc

NGINX Ingress Controller による JWT の制御に加え、NGINXより提供するJavaScript Moduleを利用することにより、OIDCのRPとして動作することが可能です。
このサンプルでは、KeycloakをIDPとして動作させ、クライアントのリクエストを適切に認証することを確認いただけます。

サンプルアプリケーションをデプロイ

リソースをデプロイします。ここでIDPとして動作させる KeyCloak をデプロイします

cd ~/kubernetes-ingress/examples/custom-resources/oidc
kubectl apply -f tls-secret.yaml
kubectl apply -f webapp.yaml
kubectl apply -f keycloak.yaml
kubectl apply -f virtual-server-idp.yaml

このサンプルで利用するFQDNを確認します。ラボ環境のJumpHostでは予め双方のFQDNを登録しています。

## cd ~/kubernetes-ingress/examples/custom-resources/oidc
grep host virtual-server*yaml
実行結果サンプル
1virtual-server-idp.yaml:  host: keycloak.example.com
2virtual-server.yaml:  host: webapp.example.com
ブラウザからKeyCloakにアクセスし、設定を行います。
Chromeを開き、 https://keycloak.example.com へアクセスしてください。

Note

Keycloak はデプロイから起動まで数分(2~3分)かかります。正しく疎通ができない場合は一定時間おいて再度接続してください

../../_images/keycloak_top.jpg

Administration Console を開きます。ログイン画面が表示されますので以下の情報でログインしてください。

  • ログイン情報

usename

admin

password

admin

../../_images/keycloak_login.jpg

左メニューより Clients を開き、 Create から新規作成を行います。

../../_images/keycloak_clients.jpg

Client ID: nginx-plus を指定し、 Save します。

../../_images/keycloak_clients_new.jpg

SettingsタブのAccess Type: confidential を選択し、Valid Redirect URIs: https://webapp.example.com:443/_codexch を入力し、 Save します。

../../_images/keycloak_clients_setting.jpg

Credentialsタブを開きます。後ほどSecretの値を利用しますので表示されている文字列を記録しておきます。

../../_images/keycloak_clients_secret.jpg

Rolesタブを開き、 Add Role から追加を行います。

../../_images/keycloak_clients_role.jpg

Role Name: nginx-keycloak-role を指定し、 Save します。

../../_images/keycloak_clients_role2.jpg

左メニュー Users を開き、 Add user からユーザの新規作成を行います。

../../_images/keycloak_clients_users.jpg

Username: nginx-user を指定し、 Save します。

../../_images/keycloak_clients_users_new.jpg

Credentialsタブを開き、Password: test を入力、Temporary: Off を選択し、nginx-userのパスワードを設定します。

../../_images/keycloak_clients_users_pass.jpg

Role Mappingsタブを開き、Client Roles: nginx-plus を選択し、Available Rolesに表示される nginx-keycloak-role を選択し、 Add selected でRoleをAssignします。

../../_images/keycloak_clients_users_role_mapping.jpg

これでKeycloakの準備は完了しました。

先程、Keycloakで確認したSecretの内容をbase64エンコードします

echo -n "f0558674-70a1-45a9-8c90-02245628b8f1" | base64
実行結果サンプル
1ZjA1NTg2NzQtNzBhMS00NWE5LThjOTAtMDIyNDU2MjhiOGYx

client-secret.yaml に設定します

## cd ~/kubernetes-ingress/examples/custom-resources/oidc
vi client-secret.yaml
Client Secret の指定
1apiVersion: v1
2kind: Secret
3metadata:
4  name: oidc-secret
5type: nginx.org/oidc
6data:
7  client-secret: ***BASE64 EncodeしたSECRET情報***

OIDC PolicyとClientSecretをデプロイします。

## cd ~/kubernetes-ingress/examples/custom-resources/oidc
kubectl apply -f client-secret.yaml
kubectl apply -f oidc.yaml

本書の環境では単一のPodでNGINX Ingress Controllerを動作させているためZone Synchronizationの設定はしません。必要となる方は手順を参考に実施してください。

最後にNGINX Ingress ControllerをWebアプリケーション用のOIDC RP として動作させるため、VirtualServerを作成します。kbue-dnsのIPアドレスを確認し、virtual-server.yamlに設定を追加します。

kubectl get svc -n kube-system | grep kube-dns
実行結果サンプル
1kube-dns   ClusterIP   10.96.0.10   <none>        53/UDP,53/TCP,9153/TCP   12d
## cd ~/kubernetes-ingress/examples/custom-resources/oidc
vi virtual-server.yaml
server-snippetsにkube-dnsのIPをresolverとして指定
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  tls:
 8    secret: tls-secret
 9    redirect:
10      enable: true
11  server-snippets: |
12    resolver 10.96.0.10;                     # kube-dnsのIPアドレスを指定します
13  upstreams:
14    - name: webapp
15      service: webapp-svc
16      port: 80
17  routes:
18    - path: /
19      policies:
20      - name: oidc-policy
21      action:
22        pass: webapp
## cd ~/kubernetes-ingress/examples/custom-resources/oidc
kubectl apply -f virtual-server.yaml

リソースを確認

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get secret | grep -e oidc -e tls-secret
実行結果サンプル
1oidc-secret           nginx.org/oidc                        1      4m29s
2tls-secret            kubernetes.io/tls                     2      21m
kubectl get deployment
実行結果サンプル
1NAME       READY   UP-TO-DATE   AVAILABLE   AGE
2keycloak   1/1     1            1           22m
3webapp     1/1     1            1           22m
kubectl get svc
実行結果サンプル
1NAME         TYPE        CLUSTER-IP      EXTERNAL-IP   PORT(S)    AGE
2keycloak     ClusterIP   10.97.4.138     <none>        8080/TCP   22m
3kubernetes   ClusterIP   10.96.0.1       <none>        443/TCP    12d
4webapp-svc   ClusterIP   10.104.69.230   <none>        80/TCP     22m
kubectl get policy
実行結果サンプル
1NAME          STATE   AGE
2oidc-policy   Valid   9m28s
kubectl get vs
実行結果サンプル
1NAME       STATE   HOST                   IP    PORTS   AGE
2keycloak   Valid   keycloak.example.com                 23m
3webapp     Valid   webapp.example.com                   7m40s

動作確認

Chromeブラウザを開き、 Secret Tab (New Incognito Window) を開いてください。

../../_images/chrome_secret_tab.jpg

https://webapp.example.com へアクセスしてください。

../../_images/chrome_webapp.jpg

Keycloakのログイン画面が表示されます。先程設定を行った nginx-user でログインしてください。

  • ログイン情報

usename

password

nginx-user

test

../../_images/chrome_webapp_keycloak_login.jpg

ログインが正常に行われた場合、Webアプリケーションの結果をブラウザで確認いただけます。

../../_images/chrome_webapp_logined.jpg

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/oidc
kubectl delete -f webapp.yaml
kubectl delete -f keycloak.yaml
kubectl delete -f virtual-server-idp.yaml
kubectl delete -f client-secret.yaml
kubectl delete -f oidc.yaml
kubectl delete -f virtual-server.yaml

Ingress MTLS

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/ingress-mtls

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/ingress-mtls
kubectl apply -f webapp.yaml
kubectl apply -f ingress-mtls-secret.yaml
kubectl apply -f ingress-mtls.yaml
kubectl replace --force -f tls-secret.yaml
kubectl apply -f virtual-server.yaml

リソースを確認

ポイントとなるファイルの内容を確認します。

ingress-mtls-secret.yaml でクライアント証明書の評価に用いる証明書を作成します。

ingress-mtls-secret.yaml
1kind: Secret
2metadata:
3  name: ingress-mtls-secret
4apiVersion: v1
5type: nginx.org/ca
6data:
7  ca.crt: **省略**

ingress-mtls.yaml は別途作成した ingress-mtls-secret をclientCertSecretに指定し(7)、Virtual Serverで利用するPolicyを作成します。

ingress-mtls.yaml
1apiVersion: k8s.nginx.org/v1
2kind: Policy
3metadata:
4  name: ingress-mtls-policy
5spec:
6  ingressMTLS:
7    clientCertSecret: ingress-mtls-secret
8    verifyClient: "on"
9    verifyDepth: 1

作成した ingress-mtls-policy というPolicyをリクエストに対し適用するため、TLSの指定(7,8)と、Policyの指定(9,10)を行っています

virtual-server.yaml
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  tls:
 8    secret: tls-secret
 9  policies:
10  - name: ingress-mtls-policy
11  upstreams:
12  - name: webapp
13    service: webapp-svc
14    port: 80
15  routes:
16  - path: /
17    action:
18      pass: webapp

通信に利用される証明書の内容は、以下コマンドを参考に確認してください

# echo -n <ca.crt に指定された文字列> | base64 -d > ca-crt.txt
echo -n "LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUQvVENDQXVXZ0F3SUJBZ0lVSzdhbU14OFlLWG1BVG51SkZETDlWS2ZUR2ZNd0RRWUpLb1pJaHZjTkFRRUwKQlFBd2dZMHhDekFKQmdOVkJBWVRBbFZUTVFzd0NRWURWUVFJREFKRFFURVdNQlFHQTFVRUJ3d05VMkZ1SUVaeQpZVzVqYVhOamJ6RU9NQXdHQTFVRUNnd0ZUa2RKVGxneEREQUtCZ05WQkFzTUEwdEpRekVXTUJRR0ExVUVBd3dOCmEybGpMbTVuYVc1NExtTnZiVEVqTUNFR0NTcUdTSWIzRFFFSkFSWVVhM1ZpWlhKdVpYUmxjMEJ1WjJsdWVDNWoKYjIwd0hoY05NakF3T1RFNE1qQXlOVEkyV2hjTk16QXdPVEUyTWpBeU5USTJXakNCalRFTE1Ba0dBMVVFQmhNQwpWVk14Q3pBSkJnTlZCQWdNQWtOQk1SWXdGQVlEVlFRSERBMVRZVzRnUm5KaGJtTnBjMk52TVE0d0RBWURWUVFLCkRBVk9SMGxPV0RFTU1Bb0dBMVVFQ3d3RFMwbERNUll3RkFZRFZRUUREQTFyYVdNdWJtZHBibmd1WTI5dE1TTXcKSVFZSktvWklodmNOQVFrQkZoUnJkV0psY201bGRHVnpRRzVuYVc1NExtTnZiVENDQVNJd0RRWUpLb1pJaHZjTgpBUUVCQlFBRGdnRVBBRENDQVFvQ2dnRUJBTmFINVRzaTZzaUFsU085dEJnYmY3VVRwcWowMUhRTlQ2UjhtQy9pCjhLYXFaSW9XSUdvN2xhTW9xTDYydTc4ay9WOHM2Z0FJaU1DSzBjekFvTFhNSnlJQkxQeTg4Yzdtc2xwZXgxTkEKVmRtMkVTVkN6bVlERE1TT3FpVmszWmpYeC9URmo2QzhNRFhhRkZUWFg1dWdtbWdscnFCWlh0OVI5VVBwVTJMNwo1bEZ0NlJ2R3VGczgvbVZORVR5c1A0SFhCWlh2ZE9mdG1YWUkvK01hOW5CMzIzNjdmcTI0L0RKZ2YvK2xRbUsxCkJLR3poSTZSc1pSSmdWOXdpK1VuZTBYNjlaS2lLOFdXU3lZS252YnRrcHZuTDA2dGNJaXJZNi80UzZ4Sm1HRVQKZEJUNmVxc0NoSUpQUStWSEp5dTROdnV6WmVCUXpGdmMwNytnUGZkVWZra1FXODhDQXdFQUFhTlRNRkV3SFFZRApWUjBPQkJZRUZKUGdhcnFYa00rdEJ0djVhdndTUWhUQmpTU2VNQjhHQTFVZEl3UVlNQmFBRkpQZ2FycVhrTSt0CkJ0djVhdndTUWhUQmpTU2VNQThHQTFVZEV3RUIvd1FGTUFNQkFmOHdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUIKQUl3WXpoY0s4OWtRL0xGWjZFRHgrQWp2bnJTVSs1cmdwQkgrRjVTNUUyY3pXOE5rNXhySnl0Y0ZUbUtlKzZScwpENHlxeTZSVVFEeWNYaDlPelBjbzgzYTBoeFlCZ1M5MWtJa25wYWF4dndLRDJleWc3UGNnK1lkS1FhZFlMcUY0CmI3cWVtc1FVVkpOWHdkZS9VanRBejlEOTh4dngwM2hQY2Qwb2dzUUhWZ21BZVpFd2l3UzFmTy9WNUE4dTl3MEkKcHlJRTVReXlHcHNpS2dpalpiMmhrS05RVHVJcEhiVnFydVA4eEV6TlFnamhkdS9uUW5OYy9lRUltVUlrQkFUVQpiSHdQc2xwYzVhdVV1TXJxR3lEQ0p2QUJpV3J2SmE3Yi9XcmtDT3FUWVhtR2NGM0w1ZU9FeTBhYkp0M2NNcSs5CnJLTUNVQWlkNG0yNEthWnc3OUk2anNBPQotLS0tLUVORCBDRVJUSUZJQ0FURS0tLS0tCg==" | base64 -d > ca-crt.txt
openssl x509 -text -noout -in ca-crt.txt
実行結果サンプル
 1Certificate:
 2  Data:
 3      Version: 3 (0x2)
 4      Serial Number:
 5          2b:b6:a6:33:1f:18:29:79:80:4e:7b:89:14:32:fd:54:a7:d3:19:f3
 6      Signature Algorithm: sha256WithRSAEncryption
 7      Issuer: C = US, ST = CA, L = San Francisco, O = NGINX, OU = KIC, CN = kic.nginx.com, emailAddress = kubernetes@nginx.com
 8      Validity
 9          Not Before: Sep 18 20:25:26 2020 GMT
10          Not After : Sep 16 20:25:26 2030 GMT
11      Subject: C = US, ST = CA, L = San Francisco, O = NGINX, OU = KIC, CN = kic.nginx.com, emailAddress = kubernetes@nginx.com
12      **省略**
openssl x509 -text -noout -in client-cert.pem
実行結果サンプル
 1Certificate:
 2    Data:
 3        Version: 1 (0x0)
 4        Serial Number: 1 (0x1)
 5        Signature Algorithm: sha256WithRSAEncryption
 6        Issuer: C = US, ST = CA, L = San Francisco, O = NGINX, OU = KIC, CN = kic.nginx.com, emailAddress = kubernetes@nginx.com
 7        Validity
 8            Not Before: Sep 18 20:27:15 2020 GMT
 9            Not After : Sep 16 20:27:15 2030 GMT
10        Subject: C = US, ST = CA, L = San Francisco, O = NGINX
11        **省略**

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get deployment
実行結果サンプル
1NAME     READY   UP-TO-DATE   AVAILABLE   AGE
2webapp   1/1     1            1           8s
kubectl get secret  | grep -e tls-secret
実行結果サンプル
1ingress-mtls-secret   nginx.org/ca                          1      32s
2tls-secret            kubernetes.io/tls                     2      31s
kubectl get policy
実行結果サンプル
1NAME                  STATE   AGE
2ingress-mtls-policy   Valid   44s
kubectl get vs
実行結果サンプル
1NAME     STATE   HOST                 IP    PORTS   AGE
2webapp   Valid   webapp.example.com                 48s

動作確認

curlコマンドの結果を確認します。
クライアント証明書が要求(13)に対し、応答をしますが(17)、証明書が無いため署名データのやり取りは確認できません。サーバ証明書の内容がServer certificateに表示されています(24-28)。
クライアント証明書が提示されなかったため、リクエストが拒否され、Webサーバから400 Bad Requestと共にエラーが応答されています(36,44)。
curl -v -k --resolve webapp.example.com:443:127.0.0.1 https://webapp.example.com:443/
実行結果サンプル
 1* Added webapp.example.com:443:127.0.0.1 to DNS cache
 2* Hostname webapp.example.com was found in DNS cache
 3*   Trying 127.0.0.1:443...
 4* TCP_NODELAY set
 5* Connected to webapp.example.com (127.0.0.1) port 443 (#0)
 6* ALPN, offering h2
 7* ALPN, offering http/1.1
 8* successfully set certificate verify locations:
 9*   CAfile: /etc/ssl/certs/ca-certificates.crt
10  CApath: /etc/ssl/certs
11* TLSv1.3 (OUT), TLS handshake, Client hello (1):
12* TLSv1.3 (IN), TLS handshake, Server hello (2):
13* TLSv1.2 (IN), TLS handshake, Certificate (11):
14* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
15* TLSv1.2 (IN), TLS handshake, Request CERT (13):
16* TLSv1.2 (IN), TLS handshake, Server finished (14):
17* TLSv1.2 (OUT), TLS handshake, Certificate (11):
18* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
19* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
20* TLSv1.2 (OUT), TLS handshake, Finished (20):
21* TLSv1.2 (IN), TLS handshake, Finished (20):
22* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
23* ALPN, server accepted to use http/1.1
24* Server certificate:
25*  subject: CN=webapp.example.com
26*  start date: Sep 29 22:19:59 2020 GMT
27*  expire date: Sep 27 22:19:59 2030 GMT
28*  issuer: CN=webapp.example.com
29*  SSL certificate verify result: self signed certificate (18), continuing anyway.
30> GET / HTTP/1.1
31> Host: webapp.example.com
32> User-Agent: curl/7.68.0
33> Accept: */*
34>
35* Mark bundle as not supporting multiuse
36< HTTP/1.1 400 Bad Request
37< Server: nginx/1.21.3
38< Date: Wed, 19 Jan 2022 12:16:54 GMT
39< Content-Type: text/html
40< Content-Length: 237
41< Connection: close
42<
43<html>
44<head><title>400 No required SSL certificate was sent</title></head>
45<body>
46<center><h1>400 Bad Request</h1></center>
47<center>No required SSL certificate was sent</center>
48<hr><center>nginx/1.21.3</center>
49</body>
50</html>
51* Closing connection 0
52* TLSv1.2 (OUT), TLS alert, close notify (256):
curlコマンドの結果を確認します。
クライアント証明書が要求され(13)、その要求に対し証明書を応答します(17,19)。サーバ証明書の内容がServer certificateに表示されています(25-29)。
正しくクライアントの証明書が評価できたため、リクエストが許可され、Webサーバから200 OKが応答されており(37)、正しいレスポンスが表示されています(46-50)
curl -v -k --resolve webapp.example.com:443:127.0.0.1 https://webapp.example.com:443/ --cert ./client-cert.pem --key ./client-key.pem
実行結果サンプル
 1* Added webapp.example.com:443:127.0.0.1 to DNS cache
 2* Hostname webapp.example.com was found in DNS cache
 3*   Trying 127.0.0.1:443...
 4* TCP_NODELAY set
 5* Connected to webapp.example.com (127.0.0.1) port 443 (#0)
 6* ALPN, offering h2
 7* ALPN, offering http/1.1
 8* successfully set certificate verify locations:
 9*   CAfile: /etc/ssl/certs/ca-certificates.crt
10  CApath: /etc/ssl/certs
11* TLSv1.3 (OUT), TLS handshake, Client hello (1):
12* TLSv1.3 (IN), TLS handshake, Server hello (2):
13* TLSv1.2 (IN), TLS handshake, Certificate (11):
14* TLSv1.2 (IN), TLS handshake, Server key exchange (12):
15* TLSv1.2 (IN), TLS handshake, Request CERT (13):
16* TLSv1.2 (IN), TLS handshake, Server finished (14):
17* TLSv1.2 (OUT), TLS handshake, Certificate (11):
18* TLSv1.2 (OUT), TLS handshake, Client key exchange (16):
19* TLSv1.2 (OUT), TLS handshake, CERT verify (15):
20* TLSv1.2 (OUT), TLS change cipher, Change cipher spec (1):
21* TLSv1.2 (OUT), TLS handshake, Finished (20):
22* TLSv1.2 (IN), TLS handshake, Finished (20):
23* SSL connection using TLSv1.2 / ECDHE-RSA-AES256-GCM-SHA384
24* ALPN, server accepted to use http/1.1
25* Server certificate:
26*  subject: CN=webapp.example.com
27*  start date: Sep 29 22:19:59 2020 GMT
28*  expire date: Sep 27 22:19:59 2030 GMT
29*  issuer: CN=webapp.example.com
30*  SSL certificate verify result: self signed certificate (18), continuing anyway.
31> GET / HTTP/1.1
32> Host: webapp.example.com
33> User-Agent: curl/7.68.0
34> Accept: */*
35>
36* Mark bundle as not supporting multiuse
37< HTTP/1.1 200 OK
38< Server: nginx/1.21.3
39< Date: Wed, 19 Jan 2022 12:16:56 GMT
40< Content-Type: text/plain
41< Content-Length: 157
42< Connection: keep-alive
43< Expires: Wed, 19 Jan 2022 12:16:55 GMT
44< Cache-Control: no-cache
45<
46Server address: 192.168.127.22:8080
47Server name: webapp-64d444885-x5d4p
48Date: 19/Jan/2022:12:16:56 +0000
49URI: /
50Request ID: c1b1c9c9b30331cbc7f034e026b939fc
51* Connection #0 to host webapp.example.com left intact

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/ingress-mtls
kubectl delete -f webapp.yaml
kubectl delete -f ingress-mtls-secret.yaml
kubectl delete -f ingress-mtls.yaml
kubectl delete -f tls-secret.yaml
kubectl delete -f virtual-server.yaml
rm ca-crt.txt

Egress MTLS

https://github.com/nginx/kubernetes-ingress/tree/v3.1.1/examples/custom-resources/egress-mtls

サンプルアプリケーションをデプロイ

cd ~/kubernetes-ingress/examples/custom-resources/egress-mtls
kubectl apply -f secure-app.yaml
kubectl apply -f egress-mtls-secret.yaml
kubectl apply -f egress-trusted-ca-secret.yaml
kubectl apply -f egress-mtls.yaml
kubectl apply -f virtual-server.yaml

リソースを確認

ファイルの内容を確認します。
secure-app.yaml は、Kubernetes環境内で動作するアプリケーションで、クライアント証明書の評価を行います。ポイントとなる箇所を以下に示します。
  • volumeMountsでそれぞれのPathにVolumeをマウントしています。/etc/nginx/sslに app-tls-secret というSecret(22,29)、/etc/nginx/conf.d/に secure-config というConfigMap(24,32)の内容がそれぞれマウントされます

  • secure-config というConfigMapではNGINXの設定を指定します。SSLの終端(58,59)及び、クライアント証明書(61,62)の評価を行うよう設定を記述しています

secure-app.yaml
 1apiVersion: apps/v1
 2kind: Deployment
 3metadata:
 4  name: secure-app
 5spec:
 6  replicas: 1
 7  selector:
 8    matchLabels:
 9      app: secure-app
10  template:
11    metadata:
12      labels:
13        app: secure-app
14    spec:
15      containers:
16        - name: secure-app
17          image: nginxdemos/nginx-hello:plain-text
18          ports:
19            - containerPort: 8443
20          volumeMounts:
21            - name: secret
22              mountPath: /etc/nginx/ssl
23              readOnly: true
24            - name: config-volume
25              mountPath: /etc/nginx/conf.d
26      volumes:
27        - name: secret
28          secret:
29            secretName: app-tls-secret
30        - name: config-volume
31          configMap:
32            name: secure-config
33---
34apiVersion: v1
35kind: Service
36metadata:
37  name: secure-app
38spec:
39  ports:
40    - port: 8443
41      targetPort: 8443
42      protocol: TCP
43      name: https
44  selector:
45    app: secure-app
46---
47apiVersion: v1
48kind: ConfigMap
49metadata:
50  name: secure-config
51data:
52  app.conf: |-
53    server {
54      listen 8443 ssl;
55
56      server_name secure-app.example.com;
57
58      ssl_certificate /etc/nginx/ssl/tls.crt;
59      ssl_certificate_key /etc/nginx/ssl/tls.key;
60
61      ssl_verify_client on;
62      ssl_client_certificate /etc/nginx/ssl/ca.crt;
63
64      default_type text/plain;
65
66      location / {
67        return 200 "hello from pod $hostname\n";
68      }
69    }
70---
71apiVersion: v1
72kind: Secret
73metadata:
74  name: app-tls-secret
75type: Opaque
76data:
77  tls.crt: **省略**
78  tls.key: **省略**
79  ca.crt: **省略**

egress-mtls.yaml は、VirtualServerに適用する EgressTlsのPolicyとなります。アプリケーションへ転送する際にの証明書として egress-mtls-secretegress-trusted-ca-secret として作成したSecretを参照します(7,8)。

egress-mtls.yaml
 1apiVersion: k8s.nginx.org/v1
 2kind: Policy
 3metadata:
 4  name: egress-mtls-policy
 5spec:
 6  egressMTLS:
 7    tlsSecret: egress-mtls-secret
 8    trustedCertSecret: egress-trusted-ca-secret
 9    verifyServer: on
10    verifyDepth: 2
11    serverName: on
12    sslName: secure-app.example.com

virtual-server.yaml は、upstreamへtlsを有効にし(11,12)、routesで、 egress-mtls-policy を指定しています(14,15)。

virtual-server.yaml
 1apiVersion: k8s.nginx.org/v1
 2kind: VirtualServer
 3metadata:
 4  name: webapp
 5spec:
 6  host: webapp.example.com
 7  upstreams:
 8  - name: secure-app
 9    service: secure-app
10    port: 8443
11    tls:
12      enable: true
13  routes:
14  - path: /
15    policies:
16    - name: egress-mtls-policy
17    action:
18      pass: secure-app

以下の通り、各リソースを適切に作成されていることを確認します。

kubectl get deployment
実行結果サンプル
1NAME         READY   UP-TO-DATE   AVAILABLE   AGE
2secure-app   1/1     1            1           73s
kubectl get pod
実行結果サンプル
1NAME                          READY   STATUS    RESTARTS   AGE
2secure-app-6dc947cc5f-8855b   1/1     Running   0          75s
kubectl get svc | grep secure-app
実行結果サンプル
1secure-app   ClusterIP   10.101.84.115   <none>        8443/TCP   5m17s
kubectl get secret | grep -e app-tls -e egress
実行結果サンプル
1app-tls-secret             Opaque                                3      6m53s
2egress-mtls-secret         kubernetes.io/tls                     2      6m48s
3egress-trusted-ca-secret   nginx.org/ca                          1      6m42s

動作確認

curl -v -H "Host:webapp.example.com" http://localhost/
実行結果サンプル
 1*   Trying 127.0.0.1:80...
 2* TCP_NODELAY set
 3* Connected to localhost (127.0.0.1) port 80 (#0)
 4> GET / HTTP/1.1
 5> Host:webapp.example.com
 6> User-Agent: curl/7.68.0
 7> Accept: */*
 8>
 9* Mark bundle as not supporting multiuse
10< HTTP/1.1 200 OK
11< Server: nginx/1.21.3
12< Date: Wed, 19 Jan 2022 15:14:03 GMT
13< Content-Type: text/plain
14< Content-Length: 43
15< Connection: keep-alive
16<
17hello from pod secure-app-6dc947cc5f-8855b
18* Connection #0 to host localhost left intact

リソースの削除

## cd ~/kubernetes-ingress/examples/custom-resources/egress-mtls
kubectl delete -f secure-app.yaml
kubectl delete -f egress-mtls-secret.yaml
kubectl delete -f egress-trusted-ca-secret.yaml
kubectl delete -f egress-mtls.yaml
kubectl delete -f virtual-server.yaml