smtprelay icon indicating copy to clipboard operation
smtprelay copied to clipboard

doc: Full Usage Guide w/ Working Examples

Open coolaj86 opened this issue 1 year ago • 5 comments

I just set this up for a client. This is the config we ended up going with.

  • Directory Structure
  • TLS Config via Let's Encrypt / Caddy
  • SMTP Relay Config

Directory Structure

  • caddy (for automated TLS certificates via Let's Encrypt)
  • smtprelay
/home/app/
├── .config/
│   ├── caddy/
│   │   ├── caddy.env
│   │   └── Caddyfile
│   └── smtprelay/
│       ├── allowed_users.txt
│       └── smtprelay.ini
└── .local/
    └── share/
        └── caddy/
            └── certificates/
                └── acme-v02.api.letsencrypt.org-directory/
                    └── smtp.example.com/
                        ├── smtp.example.com.crt
                        ├── smtp.example.com.json
                        └── smtp.example.com.key

Let's Encrypt TLS Certs via caddy

caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env
  1. Install go, xcaddy, and (optional) serviceman via https://webinstall.dev
    curl https://webi.sh/ | sh
    source ~/.config/envman/PATH.env
    
    webi go xcaddy serviceman
    source ~/.config/envman/PATH.env
    
  2. Build caddy with DNS support (no webserver required). Example w/ DNSimple:
    #!/bin/sh
    
    g_caddy_version='v2.7.6'
    CGO_ENABLED=0 xcaddy build "${g_caddy_version}" \
        --with github.com/caddy-dns/lego-deprecated \
        --output ~/bin/caddy-"${g_caddy_version}"-dnsimple
    
    ln -sf caddy-"${g_caddy_version}"-dnsimple ~/bin/caddy
    
  3. Configure caddy via ~/.config/caddy/Caddyfile:
    mkdir -p ~/.config/caddy
    touch ~/.config/caddy/caddy.env
    chmod 0600 ~/.config/caddy/caddy.env
    
    # note: to keep ports 80 and 443 free, use "https://smtp.example.com:1234" 
    # instead of just the hostname here
    smtp.example.com {
            tls {
                   dns lego_deprecated dnsimple
            }
    }
    
  4. Add DNSimple's ENVs to ~/.config/caddy/caddy.env:
    DNSIMPLE_OAUTH_TOKEN='dnsimple_a_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
    
  5. Test the configuration
    caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env
    
  6. Start caddy as a service with serviceman (Linux, macOS, & Windows)
    my_app_user="$(id -u -n)"
    sudo env PATH="${PATH}" \
        serviceman add --system --cap-net-bind \
            --username "${my_app_user}" --name caddy -- \
            caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env
    
    (--cap-net-bind is only necessary if using default ports 80 & 443)

smtprelay config

smtprelay --config ~/.config/smtprelay/smtprelay.ini
  1. Create the config file ~/.config/smtprelay/smtprelay.ini:
    mkdir -p ~/.config/smtprelay/
    touch ~/.config/smtprelay/smtprelay.ini
    chmod 0600 ~/.config/smtprelay/smtprelay.ini
    
    ; Hostname for this SMTP server
    hostname = smtp.example.com
    
    ; File which contains username and password used for
    ; authentication before they can send mail.
    allowed_users = /home/app/.config/smtprelay/allowed_users.txt
    
    ; Networks that are allowed to send mails to us
    ; Defaults to localhost. If set to "", then any address is allowed.
    ;allowed_nets = 0.0.0.0/0 ::/0
    allowed_nets = 0.0.0.0/0
    
    ; STARTTLS and TLS are also supported but need a
    ; SSL certificate and key.
    ;listen = starttls://0.0.0.0:587 starttls://[::]:587 tls://0.0.0.0:465 tls://[::]:465
    listen = starttls://0.0.0.0:587 tls://0.0.0.0:465
    local_cert = /home/app/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/smtp.example.com/smtp.example.com.crt
    local_key  = /home/app/.local/share/caddy/certificates/acme-v02.api.letsencrypt.org-directory/smtp.example.com/smtp.example.com.key
    
    ; Enforce encrypted connection on STARTTLS ports before
    ; accepting mails from client.
    local_forcetls = true
    
    ; Relay Config (ex: Mailgun)
    remotes = starttls://user:[email protected]:587
    
  2. Set permissions via ~/.config/smtprelay/allowed_users.txt:
    # go run ./smtprelay/cmd/hasher.go 'my-password'
    # <username> <bcrypt-hash> <email,list>
    my-username $2a$10$uZntKVdnFmAZiswYLTl8auUUxeH4wOnAU5C4zz3rGWvMf2iOmhcDy @account.example.com,[email protected]
    
  3. Test the config
    smtprelay --config ~/.config/smtprelay/smtprelay.ini
    
  4. Test with Curl
    my_smtprelay_host=smtp.example.com
    my_smtprelay_auth='my-user:my-pass'
    
    my_from="[email protected]"
    my_to="[email protected]"
    my_subject='Hello, World!'
    my_ts="$(date '+%F %H:%M:%S')"
    my_text="It's ${my_ts}. Do you know where your emails are?"
    
    my_file="$(mktemp)"
    printf 'From: %s\r\nTo: %s\r\nSubject: %s\r\n\r\n%s\r\n' \
            "${my_from}" \
            "${my_to}" \
            "${my_subject}" \
            "${my_text}" \
            > "${my_file}"
    
    # requires tls on 465
    curl -v --url "smtps://${my_smtprelay_host}" \
      --ssl-reqd \
      --user "${my_smtprelay_auth}" \
      --mail-from "${my_from}" \
      --mail-rcpt "${my_to}" \
      --upload-file "${my_file}"
    
  5. Start as a system service with serviceman:
    my_app_user="$( id -u -n )"
    sudo env PATH="${PATH}" \
     serviceman add --system --cap-net-bind \
         --username "${my_app_user}" --name smtprelay -- \
         smtprelay --config ~/.config/smtprelay/smtprelay.ini
    

coolaj86 avatar Jan 27 '24 02:01 coolaj86

This is great, thanks!

Also, if you want to test SMTP directly, swaks is a great tool for this, comes in most distro repositories I believe.

When using startls (port 587 etc), you need to add --tls:

swaks --to [email protected] --from [email protected] --port 587 --tls

When testing user authentication:

swaks --to [email protected] --from [email protected] --auth-user yourusername --auth-password yourpassword --port 587 --tls

When testing a specific IP/interface, add --server 127.0.0.1, where 127.0.0.1 is the IP.

joshuataylor avatar Jun 21 '24 17:06 joshuataylor

@coolaj86 How are you shipping logs? This is the first time i've used serviceman and journalctl -xef --unit smtprelay.service is showing great output but strangely /var/log/smtprelay is an empty folder.

airbornelamb avatar Oct 15 '24 12:10 airbornelamb

If it works with systemd you could just use StandardOutput/Standard error in your service file to log to file.

Depending what you mean by shipping logs but depending what you're trying to do it might be easier to hook into joirnalctl vs writing to file.

joshuataylor avatar Oct 15 '24 12:10 joshuataylor

Thanks! Here's what I did if it helps other people: sudo vi /etc/systemd/system/smtprelay.service

[Service]
StandardOutput=append:/var/log/smtprelay/service.log
StandardError=append:/var/log/smtprelay/service_error.log

touch /var/log/smtprelay/service.log touch /var/log/smtprelay/service_error.log sudo systemctl reload smtprelay.service

Then added /var/log/smtprelay/*.log as a folder to look for in my collector configuration and it started picking it up

airbornelamb avatar Oct 15 '24 13:10 airbornelamb

Didn't find documentation how to run in container (docker) so I created a repo containing some example workflow https://github.com/schtritoff/docker-smtprelay

schtritoff avatar Feb 28 '25 03:02 schtritoff