Skip to content

How to Setup Distributed ACME in Traefik Enterprise

Traefik Enterprise uses a dedicated agent to issue and share ACME certificates between multiple clusters. This agent operates along with a Vault KV provider, which retrieves generated certificates from a Vault server. The distributed ACME agent only supports the DNS challenge, because the HTTP and TLS challenges are inherently multicluster-friendly.

This guide assumes that you are using Kubernetes.

Configure the Agent

The agent stores generated certificates in a Vault server. It must have a KV Secrets Engine version 2 enabled.

Dedicated Secrets Engine

For security reasons, the best practice is to use a dedicated Secrets Engine to store certificates.

Enabling a Secrets Engine

Here is an example to enable a new Secrets Engine under the "customPath" path: vault secrets enable -path=customPath -version=2 kv

In order to enable mTLS between the Traefik Enterprise controllers and the Distributed ACME Agent, you must provide certificates in the configuration of the agent. It is recommended to store those certificates in Kubernetes TLS Secrets and mount them on your ACME Agent and Traefik Enterprise Controller deployments or StatefulSets.

Additionally, you have to configure the certificate resolvers to use for solving DNS challenges. You'll need to configure at least one DNS provider for the agent to work.

Required Environment Variables

All the DNS providers have their own sets of required environment variables you'll need to set in order to authenticate. The Traefik documentation contains a list of providers and their associated variables.

To avoid recreating new ACME accounts every time the agent restarts, it persists account credentials on its filesystem. Mounting a Persistent Volume to /var/lib/acme-agent/state/state.json allows the agent to keep its state across restarts.

Below is a complete example of how to deploy the ACME agent with mTLS. Commented lines potentially need to be edited.

apiVersion: apps/v1
kind: Deployment
metadata:
  name: acme-agent-deployment
  namespace: traefikee
  labels:
    app: acme-agent
spec:
  selector:
    matchLabels:
      app: acme-agent
  template:
    metadata:
      labels:
        app: acme-agent
    spec:
      containers:
        - name: acme-agent
          image: traefik/acme-agent:v1.0.0
          # Edit these environment variables with what your DNS provider requires.
          env:
            - name: CLOUDFLARE_DNS_API_TOKEN
              value: $CLOUDFLARE_API_TOKEN
          volumeMounts:
            # Here we mount the configuration of the agent.
            - name: config
              mountPath: /etc/acme-agent/
            # This allows preserving the state if the agent restarts.
            - name: state
              mountPath: /var/lib/acme-agent/state/
            - name: mtls-ca
              mountPath: /var/lib/acme-agent/mtls/ca/
            - name: mtls-server
              mountPath: /var/lib/acme-agent/mtls/server/
      volumes:
        - name: config
          configMap:
            name: acme-agent-config
            items:
              - key: config.yml
                path: config.yml
        - name: state
          persistentVolumeClaim:
            claimName: acme-agent-state
        - name: mtls-ca
          secret:
            secretName: acme-agent-mtls-ca
            items:
              - key: tls.crt
                path: tls.crt
              - key: tls.key
                path: tls.key
        - name: mtls-server
          secret:
            secretName: acme-agent-mtls-server
            items:
              - key: tls.crt
                path: tls.crt
              - key: tls.key
                path: tls.key
apiVersion: v1
kind: Service
metadata:
  name: acme-agent
  namespace: traefikee
spec:
  selector:
    app: acme-agent
  ports:
    # This needs to match the port on which the agent is listening.
    - port: 5505
apiVersion: v1
kind: ConfigMap
metadata:
  name: acme-agent-config
  namespace: traefikee
data:
  # Edit the agent configuration here. Note that the agent must be restarted to take changes into account.
  config.yml: |-
    listenAddr: 0.0.0.0:5505
    tls:
      ca: /var/lib/acme-agent/mtls/ca/tls.crt
      cert: /var/lib/acme-agent/mtls/server/tls.crt
      key: /var/lib/acme-agent/mtls/server/tls.key
    vault:
      url: http://vault.traefikee.svc.cluster.local:8200
      token: root
      enginePath: customPath
    certificateResolvers:
      myResolver:
        email: [email protected]
        caServer: https://acme-staging-v02.api.letsencrypt.org/directory
        provider: cloudflare
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
  name: acme-agent-state
  namespace: traefikee
spec:
  accessModes:
    - ReadWriteOnce
  # Replace the storageClassName with the storage you use in your Kubernetes cluster.
  storageClassName: local-path
  resources:
    requests:
      # The state only consists of ACME account credentials which are a few kilobytes per account used.
      storage: 10Mi
apiVersion: v1
kind: Secret
metadata:
  name: acme-agent-mtls-ca
  namespace: traefikee
