Search code examples
ssldocker-composedocker-swarmtraefikconsul

How to upload SNI certificates to traefik/consul in a swarm


We are using the below docker stack file to deploy consul and traefik in a docker swarm. This generated SSL certificates from Lets Encrypt and works as expected. We, however, have pre-purchased wildcard chain certificates (.crt and .key) we would like to use for certain domains. How would we upload these to be used instead of the lets encrypt certificates?

version: '3'

services:
  consul-leader:
    container_name: consul-leader
    image: consul
    command: agent -server -client=0.0.0.0 -bootstrap -ui
    volumes:
      - consul-data-leader:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}'
    networks:
      - default
      - front-end
    deploy:
      labels:
        - traefik.frontend.rule=Host:consul.domain.com
        - traefik.port=8500
        - traefik.docker.network=front-end
        # Traefik service that listens to HTTP
        - traefik.redirectorservice.frontend.entryPoints=http
        - traefik.redirectorservice.frontend.redirect.entryPoint=https
        # Traefik service that listens to HTTPS
        - traefik.webservice.frontend.entryPoints=https

  consul-replica:
    container_name: consul-replica
    image: consul
    command: agent -server -client=0.0.0.0 -retry-join="consul-leader"
    volumes:
      - consul-data-replica:/consul/data
    environment:
      - CONSUL_BIND_INTERFACE=eth0
      - 'CONSUL_LOCAL_CONFIG={"leave_on_terminate": true}'
    networks:
      - default
      - front-end
    deploy:
      replicas: 0
      placement:
        preferences:
          - spread: node.id

  traefik:
    container_name: traefik
    image: traefik:v1.7
    ports:
      - target: 80
        published: 80
        mode: host
      - target: 443
        published: 443
        mode: host
    deploy:
      replicas: 1
      placement:
        constraints:
          - node.role == manager
        preferences:
          - spread: node.id
      labels:
        - traefik.frontend.rule=Host:traefik.domain.com
        - traefik.port=8080
        - traefik.docker.network=front-end
        # Traefik service that listens to HTTP
        - traefik.redirectorservice.frontend.entryPoints=http
        - traefik.redirectorservice.frontend.redirect.entryPoint=https
        # Traefik service that listens to HTTPS
        - traefik.webservice.frontend.entryPoints=https
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - /docker/ssl:/docker/ssl
    command: >
      --docker
      --docker.swarmmode
      --docker.watch
      --docker.exposedbydefault=true
      --entrypoints='Name:http Address::80'
      --entrypoints='Name:https Address::443 TLS'
      --consul
      --consul.watch
      --consul.endpoint="consul-leader:8500"
      --acme
      [email protected]
      --acme.storage="traefik/acme/account"
      --acme.entryPoint=https
      --acme.httpChallenge.entryPoint=http
      --acme.onhostrule=true
      --acme.acmelogging=true
      --logLevel=INFO
      --accessLog
      --api
    networks:
      - default
      - front-end
    depends_on:
      - consul-leader

volumes:
  consul-data-leader:
  consul-data-replica:

networks:
  front-end:
    name: front-end
    driver: overlay

Solution

  • Traefik is able to be configured with multiple certificates per entrypoint i.e.

    [entryPoints.http.tls]      
      [[entryPoints.http.tls.certificates]]
        certFile = "path/to/my-lets-encrypt.cert"
        keyFile = "path/to/my-lets-encrypt.key"
      [[entryPoints.http.tls.certificates]]
        certFile = "path/to/my-purchased.cert"
        keyFile = "path/to/my-purchased.key"
    

    The decision which will serve is based on the host header of the request. That means that if you want the domain: my-important-domain.com to be served with a purchased certificate you have to be sure that only that certificate is issued for that domain as subject or SAN. That means that these domains need to be excluded from ACME traefik config.

    Check below the relevant docs where it is described how you can do that

    # Domains list.
    # Only domains defined here can generate wildcard certificates.
    # The certificates for these domains are negotiated at traefik startup only.
    #
    # [[acme.domains]]
    #   main = "local1.com"
    #   sans = ["test1.local1.com", "test2.local1.com"]
    # [[acme.domains]]
    #   main = "local2.com"
    # [[acme.domains]]
    #   main = "*.local3.com"
    #   sans = ["local3.com", "test1.test1.local3.com"]