/kubernetes-letsencrypt

A Kubernetes controller to retrieve Let's Encrypt certificates based on service annotations (unmaintained)

Primary LanguageKotlinMIT LicenseMIT

Kubernetes Letsencrypt Controller

Build Status

This implements a Kubernetes controller that automatically requests and refreshes Letsencrypt certificates based on service annotations.

This controller currently supports Amazon Route 53 and Google Cloud DNS as the DNS targets.

Setup

Launch the controller into your cluster using

kubectl apply -f letsencrypt-controller.yaml

This will use a release or snapshot version (depending on your git checkout) hosted on my Docker Hub account.

The pod must run with the permissions required for updating records in the DNS zones that you maintain.

On AWS, consider using a project such as kube2iam to grant permissions to individual pods.

Please refer to the 'Building' section for using your own image.

Configuration

The controller currently supports three configuration options via environment variables:

  • ACME_URL: This can be set to an alternative ACME directory URL, for example the Letsencrypt staging server if you only want to test out the controller.
  • CLOUD_PLATFORM: This can be set to either GCP or AWS to override the automatic platform detection. You can use this to for example use Route53 as the DNS backend with a cluster running on Google's Cloud Platform. If you override this option you must provide credentials for the DNS backend, for example via the environment variables for the Google Cloud Java SDK or the AWS Java SDK
  • LOG_LEVEL: This can be used to set the log level to something other than the default (INFO).

Usage

Simply add an annotation to your services, for example:

---
apiVersion: v1
kind: Service
metadata:
  name: my-app
  labels:
    app: my-app
  annotations:
    acme/certificate: www.yourdomain.com
spec:
  type: LoadBalancer
[...]

The controller will notice this and, assuming you have a matching hosted zone, create a certificate and store it as a secret named www-yourdomain-com-tls.

You can override the name of the secret by specifying an annotation called acme/secretName.

You may specify multiple domains to include in a certificate as a JSON array. This requires setting the acme/secretName annotation. For example:

[...]
metadata:
  annotations:
    acme/certificate: '["yourdomain.com", "www.yourdomain.com"]'
    acme/secretName: mydomain-certificate
[...]

The certificate secret will contain four files named certificate.pem, chain.pem, key.pem and fullchain.pem. You can mount these into whatever application you use to terminate TLS.

If required, you can configure these file names via the environment variables, CERTIFICATE_FILENAME, CHAIN_FILENAME, KEY_FILENAME, FULLCHAIN_FILENAME.

The secret will always be created in the same namespace as your service. Removing the annotation will never remove a secret.

Certificate renewals

Every secret will be annotated with the certificate expiry date. The controller will refresh the certificate and update the secret once the expiry date is close.

Currently this update happens within 1-2 days of expiry. The reason for the short time-interval is that Letsencrypt has a long-term desire to reduce the certificate lifespans so I am trying to be future-proof here.

Overview

The controller first attempts to find a secret in the Kubernetes kube-system namespace with the name letsencrypt-keypair. This secret is expected to contain the key pair used for authentication with the Letsencrypt service.

If no such key pair is found the controller will create one and store it as a secret.

On startup the controller will check all existing services for an annotation

Building

All build lifecycle steps are handled in Gradle. After determining your desired image name, you can build a new image with:

# Run test suite
./gradlew test

# Create local Docker image
./gradlew dockerBuildImage

This will build an image locally with the tag tazjin/letsencrypt-controller:${VERSION}, where ${VERSION} is the one specified in build.gradle.kts.

Contributing

Feel free to contribute pull requests, file bugs and open issues with feature suggestions!

Please follow the code of conduct.