type: kubernetes.io/tls
data:
  tls.crt: # Your base64-encoded certificate goes here
  tls.key: # Your base-64 encoded key goes here
---
apiVersion: v1
kind: Secret
metadata:
  name: acme-agent-mtls-server
  namespace: traefikee
type: kubernetes.io/tls
data:
  tls.crt: # Your base64-encoded certificate goes here
  tls.key: # Your base64-encoded key goes here
---
apiVersion: v1
kind: Secret
metadata:
  name: acme-agent-mtls-client
  namespace: traefikee
type: kubernetes.io/tls
data:
  tls.crt: # Your base64-encoded certificate goes here
  tls.key: # Your base64-encoded key goes here

Configure Traefik Enterprise

In the Traefik Enterprise static configuration, you'll need to enable the Distributed ACME certificate resolver as well as the Vault KV provider. The certificate resolver will issue requests to the Distributed ACME agent, and the Vault KV provider is in charge of fetching certificates from the Vault server that runs with the ACME agent.

Just like the ACME agent, Traefik Enterprise controllers can be configured to use mTLS, in which case the certificate resolver configuration needs paths to the mTLS certificates. It is also recommended to store them in Kubernetes TLS Secrets and mount them on your Traefik Enterprise Controller StatefulSets.

Below is an example of a Traefik Enterprise static configuration that uses the distributed ACME agent:

entryPoints:
  https:
    address: 0.0.0.0:443

providers:
  kubernetesCRD: { }
  plugin:
    vault:
      url: http://vault.traefikee.svc.cluster.local:8200
      token: root
      enginePath: customPath

certificatesResolvers:
  # Define your certificate resolver here.
  myResolver:
    distributedACME:
      url: https://acme-agent.traefikee.svc.cluster.local:5505
      tls:
        ca: /var/lib/acme-agent/mtls/ca/tls.crt
        cert: /var/lib/acme-agent/mtls/client/tls.crt
        key: /var/lib/acme-agent/mtls/client/tls.key

If you are using mTLS, which is recommended for a production environment, you also need to update your Traefik Enterprise StatefulSet and add the following volumes and volume mounts so that it has access to mTLS certificates:

spec:
  template:
    spec:
      containers:
        - name: "default-controller"
          volumeMounts:
          - name: mtls-ca
            mountPath: /var/lib/acme-agent/mtls/ca/
          - name: mtls-client
            mountPath: /var/lib/acme-agent/mtls/client/
      volumes:
      - name: mtls-ca
        secret:
          secretName: acme-agent-mtls-ca
      - name: mtls-client
        secret:
          secretName: acme-agent-mtls-client

You can apply the above patch on your existing Traefik Enterprise StatefulSet with the following command:

kubectl patch statefulset -n traefikee default-controller -p "$(cat traefikee-mtls-patch.yml)"
kubectl patch statefulset -n traefikee default-controller -p (cat traefikee-mtls-patch.yml | string collect)
kubectl patch statefulset -n traefikee default-controller -p $(Get-Content traefikee-mtls-patch.yml -Raw)

Configure your IngressRoutes

To use the distributed ACME agent you must reference the certificate resolver configured in your static configuration:

apiVersion: traefik.containo.us/v1alpha1
kind: IngressRoute
metadata:
  name: myservice
  namespace: traefikee
spec:
  entryPoints:
    - https
  routes:
    - match: Host(`myservice.corp.com`)
      kind: Rule
      services:
        - name: myservice
          namespace: traefikee
          port: 80
  tls:
    # Reference your certificate resolver here.
    certResolver: myResolver

Configuration References

Agent Configuration

listenAddr

Required, Default="0.0.0.0:5505"

Defines the interface and port on which the distributed ACME agent listens for requests.

listenAddr: 0.0.0.0:5505

checkRenewalInterval

Optional, Default="24h"

Defines the interval at which the agent checks if ACME certificates need to be renewed.

checkRenewalInterval: 24h

tls.ca, tls.cert and tls.key

Optional, Default=""

Paths to certificates required for enabling mTLS to secure communication between Traefik Enterprise and the ACME agent. If these are not set, mTLS is disabled and the connection between the ACME agent and Traefik Enterprise controllers is not secured.

tls:
  ca: /var/lib/acme-agent/mtls/ca/ca.pem
  cert: /var/lib/acme-agent/mtls/server/server.pem
  key: /var/lib/acme-agent/mtls/server/server-key.pem

vault.url

Required, Default=""

Defines the URL of the Vault server, including the scheme and port.

vault:
  url: https://vault.devault.svc.cluster.local:9200

vault.token

Optional(one of vault.token or vault.appRole must be set), Default=""

