8.1 KiB
Email Setup for amzebs-01 (amz.at)
This host is configured to send emails via Laravel with DKIM signing.
Configuration Overview
- Postfix: Localhost-only SMTP server (no external access)
- Rspamd: DKIM signing with host-specific key
- Domain: amz.at
- DKIM Selector: amzebs-01
- Secret Management: DKIM private key stored in sops
Initial Setup (Before First Deployment)
1. Generate DKIM Key Pair
You need to generate a DKIM key pair locally first. You'll need rspamd package installed.
Option A: Using rspamd (if installed locally)
# Create a temporary directory
mkdir -p /tmp/dkim-gen
# Generate the key pair
rspamadm dkim_keygen -s amzebs-01 -d amz.at -k /tmp/dkim-gen/amz.at.amzebs-01.key
This will output:
- Private key saved to
/tmp/dkim-gen/amz.at.amzebs-01.key - Public key printed to stdout (starts with
v=DKIM1; k=rsa; p=...)
Option B: Using OpenSSL (alternative)
# Create temporary directory
mkdir -p /tmp/dkim-gen
# Generate private key (2048-bit RSA)
openssl genrsa -out /tmp/dkim-gen/amz.at.amzebs-01.key 2048
# Extract public key in the correct format for DNS
openssl rsa -in /tmp/dkim-gen/amz.at.amzebs-01.key -pubout -outform PEM | \
grep -v '^-----' | tr -d '\n' > /tmp/dkim-gen/public.txt
# Display the DNS record value
echo "v=DKIM1; k=rsa; p=$(cat /tmp/dkim-gen/public.txt)"
Save the public key output! You'll need it for DNS configuration later.
2. Add DKIM Private Key to Sops Secrets
Now you need to encrypt and add the private key to your secrets file.
Step 1: View the private key
cat /tmp/dkim-gen/amz.at.amzebs-01.key
Step 2: Edit the secrets file
cd /home/dominik/projects/cloonar/cloonar-nixos/hosts/amzebs-01
sops secrets.yaml
Step 3: Add the key to secrets.yaml
In the sops editor, add a new key called rspamd-dkim-key with the entire private key content including the BEGIN/END markers:
rspamd-dkim-key: |
-----BEGIN PRIVATE KEY-----
MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQC...
(paste the entire key content here)
...
-----END PRIVATE KEY-----
Important:
- Make sure to use the pipe
|character for multiline content - Keep the proper indentation (2 spaces before each line of the key)
- Include the full BEGIN/END markers
Step 4: Save and exit
Save the file in sops (it will be encrypted automatically).
Step 5: Clean up temporary files
rm -rf /tmp/dkim-gen
3. Verify Secret is Encrypted
Check that the secret is properly encrypted:
cat hosts/amzebs-01/secrets.yaml
You should see encrypted content, not the plain private key.
4. Extract Public Key for DNS (if needed later)
If you didn't save the public key earlier, you can extract it after deployment:
# On the server after deployment
sudo cat /var/lib/rspamd/dkim/amz.at.amzebs-01.key | \
openssl rsa -pubout -outform PEM 2>/dev/null | \
grep -v '^-----' | tr -d '\n'
Then format it as:
v=DKIM1; k=rsa; p=<output_from_above>
Deployment
1. Deploy Configuration
After adding the DKIM private key to sops, deploy the configuration:
# Build and switch on the remote host
nixos-rebuild switch --flake .#amzebs-01 --target-host amzebs-01 --use-remote-sudo
Or if deploying locally on the server:
sudo nixos-rebuild switch
2. Verify Deployment
Check that the services are running:
# Check rspamd-dkim-setup service
systemctl status rspamd-dkim-setup
# Check that rspamd is running
systemctl status rspamd
# Check that postfix is running
systemctl status postfix
# Verify DKIM key was deployed
ls -la /var/lib/rspamd/dkim/amz.at.amzebs-01.key
DNS Configuration
Add the following DNS records to your amz.at domain:
SPF Record
Type: TXT
Name: @
Value: v=spf1 mx a:amzebs-01.amz.at ~all
DKIM Record
Type: TXT
Name: amzebs-01._domainkey
Value: [Your public key from step 1 above]
The DKIM record will look something like:
v=DKIM1; k=rsa; p=MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA...
DMARC Record
Type: TXT
Name: _dmarc
Value: v=DMARC1; p=quarantine; rua=mailto:postmaster@amz.at; ruf=mailto:postmaster@amz.at; fo=1
Explanation:
p=quarantine: Failed messages should be quarantined (you can change top=rejectafter testing)rua=mailto:...: Aggregate reports sent to this addressruf=mailto:...: Forensic reports sent to this addressfo=1: Generate forensic reports for any failure
Laravel Configuration
Update your Laravel application's .env file:
Option A: Using sendmail (Recommended)
MAIL_MAILER=sendmail
MAIL_FROM_ADDRESS=noreply@amz.at
MAIL_FROM_NAME="${APP_NAME}"
Option B: Using SMTP
MAIL_MAILER=smtp
MAIL_HOST=127.0.0.1
MAIL_PORT=25
MAIL_USERNAME=null
MAIL_PASSWORD=null
MAIL_ENCRYPTION=null
MAIL_FROM_ADDRESS=noreply@amz.at
MAIL_FROM_NAME="${APP_NAME}"
Note: Laravel can use ANY email address with @amz.at domain. All will be DKIM signed automatically.
Testing Email
Test from Command Line
# Send a test email
echo "Test email body" | mail -s "Test Subject" test@example.com -aFrom:test@amz.at
Check Postfix Queue
# View mail queue
mailq
# View logs
journalctl -u postfix -f
Check Rspamd Logs
# View rspamd logs
journalctl -u rspamd -f
Test DKIM Signature
Send an email to a Gmail account or use an email testing service like:
They will show you:
- If DKIM signature is valid
- If SPF passes
- If DMARC passes
- Your spam score
Verification Commands
# Check if Postfix is running
systemctl status postfix
# Check if Rspamd is running
systemctl status rspamd
# Check if Postfix is listening on localhost only
ss -tlnp | grep master
# View DKIM public key again
systemctl start rspamd-show-dkim
journalctl -u rspamd-show-dkim
# Check if DKIM key exists
ls -la /var/lib/rspamd/dkim/
Security Notes
- Localhost-only: Postfix is configured to listen ONLY on 127.0.0.1
- No authentication: Not needed since only local processes can connect
- No firewall changes: No external ports opened for email
- DKIM signing: All outgoing emails are automatically signed with DKIM
- Host-specific key: Using selector "amzebs-01" allows multiple hosts to send for amz.at
Troubleshooting
Email not being sent
- Check Postfix status:
systemctl status postfix - Check queue:
mailq - Check logs:
journalctl -u postfix -n 100
DKIM not signing
- Check Rspamd status:
systemctl status rspamd - Check if key exists:
ls -la /var/lib/rspamd/dkim/amz.at.amzebs-01.key - Check Rspamd logs:
journalctl -u rspamd -n 100
Permission errors
# Ensure proper ownership
chown -R rspamd:rspamd /var/lib/rspamd/dkim/
chmod 600 /var/lib/rspamd/dkim/*.key
Rotate DKIM key
# 1. Generate new key pair locally (follow "Initial Setup" steps)
# 2. Update the rspamd-dkim-key in secrets.yaml with new key
# 3. Deploy the configuration
nixos-rebuild switch
# 4. Restart the setup service to copy new key
systemctl restart rspamd-dkim-setup
# 5. Restart rspamd to use new key
systemctl restart rspamd
# 6. Update DNS with new public key
# 7. Wait for DNS propagation before removing old DNS record
Related Files
- Postfix config:
hosts/amzebs-01/modules/postfix.nix - Rspamd config:
hosts/amzebs-01/modules/rspamd.nix - Main config:
hosts/amzebs-01/configuration.nix - Secrets file:
hosts/amzebs-01/secrets.yaml(encrypted)
Sops Secret Configuration
The DKIM private key is stored as a sops secret with the following configuration:
sops.secrets.rspamd-dkim-key = {
owner = "rspamd";
group = "rspamd";
mode = "0400";
};
This ensures:
- Only the rspamd user can read the key
- The key is decrypted at boot time by sops-nix
- The key is encrypted in version control
- The key persists across rebuilds
The key is automatically copied from the sops secret path to /var/lib/rspamd/dkim/amz.at.amzebs-01.key by the rspamd-dkim-setup.service on every boot.