c6f37bd9d2
Automatic Documentation Deployment / Sync Docs to https://kb.bunny-lab.io (push) Successful in 7s
838 lines
15 KiB
Markdown
838 lines
15 KiB
Markdown
---
|
|
|
|
tags:
|
|
|
|
* Proxmox Mail Gateway
|
|
* PMG
|
|
* Mailcow
|
|
* Email
|
|
* SMTP
|
|
* Gateway
|
|
* Spam Filtering
|
|
|
|
---
|
|
|
|
# Proxmox Mail Gateway Inbound Filtering for Mailcow
|
|
|
|
## Purpose
|
|
|
|
This document defines the procedure for placing `Proxmox Mail Gateway` in front of an existing `Mailcow` server for inbound SMTP filtering.
|
|
|
|
PMG will handle inbound SMTP inspection before delivering accepted mail to Mailcow.
|
|
|
|
This document covers **inbound SMTP filtering only**.
|
|
|
|
It does not move:
|
|
|
|
* Outbound SMTP delivery
|
|
* DKIM signing
|
|
* SMTP submission
|
|
* IMAP
|
|
* POP3
|
|
* ManageSieve
|
|
* Mailcow certificates
|
|
* Mailcow web access
|
|
* Roundcube access
|
|
|
|
## Assumptions
|
|
|
|
Mailcow is already deployed and functional.
|
|
|
|
Mailcow already handles:
|
|
|
|
* Mailbox hosting
|
|
* User authentication
|
|
* Webmail
|
|
* Mailcow admin interface
|
|
* IMAP
|
|
* POP3
|
|
* SMTP submission
|
|
* Outbound delivery
|
|
* DKIM signing
|
|
* TLS certificates for `mail.bunny-lab.io`
|
|
|
|
Example environment:
|
|
|
|
```text
|
|
Proxmox Mail Gateway: 192.168.3.15
|
|
Mailcow Server: 192.168.3.61
|
|
Mail Hostname: mail.bunny-lab.io
|
|
Mail Domain: bunny-lab.io
|
|
Firewall: pfSense
|
|
Reverse Proxy: Traefik
|
|
```
|
|
|
|
!!! warning "Inbound SMTP Only"
|
|
Only move public inbound SMTP port `25` to PMG during this stage.
|
|
|
|
```
|
|
Do not move mail client ports, outbound relay behavior, DKIM signing, or Mailcow web access.
|
|
```
|
|
|
|
## Architecture
|
|
|
|
### Existing Mail Flow
|
|
|
|
```text
|
|
Internet
|
|
|
|
|
v
|
|
pfSense WAN :25
|
|
|
|
|
v
|
|
Mailcow 192.168.3.61:25
|
|
```
|
|
|
|
### Target Mail Flow
|
|
|
|
```text
|
|
Internet
|
|
|
|
|
v
|
|
pfSense WAN :25
|
|
|
|
|
v
|
|
PMG 192.168.3.15:25
|
|
|
|
|
v
|
|
Mailcow 192.168.3.61:25
|
|
```
|
|
|
|
### Final Service Ownership
|
|
|
|
```text
|
|
PMG
|
|
- Inbound SMTP on port 25
|
|
- Spam filtering
|
|
- Virus filtering
|
|
- Tracking Center
|
|
- Quarantine
|
|
- Delivery of accepted inbound mail to Mailcow
|
|
|
|
Mailcow
|
|
- Mailbox hosting
|
|
- User authentication
|
|
- Webmail
|
|
- Mailcow admin interface
|
|
- IMAP
|
|
- POP3
|
|
- SMTP submission
|
|
- Outbound mail delivery
|
|
- DKIM signing
|
|
- TLS certificates for mail.bunny-lab.io
|
|
|
|
Traefik
|
|
- Public HTTP
|
|
- Public HTTPS
|
|
- Mailcow / Roundcube frontend routing
|
|
```
|
|
|
|
## DNS
|
|
|
|
Public DNS remains unchanged.
|
|
|
|
```text
|
|
bunny-lab.io MX 10 mail.bunny-lab.io
|
|
mail.bunny-lab.io A <Public WAN IP>
|
|
```
|
|
|
|
The public DNS records continue pointing to the WAN IP.
|
|
|
|
The firewall determines where inbound SMTP is delivered internally.
|
|
|
|
```text
|
|
pfSense WAN :25 -> PMG 192.168.3.15:25
|
|
```
|
|
|
|
!!! note "DNS Does Not Point to PMG Directly"
|
|
The public MX and A records do not point to the internal PMG IP.
|
|
|
|
```
|
|
NAT controls the internal SMTP destination.
|
|
```
|
|
|
|
## Firewall and NAT Design
|
|
|
|
Only public inbound SMTP changes.
|
|
|
|
Change this:
|
|
|
|
```text
|
|
WAN :25 -> Mailcow 192.168.3.61:25
|
|
```
|
|
|
|
To this:
|
|
|
|
```text
|
|
WAN :25 -> PMG 192.168.3.15:25
|
|
```
|
|
|
|
Leave Mailcow client access ports pointed directly at Mailcow.
|
|
|
|
```text
|
|
WAN :465 -> Mailcow 192.168.3.61:465
|
|
WAN :587 -> Mailcow 192.168.3.61:587
|
|
WAN :993 -> Mailcow 192.168.3.61:993
|
|
WAN :995 -> Mailcow 192.168.3.61:995
|
|
WAN :110 -> Mailcow 192.168.3.61:110
|
|
WAN :143 -> Mailcow 192.168.3.61:143
|
|
WAN :4190 -> Mailcow 192.168.3.61:4190
|
|
```
|
|
|
|
Leave web traffic on the existing reverse proxy path.
|
|
|
|
```text
|
|
WAN :80 -> Traefik :80
|
|
WAN :443 -> Traefik :443
|
|
```
|
|
|
|
!!! warning "Do Not Move Mail Client Ports to PMG"
|
|
PMG is an SMTP gateway.
|
|
|
|
```
|
|
Do not forward IMAP, POP3, SMTPS, Submission, or ManageSieve ports to PMG.
|
|
```
|
|
|
|
## Initial PMG Access
|
|
|
|
Access the PMG management interface.
|
|
|
|
```text
|
|
https://192.168.3.15:8006
|
|
```
|
|
|
|
Use the `root` credentials configured during PMG installation.
|
|
|
|
!!! note "Certificate Warning"
|
|
Browser certificate warnings are expected when accessing PMG by IP address unless a trusted certificate has already been configured for the management interface.
|
|
|
|
## Pre-Cutover Connectivity Checks
|
|
|
|
Confirm PMG can reach Mailcow on SMTP port `25`.
|
|
|
|
Run from the PMG shell:
|
|
|
|
```sh
|
|
# Confirm PMG can reach Mailcow SMTP
|
|
nc -vz 192.168.3.61 25
|
|
```
|
|
|
|
Expected result:
|
|
|
|
```text
|
|
(UNKNOWN) [192.168.3.61] 25 (smtp) open
|
|
```
|
|
|
|
Reverse DNS warnings are not automatically failures.
|
|
|
|
```text
|
|
inverse host lookup failed: Unknown host
|
|
```
|
|
|
|
If port `25` still reports as open, SMTP connectivity is working.
|
|
|
|
Confirm Mailcow presents an SMTP banner.
|
|
|
|
```sh
|
|
# Connect from PMG directly to Mailcow SMTP
|
|
nc 192.168.3.61 25
|
|
```
|
|
|
|
Expected banner:
|
|
|
|
```text
|
|
220-mail.bunny-lab.io ESMTP Postcow
|
|
220 mail.bunny-lab.io ESMTP Postcow
|
|
```
|
|
|
|
Exit the SMTP session.
|
|
|
|
```text
|
|
quit
|
|
```
|
|
|
|
!!! note "Mailcow SMTP Banner"
|
|
Mailcow commonly identifies its SMTP service as `Postcow`.
|
|
|
|
```
|
|
That is expected.
|
|
```
|
|
|
|
## PMG Mail Proxy Ports
|
|
|
|
In PMG, navigate to:
|
|
|
|
```text
|
|
Configuration > Mail Proxy > Ports
|
|
```
|
|
|
|
Confirm:
|
|
|
|
```text
|
|
External SMTP Port: 25
|
|
```
|
|
|
|
No outbound filtering is configured during this stage.
|
|
|
|
!!! note "Internal SMTP Port"
|
|
PMG also has an internal SMTP port used for outbound filtering from an internal mail server.
|
|
|
|
```
|
|
This deployment does not use outbound PMG filtering yet.
|
|
```
|
|
|
|
## PMG Relay Domains
|
|
|
|
In PMG, navigate to:
|
|
|
|
```text
|
|
Configuration > Mail Proxy > Relay Domains
|
|
```
|
|
|
|
Add the accepted mail domain.
|
|
|
|
```text
|
|
bunny-lab.io
|
|
```
|
|
|
|
This authorizes PMG to accept mail for the domain.
|
|
|
|
!!! warning "Relay Domains Are Required"
|
|
If the domain is missing from Relay Domains, PMG may reject inbound mail because it is not configured as responsible for that domain.
|
|
|
|
## PMG Default Relay
|
|
|
|
In PMG, navigate to:
|
|
|
|
```text
|
|
Configuration > Mail Proxy > Relaying
|
|
```
|
|
|
|
Configure Mailcow as the default relay.
|
|
|
|
```text
|
|
Default Relay: 192.168.3.61
|
|
Relay Port: 25
|
|
Relay Protocol: smtp
|
|
Disable MX Lookup: Yes
|
|
Smarthost: none
|
|
```
|
|
|
|
Target internal relay path:
|
|
|
|
```text
|
|
PMG 192.168.3.15
|
|
|
|
|
v
|
|
Mailcow 192.168.3.61:25
|
|
```
|
|
|
|
!!! note "Disable MX Lookup"
|
|
PMG should deliver accepted inbound mail directly to the internal Mailcow server.
|
|
|
|
```
|
|
It should not perform public MX lookup for the local mail domain.
|
|
```
|
|
|
|
!!! note "No Smarthost"
|
|
Leave `Smarthost` unset or set to `none` for inbound-only filtering.
|
|
|
|
```
|
|
Smarthost configuration is used for outbound relay behavior.
|
|
```
|
|
|
|
## Mailcow Forwarding Host
|
|
|
|
Configure Mailcow to trust PMG as a forwarding host.
|
|
|
|
In Mailcow, navigate to:
|
|
|
|
```text
|
|
System > Configuration Dropdown > Options > Forwarding Hosts Dropdown
|
|
```
|
|
|
|
Add the PMG IP address.
|
|
|
|
```text
|
|
192.168.3.15
|
|
```
|
|
|
|
Set the forwarding-host spam filter option to:
|
|
|
|
```text
|
|
Inactive
|
|
```
|
|
|
|
!!! note "Forwarding Host Behavior"
|
|
After cutover, Mailcow sees PMG as the immediate SMTP source for inbound mail.
|
|
|
|
```
|
|
Trusting PMG allows Mailcow to interpret forwarded mail correctly.
|
|
```
|
|
|
|
!!! note "Spam Filtering Placement"
|
|
PMG is the primary inbound spam and virus filtering system.
|
|
|
|
```
|
|
Leave Mailcow forwarding-host spam filtering inactive to avoid double-filtering messages already inspected by PMG.
|
|
```
|
|
|
|
## Outbound Mail
|
|
|
|
Leave outbound mail unchanged.
|
|
|
|
```text
|
|
Mailcow 192.168.3.61
|
|
|
|
|
v
|
|
Internet
|
|
```
|
|
|
|
Do not configure Mailcow to relay outbound mail through PMG during this stage.
|
|
|
|
Do not change:
|
|
|
|
```text
|
|
Relayhost
|
|
Outbound firewall rules
|
|
DKIM signing
|
|
SPF record
|
|
DMARC record
|
|
```
|
|
|
|
!!! warning "Do Not Move DKIM"
|
|
DKIM signing applies to outbound mail.
|
|
|
|
```
|
|
This document only moves inbound SMTP filtering.
|
|
```
|
|
|
|
## Filtering Policy
|
|
|
|
Initial filtering ownership:
|
|
|
|
```text
|
|
PMG = primary inbound SMTP filtering, tracking, quarantine
|
|
Mailcow = mailbox hosting, authentication, webmail, mail client access
|
|
```
|
|
|
|
Avoid configuring both PMG and Mailcow to aggressively quarantine the same inbound mail stream.
|
|
|
|
!!! note "Keep Filtering Boring"
|
|
PMG should own edge filtering first.
|
|
|
|
```
|
|
Mailcow should continue owning mailbox and client access behavior.
|
|
```
|
|
|
|
## SMTP NAT Cutover
|
|
|
|
After PMG relay domains, PMG default relay, and Mailcow forwarding host settings are configured, update the pfSense NAT rule.
|
|
|
|
Change:
|
|
|
|
```text
|
|
WAN :25 -> Mailcow 192.168.3.61:25
|
|
```
|
|
|
|
To:
|
|
|
|
```text
|
|
WAN :25 -> PMG 192.168.3.15:25
|
|
```
|
|
|
|
Do not change the remaining Mailcow port forwards.
|
|
|
|
```text
|
|
465 -> 192.168.3.61
|
|
587 -> 192.168.3.61
|
|
993 -> 192.168.3.61
|
|
995 -> 192.168.3.61
|
|
143 -> 192.168.3.61
|
|
110 -> 192.168.3.61
|
|
4190 -> 192.168.3.61
|
|
```
|
|
|
|
Do not change the Traefik web path.
|
|
|
|
```text
|
|
80 -> Traefik
|
|
443 -> Traefik
|
|
```
|
|
|
|
!!! warning "Cutover Point"
|
|
Changing `WAN :25` is the actual inbound mail cutover.
|
|
|
|
```
|
|
External SMTP servers will begin connecting to PMG instead of Mailcow directly.
|
|
```
|
|
|
|
## Validation
|
|
|
|
### External SMTP Reachability
|
|
|
|
From an external system:
|
|
|
|
```sh
|
|
# Confirm public SMTP is reachable
|
|
nc -vz mail.bunny-lab.io 25
|
|
```
|
|
|
|
Alternative:
|
|
|
|
```sh
|
|
# Confirm public SMTP banner using telnet
|
|
telnet mail.bunny-lab.io 25
|
|
```
|
|
|
|
Expected result:
|
|
|
|
```text
|
|
Port 25 open
|
|
SMTP banner returned by gateway
|
|
```
|
|
|
|
!!! note "Internal Testing Limitations"
|
|
Internal tests may not represent public mail flow if NAT reflection or split-horizon DNS is involved.
|
|
|
|
```
|
|
Prefer external testing.
|
|
```
|
|
|
|
If external port testing is unavailable, send real mail from an outside provider.
|
|
|
|
Usable external sources:
|
|
|
|
```text
|
|
Gmail
|
|
Outlook.com
|
|
iCloud
|
|
Proton Mail
|
|
Work mailbox hosted outside Mailcow
|
|
```
|
|
|
|
### Inbound Delivery
|
|
|
|
Send an external message to a Mailcow-hosted mailbox.
|
|
|
|
Expected path:
|
|
|
|
```text
|
|
External mailbox
|
|
|
|
|
v
|
|
mail.bunny-lab.io
|
|
|
|
|
v
|
|
pfSense WAN :25
|
|
|
|
|
v
|
|
PMG 192.168.3.15
|
|
|
|
|
v
|
|
Mailcow 192.168.3.61
|
|
|
|
|
v
|
|
User mailbox
|
|
```
|
|
|
|
Check PMG:
|
|
|
|
```text
|
|
PMG > Tracking Center
|
|
```
|
|
|
|
Expected PMG status:
|
|
|
|
```text
|
|
Status: accepted/delivered
|
|
Relay: 192.168.3.61[192.168.3.61]:25
|
|
```
|
|
|
|
Check Mailcow:
|
|
|
|
```text
|
|
System > Logs
|
|
```
|
|
|
|
or review the relevant Mailcow Postfix and Dovecot logs.
|
|
|
|
### Mail Client Access
|
|
|
|
Confirm normal mail client behavior remains unchanged.
|
|
|
|
Test:
|
|
|
|
```text
|
|
IMAP receive
|
|
SMTP submission send
|
|
Mobile mail client access
|
|
Desktop mail client access
|
|
Webmail / Roundcube access
|
|
Mailcow UI access
|
|
```
|
|
|
|
Expected service paths:
|
|
|
|
```text
|
|
IMAPS: 993 -> Mailcow
|
|
Submission: 587 -> Mailcow
|
|
SMTPS: 465 -> Mailcow
|
|
Web: 443 -> Traefik -> Mailcow
|
|
```
|
|
|
|
Confirm outbound mail still works by replying from a Mailcow-hosted mailbox to the external sender.
|
|
|
|
### PMG Queues
|
|
|
|
Check PMG queues after test delivery.
|
|
|
|
```text
|
|
PMG > Queues
|
|
```
|
|
|
|
Expected state:
|
|
|
|
```text
|
|
Queue empty or near-empty after delivery
|
|
```
|
|
|
|
Queue status confirms PMG is not silently holding or deferring mail because of relay, DNS, or delivery errors.
|
|
|
|
## Validation Checklist
|
|
|
|
* [ ] Public MX record points to `mail.bunny-lab.io`
|
|
* [ ] `mail.bunny-lab.io` resolves to the correct public WAN IP
|
|
* [ ] DNS records are unchanged
|
|
* [ ] PMG can reach Mailcow on `192.168.3.61:25`
|
|
* [ ] Mailcow SMTP banner is visible from PMG
|
|
* [ ] PMG external SMTP port is `25`
|
|
* [ ] PMG has `bunny-lab.io` configured as a relay domain
|
|
* [ ] PMG default relay points to `192.168.3.61`
|
|
* [ ] PMG relay port is `25`
|
|
* [ ] PMG relay protocol is `smtp`
|
|
* [ ] PMG internal delivery has MX lookup disabled
|
|
* [ ] PMG smarthost is unset or `none`
|
|
* [ ] Mailcow trusts `192.168.3.15` as a forwarding host
|
|
* [ ] Mailcow forwarding-host spam filter is `Inactive`
|
|
* [ ] Firewall forwards `WAN :25` to `192.168.3.15:25`
|
|
* [ ] Firewall still forwards mail client ports directly to Mailcow
|
|
* [ ] Traefik still handles Mailcow / Roundcube web traffic
|
|
* [ ] Inbound test mail appears in PMG Tracking Center
|
|
* [ ] PMG Tracking Center shows `accepted/delivered`
|
|
* [ ] PMG log shows delivery to `192.168.3.61:25`
|
|
* [ ] Inbound test mail is delivered to the Mailcow mailbox
|
|
* [ ] PMG queue is empty after delivery
|
|
* [ ] Mobile email client still works
|
|
* [ ] Desktop email client still works
|
|
* [ ] Webmail still works
|
|
* [ ] Replying outbound from Mailcow still works
|
|
* [ ] DKIM behavior is unchanged
|
|
* [ ] SPF record is unchanged
|
|
* [ ] DMARC record is unchanged
|
|
* [ ] Outbound mail routing is unchanged
|
|
|
|
## Troubleshooting
|
|
|
|
### Inbound Mail Never Reaches PMG
|
|
|
|
Verify NAT.
|
|
|
|
```text
|
|
WAN :25 -> 192.168.3.15:25
|
|
```
|
|
|
|
Verify inbound port `25` is not blocked by the ISP.
|
|
|
|
From an external system:
|
|
|
|
```sh
|
|
# Test public SMTP reachability
|
|
nc -vz mail.bunny-lab.io 25
|
|
```
|
|
|
|
If external testing is unavailable, send a real external test message and check:
|
|
|
|
```text
|
|
PMG > Tracking Center
|
|
```
|
|
|
|
### PMG Receives Mail but Does Not Deliver to Mailcow
|
|
|
|
Verify PMG relay settings.
|
|
|
|
```text
|
|
Default Relay: 192.168.3.61
|
|
Relay Port: 25
|
|
Relay Protocol: smtp
|
|
Disable MX Lookup: Yes
|
|
```
|
|
|
|
Verify Mailcow SMTP is reachable from PMG.
|
|
|
|
```sh
|
|
# Test Mailcow SMTP from PMG
|
|
nc -vz 192.168.3.61 25
|
|
```
|
|
|
|
Confirm the Mailcow SMTP banner.
|
|
|
|
```sh
|
|
# Inspect Mailcow SMTP banner from PMG
|
|
nc 192.168.3.61 25
|
|
```
|
|
|
|
Expected banner:
|
|
|
|
```text
|
|
220-mail.bunny-lab.io ESMTP Postcow
|
|
220 mail.bunny-lab.io ESMTP Postcow
|
|
```
|
|
|
|
### PMG Shows Reverse DNS Warning for Mailcow
|
|
|
|
A warning like this is not automatically a failure:
|
|
|
|
```text
|
|
inverse host lookup failed: Unknown host
|
|
```
|
|
|
|
If the connection still reports port `25` as open, SMTP connectivity is working.
|
|
|
|
### Mailcow Rejects Mail from PMG
|
|
|
|
Verify Mailcow trusts PMG as a forwarding host.
|
|
|
|
```text
|
|
192.168.3.15
|
|
```
|
|
|
|
Verify the recipient domain exists in Mailcow.
|
|
|
|
```text
|
|
bunny-lab.io
|
|
```
|
|
|
|
Verify the recipient mailbox or alias exists in Mailcow.
|
|
|
|
### Mail Clients Stop Working
|
|
|
|
Verify only inbound SMTP port `25` was moved to PMG.
|
|
|
|
These ports should still forward directly to Mailcow:
|
|
|
|
```text
|
|
465
|
|
587
|
|
993
|
|
995
|
|
110
|
|
143
|
|
4190
|
|
```
|
|
|
|
Expected service ownership:
|
|
|
|
```text
|
|
PMG -> inbound SMTP gateway only
|
|
Mailcow -> client access and mailbox services
|
|
```
|
|
|
|
### Roundcube or Mailcow Web UI Stops Working
|
|
|
|
Verify web traffic was not moved to PMG.
|
|
|
|
Expected path:
|
|
|
|
```text
|
|
WAN :80 -> Traefik :80
|
|
WAN :443 -> Traefik :443
|
|
```
|
|
|
|
PMG should not replace Traefik for Mailcow or Roundcube web access.
|
|
|
|
### Outbound Mail Stops Working
|
|
|
|
Outbound mail should not change during this deployment.
|
|
|
|
Verify no changes were made to:
|
|
|
|
```text
|
|
Mailcow relayhost
|
|
Outbound firewall behavior
|
|
DKIM signing
|
|
SPF record
|
|
DMARC record
|
|
Public DNS records
|
|
```
|
|
|
|
### Spam Filtering Behavior Is Confusing
|
|
|
|
Use one primary inbound filtering authority.
|
|
|
|
Recommended initial state:
|
|
|
|
```text
|
|
PMG = primary inbound edge spam filter
|
|
Mailcow = mailbox hosting and client access
|
|
```
|
|
|
|
Avoid dual aggressive quarantine policies until basic mail flow is stable.
|
|
|
|
## Confirmed Final State
|
|
|
|
After Stage 1, the environment should operate as follows:
|
|
|
|
```text
|
|
Inbound SMTP:
|
|
Internet -> pfSense WAN :25 -> PMG 192.168.3.15:25 -> Mailcow 192.168.3.61:25
|
|
|
|
Outbound SMTP:
|
|
Mailcow -> Internet
|
|
|
|
Mail Client Access:
|
|
Clients -> Mailcow
|
|
|
|
Webmail / Roundcube:
|
|
Internet -> Traefik -> Mailcow
|
|
```
|
|
|
|
The only public NAT behavior changed is:
|
|
|
|
```text
|
|
WAN :25
|
|
```
|
|
|
|
Unchanged components:
|
|
|
|
```text
|
|
DNS records
|
|
DKIM behavior
|
|
SPF record
|
|
DMARC record
|
|
Outbound mail routing
|
|
SMTP submission
|
|
IMAP
|
|
POP3
|
|
ManageSieve
|
|
Mailcow certificates
|
|
Traefik web routing
|
|
Mailcow / Roundcube web access
|
|
```
|
|
|
|
## Deployment Status
|
|
|
|
This document completes Stage 1 of the PMG deployment.
|
|
|
|
```text
|
|
Stage 1: Inbound filtering only
|
|
Stage 2: Optional outbound filtering
|
|
```
|
|
|
|
At the end of Stage 1:
|
|
|
|
```text
|
|
PMG = inbound SMTP filtering only
|
|
Mailcow = mailboxes, webmail, authenticated submission, certificates, DKIM, outbound delivery, user-facing mail services
|
|
```
|