Mailcow behind Traefik 2.x

I found it challenging to find up to date, clear instruction on setting this up, so this post collects the relevant info in one place.

Mailcow behind Traefik 2.x

All of the information necessary to get mailcow functioning properly behind traefik 2.x already exists on the web, but I found it somewhat challenging to find all the relevant info in one place.

So first off, traefik.  Lets setup a reasonably straightforward traefik instance that'll allow lots of services, and many domains, to run behind it...

Create directory, /opt/traefik and within it create ad directory data where we'll store our traefik.yml and acme.json.  On that note, touch data/acme.json and very importantly chmod 600 data/acme.json ... that last point was a huge head scratcher when suddenly my cert resolver stopped working while I was trying to get wildcard certs setup. If the permissions on that file are less strict, traefik will throw out any resolvers associated with that for storage – but then in logs doesn't mention that, just says "resolver foo does not exist" or whatever. Frustrating.

Ok.. so we've got our /opt/traefik with a data directory that contains an empty acme.json file with appropriate permissions.

Now lets add a traefik.yml file in that data directory too with the following content:

api:
  dashboard: true

entryPoints:
  http:
    address: ":80"
  https:
    address: ":443"

tls:
  options:
    default:
      sniStrict: true
      minVersion: VersionTLS12

providers:
  docker:
    endpoint: "unix:///var/run/docker.sock"
    exposedByDefault: false

certificatesResolvers:
  default:
    acme:
      email: you@example.com
      storage: acme.json
      tlsChallenge: {}
      dnsChallenge:
        provider: digitalocean
        delayBeforeCheck: 0
      httpChallenge:
        entryPoint: http

Obviously change the email, and dnschallenge provider should be changed or omit as your situation dictates. I don't know for sure if having all three challenge types in there is correct, I imagine if one fails it'll try the next, etc.. but I don't know for certain. In any case its' working for me, including wildcard cert generation.

Next create the docker-compose.yml file in your /opt/traefik directory with the following contents:

version: '3'

services:
  traefik:
    image: traefik:2.2
    container_name: traefik
    restart: always
    security_opt:
      - no-new-privileges:true
    networks:
      - proxy
    ports:
      - 80:80
      - 443:443
    environment:
      - DO_AUTH_TOKEN=YOUR_DIGITALOCEAN_TOKEN
    volumes:
      - /etc/localtime:/etc/localtime:ro
      - /var/run/docker.sock:/var/run/docker.sock:ro
      - ./data/traefik.yml:/traefik.yml:ro
      - ./data/acme.json:/acme.json
    labels:
      - traefik.enable=true
      - traefik.http.middlewares.traefik-auth.basicauth.users=USER:HASH
      - traefik.http.routers.traefik-secure.service=api@internal
      - traefik.http.routers.traefik-secure.entrypoints=https
      - traefik.http.routers.traefik-secure.rule=Host(`traefik.example.com`)
      - traefik.http.routers.traefik-secure.middlewares=traefik-auth
      - traefik.http.routers.traefik-secure.tls=true
      - traefik.http.routers.traefik-secure.tls.certResolver=default
      # Omit next two lines to not do wildcard certs:
      - traefik.http.routers.traefik-secure.tls.domains[0].main=example.com
      - traefik.http.routers.traefik-secure.tls.domains[0].sans=*.example.com

      # Global Redirect to https
      - traefik.http.routers.http-catchall.rule=hostregexp(`{host:.+}`)
      - traefik.http.routers.http-catchall.entrypoints=http
      - traefik.http.routers.http-catchall.middlewares=redirect-to-https
      - traefik.http.middlewares.redirect-to-https.redirectscheme.scheme=https

networks:
  proxy:
    external: true

Now since our proxy network is external we'll need to manually create it:

docker network create web

Lets get your basic auth user & password setup too:

htpasswd -nb admin secure_password
Note: htpasswd is not usually available by default in a fresh linux install

Use the hash output from that and replace USER:HASH in the `docker-compose.yml` file with the username you used, and the output hash.

All that's left to do for this part is fire that bad boy up:

docker-compose up -d
Run from within /opt/traefik

Assuming you had docker, docker-compose, etc installed and your dns is setup, you should now be able to access traefik at traefik.yourdomain.com, and if you hit the http version it should automagically redirect to https ... that global redirect to https section of our docker-compose config is pretty magical, it catches any http traffic to any host and redirects to https, automatically.

Mailcow

Follow all the steps for getting mailcow up and running from their documentation.

When editing the mailcow.conf file, be sure to set the following, since traefik will proxy the web connections, and handle cert generation:

HTTP_PORT=8080
HTTP_BIND=127.0.0.1
HTTPS_PORT=8443
HTTPS_BIND=127.0.0.1
SKIP_LETS_ENCRYPT=y

Before you start it though, add a new file, docker-compose.override.yml to the install directory /opt/mailcow_dockerized with the following content:

version: '2.1'

services:
  nginx-mailcow:
    expose:
      - 8080
    labels:
      - traefik.enable=true
      - traefik.http.routers.nginx-mailcow.rule=HostRegexp(`{host:(autodiscover|autoconfig|webmail|mail|email).+}`)
      - traefik.http.routers.nginx-mailcow.entrypoints=https
      - traefik.http.routers.nginx-mailcow.rule=Host(`${MAILCOW_HOSTNAME}`)
      - traefik.http.routers.nginx-mailcow.tls=true
      - traefik.http.routers.nginx-mailcow.tls.certresolver=default
      # Uncomment to use wildcard cert:
      # - traefik.http.routers.nginx-mailcow.tls.domains[0].main=example.com
      # - traefik.http.routers.nginx-mailcow.tls.domains[0].sans=*.example.com
      - traefik.http.routers.nginx-mailcow.service=nginx-mailcow
      - traefik.http.services.nginx-mailcow.loadbalancer.server.port=8080
      - traefik.docker.network=proxy
    networks:
      - proxy


  certdumper:
      image: humenius/traefik-certs-dumper
      network_mode: none
      command: --restart-containers mailcow_postfix-mailcow_1,mailcow_dovecot-mailcow_1,mailcow_nginx-mailcow_1
      volumes:
        - /opt/traefik/data:/traefik:ro
        - /var/run/docker.sock:/var/run/docker.sock:ro
        - ./data/assets/ssl:/output:rw
      environment:
        - DOMAIN=${MAILCOW_HOSTNAME}
        # If using wildcard certs instead of an explicit host cert,
        # use following line instead with just the TLD so certdumper
        # is able to find the cert.
        # - DOMAIN=YourDomain.com

networks:
  proxy:
    external: true

That should be it.  This will get the mailcow web UI running behind traefik using either the FQDN for its cert, or wildcards.. note; if it's the same domain as traefik and traefik already has wildcard certs, you must use the domain name option instead of ${MAILCOW_HOSTNAME} for the domain env setting for cert dumper or it won't be able to find the cert, and if the same domain, you can omit the wildcard cert labels on nginx because that wildcard cert would already be generated by traefik anyway...

Ok, fire that puppy up:

docker-compose up -d
Run from within /opt/mailcow_dockerized

That should get you up and running with mailcow proxied behind traefik. All the mail ports are open directly, but web traffic gets proxied.

If something isn't working, you probably missed a step here, omit a quote or something.. or you have other issues that are related to mailcow – likely dns config or firewall issues. That's all beyond the scope of this post.


Share Tweet Send
0 Comments