【Kubernetes】WebサイトをHTTPS化する(後編)

cert-manager

Kubernetes環境のWebサイトでLet’s Encryptの証明書を利用してHTTPS化します。
証明書は90日で失効してしまうためcert-managerを利用して証明書更新を自動化します。
※長くなるため、前編・後編に分けています。

  1. ingressControllerの導入
  2. cert-managerで証明書更新の自動化 ★ここ

Let’s Encryptとは

無償で利用可能なSSL/TSL証明書を発行してくれる認証局です。
Webサーバとドメインが正しいことを検証できれば証明書の発行ができます。
証明書発行にはHTTPDNSの2種類あり、HTTPの場合TCP80番ポートを解放する必要があります。
DNSサーバのTXTレコードにAPIトークンを登録することで証明書の発行が可能で、TCP80番ポートがブロックされていても利用可能です。
今回は簡単なHTTPでの証明書発行を利用します。

cert-managerとは

証明書の自動更新を行うツールです。
Let’s Encryptの証明書をACMEプロトコルで自動更新します。

cert-managerのリソースについて

  • ClusterIssuer … 発行先や発行者情報を定義します。(メールアドレスや問い合わせ先など)
  • Certificate … 発行者のDN情報を定義します。
  • Secret … 証明書を管理します。
  • Order … チャレンジ要求を作成します。
  • Challenge … ACME Issuerに証明書要求をします。

cert-managerの導入

環境

名前バージョン
CentOS-Stream-9
kubeletv1.26.2
kubeadmv1.26.2
containerdv1.6.16
calicov3.25
metallbv0.13.9
cert-managerv1.12.0

cert-managerのデプロイ

cert-managerをデプロイします。

kubectl apply -f https://github.com/cert-manager/cert-manager/releases/download/v1.12.0/cert-manager.yaml

3つのPodがRunningになれば正常です。

[user001@k8s-mas01~]# kubectl get pod -n cert-manager
NAME                                       READY   STATUS    RESTARTS       AGE
cert-manager-66d976bfd7-vfhr4              1/1     Running   13 (13h ago)   16d
cert-manager-cainjector-66b9cf5bd6-mbjml   1/1     Running   13 (13h ago)   16d
cert-manager-webhook-7b46c97bd9-nmn8f      1/1     Running   13 (14h ago)   16d

Issuer作成

