It was convenient to use "cert-manager" + DNS authentication to automatically update Let's Encrypt on Kubernetes (GKE) without processing on the service side.
I wrote Introductory note before, but the procedure had changed, so I rewrote it with how to get a wildcard certificate.
Based on the official manual, I have added some additional information, mainly where I got stuck.
Procedure (up to cert-manager installation)
Bring Kubernetes up to date
If it is 1.12 or earlier, the work requires a lot of work, so it should be 1.13 or higher.
Added namespace "cert-manager
kubectl create namespace cert-manager # Validation invalidation (transcribed from manual) kubectl label namespace cert-manager certmanager.k8s.io/disable-validation=true
install
kubectl apply -f https://github.com/jetstack/cert-manager/releases/download/v0.8.0/cert-manager.yaml
empowerment
kubectl create clusterrolebinding cluster-admin-binding \ --clusterrole=cluster-admin \ --user=$(gcloud config get-value core/account)
Overall flow
This completes the installation of cert-manager. Before proceeding to the next step, we will briefly describe the entire process.
How cert-manager works
The cert-manager consists largely of "Issuer" and "Certificate".
Certificate" mainly describes settings related to domains and certificates, and "Issue" describes settings related to the means of obtaining certificates and accounts.
The certificate is made available by executing the acquisition of the certificate in "Issuer" and storing it in the secret defined in "Certificate".
Difference between Issure and ClusterIssure
There are two types of "Issure": "Issure" and "ClusterIssure.
Issue" is an "Issue" associated with a namespace, and the certificate is valid only for the namespace in which the "Issue" is located.
ClusterIssure" on the other hand is an "Issure" tied to a cluster, and the certificate is valid in all namespaces.
Mechanism of Certificate Usage
The certificate obtained above is stored in Kubernetes as a secret. The certificate is then used by referencing the secret from Ingress. (The method of using certificates with Ingress is described in another article "How to communicate HTTPS with Kubernetes (GKE) (Ingress version)")
How Let's Encrypt DNS Authentication Works
In Let's Encrypt DNS authentication, Let's Encrypt checks if the claimant is the owner of the domain by checking if the specified value was written to the DNS record. Therefore, cert-manager allows the Issuer to edit DNS records by associating a service account with the Issuer that can edit GCP's DNS records, allowing the Issuer to pass the check from Let's Encrypt.
Procedure (remaining)
GCP Service Account Creation
Create a service account that allows you to edit DNS records
- Create any account by [GCP]-[IAM and Administration]-[Service Account]-[Create Service Account
- Add the account created in [GCP]-[IAM and Administration]-[IAM]-[Add] (with the role [DNS]-[DNS Admin])
- [GCP]-[APIs and Services]-[Authentication Information]-[Create Authentication Information]-[Service Account Key] to create a key JSON file (select service account and save key file with key type as JSON)
Register the key file in Kubernetes secret so that the contents of the key file can be referenced by cert-manager.
kubectl create secret generic <シークレット名> \ --from-file=key.json=<キーファイルパス> \ --namespace=cert-manager
The cert-manager itself will be created in the namespace "cert-manager" created during installation, so register the key file in the namespace "cert-manager" so that it can be accessed from there.
Issuer made
issuer.yaml
apiVersion: certmanager.k8s.io/v1alpha1 kind: ClusterIssuer metadata: name: letsencrypt-issuer-prod spec: acme: server: https://acme-v02.api.letsencrypt.org/directory # ステージの場合のURLは : https://acme-staging-v02.api.letsencrypt.org/directory email: <Let's Encryptに登録するemail> privateKeySecretRef: name: letsencrypt-issuer-prod dns01: providers: - name: clouddns clouddns: serviceAccountSecretRef: name: <DNSサービスアカウントのキーファイルのシークレット名> key: key.json project: <GCPプロジェクト名>
kubectl apply -f issuer.yaml
Certificate作成
certificate.yaml
apiVersion: certmanager.k8s.io/v1alpha1 kind: Certificate metadata: name: letsencrypt-certificate-prod spec: secretName: letsencrypt-tls-secret-prod # Ingressから参照するシークレット名 issuerRef: name: letsencrypt-issuer-prod # Issuer名 kind: ClusterIssuer commonName: "*.example.com" # ドメイン名 dnsNames: - example.com # CloudDNSのDNS名 acme: config: - dns01: provider: clouddns # Issuerのprovider名 domains: - example.com # ドメイン名 - "*.example.com" # ワイルドカード
kubectl apply -f certificate.yaml
confirmation
kubectl describe certificate,issuer,clusterissuer --all-namespaces
You can check the status at
If all goes well, the certificate's "Events" will appear as shown below.
Events: Type Reason Age From Message ---- ------ ---- ---- ------- Normal OrderCreated 5m5s cert-manager Created Order resource … Normal OrderComplete 5m3s cert-manager Order … Normal CertIssued 5m3s cert-manager Certificate issued successfully
The "cert-manager" pod in the "cert-manager" namespace is responsible for obtaining certificates, so if it does not work, check the pod's logs to determine the cause.
About Wildcard Certificates
Normally one certificate is issued for one URL.
Wildcard certificates, on the other hand, can be used for any subdomain.
Since the certificate obtained with cert-manager can be used for both arbitrary subdomains ("*.example.com") and no subdomain ("example.com"), I think it would be a good idea to get a wildcard certificate for now.
Certificate Usage
A sample of Ingress is described below. By setting the secret of tls to the secret name set in Certificate, the certificate of Ingress and Certificate are linked. (How to use Certificate with Ingress is described in another article "How to communicate HTTPS with Kubernetes (GKE) (Ingress version)")
ingress.yaml
apiVersion: extensions/v1beta1 kind: Ingress metadata: name: ingress annotations: kubernetes.io/ingress.allow-http: "false" kubernetes.io/ingress.global-static-ip-name: "<static ip名>" spec: tls: - secretName: letsencrypt-tls-secret-prod backend: serviceName: nginx servicePort: 80
supplementary explanation
If you keep repeating trial and error, you may get stuck on Let's Encrypt Limitations, It is better to test in staging rather than in production at first.
Staging is done by setting "server" in "issuer.yaml" to "https://acme-staging-v02.api.letsencrypt.org/directory
".
reference information
- https://qiita.com/apstndb/items/3a39a1e6acacbbc30765
- https://github.com/ahmetb/gke-letsencrypt
- https://cert-manager.readthedocs.io/en/latest/
- https://medium.com/google-cloud/kubernetes-w-lets-encrypt-cloud-dns-c888b2ff8c0e
- https://github.com/jetstack/cert-manager/issues/339