This setup log describes and records the process I completed to configure reverse proxying with SSL certificates for my internal selfhosted services.

DNS setup

Static IP

I will be hosting my reverse proxy using Docker, which is running in an LXC within Proxmox. This Docker host must have a static IP, which will be referenced by the domains configured with certificates in following steps. All configuration is performed from the docker user on this LXC, an unprivileged member of the docker group.

I created the LXC, and added the default adapter’s MAC address to my router’s DHCP lease reservations as 192.168.1.200.

DNS forwarding

Although DNS entries for the IP configured above could be added as CNAME records in the DNS provider’s dashboard (Cloudflare in my case), I opted to configure my local DNS forwarder instead to keep all requests internal.

On EdgeOS, as in the case of my Edgerouter X, the following commands can be used to add forwarding for all subdomains to dnsmasq, the provider of DNS forwarding. As I am already using dnsmasq to forward requests to external nameservers when needed, adding additional configuration was a minor change.

configure
set service dns forwarding options 'address=/athome.rodeo/192.168.1.200'

This will resolve a.athome.rodeo, a.b.athome.rodeo, ... to the IP address on which my reverse proxy is running.

NOTE

If adapting this configuration to your own environment, be sure to modify any IP addresses, domains, and file paths/usernames where appropriate. This applies to configuration files, as well as commands.

Traefik setup

I used the following three configuration files to setup Traefik on the Docker host configured as the endpoint for my newly forwarded domain. As I am also using Portainer for container management, the docker-compose.yml file was used to create a stack within it. The starting point for these configuration files was adapted from the reference published here.

Folder configuration

To use the absolute paths specified below, create the following files and folders as the docker user on the Docker host:

cd ~
mkdir -p config/portainer/data
mkdir config/traefik
cd $_
touch acme.json
chmod 600 acme.json

Create the remaining files included below in the same ~/config/traefik/ directory.

traefik.yml

Explicitly setting external resolvers for the DNS challenge prevents the local network DNS forwarding configured previously from interfering with requests associated with certificate management. This may not be necessary if configuring DNS through e.g. a registrar’s dashboard.

config.yml

docker-compose.yml

Compose variables

Cloudflare API token

The docker-compose.yml above requires a Cloudflare API token (CF_DNS_API_TOKEN), which can be created from the Cloudflare dashboard My Profile API Tokens Create Token. The token requires these scopes:

  • Zone / Zone / Read
  • Zone / DNS / Edit

Select the zone(s) which will be used as domain(s) for internal services. An email and API key can be used instead, but will be granted less restrictive access to your Cloudflare account.

Basic auth password

With the above configuration, Traefik, uses HTTP basic auth when accessing the dashboard. Generate a password hash using htpasswd, as recommended in the docs:

sudo apt install apache2-utils
htpasswd -nB <username> | sed -e s/\\$/\\$\\$/g

Any references to $ are escaped by doubling them to $$ via sed. When evaluated, this results in a single $ in the Docker label.

Paste the htpasswd output, including the username, in the traefik.http.middlewares.traefik-auth.basicauth.users config key in docker-compose.yml.

Keep the original output format of user:hash, e.g. reed:$$apr1$$fakehash$$somehashtext. Multiple user:hash combinations may be supplied, comma-separated, with no spaces. See here for a full reference.

Starting service

Creating network

Prior to creating the Traefik container with docker compose (or Portainer), the network proxy must be created. As the compose specifies this as an external network, Docker expects it to already exist and will not create it.

To create the network using Docker, run:

docker network create proxy

To create the network in Portainer, use the Networks list. All options may be left as default, with the name set to proxy.

Starting

Once the proxy network has been created, run the Docker compose file with docker, by creating a Portainer stack, etc.

At this point, the Traefik dashboard should be available (behind basic auth using the credentials created above) at the domain configured in compose file labels. In my case, this is at https://traefik.athome.rodeo.

Adding new entries

Here is an example of a Docker compose file to deploy Portainer behind Traefik:

docker-compose.yml
version: "3"
services:
  portainer:
    image: portainer/portainer-ce
    container_name: portainer
    restart: always
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - /home/docker/config/portainer/data:/data
    labels:
      - "traefik.enable=true"
      - "traefik.http.routers.portainer.entrypoints=http"
      - "traefik.http.routers.portainer.rule=Host(`portainer.athome.rodeo`)"
      - "traefik.http.middlewares.portainer-https-redirect.redirectscheme.scheme=https"
      - "traefik.http.routers.portainer.middlewares=portainer-https-redirect"
      - "traefik.http.routers.portainer-secure.entrypoints=https"
      - "traefik.http.routers.portainer-secure.rule=Host(`portainer.athome.rodeo`)"
      - "traefik.http.routers.portainer-secure.tls=true"
      - "traefik.http.routers.portainer-secure.service=portainer"
      - "traefik.http.services.portainer.loadbalancer.server.port=9443"
      - "traefik.docker.network=proxy"
networks:
  proxy:
    external: true

One option for the ability to manage Traefik from Portainer is as follows:

Run Portainer using the command:

docker run -d -p 9443:9443 --name portainer --restart=always -v /var/run/docker.sock:/var/run/docker.sock -v /home/docker/config/portainer/data:/data portainer/portainer-ce:latest

Next, access Portainer at port 9443, create an account, and create a Traefik stack using the relevant files. Once this has started, stop the portainer container. Recreate it from the compose file above using docker-compose up --force-recreate -d, at which point Portainer will be available behind Traefik, but the latter can still be managed from within Portainer.