Setup Traefik with Consul ACL in Docker

Start by going to your home directory:

cd ~

Next, spin up the Consul Docker container with the following command:

docker run -d -v $PWD/consul/data/:/consul/data/ -v $PWD/consul/config/:/consul/config/ -e 'CONSUL_LOCAL_CONFIG={"skip_leave_on_interrupt": true, "data_dir":"/consul/data"}' --name=consul -p 8500:8500 consul agent -ui -server -bootstrap-expect 1 -client=0.0.0.0 -bind=0.0.0.0

The important parts to note are the -v for the Consul volume to be stored to the local disk and the data_dir in CONSUL_LOCAL_CONFIG.

Next, generate the master token UUID with the following command:

uuidgen

Next, generate the agent token UUID with the following command:

uuidgen

Next, enable ACL in Consul by creating a file called master-token.json in the ~/consul/config:

nano ~/consul/config/master-token.json

Paste the following in master-token.json:

{
  "acl_master_token":"YOUR_MASTER_UUID_TOKEN_GENERATED_ABOVE",
  "acl_agent_token":"YOUR_AGENT_UUID_TOKEN_GENERATED_ABOVE",
  "acl_datacenter":"dc1",
  "acl_default_policy":"deny",
  "acl_down_policy":"deny"
}

Save this file with CTRL + O and then nagivate to http://your-consul-ip:8500 and click on the ACL menu item. You should see "Anonymous Token" and "Master Token".

Next, create the Agent Token by clicking on create on the top right. Input "Agent Token" in the name and Tick "client". Paste the following in the policy:

node "" {
    policy = "write"
}

service "" {
    policy = "write"
}

Paste the agent token UUID generated above in the ID field and click save. Your screen should look like this when opening the Agent Token:

The final step is to allow Traefik to use Consul's ACL. To do this, create a new ACL token as above with the name "traefik", tick "client", and paste the following in the policy:

key "traefik" {
    policy = "write"
}

session "" {
    policy = "write"
}

service "" {
    policy = "read"
}

node "" {
    policy = "write"
}

Your screen should look like this when opening the traefik token:

Click "Copy token ID" in the traefik ACL token image shown above. The token should be used in the commands to follow.

Next, spin up a Traefik Docker container with the following command:

docker run -e "CONSUL_HTTP_TOKEN=YOUR_TRAEFIK_ACL_CONSUL_TOKEN" traefik \
        storeconfig \
        --consul \
        --consul.prefix="traefik" \
        --consul.watch \
        --consul.endpoint="YOUR_CONSUL_ENDPOINT_WITH_PORT" \
        --consulcatalog=true \
        --consulcatalog.endpoint="YOUR_CONSUL_ENDPOINT_WITH_PORT" \
      	--consulcatalog.domain="YOUR_DOMAIN" \
      	--consulcatalog.frontEndRule="Host:YOUR_TRAEFIK_DASHBOARD_URL" \
        --consulcatalog.constraints="tag==public" \
        --entryPoints='Name:https Address::443 TLS' \
        --entryPoints='Name:http Address::80' \
        --entryPoints='Name:api Address::8080' \
        --acme.entrypoint=https \
        --acme=true \
        --acme.ondemand=false \
        --acme.onhostrule=true \
        --acme.email="YOUR_ACME_EMAIL_ADDRESS" \
        --acme.storage="traefik/acme/account" \
        --acme.httpChallenge \
		--acme.httpChallenge.entryPoint="http" \
        --api \
        --api.entryPoint="api" \
      	--api.dashboard="true" \
      	--rest \
      	--rest.entryPoint="api" \
      	--InsecureSkipVerify="true" \
      	--traefiklog.filepath="traefik.log" \
      	--loglevel="DEBUG"

There are a couple of things you'll need to replace in the above command:

YOUR_TRAEFIK_ACL_CONSUL_TOKEN: Replace with the copied token from above
YOUR_CONSUL_ENDPOINT_WITH_PORT: Replace with the IP of the machine running the Consul Docker container along with the port. E.g 192.168.200.67:8500
YOUR_DOMAIN: Replace with the domain you want Traefik to run on
YOUR_TRAEFIK_DASHBOARD_URL: Replace with dashboard.YOUR-DOMAIN.co.za
YOUR_ACME_EMAIL_ADDRESS: Replace with your email address to receive ACME notifications

