{"id":858,"date":"2023-01-12T22:52:10","date_gmt":"2023-01-12T15:52:10","guid":{"rendered":"https:\/\/rotreein.com\/?p=858"},"modified":"2025-02-10T08:08:52","modified_gmt":"2025-02-10T01:08:52","slug":"cert-manager-with-cloudflare","status":"publish","type":"post","link":"https:\/\/rotreein.com\/?p=858","title":{"rendered":"Cert-Manager with Let\u2019s Encrypt &amp; Cloudflare"},"content":{"rendered":"\n<p>In this guide, we\u2019ll walk through the steps to set up Cert-Manager on a Kubernetes cluster, configure a Cloudflare API token for DNS-01 challenge validation, and issue SSL\/TLS certificates for your domain. This process ensures that your applications are secured with Let\u2019s Encrypt certificates, leveraging Cloudflare for DNS-based verification<br><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Create CRD<\/h2>\n\n\n\n<p>Before installing Cert-Manager, it\u2019s essential to apply the required Custom Resource Definitions (CRDs). These CRDs define the custom resources that Cert-Manager uses to manage SSL\/TLS certificates and integrate seamlessly with Kubernetes.<\/p>\n\n\n\n<p>Run the following command to install the CRDs<\/p>\n\n\n\n<pre data-mode=\"powershell\" data-theme=\"github\" data-fontsize=\"11\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">kubectl apply -f https:\/\/github.com\/cert-manager\/cert-manager\/releases\/download\/v1.12.0\/cert-manager.crds.yaml\n<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\">Install cert-manager with helm<\/h2>\n\n\n\n<p>to simplify we can install cert-manager helm chart with this command<\/p>\n\n\n\n<pre data-mode=\"php\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">$ helm repo add jetstack https:\/\/charts.jetstack.io\n$ helm repo update<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<pre data-mode=\"php\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">$ helm install cert-manager jetstack\/cert-manager --version v1.12.0 --namespace cert-manager-playground<\/pre>\n\n\n\n<p><\/p>\n\n\n\n<p><\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><br><br>Creating a Cloudflare API Token<\/h2>\n\n\n\n<figure class=\"wp-block-image size-full\"><img loading=\"lazy\" decoding=\"async\" width=\"1802\" height=\"990\" src=\"https:\/\/rotreein.com\/wp-content\/uploads\/2025\/01\/image-6.png\" alt=\"\" class=\"wp-image-860\"\/><\/figure>\n\n\n\n<p>Once created, save the API token securely, as it will be added to your Kubernetes cluster as a secret.<br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\">Adding the Cloudflare API Token to Kubernetes<\/h3>\n\n\n\n<p>Create a Kubernetes secret to store your Cloudflare API token. Replace <code>&lt;change_with_your_api_token&gt;<\/code> with your actual token:<\/p>\n\n\n\n<pre data-mode=\"javascript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">apiVersion: v1\nkind: Secret\nmetadata:\n  name: cloudflare-api-token\n  namespace: cert-manager-playground\ntype: Opaque\nstringData:\n  api-token: &lt;change_with_your_api_token><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\">Apply the secret<\/h3>\n\n\n\n<pre data-mode=\"text\" data-theme=\"github\" data-fontsize=\"11\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">kubectl apply -f cloudflare-api-secret.yaml<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><br>Issuing a Certificate<\/h3>\n\n\n\n<p>To issue a certificate, create a Certificate resource. Below is a example for the domain <code>opreks.my.id<\/code><\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">apiVersion: cert-manager.io\/v1\nkind: ClusterIssuer\nmetadata:\n  name: cloudflare-issuer\nspec:\n  acme:\n    email: &lt;change_with_your_email>\n    server: https:\/\/acme-v02.api.letsencrypt.org\/directory\n    privateKeySecretRef:\n      name: cluster-issuer-account-key\n    solvers:\n    - dns01:\n        cloudflare:\n          email: &lt;change_with_your_email>\n          apiTokenSecretRef:\n            name: cloudflare-api-token\n            key: api-token<\/pre>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\"># certificate.yml\napiVersion: cert-manager.io\/v1\nkind: Certificate\nmetadata:\n  name: certificate-opreks-my-id #&lt;change_with_your_cert_name>\nspec:\n  duration: 2160h # 90d\n  renewBefore: 360h # 15d\n  subject:\n    organizations:\n      - testorganization\n  privateKey:\n    algorithm: RSA\n    encoding: PKCS1\n    size: 2048\n  dnsNames:\n    - \"*.opreks.my.id\"\n  secretName: certificate-opreks-my-id\n  issuerRef:\n    name: cloudflare-issuer\n    kind: ClusterIssuer\n    group: cert-manager.io<\/pre>\n\n\n\n<pre data-mode=\"text\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">$kubectl apply -f cluster-issuer.yaml\n\n$kubectl apply -f certificate.yaml<\/pre>\n\n\n\n<pre data-mode=\"php\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">default                   0s          Normal   cert-manager.io      CertificateRequest\/certificate-opreks-my-id-mtzx8   Certificate request has been approved by cert-manager.io\ndefault                   0s          Normal   OrderCreated         CertificateRequest\/certificate-opreks-my-id-mtzx8   Created Order resource default\/certificate-opreks-my-id-mtzx8-2839657829\ndefault                   0s          Normal   Created              Order\/certificate-opreks-my-id-mtzx8-2839657829     Created Challenge resource \"certificate-opreks-my-id-mtzx8-2839657829-4201251540\" for domain \"opreks.my.id\"\ndefault                   0s          Normal   Started              Challenge\/certificate-opreks-my-id-mtzx8-2839657829-4201251540   Challenge scheduled for processing\ndefault                   0s          Normal   Presented            Challenge\/certificate-opreks-my-id-mtzx8-2839657829-4201251540   Presented challenge using DNS-01 challenge mechanism\ndefault                   1s          Normal   DomainVerified       Challenge\/certificate-opreks-my-id-mtzx8-2839657829-4201251540   Domain \"opreks.my.id\" verified with \"DNS-01\" validation\ndefault                   0s          Normal   Complete             Order\/certificate-opreks-my-id-mtzx8-2839657829                  Order completed successfully\ndefault                   0s          Normal   CertificateIssued    CertificateRequest\/certificate-opreks-my-id-mtzx8                Certificate fetched from issuer successfully\ndefault                   0s          Normal   Issuing              Certificate\/certificate-opreks-my-id                             The certificate has been successfully issued<\/pre>\n\n\n\n<p><br><\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><br>Logs and Validation<\/h3>\n\n\n\n<p>Once you\u2019ve applied the configurations, the Cert-Manager will start the certificate issuance process. You can monitor the logs to ensure everything is working as expected. Here\u2019s an example of successful logs:<\/p>\n\n\n\n<p><\/p>\n\n\n\n<pre data-mode=\"php\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">echo                      0s                  Normal   Started              Challenge\/certificate-opreks-my-id-f45mg-3606330728-3498652677   Challenge scheduled for processing\necho                      0s                  Normal   Presented            Challenge\/certificate-opreks-my-id-f45mg-3606330728-3498652677   Presented challenge using DNS-01 challenge mechanism\necho                      0s                  Normal   DomainVerified       Challenge\/certificate-opreks-my-id-f45mg-3606330728-3498652677   Domain \"echo.opreks.my.id\" verified with \"DNS-01\" validation\necho                      0s                  Normal   Complete             Order\/certificate-opreks-my-id-f45mg-3606330728                  Order completed successfully\necho                      0s                  Normal   CertificateIssued    CertificateRequest\/certificate-opreks-my-id-f45mg                Certificate fetched from issuer successfully\necho                      0s                  Normal   Issuing              Certificate\/certificate-opreks-my-id                             The certificate has been successfully issued\n<\/pre>\n\n\n\n<p><br>To validate our Kubernetes setup, we will deploy a simple echo service that responds to HTTP requests. This service will be useful for testing network connectivity and verifying the functionality of our cluster. Please create <strong>deployment.yaml <\/strong>file below<\/p>\n\n\n\n<pre data-mode=\"typescript\" data-theme=\"twilight\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">apiVersion: apps\/v1\nkind: Deployment\nmetadata:\n  name: echo-service\n  namespace: echo\n  labels:\n    app: echo-service\nspec:\n  replicas: 1\n  selector:\n    matchLabels:\n      app: echo-service\n  template:\n    metadata:\n      labels:\n        app: echo-service\n    spec:\n      containers:\n      - name: echo-service\n        image: hashicorp\/http-echo:latest\n        args:\n        - \"-text=Hello, World!\"\n        ports:\n        - containerPort: 5678\n---\napiVersion: networking.k8s.io\/v1\nkind: Ingress\nmetadata:\n  name: echo-ingress\n  # annotations:\n  #   cert-manager.io\/cluster-issuer: \"cloudflare-issuer\"\n  namespace: echo\nspec:\n  ingressClassName: nginx-external  # Specify the ingress class here\n  rules:\n  - host: echo.opreks.my.id\n    http:\n      paths:\n      - backend:\n          service:\n            name: echo-service\n            port:\n              number: 80\n        path: \/\n        pathType: ImplementationSpecific\n  tls:\n    - hosts:\n        - echo.opreks.my.id\n      secretName: certificate-opreks-my-id\n---\napiVersion: v1\nkind: Service\nmetadata:\n  name: echo-service\n  namespace: echo\nspec:\n  selector:\n    app: echo-service\n  ports:\n    - protocol: TCP\n      port: 80\n      targetPort: 5678\n  type: ClusterIP\n<\/pre>\n\n\n\n<pre data-mode=\"text\" data-theme=\"github\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\">$kubectl apply -f deployment.yaml<\/pre>\n\n\n\n<p>Testing access from the client using Nmap to verify the certificate<\/p>\n\n\n\n<pre data-mode=\"text\" data-theme=\"chrome\" data-fontsize=\"14\" data-lines=\"Infinity\" class=\"wp-block-simple-code-block-ace\"> nmap --script ssl-cert -p 443 echo.opreks.my.id\n\nStarting Nmap 7.95 ( https:\/\/nmap.org ) at 2023-01-27 13:19 WIB\nNmap scan report for echo.opreks.my.id (54.169.147.155)\nHost is up (0.021s latency).\nOther addresses for echo.opreks.my.id (not scanned): 13.213.98.236\nrDNS record for 54.169.147.155: ec2-54-169-147-155.ap-southeast-1.compute.amazonaws.com\n\nPORT    STATE SERVICE\n443\/tcp open  https\n| ssl-cert: Subject: commonName=echo.opreks.my.id\n| Subject Alternative Name: DNS:echo.opreks.my.id\n| Issuer: commonName=R11\/organizationName=Let's Encrypt\/countryName=US\n| Public Key type: rsa\n| Public Key bits: 2048\n| Signature Algorithm: sha256WithRSAEncryption\n| Not valid before: 2023-01-27T05:17:23\n| Not valid after:  2023-04-27T05:17:22\n| MD5:   c623:85b3:6575:d70a:bf0b:0475:2d68:1cea\n|_SHA-1: db9a:17e1:0c12:5720:994b:57f2:b402:8b32:11dc:fe36<\/pre>\n","protected":false},"excerpt":{"rendered":"<p>In this guide, we\u2019ll walk through the steps to set up Cert-Manager on a Kubernetes cluster, configure a Cloudflare API token for DNS-01 challenge validation, and issue SSL\/TLS certificates for your domain. This process ensures that your applications are secured with Let\u2019s Encrypt certificates, leveraging Cloudflare for DNS-based verification Create CRD Before installing Cert-Manager, it\u2019s&hellip;<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[9],"tags":[],"_links":{"self":[{"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/posts\/858"}],"collection":[{"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/rotreein.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=858"}],"version-history":[{"count":34,"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/posts\/858\/revisions"}],"predecessor-version":[{"id":910,"href":"https:\/\/rotreein.com\/index.php?rest_route=\/wp\/v2\/posts\/858\/revisions\/910"}],"wp:attachment":[{"href":"https:\/\/rotreein.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=858"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/rotreein.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=858"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/rotreein.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=858"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}