--- title: Let's Encryptの証明書をcert-managerとAzure DNSで発行しSpring Bootアプリに設定するメモ tags: ["Kubernetes", "Let's Encrypt", "TLS", "cert-manager", "Azure"] categories: ["Dev", "CaaS", "Kubernetes", "cert-manager"] date: 2020-07-26T06:33:35Z updated: 2020-07-27T04:06:12Z --- Let's Encryptの証明書生成およびSecretの作成を[cert-manager](https://cert-manager.io)で行い、Spring Bootアプリに設定するメモです。 Azure DNSでDNS Challengeを使います。 **目次** ### cert-managerのインストール https://cert-manager.io/docs/installation/kubernetes の通りインストールします。 ``` kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.16.0/cert-manager.yaml ``` Podを確認。 ``` $ kubectl get pods -n cert-manager NAME READY STATUS RESTARTS AGE cert-manager-cainjector-6d444776dd-f7wxx 1/1 Running 0 2m58s cert-manager-fbd8f9986-wbsmw 1/1 Running 0 2m58s cert-manager-webhook-86689cb4c9-4tnwm 1/1 Running 0 2m58s ``` ### Azure CLIでService Principalを作成 https://cert-manager.io/docs/configuration/acme/dns01/azuredns の通り作成します。 Resource Group `maki-lol`とDNS Zone `maki.lol` は用意済み。 ![image](https://user-images.githubusercontent.com/106908/88461830-03ce3d80-cee2-11ea-9de1-505d3fe0a6e3.png) ``` az login ``` でブラウザ経由でログイン。 ``` AZURE_CERT_MANAGER_NEW_SP_NAME=cert-manager AZURE_DNS_ZONE_RESOURCE_GROUP=maki-lol AZURE_DNS_ZONE=maki.lol DNS_SP=$(az ad sp create-for-rbac --name ${AZURE_CERT_MANAGER_NEW_SP_NAME}) AZURE_CERT_MANAGER_SP_APP_ID=$(echo ${DNS_SP} | jq -r '.appId') AZURE_CERT_MANAGER_SP_PASSWORD=$(echo ${DNS_SP} | jq -r '.password') AZURE_TENANT_ID=$(echo ${DNS_SP} | jq -r '.tenant') AZURE_SUBSCRIPTION_ID=$(az account show | jq -r '.id') ``` Service Principalには`DNS Zone Contributor` Roleのみ付けます。 ``` az role assignment delete --assignee ${AZURE_CERT_MANAGER_SP_APP_ID} --role Contributor DNS_ID=$(az network dns zone show --name ${AZURE_DNS_ZONE} --resource-group ${AZURE_DNS_ZONE_RESOURCE_GROUP} --query "id" --output tsv) az role assignment create --assignee ${AZURE_CERT_MANAGER_SP_APP_ID} --role "DNS Zone Contributor" --scope ${DNS_ID} ``` ### Let's EncryptのIssuer作成 Let's EncryptのIssuerリソースを作ります。Namespaceレベルの`Issuer`とClusterレベルの`ClusterIssuer`がありますが、 今回は`ClusterIssuer`リソースを作成します。 Service PrincipalのClient Secret保存する`Secret`を作成します。 ``` cat < azuredns-config.yml kind: Secret apiVersion: v1 metadata: name: azuredns-config namespace: cert-manager stringData: client-secret: ${AZURE_CERT_MANAGER_SP_PASSWORD} EOF ``` Azure DNSを使った`ClusterIssuer`を作成します。 ``` cat < letsencrypt-cluster-issuer.yml apiVersion: cert-manager.io/v1alpha2 kind: ClusterIssuer metadata: name: letsencrypt spec: acme: server: https://acme-v02.api.letsencrypt.org/directory email: makingx+cert-manager@gmail.com privateKeySecretRef: name: letsencrypt solvers: - dns01: azuredns: clientID: ${AZURE_CERT_MANAGER_SP_APP_ID} clientSecretSecretRef: name: azuredns-config key: client-secret subscriptionID: ${AZURE_SUBSCRIPTION_ID} tenantID: ${AZURE_TENANT_ID} resourceGroupName: ${AZURE_DNS_ZONE_RESOURCE_GROUP} hostedZoneName: ${AZURE_DNS_ZONE} environment: AzurePublicCloud EOF ``` `kubectl apply`でこれらを適用します。 ``` kubectl apply -f azuredns-config.yml -f letsencrypt-cluster-issuer.yml ``` `ClusterIssuer`リソースが作成され、`READY`であることを確認します。 ``` $ kubectl get clusterissuer NAME READY AGE letsencrypt True 15s ``` ### Certificateの作成 次のこのIssuerから`Certificate`リソースを作成します。 `*.demo.maki.lol`と`demo.maki.lol`に対する証明書を発行します。 ``` cat < demo-maki-lol.yml apiVersion: cert-manager.io/v1alpha2 kind: Certificate metadata: name: demo-maki-lol namespace: default spec: secretName: demo-maki-lol-tls issuerRef: name: letsencrypt kind: ClusterIssuer dnsNames: - '*.demo.maki.lol' - demo.maki.lol EOF ``` `kubectl apply`でこれらを適用します。 ``` kubectl apply -f demo-maki-lol.yml ``` 2~3分経つと`Certificate`リソースが`READY`になります。 ``` $ kubectl get certificate -n default NAME READY SECRET AGE demo-maki-lol True demo-maki-lol-tls 2m29s ``` TLSの`Secret`も作成されます。 ``` $ kubectl get secret -n default NAME TYPE DATA AGE ... demo-maki-lol-tls kubernetes.io/tls 2 7m6s ... ``` TLS証明書の中身を見てみます。 ``` $ kubectl get secret demo-maki-lol-tls -o go-template='{{index .data "tls.crt" | base64decode}}' | openssl x509 -noout -text Certificate: Data: Version: 3 (0x2) Serial Number: 03:1f:52:6c:2a:21:51:fa:07:37:2c:37:02:90:34:24:a0:a2 Signature Algorithm: sha256WithRSAEncryption Issuer: C=US, O=Let's Encrypt, CN=Let's Encrypt Authority X3 Validity Not Before: Jul 26 03:02:03 2020 GMT Not After : Oct 24 03:02:03 2020 GMT Subject: CN=*.demo.maki.lol Subject Public Key Info: Public Key Algorithm: rsaEncryption Public-Key: (2048 bit) Modulus: 00:ab:c8:4f:a6:cf:92:12:1e:10:2e:6c:50:20:8d: 97:3e:ae:45:e5:37:5d:20:eb:36:88:36:53:01:ff: cb:98:61:da:02:37:a2:70:b6:c6:3c:16:42:8d:02: 16:71:94:77:70:b3:6d:07:c0:8c:37:29:ad:e9:51: 37:eb:5c:4b:9d:33:fd:d1:e5:7c:f1:8f:35:5d:18: b0:23:45:ee:37:ab:58:8f:ca:f9:41:e0:f4:d6:c6: d1:00:f4:a7:92:13:3e:f0:8c:0b:c8:2e:15:33:37: 04:7c:26:e8:44:b6:9f:10:4f:c2:57:a0:fb:05:a3: c7:a7:94:7b:2b:78:fc:15:71:08:91:1e:04:ce:7c: 46:82:82:09:94:e9:30:98:62:7b:d3:d7:67:d1:4a: 5b:89:b2:81:bf:70:44:db:8c:4c:e0:d3:41:38:1b: 8f:24:c9:b2:54:18:2d:b3:a6:64:52:45:7e:42:d3: d6:7f:35:3f:eb:04:4a:dd:0b:62:c5:ba:d0:9d:8a: ff:3c:6c:a2:7e:3a:2f:9b:c1:a6:92:95:67:64:8e: d1:46:da:2d:19:ac:d9:16:dc:4f:fb:c7:6a:a6:d7: 4c:3f:7f:0f:cc:ed:6a:55:77:e8:29:95:8b:3e:83: 90:e0:77:0f:69:53:4b:60:87:09:e9:5e:34:7b:bd: f1:0d Exponent: 65537 (0x10001) X509v3 extensions: X509v3 Key Usage: critical Digital Signature, Key Encipherment X509v3 Extended Key Usage: TLS Web Server Authentication, TLS Web Client Authentication X509v3 Basic Constraints: critical CA:FALSE X509v3 Subject Key Identifier: 21:E0:6A:FF:D0:43:73:09:CF:64:40:E0:7F:26:1B:C8:7A:BB:65:27 X509v3 Authority Key Identifier: keyid:A8:4A:6A:63:04:7D:DD:BA:E6:D1:39:B7:A6:45:65:EF:F3:A8:EC:A1 Authority Information Access: OCSP - URI:http://ocsp.int-x3.letsencrypt.org CA Issuers - URI:http://cert.int-x3.letsencrypt.org/ X509v3 Subject Alternative Name: DNS:*.demo.maki.lol, DNS:demo.maki.lol X509v3 Certificate Policies: Policy: 2.23.140.1.2.1 Policy: 1.3.6.1.4.1.44947.1.1.1 CPS: http://cps.letsencrypt.org 1.3.6.1.4.1.11129.2.4.2: ......v.^.s..V...6H}.I.2z.........u..qEX...s.H.......G0E.!... .F.[>.K.(.....)[...9=.o....{ . =v..f...t>....i!.L..|..sV....w...u......... N.f.+..% gk..p..IS-...^...s.H.......F0D. o..T.~%..|U,E$.....J=$l.......... *....h.8"i.j/.U......s.i.."?..<. Signature Algorithm: sha256WithRSAEncryption 5c:a0:d7:58:4d:ff:63:bc:6d:d7:71:1f:49:6b:e7:ae:e9:8a: 00:e0:dc:b3:77:e1:7c:93:46:f7:29:fd:14:04:45:99:f9:d0: 0b:de:e0:8d:43:65:db:76:6e:22:ef:e6:ea:71:5f:e0:44:1d: 0d:b4:27:f1:8e:84:5b:24:e7:e3:9c:78:46:2e:6a:de:59:a6: 72:fc:02:2b:81:05:d6:eb:a7:08:69:62:81:91:dd:f3:79:92: 35:3a:51:0f:70:8e:80:53:0c:32:f1:24:a9:b7:9d:d1:e0:c4: 82:b1:0f:aa:71:19:1b:26:48:ee:a0:3d:1a:06:f2:d9:5e:bb: 67:70:71:99:72:24:ca:08:e9:b5:4f:e6:ac:98:c7:20:05:e6: 3b:d8:d9:0a:64:31:e4:47:93:be:ac:69:b2:36:d1:ec:ac:96: 81:5e:2f:63:73:66:5b:b9:bf:93:16:d5:2f:a5:31:e7:58:22: 55:b1:58:28:8a:54:c1:86:31:7d:58:11:c0:b6:37:7b:23:9e: e6:fe:31:84:bc:02:d3:65:ca:c2:74:4e:d2:21:ab:34:d0:2d: 60:7a:18:7b:1b:02:e6:f1:27:fa:cd:5d:eb:09:d3:5e:25:91: af:99:a7:f4:1f:1f:98:e3:80:37:4e:c0:27:f4:c4:66:a9:f7: 94:57:49:81 ``` IssuerがLet's Encryptで、Subject Alternative Nameが`DNS:*.demo.maki.lol, DNS:demo.maki.lol`になっています。 ### 発行したTLS証明書をSpring Bootアプリに設定 https://blog.ik.am/entries/488 の記事のようにPEM形式をJKS形式に変換し、Spring BootアプリをTLS対応させます。 ``` cat <<'EOF' > hello-cnb.yml apiVersion: apps/v1 kind: Deployment metadata: name: hello-cnb spec: replicas: 1 selector: matchLabels: app: hello-cnb template: metadata: labels: app: hello-cnb spec: initContainers: - image: openjdk:11-jdk-slim name: pem-to-keystore volumeMounts: - name: keystore-volume mountPath: /keystores - name: demo-maki-lol-tls mountPath: /demo-maki-lol-tls env: - name: SERVER_SSL_KEY_PASSWORD valueFrom: secretKeyRef: name: hello-cnb key: server.ssl.key-password command: - sh - -ce - | openssl pkcs12 -export \ -name hello-cnb \ -in /demo-maki-lol-tls/tls.crt \ -inkey /demo-maki-lol-tls/tls.key \ -out /keystores/hello-cnb.p12 \ -password pass:foobar keytool -importkeystore \ -destkeystore /keystores/hello-cnb.jks \ -srckeystore /keystores/hello-cnb.p12 \ -deststoretype pkcs12 \ -srcstoretype pkcs12 \ -alias hello-cnb \ -deststorepass ${SERVER_SSL_KEY_PASSWORD} \ -destkeypass ${SERVER_SSL_KEY_PASSWORD} \ -srcstorepass foobar \ -srckeypass foobar \ -noprompt containers: - image: making/hello-cnb:latest name: hello-cnb ports: - containerPort: 8443 volumeMounts: - name: keystore-volume mountPath: /keystores readOnly: true env: - name: INFO_MESSAGE value: Hello World! - name: JAVA_OPTS value: "-XX:ReservedCodeCacheSize=32M -Xss512k" - name: BPL_JVM_THREAD_COUNT value: "20" - name: BPL_JVM_HEAD_ROOM value: "5" - 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/hello-cnb.jks - name: SERVER_SSL_KEY_STORE_TYPE value: PKCS12 - name: SERVER_SSL_KEY_ALIAS value: hello-cnb - name: SERVER_SSL_KEY_PASSWORD valueFrom: secretKeyRef: name: hello-cnb key: server.ssl.key-password - name: SERVER_SSL_KEY_STORE_PASSWORD valueFrom: secretKeyRef: name: hello-cnb key: server.ssl.key-password - name: SERVER_HTTP2_ENABLED value: "true" resources: limits: memory: "256Mi" requests: memory: "256Mi" readinessProbe: httpGet: path: /actuator/health/readiness port: 8443 scheme: HTTPS initialDelaySeconds: 10 timeoutSeconds: 3 failureThreshold: 3 periodSeconds: 5 livenessProbe: httpGet: path: /actuator/health/liveness port: 8443 scheme: HTTPS initialDelaySeconds: 20 timeoutSeconds: 1 periodSeconds: 10 failureThreshold: 1 volumes: - name: keystore-volume emptyDir: {} - name: demo-maki-lol-tls secret: secretName: demo-maki-lol-tls --- kind: Service apiVersion: v1 metadata: name: hello-cnb spec: type: LoadBalancer selector: app: hello-cnb ports: - protocol: TCP port: 443 targetPort: 8443 EOF ``` `kubectl apply`でこれらを適用します。 ``` kubectl create secret generic hello-cnb \ --dry-run -o yaml \ --from-literal server.ssl.key-password=thisisasecret \ > hello-cnb-secret.yml kubectl apply -f hello-cnb.yml -f hello-cnb-secret.yml ``` `/etc/hosts`に ``` hello-cnb.demo.maki.lol ``` を追加して、 https://hello-cnb.demo.maki.lol/actuator/info にアクセス。 ![image](https://user-images.githubusercontent.com/106908/88472837-1e3fff80-cf52-11ea-90c8-13257f425457.png)