axllent / mailpit

An email and SMTP testing tool with API for developers
https://mailpit.axllent.org
MIT License
5.71k stars 139 forks source link

Configure a Traefik proxy #286

Closed SimoSca closed 6 months ago

SimoSca commented 6 months ago

First of all, thanks for your work!

As requested in https://mailpit.axllent.org/docs/configuration/proxy/, I'll share my custom notes about usage of Traefik Proxy via docker containers.

I have a "global" traefik instance running over the machine, and single docker-compose.yml files with dns services exposed via Traefik, and that's why I'll use the network_mode: bridge.

Traefik tls

Before using traefik, you must have a traefik service running and configured with the smtp entrypoint, which simply could listen for example on port 25.

You can use traefik to manage the smtp traffic via tls on smtp entrypoint:

services:
  joomla:
    depends_on:
      - mailpit
    extra_hosts:
      - "my-project-dns-smtp.example.invalid:host-gateway"
  mailpit:
    image: axllent/mailpit:latest
    restart: unless-stopped
    labels:
      - "traefik.enable=true"
      # WEB GUI
      - "traefik.tcp.routers.my-project_gui.entrypoints=web-secure"
      - "traefik.http.routers.my-project_gui.rule=Host(`my-project-dns.example.invalid`)"
      - "traefik.http.routers.my-project_gui.service=svc_my-project_gui"
      - "traefik.http.services.svc_my-project_gui.loadbalancer.server.port=8025"
      # SMTP SERVER
      # tls true is needed to use HostSNI
      - "traefik.tcp.routers.my-project_smtp.tls=true"
      # You must specify HostSNI, if "*" or specific hostname, depends on your needs
      #- "traefik.tcp.routers.my-project_smtp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.my-project_smtp.rule=HostSNI(`my-project-dns-smtp.example.invalid`)"
      - "traefik.tcp.routers.my-project_smtp.entrypoints=smtp"
      - "traefik.tcp.routers.my-project_smtp.service=my-project_smtp"
      - "traefik.tcp.services.my-project_smtp.loadbalancer.server.port=1025"
    hostname: 'mailpit-host'
    network_mode: "bridge"

in this case, you MUST set the smtp port to 25 (that is the traefik entrypoint for smtp) and the hostname to my-project-dns-smtp.example.invalid plus the ssl/tls directive.

NOTE:

As another case, you can use traefik simply as passthrough, so the service manages the certificates:

services:
  joomla:
    depends_on:
      - mailpit
    extra_hosts:
      - "my-project-dns-smtp.example.invalid:host-gateway"
  mailpit:
    image: axllent/mailpit:latest
    restart: unless-stopped
    volumes:
      - ./wildcard.fullchain.pem:/etc/ssl/certs/wildcard.fullchain.pem
      - ./wildcard.privkey.pem:/etc/ssl/private/wildcard.privkey.pem
    environment:
      # If you use letsencrypt certificates, set the "fullchain" cert and related private key
      MP_SMTP_TLS_CERT: /etc/ssl/certs/wildcard.fullchain.pem
      MP_SMTP_TLS_KEY: /etc/ssl/private/wildcard.privkey.pem
      MP_SMTP_REQUIRE_TLS: true
      # MP_SMTP_REQUIRE_STARTTLS: true
    labels:
      - "traefik.enable=true"
      # WEB GUI
      - "traefik.tcp.routers.my-project_gui.entrypoints=web-secure"
      - "traefik.http.routers.my-project_gui.rule=Host(`my-project-dns.example.invalid`)"
      - "traefik.http.routers.my-project_gui.service=svc_my-project_gui"
      - "traefik.http.services.svc_my-project_gui.loadbalancer.server.port=8025"
      # SMTP SERVER
      # tls true is needed to use HostSNI
      - "traefik.tcp.routers.my-project_smtp.tls=true"
      #- "traefik.tcp.routers.my-project_smtp.rule=HostSNI(`*`)"
      - "traefik.tcp.routers.my-project_smtp.rule=HostSNI(`my-project-dns-smtp.example.invalid`)"
      - "traefik.tcp.routers.my-project_smtp.entrypoints=smtp" # opzionale, ma meglio specificarlo
      - "traefik.tcp.routers.my-project_smtp.service=my-project_smtp"
      - "traefik.tcp.routers.my-project_smtp.tls.passthrough=true"
      - "traefik.tcp.services.my-project_smtp.loadbalancer.server.port=1025"
    hostname: 'mailpit-host'
    network_mode: "bridge"

