--- title: Spring BootアプリをKubernetesにデプロイする際にアプリ側でTLS Terminationをするメモ tags: ["Spring Boot", "TLS", "Kubernetes", "PKS"] categories: ["Dev", "CaaS", "Kubernetes"] date: 2019-08-19T08:39:00Z updated: 2019-09-29T14:08:56Z --- KubernetesにSpring Bootアプリを置く場合はIngressを使用するなど、前段でTLS Terminationすることが多いと思うが、 アプリ側で実施したい場合のやり方を説明する。 `X-Forwaded-Proto`ヘッダーがLBが正しく設定してくれず、ログイン後のリダイレクトがうまく行かない(HTTPにリダイレクトされる)ことがあったのでメモ > PKS + NSX-TでNSX-T Ingressを使うケースで発生。 Spring Boot 2.1.5で動作確認しているが、2.xであれば同じはず。 サンプルアプリとして[Spring Music](https://github.com/cloudfoundry-samples/spring-music)を使用する。 このアプリ自体はただのSpring Bootアプリであり、普通に8080番をlistenするHTTPアプリである。 `docker run`すると[http://localhost:8080](http://localhost:8080)でアクセスできる。 ```bash docker run --rm -p 8080:8080 trisberg/spring-music:cnb ``` ![image](https://user-images.githubusercontent.com/106908/63246500-c60e7100-c29d-11e9-951f-074a7de57184.png) ソースコードを変更せずに、このDokcerイメージをそのまま使ってTLS Terminationしたい。 次の`spring-music.yml`を作成する。ポイントは`initContainers`でTLS証明書(pem形式)をjks形式に変換し、マウントしたボリュームに配置しているところ。 あとは環境変数`SERVER_SSL_****`を設定し、アプリ側のTLS設定を行う。 > [この記事](https://blog.ik.am/entries/450)の応用版 ```yaml apiVersion: v1 kind: Namespace metadata: name: spring-music --- apiVersion: apps/v1 kind: Deployment metadata: name: spring-music namespace: spring-music spec: replicas: 1 selector: matchLabels: app: spring-music template: metadata: labels: app: spring-music spec: initContainers: - image: openjdk:11-jdk-slim name: pem-to-keystore volumeMounts: - name: keystore-volume mountPath: /keystores - name: spring-music-tls mountPath: /spring-music-tls env: - name: SERVER_SSL_KEY_PASSWORD valueFrom: secretKeyRef: name: spring-music key: server.ssl.key-password command: - sh - -c - | set -e openssl pkcs12 -export \ -name spring-music \ -in /spring-music-tls/tls.crt \ -inkey /spring-music-tls/tls.key \ -out /keystores/spring-music.p12 \ -password pass:foobar keytool -importkeystore \ -destkeystore /keystores/spring-music.jks \ -srckeystore /keystores/spring-music.p12 \ -deststoretype pkcs12 \ -srcstoretype pkcs12 \ -alias spring-music \ -deststorepass ${SERVER_SSL_KEY_PASSWORD} \ -destkeypass ${SERVER_SSL_KEY_PASSWORD} \ -srcstorepass foobar \ -srckeypass foobar \ -noprompt containers: - image: trisberg/spring-music:cnb name: spring-music ports: - containerPort: 8443 volumeMounts: - name: keystore-volume mountPath: /keystores readOnly: true env: - name: SERVER_PORT value: "8443" - name: SERVER_SSL_ENABLED value: "true" - name: SERVER_SSL_PROTOCOL value: TLSv1.2 - name: SERVER_SSL_KEY_STORE value: file:///keystores/spring-music.jks - name: SERVER_SSL_KEY_STORE_TYPE value: PKCS12 - name: SERVER_SSL_KEY_ALIAS value: spring-music - name: SERVER_SSL_KEY_PASSWORD valueFrom: secretKeyRef: name: spring-music key: server.ssl.key-password - name: SERVER_SSL_KEY_STORE_PASSWORD valueFrom: secretKeyRef: name: spring-music key: server.ssl.key-password readinessProbe: httpGet: path: /actuator/health port: 8443 scheme: HTTPS initialDelaySeconds: 10 timeoutSeconds: 3 failureThreshold: 3 periodSeconds: 5 livenessProbe: httpGet: path: /actuator/info port: 8443 scheme: HTTPS initialDelaySeconds: 20 timeoutSeconds: 1 periodSeconds: 10 failureThreshold: 1 volumes: - name: keystore-volume emptyDir: {} - name: spring-music-tls secret: secretName: spring-music-tls --- kind: Service apiVersion: v1 metadata: name: spring-music namespace: spring-music spec: type: NodePort selector: app: spring-music ports: - protocol: TCP port: 8443 ``` Keystoreのパスワードは次のようにSecretに保存する。 ```bash kubectl -n spring-music create secret generic spring-music \ --dry-run -o yaml \ --from-literal server.ssl.key-password=thisisasecret \ > spring-music-secret.yml ``` 次にTLS証明書を作成する。Let's Encryptなどを使用する場合はここはスキップして良い。 今回は`sslip.io`ドメインを使用し、この`*.sslip.io`に対する自己署名証明書を作成する。 `sslip.io`は`a-b-c-d.sslip.io`を`a.b.c.d`に解決してくれるDNSサービスである。 例えば、`10-195-99-214.sslip.io`は`10.195.99.214`を返す。 `*.sslip.io`に対して、`openssl`コマンドで自己署名証明書を作成する。[こちら](https://raw.githubusercontent.com/aws-quickstart/quickstart-pivotal-cloudfoundry/master/scripts/gen_ssl_certs.sh)からスクリプトをダウンロードし、次のコマンドを実行。 ``` wget https://raw.githubusercontent.com/aws-quickstart/quickstart-pivotal-cloudfoundry/master/scripts/gen_ssl_certs.sh chmod +x gen_ssl_certs.sh ./gen_ssl_certs.sh sslip.io ``` この証明書をSecretに保存する。 ```bash kubectl -n spring-music create secret tls spring-music-tls \ --dry-run -o yaml \ --cert=sslip.io.crt \ --key=sslip.io.key \ > spring-music-secret-tls.yml ``` これでマニフェストが揃ったので、`kubectl apply`を実行する。 ```bash kubectl apply -f spring-music.yml \ -f spring-music-secret.yml \ -f spring-music-secret-tls.yml ``` 次のコマンドでログを確認。 ```bash kubectl -n spring-music logs \ -f $(kubectl -n spring-music get pod -l app=spring-music -o jsonpath='{.items[0].metadata.name}') ``` 次のメッセージが出ていればOK ``` 2019-08-19 08:13:27.700 INFO 1 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 8443 (https) ``` ``` $ kubectl -n spring-music get all NAME READY STATUS RESTARTS AGE pod/spring-music-6d6d57586b-w6mfc 1/1 Running 0 31s NAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE service/spring-music NodePort 10.100.200.241 8443:31308/TCP 30s NAME READY UP-TO-DATE AVAILABLE AGE deployment.apps/spring-music 1/1 1 1 31s NAME DESIRED CURRENT READY AGE replicaset.apps/spring-music-6d6d57586b 1 1 1 32s ``` この例では`type: NodePort`でサービスを公開している。ブラウザで`https://.sslip.io:`にアクセスして動作確認。 ![image](https://user-images.githubusercontent.com/106908/63249916-6e740380-c2a5-11e9-8551-e49e300a21c0.png) ![image](https://user-images.githubusercontent.com/106908/63249944-7cc21f80-c2a5-11e9-879d-3709efd09856.png)