godaddy domain+dns, traefik + poste.io: own mailserver, ISP blocking port 25 + dynamic ip


Recommended Posts

First question you might have: "why?". Well, because I can and want to understand. Selfhosted ftw!

 

My ISP blocks port 25 and changes my ip every 6 months. Until a while ago, I could use their smtp servers to send mails for my domain, but they have become more restrictive lately. I thought to post my findings here to achieve a cheap running mailserver on unraid.
I'm not trying to sell any services (godaddy, dynu), I'm just explaining how I fixed things.
Total cost (besides unraid power cost) = 30$ / year, of which 20$ coz of ISP blocking that port.

 

So I have
 

  • A domain example.com at godaddy.com (an .xyz domain is 2$ per year). Don't buy any extra services. No certificates, nothing.
    Also, go to https://developer.godaddy.com/keys to generate an API key/secret pair

     
  • An updated unraid nas ;-) (I'm on 6.9.2 now)

     
  • A custom created docker network "traefik_secureproxy" (but imho, this isn't really needed, and you can use 'bridge')

     
  • A container that keeps my dynamic IP A-record up-to-date, using the godaddy API: https://github.com/TrueOsiris/docker-godaddypy
     
    docker run -d --name='godaddypy' --net='bridge' --cpset-cpus='7' \
    -e TZ='Europe/Paris' \
    -e 'HOST_OS'="Unraid" \
    -e 'GODADDY_KEY'='yourhashedkey' \
    -e 'GODADDY_SECRET'='yourhashedsecret' \
    -e 'DOMAINS'='example.com,mail.example.com' \
    -v '/mnt/user/docker/godaddypy':'/logdir'
    --restart=unless-stopped
    'trueosiris/godaddypy'


     

  • 2 paid services at dynu.com: SMTP Outbound Relay + Email Store/Forward (20$ per year total).
    MX and TXT records need to be manually added to godaddy's dns for the domain, once, as per dynu.com's instructions. (see screenshot below)

    image.png.e0b505dcf73f68c1be01203eaa84de20.png        image.png.9372b096fa9c44d3e430f0e8c08bc69d.png

    Generate the TXT fields on your domain on the dns management on the godaddy.com website, as per instructions on dynu.com for both services. Also, add the MX records of dynu.

    image.thumb.png.8e1b31cab8172f304b7c25b2de23ffc9.png
    image.thumb.png.c42ace793b1b4578c473eec63b209948.png

     
  • A Traefik container configured with godaddy & letsencrypt, with entrypoints, routers & services as configured below.
    The acme.json arrives in '/mnt/user/docker/traefik/resolvers/godaddy/' on the host.
    Btw, I altered the http port of unraid, so I could use 80 & 443 for traefik. Many posts on this forum on how to do this.

    I've setup traefik using only .yml files. No labels, no docker-compose, no .toml.
    I'm posting only fragments of my setup, anonymized.

    image.thumb.png.be5292b8792b2b977b0c16ef4f529292.png

    First, create the /mnt/user/docker/traefik/.variables.env file
    GODADDY_API_KEY=yourgodaddyapikey
    GODADDY_API_SECRET=yourgodaddyapisecret
    ACME_EMAIL=your.email@example.com

    The start the traefik container
    docker run -d --name='a_traefik' \
    --net='traefik_secureproxy' \
    -e TZ="Europe/Paris" \
    -e HOST_OS="Unraid" \
    -p '80:80/tcp' \
    -p '443:443/tcp' \
    -p '8083:8080/tcp' \
    -v '/var/run/docker.sock':'/var/run/docker.sock':'rw' \
    -v '/mnt/user/docker/traefik':'/etc/traefik':'rw' \
    --restart=unless-stopped \
    --env-file=/mnt/user/docker/traefik/.variables.env \
    'traefik:2.8.1'

    tree /mnt/user/docker/traefik/
    /mnt/user/docker/traefik/
    ├── dynamic
    │   └── providers
    │       ├── middlewares.yml
    │       ├── routers.yml
    │       └── services.yml
    ├── traefik.yml
    ├...

    traefik.yml
    ...
    
    entryPoints:
      web:
        address: ":80"
        http:
          redirections:
            entrypoint:
              to: websecure
              scheme: https
      websecure:
        address: ":443"
      traefik:
        address: ":8080"
      smtp:
        address: ":25"
      imapsecure:
        address: ":993"
    
    providers:
      docker:
        exposedByDefault: false
        httpClientTimeout: 300
        network: traefik_secureproxy
        endpoint: "unix:///var/run/docker.sock"
        watch: true
      file:
        directory: /etc/traefik/dynamic/providers
        watch: true
    
    certificatesResolvers:
      godaddy1:
        ### variables.env contains GODADDY_API_KEY and GODADDY_API_SECRET
        acme:
          email: "[email protected]"
          ### caServer production or staging
          #caServer: https://acme-staging-v02.api.letsencrypt.org/directory
          storage: /etc/traefik/resolvers/godaddy/acme.json
          certificatesDuration: 2160 # 2160 is default. equals 90 days.
          dnsChallenge:
            provider: godaddy
            delayBeforeCheck: 5
            resolvers:
              - "ns05.domaincontrol.com:53"
              - "8.8.8.8:53"
    
    ...


    services.yml

    tcp:
      services:
    
        ### T C P ###
    
        service_tcp_mail_smtp:
          loadBalancer:
            servers:
              - address: 10.10.0.6:25
    
        service_tcp_mail_imapsecure:
          loadBalancer:
            servers:
              - address: 10.10.0.6:993
    
    
        ### H T T P ###
    
    http:
      services:
        service_mailwebui:
          loadBalancer:
            servers:
              - url: http://10.10.0.6:8282


    routers.yml

    tcp:
      routers:
    
        tcp_router_mail_smtp:
          rule: "HostSNI(`mail.example.com`)"
          entrypoints:
            - smtp
          tls:
            certResolver: godaddy1
          service: "service_tcp_mail_smtp"
          priority: 90
    
        tcp_router_mail_imapsecure:
          rule: "HostSNI(`mail.example.com`)"
          entrypoints:
            - imapsecure
          tls:
            certResolver: godaddy1
          service: "service_tcp_mail_imapsecure"
          priority: 87
    
    
    http:
      routers:
    
        router_mailwebui:
          rule: "Host(`mail.example.com`) || (Host(`example.com`) && Path(`/.well-known`)) || Host(`webmail.example.com`)"
          entrypoints:
            - web
            - websecure
          tls:
            certResolver: godaddy1
          service: "service_mailwebui"
          priority: 92


     

  • An acme certificates container, which watches traefik's acme.json for changes and turns them into .pem files.
    On certificate change, the poste container will be restarted.
     
    docker run -d --name='acme_certificates' --net='bridge' -e TZ="Europe/Paris" -e HOST_OS="Unraid" \
    -e 'COMBINE_PKCS12'='yes' \
    -e 'CONVERT_KEYS_TO_RSA'='yes' \
    -e 'COMBINED_PEM'='combined.pem' \
    -e 'DOMAIN'='mail.example.com,example.com' \
    -v '/mnt/user/docker/traefik/resolvers/godaddy/':'/traefik':'ro' \
    -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates':'/output':'rw' \
    -v '/var/run/docker.sock':'/var/run/docker.sock':'ro' \
    'humenius/traefik-certs-dumper:latest' --restart-containers poste


    The files arrive in '/mnt/user/docker/traefik/resolvers/godaddy/certificates' as combined.pem in subfolder mail.example.com the rest of the certificates.

     

  • The poste.io container ... but ... with some steps. You want to put roundcube in an external volume.

    • mount a temporary volume
      -v /mnt/user/docker/poste/www:/tmp/www
      So the docker command becomes this
       

      docker run -d --name='poste' --net='bridge' -e TZ="Europe/Paris" -e HOST_OS="Unraid" \
      -e 'HTTPS'='OFF' \ 
      -e 'HTTP_PORT'='8282' \
      -e 'VIRTUAL_HOST'='mail.example.com,imap.example.com,smtp.example.com' \
      -e 'HOSTNAME'='mail.example.com' \
      -p '8282:8282/tcp' \
      -p '25:25/tcp' \
      -p '4190:4190/tcp' \
      -p '993:993/tcp' \
      -v '/mnt/user/docker/poste/data':'/data':'rw' \
      -v '/mnt/user/docker/poste/www':'/tmp/www':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/cert.pem':'/data/ssl/letsencrypt/cert.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/key.pem':'/data/ssl/letsencrypt/key.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/combined.pem':'/data/ssl/letsencrypt/fullchain.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/key.pem':'/etc/ssl/server.key':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/cert.pem':'/etc/ssl/server.crt':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/combined.pem':'/etc/ssl/server-combined.crt':'rw' \
      -h "mail.example.com" \
      'analogic/poste.io'

       

    • then connect to the container

      docker exec -it poste /bin/bash

      and copy /opt/www

      cp -r /opt/www/* /tmp/www/

       

    • The restart the container with the /tmp/www changed to /opt/www
      docker run -d --name='poste' --net='bridge' -e TZ="Europe/Paris" -e HOST_OS="Unraid" \
      -e 'HTTPS'='OFF' \ 
      -e 'HTTP_PORT'='8282' \
      -e 'VIRTUAL_HOST'='mail.example.com,imap.example.com,smtp.example.com' \
      -e 'HOSTNAME'='mail.example.com' \
      -p '8282:8282/tcp' \
      -p '25:25/tcp' \
      -p '4190:4190/tcp' \
      -p '993:993/tcp' \
      -v '/mnt/user/docker/poste/data':'/data':'rw' \
      -v '/mnt/user/docker/poste/www':'/opt/www':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/cert.pem':'/data/ssl/letsencrypt/cert.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/key.pem':'/data/ssl/letsencrypt/key.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/combined.pem':'/data/ssl/letsencrypt/fullchain.pem':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/key.pem':'/etc/ssl/server.key':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/cert.pem':'/etc/ssl/server.crt':'rw' \
      -v '/mnt/user/docker/traefik/resolvers/godaddy/certificates/mail.example.com/combined.pem':'/etc/ssl/server-combined.crt':'rw' \
      -h "mail.example.com" \
      'analogic/poste.io'

       

    • create folder
      mkdir -/mnt/user/docker/poste/data/_override/etc/dovecot/conf.d/

      and 2 files within this folder: 10-ssl.conf and 90-sieve.conf

      10-ssl.conf

      ssl = required
      ssl = yes
      ssl_cert=</data/ssl/letsencrypt/cert.pem
      ssl_key=</data/ssl/letsencrypt/key.pem
      #ssl_dh=</etc/ssl/dh4096.pem
      #ssl_dh=</data/ssl/letsencrypt/fullchain.pem
      
      ssl_min_protocol = TLSv1.2
      ssl_cipher_list = ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384:DHE-RSA-CHACHA20-POLY1305:ECDHE-ECDSA-AES128-SHA256:ECDHE-RSA-AES128-SHA256:ECDHE-ECDSA-AES128-SHA:ECDHE-RSA-AES128-SHA:ECDHE-ECDSA-AES256-SHA384:ECDHE-RSA-AES256-SHA384:ECDHE-ECDSA-AES256-SHA:ECDHE-RSA-AES256-SHA:DHE-RSA-AES128-SHA256:DHE-RSA-AES256-SHA256:AES128-GCM-SHA256:AES256-GCM-SHA384:AES128-SHA256:AES256-SHA256:AES128-SHA:AES256-SHA:DES-CBC3-SHA:ECDHE-ECDSA-AES128-GCM-SHA256:ECDHE-RSA-AES128-GCM-SHA256:ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-RSA-AES256-GCM-SHA384:ECDHE-ECDSA-CHACHA20-POLY1305:ECDHE-RSA-CHACHA20-POLY1305:DHE-RSA-AES128-GCM-SHA256:DHE-RSA-AES256-GCM-SHA384
      
      #ssl_prefer_server_ciphers = yes
      
      # debug
      auth_verbose=yes
      auth_debug=yes
      #auth_debug_passwords=yes
      mail_debug=yes
      verbose_ssl=yes
      #auth_verbose_passwords=plain


      90-sieve.conf

      plugin {
        sieve = file:~/sieve;active=~/.dovecot.sieve
        #sieve_default = /var/lib/dovecot/sieve/default.sieve
        sieve_default = /data/custom-sieve/default.sieve
        #sieve_default_name =
        #sieve_global =
        #sieve_discard =
        sieve_before = /data/custom-sieve/sieve.d/
        #sieve_before2 = ldap:/etc/sieve-ldap.conf;name=ldap-domain
        #sieve_before3 = (etc...)
        #sieve_after =
        #sieve_after2 =
        #sieve_after2 = (etc...)
        sieve_extensions = +editheader
        #sieve_global_extensions =
        #sieve_plugins =
        #recipient_delimiter = +
        #sieve_max_script_size = 1M
      sieve_max_actions = 55
      sieve_max_redirects = 60
        #sieve_quota_max_scripts = 0
        #sieve_quota_max_storage = 0
        #sieve_user_email =
        #sieve_user_log =
        #sieve_redirect_envelope_from = sender
        #sieve_trace_dir =
        #sieve_trace_level =
        #sieve_trace_debug = no
        #sieve_trace_addresses = no

       

    • create folder
      /mnt/user/docker/poste/data/custom-sieve/

       

    • edit these files to match the outgoing smtp

      /mnt/user/docker/poste/www/webmail/config/config.inc.php

      //$config['smtp_user'] = '%u';
      $config['smtp_user'] = '[email protected]'; // this is your smtp-relay username from dynu
      //$config['smtp_pass'] = '%p';
      $config['smtp_pass'] = 'L3prichaun!'; // this is your smtp-relay password from dynu
      //$config['smtp_server'] = 'tls://127.0.0.1:587';
      $config['smtp_server'] = 'tls://relay.dynu.com:587';


      /mnt/user/docker/poste/www/webmail/config/defaults.inc.php

      $config['smtp_server'] = 'tls://relay.dynu.com';
      // SMTP port. Use 25 for cleartext, 465 for Implicit TLS, or 587 for STARTTLS (default)
      //$config['smtp_port'] = 587;
      $config['smtp_port'] = 2525;
      // SMTP username (if required) if you use %u as the username Roundcube
      // will use the current username for login
      //$config['smtp_user'] = '%u';
      $config['smtp_user'] = '[email protected]';
      // SMTP password (if required) if you use %p as the password Roundcube
      // will use the current user's password for login
      //$config['smtp_pass'] = '%p';
      $config['smtp_pass'] = 'L3prichaun!';
      $config['smtp_auth_type'] = 'PLAIN';

       

    • restart the poste container
       

  • On your firewall, forward the following TCP ports to your docker host:

    • external tcp 2525 to internal 25 on dockerhost-ip (smtp)

    • external tcp 993 to internal 993 on dockerhost-ip (imaps)

    • external tcp 4190 to internal 4190 on dockerhost-ip (sieve)

 

Conclusion:

  • You can now use webmail.example.com to use roundcube.
  • you can use a mailclient, like thunderbird, or your mail-app on your phone with these settings:
    server-in:  mail.example.com, username: [email protected], port 993, SSL/TLS, normal password
    server-out: relay.dynu.com, username: [email protected], port 2525 (or 587), starttls, normal password
    PS: In thunderbird, you need to manage the passwords for each of these servers in 'settings' (and not account settings).

 

The reason I hardcode the outgoing server in roundcube is this bug: https://bitbucket.org/analogic/mailserver/issues/961/external-relay-authentication-bug

Edited by Osiris
layout fixes
Link to comment
  • Osiris changed the title to godaddy domain+dns, traefik + poste.io: own mailserver, ISP blocking port 25 + dynamic ip

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.
Note: Your post will require moderator approval before it will be visible.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.