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
- 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
- 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
- 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
}
}
- Add DNSimple's ENVs to
~/.config/caddy/caddy.env:
DNSIMPLE_OAUTH_TOKEN='dnsimple_a_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
- Test the configuration
caddy run --config ~/.config/caddy/Caddyfile --envfile ~/.config/caddy/caddy.env
- 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
- 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
- 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
- Test the config
smtprelay --config ~/.config/smtprelay/smtprelay.ini
- 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}"
- 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
I just set this up for a client. This is the config we ended up going with.
Directory Structure
caddy(for automated TLS certificates via Let's Encrypt)smtprelayLet's Encrypt TLS Certs via caddy
go,xcaddy, and (optional)servicemanvia https://webinstall.devcaddywith DNS support (no webserver required). Example w/ DNSimple:~/.config/caddy/Caddyfile:~/.config/caddy/caddy.env:DNSIMPLE_OAUTH_TOKEN='dnsimple_a_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxcaddyas a service withserviceman(Linux, macOS, & Windows)--cap-net-bindis only necessary if using default ports 80 & 443)smtprelay config
smtprelay --config ~/.config/smtprelay/smtprelay.ini~/.config/smtprelay/smtprelay.ini:~/.config/smtprelay/allowed_users.txt:smtprelay --config ~/.config/smtprelay/smtprelay.iniserviceman: