[GH-ISSUE #149] doc: Full Usage Guide w/ Working Examples #35

Open
opened 2026-02-26 18:33:01 +03:00 by kerem · 5 comments
Owner

Originally created by @coolaj86 on GitHub (Jan 27, 2024).
Original GitHub issue: https://github.com/decke/smtprelay/issues/149

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:pass@smtp.mailgun.org: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,support@example.com
    
  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="rob-the-robot@example.com"
    my_to="alice@example.net"
    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
    
Originally created by @coolaj86 on GitHub (Jan 27, 2024). Original GitHub issue: https://github.com/decke/smtprelay/issues/149 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` ```text /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 ```sh caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env ``` 1. Install `go`, `xcaddy`, and (optional) `serviceman` via https://webinstall.dev ```sh 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: ```sh #!/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`: ```sh 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 } } ``` 5. Add DNSimple's ENVs to `~/.config/caddy/caddy.env`: ```sh DNSIMPLE_OAUTH_TOKEN='dnsimple_a_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx ``` 6. Test the configuration ```sh caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env ``` 5. Start `caddy` as a service with `serviceman` (Linux, macOS, & Windows) ```sh 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 ```sh smtprelay --config ~/.config/smtprelay/smtprelay.ini ``` 1. Create the config file `~/.config/smtprelay/smtprelay.ini`: ```sh mkdir -p ~/.config/smtprelay/ touch ~/.config/smtprelay/smtprelay.ini chmod 0600 ~/.config/smtprelay/smtprelay.ini ``` ```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:pass@smtp.mailgun.org:587 ``` 2. Set permissions via `~/.config/smtprelay/allowed_users.txt`: ```text # go run ./smtprelay/cmd/hasher.go 'my-password' # <username> <bcrypt-hash> <email,list> my-username $2a$10$uZntKVdnFmAZiswYLTl8auUUxeH4wOnAU5C4zz3rGWvMf2iOmhcDy @account.example.com,support@example.com ``` 3. Test the config ```sh smtprelay --config ~/.config/smtprelay/smtprelay.ini ``` 4. Test with Curl ```sh my_smtprelay_host=smtp.example.com my_smtprelay_auth='my-user:my-pass' my_from="rob-the-robot@example.com" my_to="alice@example.net" 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}" ``` ```sh # 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`: ```sh 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 ```
Author
Owner

@joshuataylor commented on GitHub (Jun 21, 2024):

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 youremail@example.com --from youremail@example.com --port 587 --tls

When testing user authentication:

swaks --to youremail@example.com --from youremail@example.com --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.

<!-- gh-comment-id:2183187613 --> @joshuataylor commented on GitHub (Jun 21, 2024): This is great, thanks! Also, if you want to test SMTP directly, [swaks](http://www.jetmore.org/john/code/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 youremail@example.com --from youremail@example.com --port 587 --tls ``` When testing user authentication: ``` swaks --to youremail@example.com --from youremail@example.com --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.
Author
Owner

@airbornelamb commented on GitHub (Oct 15, 2024):

@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.

<!-- gh-comment-id:2413821577 --> @airbornelamb commented on GitHub (Oct 15, 2024): @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.
Author
Owner

@joshuataylor commented on GitHub (Oct 15, 2024):

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.

<!-- gh-comment-id:2413831958 --> @joshuataylor commented on GitHub (Oct 15, 2024): 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.
Author
Owner

@airbornelamb commented on GitHub (Oct 15, 2024):

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

<!-- gh-comment-id:2413975208 --> @airbornelamb commented on GitHub (Oct 15, 2024): Thanks! Here's what I did if it helps other people: `sudo vi /etc/systemd/system/smtprelay.service` ```bash [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
Author
Owner

@schtritoff commented on GitHub (Feb 28, 2025):

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

<!-- gh-comment-id:2689609521 --> @schtritoff commented on GitHub (Feb 28, 2025): 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
Sign in to join this conversation.
No labels
bug
pull-request
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/smtprelay#35
No description provided.