lets-encrypt-issuer.yamlで作成します。
ネームスペースを跨いで証明書を適用できるように ClusterIssuer で作成しました。
Let’sEncryptの証明書はレート制限(https://letsencrypt.org/ja/docs/rate-limits/)があり、1時間に5回までしか失敗できません。検証失敗すると数日間ブロックされますので気になる方はステージング環境を利用してください。ステージング環境の証明書は認証局に(STAGING)と表示されます。ステージング環境を利用した後、本番用に切り替える場合はSecretを削除してから再発行ください。

apiVersion: cert-manager.io/v1
kind: ClusterIssuer
metadata:
  name: lets-encrypt-issuer
spec:
  acme:
    server: https://acme-v02.api.letsencrypt.org/directory #本番用
    #server: https://acme-staging-v02.api.letsencrypt.org/directory #検証用
    email: '<your-mail-address>' #メールアドレスを入力
    privateKeySecretRef:
      name: example-client-letsencrypt #
    solvers:
      - http01:
          ingress:
            class: nginx

Issuerを適用します。

kubectl apply -f lets-encrypt-issuer.yaml

kubectl describe clusterissuer で状態を確認します。
READY ステータスが Trueになれば利用可能です。

[user001@k8s-mas01~]# kubectl get clusterissuer
NAME                                                READY   AGE
clusterissuer.cert-manager.io/lets-encrypt-issuer   True    2m

Ingress作成

lets-encrypt-ingress.yaml で作成します。
tlsを設定することでcertificateが自動で作成されます。

apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ing
  annotations:
    cert-manager.io/cluster-issuer: lets-encrypt-issuer  # ClusterIssuerを設定
    acme.cert-manager.io/http01-edit-in-place: "true"
spec:
  ingressClassName: nginx
  tls:
    - hosts:
        - example.com # ドメイン記載
      secretName: example-letsencrypt-cert # cert-managerが格納するsecret名
  rules:
    - host: example.com # ドメイン記載
      http:
        paths:
          - pathType: Prefix
            path: /
            backend:
              service:
                name: www-app-service
                port:
                  number: 80

Ingressを適用します。

kubectl apply -f lets-encrypt-ingress.yaml

kubectl get clusterissuer,cert,cr,order,challenge,secret,ingress で一通りの状態を確認します。
ingressにIPアドレスが付与されるまで数分かかります。

[user001@k8s-mas01~]# kubectl get clusterissuer,cert,cr,order,challenge,secret,ingress --all-namespaces -o wide
NAME                                                READY   STATUS                                                 AGE
clusterissuer.cert-manager.io/lets-encrypt-issuer   True    The ACME account was registered with the ACME server   39s

NAMESPACE   NAME                                                     READY   SECRET                       ISSUER                STATUS                                          AGE
default     certificate.cert-manager.io/example-letsencrypt-cert   True    example-letsencrypt-cert   lets-encrypt-issuer   Certificate is up to date and has not expired   16s

NAMESPACE   NAME                                                              APPROVED   DENIED   READY   ISSUER                REQUESTOR                                         STATUS                                         AGE
default     certificaterequest.cert-manager.io/example-letsencrypt-cert-1   True                True    lets-encrypt-issuer   system:serviceaccount:cert-manager:cert-manager   Certificate fetched from issuer successfully   16s

NAMESPACE   NAME                                                                 STATE   ISSUER                REASON   AGE
default     order.acme.cert-manager.io/example-letsencrypt-cert-1-2496073388   valid   lets-encrypt-issuer            16s

NAMESPACE          NAME                                  TYPE                DATA   AGE
cert-manager       secret/cert-manager-webhook-ca        Opaque              3      46m
cert-manager       secret/example-client-letsencrypt     Opaque              1      39s
ingress-nginx      secret/ingress-nginx-admission        Opaque              3      42m

NAMESPACE   NAME                                   CLASS   HOSTS           ADDRESS         PORTS     AGE
default     ingress.networking.k8s.io/example-ing   nginx   example.com   172.16.200.1   80, 443   16s

Test用Podを作成

nginx-test.yaml で作成します。

apiVersion: apps/v1
kind: Deployment
metadata:
  name: www-app
  labels:
    app: www-app
spec:
  replicas: 3
  selector:
    matchLabels:
      app: www-app
  template:
    metadata:
      labels:
        app: www-app
    spec:
      containers:
      - name: web-container
        image: nginx
        ports:
          - containerPort: 80
---
apiVersion: v1
kind: Service
metadata:
  name: www-app-service
  labels:
    app: www-app-service
spec:
  ports:
  - port: 80
    protocol: TCP
    targetPort: 80
  selector:
    app: www-app
  type: NodePort
  externalTrafficPolicy: Local

Nginxをデプロイします。

kubectl apply -f nginx-test.yaml

WebサイトにHTTPSで接続できれば成功です。

HTTPSにできない場合

下記コマンドでどこで詰まっているか調査していきます。

kubectl get ev
kubectl get clusterissuer,cert,cr,order,challenge,secret,ingress

さらに詳細を確認したい場合は kubectl describe [resource] [resource名] で確認できます。
successfullyになっていれば証明書が発行されているはずです。

[user001@k8s-mas01~]# kubectl describe order example-letsencrypt-cert-h474n-2496073388
Events:
  Type    Reason    Age    From          Message
  ----    ------    ----   ----          -------
  Normal  Created   4m11s  cert-manager  Created Challenge resource "example-letsencrypt-cert-h474n-2496073388-454293563" for domain "example.com"
  Normal  Complete  3m45s  cert-manager  Order completed successfully

最後に

参考サイトに記載しておりますが、先人の方々が詳細に説明されておりますので、情報が足りない方はそちらを参照してください。
また近いうちにDNSでの検証を記事にしたいと思います。

参考サイト