decke / smtprelay

Simple Golang SMTP relay/proxy server
MIT License
452 stars 109 forks source link

doc: Full Usage Guide w/ Working Examples #149

Open coolaj86 opened 8 months ago

coolaj86 commented 8 months ago

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

Directory Structure

/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
joshuataylor commented 3 months ago

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.

airbornelamb commented 2 days ago

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

joshuataylor commented 2 days ago

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.

airbornelamb commented 2 days ago

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