kubernetes-certbot
Uses certbot to obtain an X.509 certificate from Let's encrypt and stores it as secret in Kubernetes for a named kubernetes cluster with an AWS load balancer entrypoint. Renews certificates automatically.
Usage
Settings are provided to the container via env variables and secrets. Modify kube.yaml
to suit your needs.
DOMAIN
: Is the top level domain for your org, e.g.example.com
, that is shared with all your clusters. You can add multiple domains with a comma to issue a multi-cert, e.g. "example.com,example.org"SUBDOMAIN
: Is the name of the cluster to issue a credential for, e.g.mycluster
to issue certs formycluster.example.com
. You can have multiple of these also, same as with domains, uses comma.NO_SUBDOMAIN
: Optional setting, set to any value to expose a cluster under the root domain, e.g. to host a website underexample.com
. NB that subdomain must still be set, e.g.production
as cluster with this setting on becomesproduction.example.com
andexample.com
ELB_NAME
: The name of your (classic) ELB to update. This is an hex value, e.g.{some-hex-value-is-name}.us-east-1.elb.amazonaws.com
STAGING
: Set to a truthy value to issue certs from letsencrypts staging environment. Start with it on - they have stringent API rate limits.LETS_ENCRYPT_EMAIL
- email to associate lets encrypt withaws
secret withkey
,secret
andregion
with permission to edit ELB
Installation
Create the service from the service.yaml
kubectl create -f service.yaml
Create an aws user with permission to update a classic elb. Here is a terraform module to get you started.
Fill in your secret in kubernetes (hot tip: prefix with a space if you don't want to store it in your bash history).
kubectl create secret generic letsencrypt-aws --from-literal=key=KEY --from-literal=secret=SECRET --from-literal=region=REGION
Fill in the settings specified above in a config file and create it.
You can put it in config/
(or elsewhere that suits, but that folder is .gitignored by default)
Use the .yaml
style creation to lift setting to the top level:
apiVersion: v1
kind: ConfigMap
metadata:
name: letsencrypt-config
namespace: default
data:
domain: example.com
no_subdomain: ""
email: my@email.com
elb_name: hex-value
staging: "true"
kubectl create -f config/my-letsencrypt.yaml
Add routing logic to it to your entry pod running in the cluster. For Nginx:
...
server {
listen 443 ssl;
ssl_certificate /letsencrypt/tls.crt;
ssl_certificate_key /letsencrypt/tls.key;
...
}
...
server {
listen 80;
location ^~ /.well-known/acme-challenge/ {
proxy_pass http://certbot;
}
...
}
You might want to create an empty secret so pod creation doesn't fail. You can use the one provided in this repo. NB: Do not use this key publicly, it is not secure
kubectl create -f dummy-tls.yaml
Mount the secret in the spec
part of your deployment:
kind: Deployment
...
spec:
template:
spec:
containers:
...
volumeMounts:
- name: letsencrypt
mountPath: /letsencrypt/
readOnly: true
volumes:
- name: letsencrypt
secret:
secretName: letsencrypt-cert
You can do this ahead of time by creating an empty letsencryt secret
Lastly, create the certbot pod. It should issue you a cert immadiately and you are off to the races:
kubectl create -f kube.yaml
Happy Encrypting!
Implementation Details and History
This is based on the work of https://github.com/pjmorr/kubernetes-certbot, but eschews the use of the ingress controller.
It uses a python image and entrypoint, because I prefer python scripting to bash. However, some bash scripts are kept and edited in place.
To make renewal more straighforward, it uses a lock file to block updates, and uses cron to remove the file to enable renewal at a set interval.
It uses the aws-cli to dynamically update the ELB when certs are issued.
My nginx setup auto-reloads, so this process doesn't handle sending a SIGHUP to Nginx to reload the new certs.
Future Work
Support hosting deeper subdomains, e.g. api.cluster.example.com
Back up letsencrypt certs on generation, to S3 most likely.
Send/signal NOHUP on renewal to allow Nginx to restart?