I am definitely missing something in the way Traefik routes ports.
I can get it to work fine when there's only one port of interest.
PS - I couldn’t figure out how to break “links”, so I put a space between the slashes.
mail.env (relavent parts)
# Subnet of the docker network. This should not conflict with any networks to which your system is connected. (Internal and external!)
SUBNET=192.168.99.0/24
#Main mail domain
DOMAIN=MYSITE.us
# Hostnames for this server, separated with commas
HOSTNAMES=mail.MYSITE.us
# Choose how secure connections will behave (value: letsencrypt, cert, notls, mail, mail-letsencrypt)
TLS_FLAVOR=cert
# existing certs should be copied to /certs
#cert.pem
TLS_CERT_FILENAME=local.crt
#key.pem
TLS_KEYPAIR_FILENAME=local.key
RELAYNETS=192.168.86.0/24,192.168.99.0/24
REAL_IP_FROM=192.168.86.0/24,192.168.99.0/24
PROXY_AUTH_WHITELIST=192.168.86.0/24,192.168.99.0/24
# is this possibly causing an issue? I moved the compose logic to my own monolithic compose file and prepended "mail-" to the containers.
# Docker-compose project name, this will prepended to containers names.
COMPOSE_PROJECT_NAME=mailu
# traefik specific
PROXY_PROTOCOL=25,465,993,995,4190
TRAEFIK_VERSION=v2
# if AVX512 is available
LD_PRELOAD=/usr/lib/libhardened_malloc.so
###end mail.env
The 192.168.86.0/24 is my network.
And 192.168.99.0/24 is the docker network.
traefik.yaml (docker compose, trimmed down)
networks:
proxy:
name: proxy
driver: bridge
ipam:
config:
- subnet: 192.168.99.0/24
webmail:
driver: bridge
clamav:
driver: bridge
oletools:
driver: bridge
internal: true
services:
## traefik proxy
# https://doc.traefik.io/traefik/setup/docker/
# htpasswd -nb admin "P@ssw0rd" | sed -e 's/\$/\$\$/g'
# paste that to the "dashboard-auth.basicauth" line below
# default admin : P@ssw0rd
traefik:
image: traefik:v3.4
container_name: traefik
restart: unless-stopped
security_opt:
- no-new-privileges:true
networks:
# Connect to the 'traefik_proxy' overlay network for inter-container communication across nodes
proxy:
ipv4_address: 192.168.99.254
webmail:
ports:
- "80:80" # http web
- "443:443" # https web
- "8080:8080" # management
- "25:25" # mail smtp
- "465:465" # mail submissions
- "587:587" # mail smtp tls
- "993:993" # mail imap
- "995:995" # mail pop3
- "4190:4190" # mail sieve
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- $DOCKERDIR/appdata/traefik/certs:/certs:ro
- $DOCKERDIR/appdata/traefik/dynamic:/dynamic:ro
- $DOCKERDIR/appdata/traefik/logs:/logs
- $DOCKERDIR/appdata/traefik/letsencrypt:/letsencrypt
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
command:
# Access Log
- "--accesslog=true"
# Optionally change format or output file (requires volume)
#- "--accesslog.format=json" # default common (common (Traefik extended CLF), genericCLF (standard CLF compatible with analyzers), or json)
- "--accesslog.filepath=/logs/access.log"
# Optionally filter logs
#- "--accesslog.filters.statuscodes=400-599"
# API & Dashboard
- "--api.dashboard=true"
- "--api.insecure=false"
# Certificates - Let's Encrypt configuration
- "--certificatesresolvers.le.acme.email=MYSITE@gmail.com" # replace with your actual email
- "--certificatesresolvers.le.acme.storage=/letsencrypt/acme.json"
##- "--certificatesresolvers.le.acme.httpchallenge.entrypoint=web"
- "--certificatesresolvers.le.acme.tlschallenge=true"
# EntryPoints HTTP
- "--entrypoints.web.address=:80"
- "--entrypoints.web.http.redirections.entrypoint.to=websecure"
- "--entrypoints.web.http.redirections.entrypoint.scheme=https"
- "--entrypoints.web.http.redirections.entrypoint.permanent=true"
# EntryPoints HTTPS
- "--entrypoints.websecure.address=:443"
- "--entrypoints.websecure.http.tls=true"
#EntryPoints MAIL
- "--entrypoints.smtp.address=:smtp" # 25
- "--entrypoints.submissions.address=:submission" # 465
- "--entrypoints.imaps.address=:imaps" # 993
- "--entrypoints.pop3s.address=:pop3s" # 995
- "--entrypoints.sieve.address=:sieve" # 4190
# Plugins
- "--experimental.plugins.traefik-get-real-ip.modulename=github.com/Paxxs/traefik-get-real-ip"
- "--experimental.plugins.traefik-get-real-ip.version=v1.0.3"
# Log
- "--log.filePath=/logs/log-file.log"
#- "--log.format=json" # default common
- "--log.level=INFO" # default ERROR (TRACE, DEBUG, INFO, WARN, ERROR, FATAL, and PANIC)
- "--log.maxBackups=3" # default 0/infinite
#- "--log.maxSize=100MB" # default 100MB
# Providers
- "--providers.docker=true"
- "--providers.docker.exposedbydefault=false"
- "--providers.docker.network=proxy"
# Attach the static configuration tls.yaml file that contains the tls configuration settings
#- "--providers.file.filename=/dynamic/tls.yaml"
- "--providers.file.directory=/dynamic/"
# Metrics
- "--metrics.prometheus=true"
# If using a dedicated metrics entry point, define it:
- "--entrypoints.metrics.address=:8082"
# ... other command arguments ...
- "--metrics.prometheus=true"
# Optionally change the entry point metrics are exposed on (defaults to 'traefik')
- "--metrics.prometheus.entrypoint=metrics"
# Add labels to metrics for routers/services (can increase cardinality)
- "--metrics.prometheus.addrouterslabels=true"
- "--metrics.prometheus.addserviceslabels=true"
# Traefik Dynamic configuration via Docker labels
labels:
# Enable self-routing
- "traefik.enable=true"
# Dashboard router
#- "traefik.http.routers.dashboard.rule=Host(`dashboard.docker.localhost`)"
- "traefik.http.routers.dashboard.rule=Host(`dashboard.MYSITE.us`)"
- "traefik.http.routers.dashboard.entrypoints=websecure"
- "traefik.http.routers.dashboard.service=api@internal"
- "traefik.http.routers.dashboard.tls=true"
# Basic-auth middleware
- "traefik.http.middlewares.dashboard-auth.basicauth.users=admin:SANITIZED."
- "traefik.http.routers.dashboard.middlewares=dashboard-auth@docker"
- "traefik.http.routers.dashboard.tls=true"
# GoAccess Site Visitor Logs (reporting) Included to show a web front-end that IS working
goaccess:
### default admin / admin ??
image: xavierh/goaccess-for-nginxproxymanager:latest
container_name: goaccess
networks:
proxy:
ipv4_address: 192.168.99.189
ports:
- "7880:7880"
volumes:
- $DOCKERDIR/appdata/traefic/logs:/opt/log
- $DOCKERDIR/appdata/goaccess/GeoLite2/GeoLite2-City.mmdb:/GeoLite2-City.mmdb
# - /path/to/host/custom:/opt/custom #optional, required if using log_type = CUSTOM
- "/etc/localtime:/etc/localtime:ro"
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
SKIP_ARCHIVED_LOGS: "True"
DEBUG: "False" #optional
BASIC_AUTH: "False" #optional
BASIC_AUTH_USERNAME: "$DOZZLE_USERNAME" #optional
BASIC_AUTH_PASSWORD: "$DOZZLE_PASSWORD" #optional
EXCLUDE_IPS: "127.0.0.1" #optional - comma delimited
LOG_TYPE: "TRAEFIKCLF" #optional - https://goaccess.io/man
security_opt:
- no-new-privileges:true
restart: unless-stopped
labels:
- "traefik.enable=true"
- "traefik.http.routers.goaccess.rule=Host(`goaccess.MYSITE.us`)"
- "traefik.http.routers.goaccess.entrypoints=websecure"
- "traefik.http.routers.goaccess.tls=true"
- "traefik.http.routers.goaccess.tls.certresolver=le"
# mail-front
# I know that mailu has a bunch of containers, the only one exposed is mail-front, so the rest are trimmed out.
mail-front:
# start from https://setup.mailu.io/2024.06/ to get yaml file started
image: ${DOCKER_ORG:-ghcr.io/mailu}/${DOCKER_PREFIX:-}nginx:${MAILU_VERSION:-2024.06}
container_name: mail_front
restart: always
env_file: $DOCKERDIR/appdata/mailu/mailu.env
logging:
driver: journald
options:
tag: mailu-front
ports:
#- "11080:80" # http
- "11443:443" # https
#- "110:110" #pop3 (old)
#- "143:143" # imap (old)
#- "25:25" # smtp
#- "465:465" # submission
#- "587:587" # smtp tls
#- "993:993" # imap
#- "995:995" # pop3
#- "4190:4190" # sieve (manage sieve, mail filtering rules)
networks:
proxy:
ipv4_address: 192.168.99.233
webmail:
#networks:
# - proxy
# - webmail
volumes:
- "$DOCKERDIR/appdata/traefik/certs:/certs"
- "$DOCKERDIR/appdata/mailu/overrides/nginx:/overrides:ro"
environment:
TZ: $TZ
PUID: $PUID
PGID: $PGID
ADMIN_ADDRESS: mail-admin
FRONT_ADDRESS: mail-front
SMTP_ADDRESS: mail-smtp
IMAP_ADDRESS: mail-imap
OLETOOLS_ADDRESS: mail-oletools
REDIS_ADDRESS: mail-redis
ANTIVIRUS_ADDRESS: mail-antivirus
ANTISPAM_ADDRESS: mail-antispam
WEBMAIL_ADDRESS: mail-webmail
depends_on:
- mail-resolver
dns:
- 192.168.99.230
labels:
- "traefik.enable=true"
#- "traefik.docker.network=proxy"
# the second part is important to ensure Mailu can get certificates from letsencrypt for all hostnames
- "traefik.http.routers.mailweb.rule=Host(`mail.MYSITE.us`) || PathPrefix(`/.well-known/acme-challenge/`)"
- "traefik.http.routers.mailweb.entrypoints=web"
- "traefik.http.routers.mailweb.service=mailweb"
- "traefik.http.services.mailweb.loadbalancer.server.port=11080"
## I tried isolating just the mail host to get the page to load from outside, but no luck
#- "traefik.http.routers.mailsecure.rule=Host(`mail.MYSITE.us`)"
#- "traefik.http.routers.mailsecure.entrypoints=websecure"
#- "traefik.http.routers.mailsecure.tls=true"
#- "traefik.http.routers.mailsecure.tls.certresolver=le"
#- "traefik.http.routers.mailsecure.service=mailsecure"
#- "traefik.http.services.mailsecure.loadbalancer.server.port=11443"
# other FQDNS can be added here:
- "traefik.tcp.routers.mailsecure.rule=HostSNI(`mail.MYSITE.us`) || HostSNI(`autoconfig.MYSITE.us`) || HostSNI(`mta-sts.MYSITE.us`)"
- "traefik.tcp.routers.mailsecure.entrypoints=websecure"
- "traefik.tcp.routers.mailsecure.tls=true"
- "traefik.tcp.routers.mailsecure.tls.certresolver=le"
#- "traefik.tcp.routers.mailsecure.tls.passthrough=true"
- "traefik.tcp.routers.mailsecure.service=mailsecure"
- "traefik.tcp.services.mailsecure.loadbalancer.server.port=11443"
- "traefik.tcp.services.mailsecure.loadbalancer.proxyProtocol.version=2"
# smtp 25
- "traefik.tcp.routers.smtp.rule=HostSNI(`*`)"
- "traefik.tcp.routers.smtp.entrypoints=smtp"
- "traefik.tcp.routers.smtp.service=smtp"
- "traefik.tcp.services.smtp.loadbalancer.servers.port=25"
- "traefik.tcp.services.smtp.loadbalancer.proxyProtocol.version=2"
# submissions 465
- "traefik.tcp.routers.submissions.rule=HostSNI(`*`)"
- "traefik.tcp.routers.submissions.entrypoints=submissions"
- "traefik.tcp.routers.submissions.service=submissions"
- "traefik.tcp.services.submissions.loadbalancer.server.port=465"
- "traefik.tcp.services.submissions.loadbalancer.proxyProtocol.version=2"
# imap 993
- "traefik.tcp.routers.imaps.rule=HostSNI(`*`)"
- "traefik.tcp.routers.imaps.entrypoints=imaps"
- "traefik.tcp.routers.imaps.service=imaps"
- "traefik.tcp.services.imaps.loadbalancer.server.port=993"
- "traefik.tcp.services.imaps.loadbalancer.proxyProtocol.version=2"
# pop3 995
- "traefik.tcp.routers.pop3s.rule=HostSNI(`*`)"
- "traefik.tcp.routers.pop3s.entrypoints=pop3s"
- "traefik.tcp.routers.pop3s.service=pop3s"
- "traefik.tcp.services.pop3s.loadbalancer.server.port=995"
- "traefik.tcp.services.pop3s.loadbalancer.proxyProtocol.version=2"
# sieve 4190
- "traefik.tcp.routers.sieve.rule=HostSNI(`*`)"
- "traefik.tcp.routers.sieve.entrypoints=sieve"
- "traefik.tcp.routers.sieve.service=sieve"
- "traefik.tcp.services.sieve.loadbalancer.server.port=4190"
#- "traefik.tcp.services.sieve.loadbalancer.servers.address=:4190"
- "traefik.tcp.services.sieve.loadbalancer.proxyProtocol.version=2"
healthcheck:
test: ['NONE']
end traefik.yaml
Port 25 is forwarding to mail_front, and it doesn't show up when mail_front is stopped
username@comp:/data5/media/Docker$ docker ps | grep 25/
bb13adf23017 Package nginx · GitHub "/bin/sh -c /start.py" 2 hours ago Up 4 seconds 80/tcp, 110/tcp, 0.0.0.0:25->25/tcp, [::]:25->25/tcp, 0.0.0.0:465->465/tcp, [::]:465->465/tcp, 0.0.0.0:587->587/tcp, [::]:587->587/tcp, 0.0.0.0:993->993/tcp, [::]:993->993/tcp, 0.0.0.0:995->995/tcp, [::]:995->995/tcp, 0.0.0.0:4190->4190/tcp, [::]:4190->4190/tcp, 143/tcp, 0.0.0.0:11443->443/tcp, [::]:11443->443/tcp mail_front
But if I try to talk to port 25 it times out, off to the void.
I can log into webmail visiting https:/ /MYHOST:11443/sso/login. It works fine from the inside.
When I try to visit https:/ /mail.MYSITE.us/webmail I get a "404 page not found", because Traefik it getting the request but not determining where to hand it off to.
The traefik access log shows that it isn't routing.
ANIPADDRESS - - [DATETIME] "GET / HTTP/2.0" 200 760904 "-" "-" 6161 "OTHERSERVICE@docker" "http://192.168.99.216:1234" 0ms
ANIPADDRESS - - [DATETIME] "GET /sso/login HTTP/2.0" 404 19 "-" "-" 6162 "-" "-" 0ms
ANIPADDRESS - - [DATETIME] "GET /webmail HTTP/2.0" 404 19 "-" "-" 2819 "-" "-" 0ms
The "-" (3rd from the last field) should be the service it routed to, like "mail_front@docker" (or maybe mailsercure@docker) to show that it passed the request there.
If I look at the traefik dashboard HTTP Routers I see 2 entries for mail-front.
Host(mail-front-docker) metrics web mailsecure@docker mail-front-docker
Host(mail-front-docker) websecure websecure-mailsecure@docker mail-front-docker
I just figured out that the metrics and web entry is coming from the basic traefik configuration section. Though I’m not sure why those are linked to mailsecure (possibly because HTTP redirects to HTTPS and that’s the service listening on that port).
If I try to hit http:/ /MYHOST:11080/sso/login it does not load. (I’m not concerned about the HTTP port though)
I also noticed that the dashboard lists the Name as websecure-mailsecure@docker, so I tried changing the router and service name to match.
## other FQDNS can be added here:
- "traefik.tcp.routers.websecure-mailsecure.rule=HostSNI(`mail.MYSITE.us`) || HostSNI(`autoconfig.MYSITE.us`) || HostSNI(`mta-sts.MYSITE.us`)"
- "traefik.tcp.routers.websecure-mailsecure.entrypoints=websecure"
#- "traefik.tcp.routers.websecure-mailsecure.tls=true"
#- "traefik.tcp.routers.websecure-mailsecure.tls.certresolver=le"
- "traefik.tcp.routers.websecure-mailsecure.tls.passthrough=true"
- "traefik.tcp.routers.websecure-mailsecure.service=mailsecure"
- "traefik.tcp.services.websecure-mailsecure.loadbalancer.server.port=11443"
- "traefik.tcp.services.websecure-mailsecure.loadbalancer.proxyProtocol.version=2"
Again, no luck.
If I look at HTTP Services it shows:
mail-front-docker@docker at http:/ /192.168.99.233:25
And it says used by routers (both of the above, metrics/web and websecure).
I don’t know why that is trying to link to the SMTP port rather than the WEBSECURE 443.
I'm getting a bunch of these in the traefik log. It looks like it's trying to get a certificate for each subdomain rather than doing a *.MYSITE.us certificate.
DATETIME ERR Unable to obtain ACME certificate for domains error="unable to generate a certificate for the domains [trilium.MYSITE.us]: acme: error: 429 :: POST :: ``https://acme-v02.api.letsencrypt.org/acme/new-order`` :: urn:ietf:params:acme:error:rateLimited :: too many failed authorizations (5) for "``trilium.MYSITE.us``" in the last 1h0m0s, retry after
DATETIME UTC: see ``https://letsencrypt.org/docs/rate-limits/#authorization-failures-per-hostname-per-account``" ACME CA=``https://acme-v02.api.letsencrypt.org/directory`` acmeCA=``https://acme-v02.api.letsencrypt.org/directory`` domains=["``trilium.MYSITE.us``"] providerName=le.acme routerName=trilium@docker rule=Host(trilium.MYSITE.us)
The acme.json has certs for 2 subdomains, neither of those is mail.MYSITE.us.
I only noticed the first one the other day, it may be slowly adding sites.
So I know that something's not right, and that there some mental disconnect between how it works and how I want it to work, but I'm not quite sure where the outage lies.
Edits: Removed some things from the traefik.yaml that I had added while messing around, they made things mad.