The Docker run command will populate Consul with the configuration required to run Traefik. Next, find the name of this container by running:

docker ps -a

Remove the container with:

docker rm container_name

A couple of directories now need to be created in Consul which the above Docker run command didn't create, namely: frontends, backends, and tls. To create these directories, navigate to the Key/Value navbar link on the Consul dashboard. Click on traefik. You should see the following screen:

Next, click on the Create button and input the following in the Key or Folder text box:

frontends/

Note the trailing slash. This tells Consul that we are creating a folder. Repeat this process for the backends and tls folders.

The final step is to run the Traefik command which will now communicate with Consul:

docker run -d \
	-v /var/run/docker.sock:/var/run/docker.sock \
  -v $PWD/traefik.log:/traefik.log \
	-e "CONSUL_HTTP_TOKEN=YOUR_TRAEFIK_ACL_CONSUL_TOKEN" \
	-p 80:80 \
	-p 443:443 \
	-p 8080:8080 \
  --name traefik \
  traefik \
  --consul \
  --consul.prefix="traefik" \
  --consul.watch \
  --consul.endpoint="YOUR_CONSUL_ENDPOINT_WITH_PORT" \
  --consulcatalog=true \
  --consulcatalog.endpoint="YOUR_CONSUL_ENDPOINT_WITH_PORT" \
  --consulcatalog.domain="YOUR_DOMAIN" \
  --consulcatalog.frontEndRule="Host:YOUR_TRAEFIK_DASHBOARD_URL" \
  --entryPoints='Name:https Address::443 TLS' \
  --entryPoints='Name:http Address::80' \
  --entryPoints='Name:api Address::8080' \
  --acme.entrypoint=https \
  --acme=true \
  --acme.ondemand=false \
  --acme.onhostrule=true \
  --acme.email="YOUR_ACME_EMAIL_ADDRESS" \
  --acme.storage="traefik/acme/account" \
  --acme.httpChallenge \
  --acme.httpChallenge.entryPoint="http" \
  --api \
  --api.entryPoint="api" \
  --api.dashboard="true" \
  --rest \
  --rest.entryPoint="api" \
  --InsecureSkipVerify="true" \
  --traefiklog.filepath="traefik.log" \
  --loglevel="DEBUG"

And Voila! Traefik is setup with Consul. Please note that the Traefik API and Traefik dashboard have been enabled in this tutorial, and I'd suggest protecting both of these with Basic Auth. To test that everything is working as expected you can create a file called test.json containing the following:

{
  "Name": "someservice",
  "Address": "192.168.200.172",
  "Port": 8443,
  "Tags": [
    "traefik.tags=public",
    "traefik.protocol=https",
    "traefik.frontend.rule=Host:subdomain.yourdomain.co.za",
    "traefik.frontend.entryPoints=http,https"
  ]
}

Replace someservice and subdomain.yourdomain.co.za with appropriate values. Take note of the traefik.tags=public tag which tells Traefik that this service should be exposed. You'll notice this in the consulcatalog.constraints form the docker run command from above.

Next, post this file to the consul endpoint along with the traefik ACL token:

curl --header "X-Consul-Token: YOUR_TRAEFIK_ACL_CONSUL_TOKEN" --upload-file dc.json http://YOUR-CONSUL-IP:8500/v1/agent/service/register

You should now see your service in Consul under the Services tab. This would create the appropriate frontend and backend rules in Traefik which can be seen by going to http://YOUR-TRAEFIK-IP:8080 and clicking on the consul_catalog provider.

We have built a neat Java web application which simplifies the creating of Frontends and Backends and adding Basic Auth to Traefik. See below a few screenshots:

Routey Dashboard for viewing, creating, editing, and deleting routes

Routey Add route screenshot for creating a route with Basic Auth and Let's Encrypt support

Should you want this software which we've called "Routey", please reach out at curt@base64.co.za. Routey serves as a simple frontend for managing "routes" and running multiple services/web applications with a single static IP address. We are in the process of integrating with Ubiquiti to create a fully scalable routing platform.