Azure Kubernetes Service(AKS)にLet's EncryptのSSL証明書を入れる


元ネタ

準備

注意

  • 静的パブリック IP アドレスは、お金がかかる
  • AKS クラスターを削除すると、静的パブリック IP アドレスも解放されて消えてしまうので注意。

静的IPアドレスを取得する

  • AKS クラスターのリソース グループ名を取得

    • AKS_NODE_RES_GROUP=`az aks show --resource-group $AKS_RES_GROUP --name $AKS_CLUSTER_NAME --query nodeResourceGroup -o tsv`
  • 静的パブリック IP アドレスを作成

    • az network public-ip create --resource-group $AKS_NODE_RES_GROUP --name myAKSPublicIP --allocation-method static
      • 上記では名前をmyAKSPublicIPにしているが、適宜変更。
  • 作成した静的パブリック IP アドレスを確認

    • az network public-ip list --query [].ipAddress
      • 複数ある場合は、後々の設定でハマるので、ポータルから「パブリック IP アドレス 」で探して削除する。
    • PUBLIC_IP_ADDRESS=`az network public-ip list --query [].ipAddress -o tsv`

nginx-ingress をIPアドレス指定でインストール

  • インストール
    • namespaceをkube-systemにしている。サンプルによってはkube-publicにしている場合もあるので注意。
helm install stable/nginx-ingress \
    --namespace kube-system \
    --set controller.service.loadBalancerIP=$PUBLIC_IP_ADDRESS
  • パブリックIPを確認
    • kubectl get service -l app=nginx-ingress --namespace kube-system
      • EXTERNAL-IP<pending>の場合は、少し待つ。
NAME                                             TYPE           CLUSTER-IP     EXTERNAL-IP     PORT(S)                      AGE
excited-starfish-nginx-ingress-controller        LoadBalancer   10.0.14.43     40.115.xxx.xxx   80:30202/TCP,443:31925/TCP   1m
excited-starfish-nginx-ingress-default-backend   ClusterIP      10.0.121.158   <none>          80/TCP                       1m
  • ブラウザで、上記の40.115.xxx.xxxにアクセスする
    • default backend - 404のように、デフォルトの404ページが表示される(起動に数分かかるので待つ)

DNS上の名前を決めて、設定

  • DNSNAME="my-aks-ingress-test123"
  • PUBLICIPID=$(az network public-ip list --query "[?ipAddress!=null]|[?contains(ipAddress, '$PUBLIC_IP_ADDRESS')].[id]" --output tsv)
  • az network public-ip update --ids $PUBLICIPID --dns-name $DNSNAME

  • これでFQDNを使ってアクセスできるようになったので、ブラウザで開いて確認する

cert-manager をインストール

  • Let's Encrypt 証明書を自動的に作成および管理する機能を提供する cert-manager を入れる
    • 0.7.2が出ているので、0.6ではなく0.7を使う。
      • kubectl apply -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/00-crds.yaml
    • cert-manager用に名前空間を作成(kube-systemではなくcert-managerという名前空間に入れる)
      • kubectl create namespace cert-manager
      • kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
        • disable-validation=trueを付けないと発行者が不明になって証明書が作成されない??
    • helmを使ってインストールする(GitHubのcert-managerのページ を見ると0.7.2が最新なのでバージョン指定する際は確認する)
      • helm repo add jetstack https://charts.jetstack.io
      • helm repo update
helm install \
  --name cert-manager \
  --namespace cert-manager \
  --version v0.7.2 \
  jetstack/cert-manager
  • 本番環境の場合は、--set ingressShim.extraArgs='{--default-issuer-name=letsencrypt-prod,--default-issuer-kind=ClusterIssuer}'も付ける。
    • 下記の例では名前空間kube-systemに入れている
    • webhook.enables=false を書かないと証明書が発行されないという情報もあるので追記
    • stable/cert-managerだとうまくいかなかったのでjetstack/cert-managerを使った
  • 上記で証明書を入れられたので、下記は参考。
helm install jetstack/cert-manager \
  --name cert-manager \
  --namespace kube-system \
  --set ingressShim.defaultIssuerName=letsencrypt-prod \
  --set ingressShim.defaultIssuerKind=ClusterIssuer \
  --set webhook.enabled=false
  • 入れ間違えたら、helm delete --purge cert-managerで消して入れ直し。

    • Error: customresourcedefinitions.apiextensions.k8s.io "certificates.certmanager.k8s.io" already exists の場合は、kubectl delete -f https://raw.githubusercontent.com/jetstack/cert-manager/release-0.7/deploy/manifests/00-crds.yaml で消す
  • これだけで(信頼されない)証明書が発行されるので、ブラウザでHTTPSで接続して確認する

CA クラスター発行者を作成

  • cluster-issuer.yamlファイルを以下の内容で作成
  • emailを、実際のメールアドレスに変更する
  • テスト用なので、stagingを使用する。本番環境ではletsencrypt-prodhttps://acme-v02.api.letsencrypt.org/directory を使用する
    • サーバはv01ではなくv02を使わないと、ずっとIssuer letsencrypt-staging not readyのままで原因が分かりづらい。
      • kubectl describe clusterissuer letsencrypt-stagingkubectl describe clusterissuer letsencrypt-prodで状況を確認。
apiVersion: certmanager.k8s.io/v1alpha1
kind: ClusterIssuer
metadata:
  name: letsencrypt-staging
  #name: letsencrypt-prod
