TLS certificates

Use TLS certificates with your managed DNS service

Most ingress controllers support TLS termination in-cluster.

For that, they need access to the TLS Certificate and Private Key.

By default, the ingress Resource Definition resource will reference a tls-cert resource with the same Resource ID as the dns it is routing from.

In order to satisfy that, the simplest thing to do is to create a tls-cert Resource Definition with the same matching criteria as that for the DNS resource definition.

Static TLS certificates

If you have a static wildcard certificate that you want to use, it can be injected by using the humanitec/template driver.

You will need a template defining the Secret manifest that will contain the certificate and also specify the secret name it will be injected with.

The following examples assume that you have a wildcard certificate for *.example.com and that you want to inject it into the tls-cert resource with the same ID as the dns resource.

Init template

The init template is used to generate the tlsSecretName field that will be used by the ingress resource to reference the tls-cert resource.

tlsSecretName: {{ .id }}-tls

Manifests template

The manifest’s template is used to generate the Secret manifest that will be injected into the cluster.

tls_secret.yaml:
  location: namespace
  data:
    apiVersion: v1
    kind: Secret
    type: kubernetes.io/tls
    metadata:
      name: {{ .init.tlsSecretName }}
    data:
      tls.crt: {{ .outputs.secrets.tls_crt | b64enc }}
      tls.key: {{ .outputs.secrets.tls_key | b64enc }}

Values template

The values template is used to generate the tls-cert resource definition.

tls_secret_name: {{ .init.tlsSecretName }}

Secrets template

The secret’s template is used to generate the actual certificate and private key.

tls_crt: |
  -----BEGIN CERTIFICATE-----
  MIIDITCCAWiqrIdx2rPPn+G+gKO7SBH9UynjDKgpSela3+XnaVXMP9sbdpE60LEJ
  ...
  ojSzdq6OvLABJKxE6N0ukQPiW8NXQvga9ltnoraxZ9dhAaYvmA==
  -----END CERTIFICATE-----


tls_key: |
  -----BEGIN PRIVATE KEY-----
  MIIEvwIBADANBgkqhkiG9w0BAQEFAASCBKkwggSlAgEAAoIBAQC2jud0ozO5qacH
  ...
  ufjZCQaXMLwfVgmhgYr/62jV7YFXFF==
  -----END PRIVATE KEY-----

Resolving TLS at the load balancer

It is also common to let cloud providers manage certificates at the edge, for example at the Load Balancer. In this case, no tls-cert resource is needed in the cluster. Instead, the ingress resource must be configured not to do TLS termination.

If you are using the humanitec/ingress driver, you can simply set the no_tls field to true.

Dynamic TLS certificates with Cert-Manager

Cert-Manager allows for dynamic TLS certification management. After the installation of Cert-Manager to a Kubernetes cluster, it can be configured using custom resource definitions like clusterIssuer and certificate to request, renew and manage TLS certificates.

Example scenario

Let’s assume we want to use a wildcard certificate like *.my-test-domain.com for dynamic preview environments. Each environment will be exposed under a different subdomain like preview-myapp-dev.my-test-domain.com or preview-myapp-test.my-test-domain.com.

Prerequisites

  • A DNS record for *.my-test-domain.com pointing to the load balancer IP of the ingress controller in the cluster.
  • Cert-manager is installed in the cluster.
  • Setup of a clusterIssuer or issuer custom resource definition enabling cert-manager to issue certificates for *.my-test-domain.com and its sub domains.

Create a DNS based on a sub domain template

Creating a DNS resource definition with the wildcard-dns driver allows us to use a template for the DNS subdomain.


dns-template.yaml (view on GitHub) :

apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: dns-template
entity:
  name: dns-template
  type: dns
  driver_type: humanitec/dns-wildcard
  driver_inputs:
    values:
      domain: "my-domain.com"
      template: '{{ index (splitList "." "${context.res.id}") 1 }}-${context.env.id}-${context.app.id}'
  provision:
    ingress:
      is_dependent: false
  criteria:
    - {}

Based on the placeholder variables for app id and environment id, the template for the host name preview-${context.app.id}-${context.env.id}.my-test-domain.com will for example render into preview-myapp-dev.my-test-domain.com.

Create a certificate CRD

Using the Template driver and the tls-cert Resource Type we can configure cert-manager dynamically for each Environment to create individually managed TLS certificates.


certificate-crd.yaml (view on GitHub) :

# This Resource Definition creates a certificate custom resource definition,
# which will instruct cert-manager to create a TLS certificate
apiVersion: entity.humanitec.io/v1b1
kind: Definition
metadata:
  id: certificate-crd
entity:
  driver_type: humanitec/template
  name: certificate-crd
  type: tls-cert
  criteria:
  - class: default
  driver_inputs:
    values:
      templates:
        init: |
          tlsSecretName: {{ .id }}-tls
          hostName: ${resources.dns.outputs.host}
          certificateName: {{ .id }}-cert
        manifests: |
          certificate-crd.yml:
            data:
              apiVersion: cert-manager.io/v1
              kind: Certificate
              metadata:
                name: {{ .init.certificateName }}
              spec:
                secretName: {{ .init.tlsSecretName }}
                duration: 2160h # 90d
                renewBefore: 720h # 30d
                isCA: false
                privateKey:
                  algorithm: RSA
                  encoding: PKCS1
                  size: 2048
                usages:
                  - server auth
                  - client auth
                dnsNames:
                  - {{ .init.hostName | toString | toRawJson }}
                # The name of the issuerRef must point to the issuer / clusterIssuer in your cluster
                issuerRef:
                  name: letsencrypt-prod
                  kind: ClusterIssuer
            location: namespace
        outputs: |
          tls_secret_name: {{ .init.tlsSecretName }}

This tls-cert resource definition will create a certificate custom resource definition in the cluster instructing cert-manager to create a tls-secret. For this it will take the host name from the dns resource ${resources.dns.outputs.host} as an input for the certificate request. As an output it will provide the name of the tls-secrets which will be used in the ingress resource definition as an input. For custom use cases it can be referred to via ${resources.tls-cert.outputs.tls_secret_name}.

Conclusion

With both in place,

  • the resource definition for the DNS template and
  • the resource definition for the tls-cert creating the certificate CRD,

we will have a valid TLS certificate with each environment exposed under a different subdomain.

These TLS certificates will be valid for 3 months and cert-manager will renew them once they are valid for less than 30 days, depending on the configuration in the certificate CRD. The creation of the resources will happen with the deployment of the environment.

Top