as you can see, with passthrough directive, we moved the effort to the "mailpit" service, so the service must manage the certificates.

STARTTLS

If you want to use starttls to secure your connection, you must exclude traefik, and contact directly the service:

services:
  joomla:
    depends_on:
      - mailpit
    extra_hosts:
      - "my-project-dns-smtp.example.invalid:host-gateway"
  mailpit:
    image: axllent/mailpit:latest
    restart: unless-stopped
    volumes:
      - ./wildcard.fullchain.pem:/etc/ssl/certs/wildcard.fullchain.pem
      - ./wildcard.privkey.pem:/etc/ssl/private/wildcard.privkey.pem
    environment:
      MP_SMTP_TLS_CERT: /etc/ssl/certs/wildcard.fullchain.pem
      MP_SMTP_TLS_KEY: /etc/ssl/private/wildcard.privkey.pem
      MP_SMTP_AUTH: "user1:pass1" # example of auth
      # MP_SMTP_REQUIRE_TLS: true
      MP_SMTP_REQUIRE_STARTTLS: true
    # It's important to expose the port:
    ports:
      - "1025:1025"
    labels:
      - "traefik.enable=true"
      # WEB GUI
      - "traefik.tcp.routers.jmy-project_gui.entrypoints=web-secure"
      - "traefik.http.routers.my-project_gui.rule=Host(`my-project-dns.example.invalid`)"
      - "traefik.http.routers.my-project_gui.service=svc_my-project_gui"
      - "traefik.http.services.svc_my-project_gui.loadbalancer.server.port=8025"
    hostname: 'mailpit-host'
    network_mode: "bridge"

In this case you must configure the client to work with starttls, point to port 1025 and hostname my-project-dns-smtp.example.invalid.

IMPORTANT

Alternatively to explicit mount, you could create a custom private image with the certificates inside, and with default environment variables MP_SMTP_TLS_CERT and MP_SMTP_TLS_KEY pointing to the certificates inside the image.

axllent commented 6 months ago

Thanks @SimoSca! Can you please confirm for me that the websocket connections works with this setup? The easy way to tell is whether the icon to the left of "Inbox" (in the side navigation) is an envelope, or a reload (circle with an arrow).

SimoSca commented 6 months ago

Hi @axllent , yes, I confirm that websocket works with this setup.

axllent commented 6 months ago

Awesome, thanks so much for the notes and confirmation. I have added this to the website so others can reference it :+1:

jortizbs commented 5 months ago

Hello @axllent and @SimoSca.

I have tried following @SimonSca's documentation, but I have not managed to make it work.

I have configured it using Traefik as TLS handler. So the Mailpit service does not handle certificates.

I have tried to test it in the following ways:

Without TLS /mailpit sendmail < email.txt -> works as the documentation states

With TLS /mailpit sendmail -S “my-domain-name”:“my-traefik-port” < email.txt -> shows error

error sending mail
FATA[2024/06/10 11:15:11] EOF

Using sendmail command directly sendmail -H 'openssl s_client -host “my-domain-name” -port “my-traefik-port -msg” < email.txt -> shows error

Connecting to 20.0.0.2
sendmail: SMTP init failed

Using openssl command directly openssl s_client -host “my-domain-name” -port “my-traefik-port -msg

In this case, it seems to connect and Traefik resolves the certificates

<<< TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 46
220 e764e14bf7b9 Mailpit ESMTP Service ready

The problem is that I can't close the message

mail from: <f@test.com>
>>> TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 30
<<< TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 26
250 2.1.0 Ok
rcpt to: <t@test.com>
>>> TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 2e
<<< TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 26
250 2.1.5 Ok
data
>>> TLS 1.2, RecordHeader [length 0005] 17 03 03 00 1d
    17 03 03 00 1d
<<< TLS 1.2, RecordHeader [length 0005] 17 03 03 00 1d
    17 03 03 00 4a
354 Begin mail entry; ends with <CR><LF>.<CR><LF>.
This is an email test.
>>> TLS 1.2, RecordHeader [length 0005].
    17 03 03 00 30
.
>>> TLS 1.2, RecordHeader [length 0005]
    17 03 03 00 1a

>>> TLS 1.2, RecordHeader [length 0005] 17 03 03 00 19 .
    17 03 03 00 19

Could you please tell me how to test it to verify that it works.

I don't know if I am doing something wrong.

Thank you very much.

jortizbs commented 5 months ago

Hi, I have seen what was the problem with openssl, I had to add the Input/Output -crlf option.

That way it allowed me to close the message to send it.