How to Secure Your Web Containers using a Traefik HTTP Challenge

Hello! If you are here, you are probably trying to enable SSL for HTTP connections to your docker containers. In this post, you will find a working configuration file for routing traffic to your web containers, and for obtaining SSL certificates from Let’s Encrypt using a traefik HTTP Challenge. Let’s get started!

How to Secure Web Containers Traefik HTTP Challenge Cover Image

What is a Traefik HTTP Challenge?

An HTTP challenge is a method used by Let’s Encrypt, our CA, to verify that you own both the server requesting a certificate, and the domain it is requesting a certificate for. Without getting too far into the weeds, it requests for the server pointed to by the domain to place some information at a specific path. If the server is expecting the HTTP Challenge from the CA, it will respond with the correct information in the correct location. This verifies to the CA that the owner of the server is also the owner of the domain, so the issue a certificate to the server.

Using a traefik HTTP Challenge to obtain certificates has some benefits and some drawbacks. In order to work correctly, your web server (your Traefik container in this case) must be listening on port 80 (the common http port). Many ISPs block traffic to port 80 due to security and spam concerns. So, if you are planning to host your containers from a residential IP, be sure to check to make sure traffic to port 80 is not block by your ISP. If it is, using a traefik DNS challenge might serve you better, On the plus side, traefik HTTP challenge is domain registrar agnostic, meaning it will work seamlessly regardless of where you register your domains, whereas the DNS challenge configuration will need to be adjusted base on your registrar.

The Configuration

Setting up our configuration to utilize traefik http challenge for SSL is quite straight forward. All we need to do is add an http certificate resolver to our Traefik container. Take a look below.

version: '3.7'

services:
  traefik:
    image: traefik:v3.1
    container_name: traefik_router
    command:
      #routers and api
      - "--providers.docker=true"
      - "--providers.docker.exposedbydefault=false"
      - "--entrypoints.web.address=:80"
      - "--entrypoints.websecure.address=:443"
      #debug
      #- "--log.level=DEBUG"    #uncomment while debugging
      #redirect to https
      - "--entrypoints.web.http.redirections.entryPoint.to=websecure"
      - "--entrypoints.web.http.redirections.entryPoint.scheme=https"
      #acme automatic ssl http challenge resolver
      - "--certificatesresolvers.le_http.acme.httpchallenge=true"
      - "--certificatesresolvers.le_http.acme.email=your@email.com"      
      - "--certificatesresolvers.le_http.acme.storage=/letsencrypt/acme.json"
      - "--certificatesresolvers.le_http.acme.httpchallenge.entrypoint=web
      - "--certificatesresolvers.le_dns.acme.caServer=https://acme-v02.api.letsencrypt.org/directory"
      #- "--certificatesresolvers.le_dns.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory"
    ports:
      - "80:80" #host:container
      - "443:443"
    volumes:
      - /var/run/docker.sock:/var/run/docker.sock
      - ./letsencrypt:/letsencrypt
    restart: unless-stopped
    networks:
      - websites_net
  web:
    image: httpd
    container_name: web-1
    volumes:
      - ./html:/usr/local/apache2/htdocs/
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      #http config
      - "traefik.http.routers.http-web_1.entrypoints=web"
      - "traefik.http.routers.http-web_1.rule=Host(`yourdomain.com`)" #change to your domain
      #https config
      - "traefik.http.routers.web_1.entrypoints=websecure"
      - "traefik.http.routers.web_1.rule=Host(`yourdomain.com`)"
      #tls config
      - "traefik.http.routers.web_1.tls=true"
      - "traefik.http.routers.web_1.tls.certresolver=le_http"
      - "traefik.docker.network=websites"
    networks:
      - websites_net

networks:
  websites_net:  # Define the network
    name: websites
    driver: bridge

Note: if you use the docker-compose above, be sure to test using the staging server to avoid being rate-limited.

Summary

Nothing too complicated is happening in this docker-compose. We have our Traefik router, which routes requests to our web server container, web. There are two routers defined, one for http traffic, and one for https. An automatic upgrade for http to https is included in our config as well. Then, we simply set up our http certificate resolver using these lines:

- "--certificatesresolvers.le_http.acme.httpchallenge=true"
- "--certificatesresolvers.le_http.acme.email=your@email.com"      
- "--certificatesresolvers.le_http.acme.storage=/letsencrypt/acme.json"
- "--certificatesresolvers.le_http.acme.httpchallenge.entrypoint=web
- "--certificatesresolvers.le_dns.acme.caServer=https://acme-v02.api.letsencrypt.org/directory"
#- "--certificatesresolvers.le_dns.acme.caServer=https://acme-staging-v02.api.letsencrypt.org/directory"

These set the challenge method, acme email, certificate storage location, CA server, and entry point to be used for the le_http (this is a name I chose) certificate resolver. For more information on these values, see the Traefik documentation. Then, we simply attach our web server container to both the web and websecure entrypoints. Then, just wait a couple minutes, visit your website’s address, and if all went well, you should be automatically connected using HTTPS!

If you are interested in an alternative method for obtaining certificates for your containers, be sure to read my post on using a Traefik DNS Challenge to obtain certificates.

Comments

Leave a Reply

Your email address will not be published. Required fields are marked *