spec:
  acme:
    server: https://acme-staging-v02.api.letsencrypt.org/directory
    #server: https://acme-v02.api.letsencrypt.org/directory
    email: [email protected]
    privateKeySecretRef:
      name: letsencrypt-staging
      #name: letsencrypt-prod
    http01: {}
  • kubectl apply -f cluster-issuer.yaml で適用
    • Error from server (InternalError): error when creating "cluster-issuer.yaml": Internal error occurred: failed calling admission webhook "clusterissuers.admission.certmanager.k8s.io": the server is currently unable to handle the request と表示されたら、しばらく待ってもう一度行う

デモ アプリを入れる

  • デフォルトの404ページで動作確認しようとしてはまったので、デモ アプリを入れる
    • helm repo add azure-samples https://azure-samples.github.io/helm-charts/
    • helm install azure-samples/aks-helloworld --namespace ingress-basic
    • helm list --namespace ingress-basic
    • helm install azure-samples/aks-helloworld --namespace ingress-basic --set title="AKS Ingress Demo" --set serviceName="ingress-demo"
    • helm list --namespace ingress-basic

イングレス ルートを作成

  • hello-world-ingress.yaml を以下で作成
  • hostshostは自分のホスト名に変更する
apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: hello-world-ingress
  namespace: ingress-basic
  annotations:
    kubernetes.io/ingress.class: nginx
    certmanager.k8s.io/cluster-issuer: letsencrypt-prod
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  tls:
  - hosts:
    - my-aks-ingress-test123.japaneast.cloudapp.azure.com
    secretName: tls-secret
  rules:
  - host: my-aks-ingress-test123.japaneast.cloudapp.azure.com
    http:
      paths:
      - path: /
        backend:
          serviceName: aks-helloworld
          servicePort: 80
      - path: /hello-world-two
        backend:
          serviceName: ingress-demo
          servicePort: 80
  • kubectl apply -f hello-world-ingress.yamlで適用

証明書の確認

  • 上記で自動的に要求される

    • kubectl describe certificate tls-secret --namespace ingress-basic
    • kubectl describe certificate tls-secret
    • kubectl describe certificate で状況確認
    • kubectl describe order
    • ずっとIssuer letsencrypt-prod not readyのままの場合、kubectl describe clusterissuer letsencrypt-stagingkubectl describe clusterissuer letsencrypt-prodで状況を確認。
      • 引数はcluster-issuerではなくclusterissuerなので注意
  • kubectl describe clusterissuer letsencrypt-prod で状態が表示できる

(略)
Status:
  Acme:
    Uri:  https://acme-v02.api.letsencrypt.org/acme/acct/56269408
  Conditions:
    Last Transition Time:  2019-05-02T07:29:25Z
    Message:               The ACME account was registered with the ACME server
    Reason:                ACMEAccountRegistered
    Status:                True
    Type:                  Ready
Events:                    <none>
  • kubectl describe certificate tls-secret --namespace ingress-basic で以下のようにイベント詳細が表示できる
(略)
Status:
  Conditions:
    Last Transition Time:  2019-05-02T07:29:51Z
    Message:               Certificate is up to date and has not expired
    Reason:                Ready
    Status:                True
    Type:                  Ready
  Not After:               2019-07-31T06:29:50Z
Events:
  Type     Reason              Age                    From          Message
  ----     ------              ----                   ----          -------
  Warning  IssuerNotFound      3m44s                  cert-manager  clusterissuer.certmanager.k8s.io "letsencrypt-prod" not found
  Warning  IssuerNotReady      3m28s (x3 over 7m31s)  cert-manager  Issuer letsencrypt-prod not ready
  Normal   Generated           3m27s                  cert-manager  Generated new private key
  Normal   GenerateSelfSigned  3m27s                  cert-manager  Generated temporary self signed certificate
  Normal   OrderCreated        3m27s                  cert-manager  Created Order resource "tls-secret-224255xxxx"
  Normal   OrderComplete       3m1s                   cert-manager  Order "tls-secret-224255xxxx" completed successfully
  Normal   CertIssued          3m1s                   cert-manager  Certificate issued successfully
  • ステータスがCertificate issuance in progress. Temporary certificate issued.の場合は、しばらく待つ。

    • Temporary certificateが発行されてから、本物のcertificateが発行される。という順番なので、ステータスで確認。
  • chromeの開発者ツールのSecurityで見ると、Certificate - valid and trusted
    の下に The connection to this site is using a valid, trusted server certificate issued by 不明な名前.となっている場合がある。対処方法不明。

ブラウザで表示して、証明書の確認

証明書オブジェクトを作成(必要に応じて)

  • certificates.yamlファイルを以下の内容で作成。
  • dnsNamesdomains を前の手順で作成した DNS 名に変更する
apiVersion: certmanager.k8s.io/v1alpha1
kind: Certificate
metadata:
  name: tls-secret
spec:
  secretName: tls-secret
  dnsNames:
  - my-aks-ingress-test123.japaneast.cloudapp.azure.com
  acme:
    config:
    - http01:
        ingressClass: nginx
      domains:
      - my-aks-ingress-test123.japaneast.cloudapp.azure.com
  issuerRef:
    name: letsencrypt-staging
    #name: letsencrypt-prod
    kind: ClusterIssuer
  • kubectl apply -f certificates.yamlで適用。