Defines the token to authenticate with Vault.

Supported Authentication Mechanisms

The Vault certificate resolver currently supports token authentication only.

vault:
  token: myVaultToken

vault.appRole

Optional(one of vault.token or vault.appRole must be set), Default=None

Enables the AppRole authentication method. See vault.appRole.* options to see what needs to be provided.

vault.appRole.roleID

Required, Default=""

Defines the ID of the role to use when authenticating to Vault with AppRole.

vault:
  appRole:
    roleID: "4cf1dc0d-f431-f76d-42fd-ed1264f0b893"

vault.appRole.secretID

Required, Default=""

Defines the ID of the secret to use when authenticating to Vault with AppRole.

vault:
  appRole:
    secretID: "63cffb45-b404-18c4-cdbb-af723aaef1e7"

vault.appRole.path

Optional, Default="approle"

Defines the path under which the AppRole authentication method is enabled in Vault.

vault:
  appRole:
    path: "customAppRolePath"

vault.enginePath

Optional, Default="tls"

Defines the path under which the PKI secret engine is enabled.

Vault Namespaces

If using Vault Namespaces, simply prefix the engine path with the name of the namespace. If using the engine path secret under the default namespace, set default/secret as the engine path.

vault:
  enginePath: customPath

vault.syncInterval"

Optional, Default="1m"

Defines the interval at which the agent checks if an ACME certificates was manually deleted in Vault.

vault:
  syncInterval: 1m

certificateResolvers.resolverName.email

Required, Default=""

The email to use for the ACME account of this resolver.

certificateResolvers:
  resolverName:
    email: [email protected]

certificateResolvers.resolverName.caServer

Required, Default=""

The URL to the ACME directory from which to obtain certificates.

certificateResolvers:
  resolverName:
    caServer: https://acme-staging-v02.api.letsencrypt.org/directory

certificateResolvers.resolverName.provider

Required, Default=""

The DNS provider used to automate DNS verifications for the ACME challenges.

Required Environment Variables

All the DNS providers have their own sets of required environment variables you'll need to set in order to authenticate. The Traefik documentation contains a list of providers and their associated variables.

certificateResolvers:
  resolverName:
    provider: cloudflare

certificateResolvers.resolverName.keyType

Required, Default="RSA4096"

The key type to use for the private keys associated with the generated certificates. Allowed values are the following: EC256, EC384, RSA2048, RSA4096, RSA8192.

certificateResolvers:
  resolverName:
    keyType: RSA4096

Certificate Resolver Configuration

url

Required, Default=""

The URL of the Distributed ACME Agent.

url: https://dacme.example.org
url = "https://dacme.example.org"

tls.ca, tls.cert and tls.key

Optional, Default=""

Paths to certificates required for enabling mTLS to secure communication between Traefik Enterprise and the ACME agent. If these are not set, mTLS is disabled and the connection between the ACME agent and Traefik Enterprise controllers is not secured.

tls:
  ca: /var/lib/acme-agent/mtls/ca/ca.pem
  cert: /var/lib/acme-agent/mtls/server/server.pem
  key: /var/lib/acme-agent/mtls/server/server-key.pem
[tls]
  ca = "/var/lib/acme-agent/mtls/ca/ca.pem"
  cert = "/var/lib/acme-agent/mtls/server/server.pem"
  key = "/var/lib/acme-agent/mtls/server/server-key.pem"

Configuration Examples

Agent Configuration

Below is a complete distributed ACME agent configuration example:

listenAddr: 0.0.0.0:5505
checkRenewalInterval: 24h
tls:
  ca: /path/to/mtls/ca.pem
  cert: /path/to/mtls/server/cert.pem
  key: /path/to/mtls/server/key.pem
vault:
  url: https://vault.corp.com:8200
  token: myVaultToken
  enginePath: customPath
  syncInterval: 1m
certificateResolvers:
  myResolver:
    email: [email protected]
    caServer: https://acme-staging-v02.api.letsencrypt.org/directory
    provider: cloudflare

Certificate Resolver Configuration

Below is a complete distributed ACME certificate resolver configuration example:

url: https://acme-agent.traefikee.svc.cluster.local:5505
tls:
  ca: /var/lib/acme-agent/mtls/ca/tls.crt
  cert: /var/lib/acme-agent/mtls/client/tls.crt
  key: /var/lib/acme-agent/mtls/client/tls.key
url = "https://acme-agent.traefikee.svc.cluster.local:5505"
[tls]
  ca = "/var/lib/acme-agent/mtls/ca/tls.crt"
  cert = "/var/lib/acme-agent/mtls/client/tls.crt"
  key = "/var/lib/acme-agent/mtls/client/tls.key"