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を使用する。 このアプリ自体はただのSpring Bootアプリであり、普通に8080番をlistenするHTTPアプリである。
docker run
するとhttp://localhost:8080でアクセスできる。
docker run --rm -p 8080:8080 trisberg/spring-music:cnb
ソースコードを変更せずに、このDokcerイメージをそのまま使ってTLS Terminationしたい。
次のspring-music.yml
を作成する。ポイントはinitContainers
でTLS証明書(pem形式)をjks形式に変換し、マウントしたボリュームに配置しているところ。
あとは環境変数SERVER_SSL_****
を設定し、アプリ側のTLS設定を行う。
この記事の応用版
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に保存する。
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
コマンドで自己署名証明書を作成する。こちらからスクリプトをダウンロードし、次のコマンドを実行。
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に保存する。
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
を実行する。
kubectl apply -f spring-music.yml \
-f spring-music-secret.yml \
-f spring-music-secret-tls.yml
次のコマンドでログを確認。
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 <none> 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://<Worker NodeのIPの.を-に置換>.sslip.io:<NodePort>
にアクセスして動作確認。