IKS IBM管理下の LetsEncrypt のTLS証明書を利用してHTTPS通信

IBM Cloud Kubernetes Service (IKS) の クラスタを構成すると、自動的にIBMクラウド管理下のサブドメインが割当られ、SSL/TLS通信に必要なTLS証明書がシークレットに自動設定されます。

これを利用して、Ingress コントローラー (IBM名称 ALB; Application Load Balancer) で、HTTPSによる暗号化通信を無料でできます。ここで生成されるTLS証明書は、LetsEncryptによって署名され、IBMによって有効期限90日の7日前を目途に自動更新されます。そのため、IKSのユーザーは、HTTPS暗号通信を使用するにあたり、サーバー証明書の購入や更新などの煩わしい作業を一切する必要がありません。


## k8sクラスタ自動生成のドメイン名とTLS証明書

IKSの標準クラスタを生成すると、ドメイン名とTLS証明書が自動生成され、次のコマンドで、ドメインとTLS証明書の格納先シークレットの名前を取得できます。

~~~
$ ibmcloud ks cluster-get iks1
Retrieving cluster iks1...
OK

Name:                   iks1
ID:                     41679d0177fa4f92ab10262fa586956c
State:                  normal
Created:                2018-10-22T04:40:42+0000
Location:               tok04
＜中略＞
Master Location:        Tokyo
Master Status:          Ready (3 days ago)
Ingress Subdomain:      iks1.jp-tok.containers.appdomain.cloud
Ingress Secret:         iks1
Workers:                4
Worker Zones:           tok04, tok02, tok05
Version:                1.12.5_1537
＜以下省略＞
~~~

上記の中で、「Ingress Subdomain:」の値が、k8s用に割り当てられたドメイン名で、もっとも下の層がクラスタ名になっています。そして、次の行の「Ingress Secret:」の値が、TLS証明書が格納されたシークレットの名前になります。次のように、証明書と鍵が格納されています。

~~~
$ kubectl describe secret iks1
Name:         iks1
Namespace:    default
Labels:       <none>
Annotations:
Type:         Opaque

Data
====
tls.crt:  3677 bytes
tls.key:  1679 bytes
~~~

これを実際に利用するIngressのマニフェストは、以下になります。ドメイン名をセットする場所は、２箇所で spec.tls と spec.rules の下になります。そして、secretNameに、このドメイン名に必要なTLS証明書が格納されたシークレット名をセットします。

~~~ingress-tls.yml
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: my-ingres
spec:
  tls:
  - hosts:
    - iks1.jp-tok.containers.appdomain.cloud
    secretName: iks1
  rules:
  - host: iks1.jp-tok.containers.appdomain.cloud
    http:
      paths:
      - path: /
        backend:
          serviceName: web-svc
          servicePort: 9080
~~~

この上記のマニフェストで生成されるイングレスは、HTTPS通信を終端して、サービス名 web-svc をポート9080へ転送します。たとえば、以下のマニフェストで動作するポッドへ接続して、インターネット上からアクセスできるようにします。

~~~deploy-web.yml
apiVersion: apps/v1beta1
kind: Deployment
metadata:
  name: web
spec:
  replicas: 10
  template:
    metadata:
      labels:
        app: web
    spec:
      containers:
        - image: 'maho/session-check:1.2'
          name: check-apl
          ports:
            - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: web-svc
spec:
  selector:
    app: web
  ports:
    - port: 9080
      targetPort: 80
~~~

前述のイングレスのマニフェスト、サービスとデプロイメントのマニフェストを、以下で適用していきます。

~~~console
$ kubectl apply -f ingress-tls.yml
ingress.extensions/my-ingres created

$ kubectl apply -f deploy-web.yml
deployment.apps/web created
service/web-svc created
~~~

## 動作確認

アクセステストの前に、オブジェクトの実行状態を確認しておきます。以下は正常に動作している状態です。

~~~console
$ kubectl get ing
NAME        HOSTS                                    ADDRESS                                      PORTS     AGE
my-ingres   iks1.jp-tok.containers.appdomain.cloud   128.168.73.198,165.192.71.230,169.56.27.94   80, 443   13s

$ kubectl get svc
NAME         TYPE           CLUSTER-IP       EXTERNAL-IP      PORT(S)         AGE
kubernetes   ClusterIP      172.21.0.1       <none>           443/TCP         120d
web-svc      ClusterIP      172.21.178.169   <none>           9080/TCP        13s

$ kubectl get pod
NAME                     READY     STATUS    RESTARTS   AGE
web-ddd5df9c7-98ssh      1/1       Running   0          17s
web-ddd5df9c7-gp8tj      1/1       Running   0          17s
web-ddd5df9c7-mllwf      1/1       Running   0          17s
web-ddd5df9c7-pjdds      1/1       Running   0          17s
web-ddd5df9c7-s6wxq      1/1       Running   0          17s
web-ddd5df9c7-s8bhc      1/1       Running   0          17s
web-ddd5df9c7-tbqnh      1/1       Running   0          17s
web-ddd5df9c7-tgw4z      1/1       Running   0          17s
web-ddd5df9c7-vskgl      1/1       Running   0          17s
web-ddd5df9c7-wpp55      1/1       Running   0          17s
~~~

## アクセステスト

HTTPSでドメイン名をアクセスします。　
下記のようにエラーなく通信できていることが確認できました。

~~~console
$ curl -s https://iks1.jp-tok.containers.appdomain.cloud
Hostname: web-ddd5df9c7-tgw4z<br>
1th time access.
<br>
HTTP_CLIENT_IP = <br>
HTTP_X_FORWARDED_FOR = 10.192.9.86<br>
HTTP_X_FORWARDED = <br>
HTTP_X_CLUSTER_CLIENT_IP = <br>
HTTP_FORWARDED_FOR = <br>
HTTP_FORWARDED = <br>
REMOTE_ADDR = 172.30.119.22<br>
~~~


## まとめ

今や全ての通信は暗号化しておくことが、常識の様になっています。しかし、ドメイン名の取得、サーバー証明書の取得や有効期限毎の更新など、必要な手間は省略したいものです。 IKSのクラスタで、自動的に確保されるドメイン名とTLS証明書を活用すると、手間を省くことができます。


参考資料
[1] https://cloud.ibm.com/docs/containers/cs_ingress.html?locale=ja#public_inside_3