~bitfehler/notariat

Dead simple TLS certificate manager for Kubernetes
README: add documentation
Dockerfile: need TLS certs to talk to CA

refs

master
browse  log 

clone

read-only
https://git.sr.ht/~bitfehler/notariat
read/write
git@git.sr.ht:~bitfehler/notariat

You can also use your local clone with git send-email.

#Notariat

Notariat is narrow-purpose, but therefore extremely simple certificate manager for Kubernetes. It can obtain certificates from any ACME-compatible CA (e.g. Let's Encrypt), but it has the following limitations:

  • only supports the ACME DNS-01 challenge
  • DNS updates are performed via DNS UPDATE using a TSIG key
  • the list of certificates to be managed is maintained by hand in a config map

#How it works

#Conceptual overview

The main input of notariat is the configuration, which, at its core, maps a set of Kubernetes secret references to lists of domain names. The main control loop will continually ensure that each provided secret references a valid TLS certificate (and key) for the corresponding list of domain names.

For notariat to work, you will need to:

  • Create a config map containing the desired configuration
  • Create at least one secret containing the required TSIG key
  • Specify some command line options for your notariat deployment

Read the rest of this document for more details on each point.

#Configuration

A more detailed explanation of how notariat works is best given by looking at an example config map:

apiVersion: v1
kind: ConfigMap
metadata:
  name: notariat-config
data:
  certificates: |
    - secret: cert-example.com
      domains:
        - "example.com"
        - "www.example.com"
      email: admin@example.com
      tsigSecretName: tsigkey

In this example, notariat will make sure that the secret cert-example.com in its current namespace (see below for more about namespaces) is a secret of type kubernetes.io/tls that contains a valid TLS certificate for the domains example.com and www.example.com. It will do this according to the following algorithm:

  • For each configured secret:
    1. if secret does not exist, obtain new certificate and store it
    2. if secret exists, check that:
      • it is valid for the configured domain names
      • it does not expire within the next 30 days
    3. depending on result of those checks, obtain new certificate or continue

#Namespaces

In the configuration, both secret and tsigSecretName can be a namespaced secret reference. The following example will work:

apiVersion: v1
kind: ConfigMap
metadata:
  name: notariat-config
data:
  certificates: |
    - secret: default/cert-example.com
      domains:
        - "example.com"
        - "www.example.com"
      email: admin@example.com
      tsigSecretName: dns/tsigkey

Note that for this to work, you will need to set up ACLs that allow notariat to access other namespace's secrets. One simple approach might be to run notariat in the namespace of your ingress controller and also as the service account created for your ingress controller, as those generally have the required access already. See the deployment example below.

#TSIG keys

The TSIG keys required for the DNS updates have to be stored in [hmac:]name:key format. Supported HMAC algorithms are hmac-sha1, hmac-sha224, hmac-sha256, hmac-sha384, and hmac-sha256. If the HMAC part is omitted it defaults to hmac-sha256. Knot's keymgr for example will output this format as a comment on the first line when invoking keymgr -t.

This key format must be supplied as a value to a key called key. To create a valid TSIG secret for notariat:

$ cat key
hmac-sha256:example.com.:PA2svcP/i7sR/etNrqLCR3Ybw4gb1YMmfVGiyyUmYlo=
$ kubectl create secret generic tsigkey --from-file=key

#ACME accounts

Notariat will create a separate account for each email encountered in the configuration. This happens on demand, i.e. only once a certificate actually needs to be obtained. Each account key is stored in a secret notariat-acme-* in notariat's current namespace.

#Command line arguments

Notariat takes the following command line arguments:

  -acme string
    	directory URL of ACME server (default "https://acme-staging-v02.api.letsencrypt.org/directory")
  -config string
    	name of ConfigMap to use
  -kubeconfig string
    	path to kubeconfig (default: in-cluster auth)
  -namespace string
    	namespace (default: current pod's namespace)

Note how it by default talks to the Let's Encrypt staging environment. If you want to issue "real" certificates, make sure to run it with -acme https://acme-v02.api.letsencrypt.org/directory (or whichever ACME compatible CA you prefer).

The -config argument is mandatory and must be set to the name of the config map that contains notariat's configuration. The config map must be in the same namespace that notariat is running in.

The -kubeconfig and -namespace arguments are intended for running notariat out-of-cluster, e.g. for development and debugging. They should not be used in deployments.

#Deployment

Build a docker image with the provided Dockerfile and push it to a registry of your choice. You then have two options:

For very small deployments, you can run one instance of notariat in each namespace that needs TLS certificates. Configure each instance accordingly. Notariat will require no special ACLs in this case.

The recommended approach however, is to run notariat along with your ingress controller, as it is the primary consumer of the TLS certificates. Here is an example deployment that will run with a mostly-default HAProxy ingress controller:

apiVersion: apps/v1
kind: Deployment
metadata:
  name: notariat
  namespace: haproxy-controller
  labels:
    app: notariat
spec:
  replicas: 1
  selector:
    matchLabels:
      app: notariat
  template:
    metadata:
      labels:
        app: notariat
    spec:
      # Account created for the ingress controller
      serviceAccountName: haproxy-kubernetes-ingress
      containers:
      - name: notariat
        image: registry.example.org/notariat:latest
        imagePullPolicy: Always
        args:
          - --config=notariat-config
          - --acme=https://acme-v02.api.letsencrypt.org/directory

This will run notariat in the same namespace and as the same service account as the ingress controller. As the ingress controller needs access to other namespaces' secrets already, no further ACLs need to be set up.

#Contact

Patches, questions, or other feedback can be sent to my public inbox.