DNS server on Proxmox Mail Gateway

From Proxmox Mail Gateway
Jump to navigation Jump to search

Introduction

One of the most effective means to detecting spam currently is the use of DnsBlocklists. These lists are used to query the IP of the connecting SMTP server, or IPs and hostnames occurring in the mail body.

Some of the DNS Blocklists used by SpamAssassin (and thus also Proxmox Mail Gateway) allow only a certain number of requests per DNS server and don't respond once your DNS server has reached it's quota. This is reflected in the mail logs and SpamAssassin hits of a mail. If you see 'URIBL_BLOCKED', 'RCVD_IN_DNSWL_BLOCKED' or 'SURBL_BLOCKED' in your mail logs or the mail headers, this is an indication that your system has reached the quota.

If you're using a shared DNS server (e.g. your ISPs, or a publicly available one like 9.9.9.9, 1.1.1.1, 8.8.8.8) it is quite likely that the Mail Gateway's requests will be blocked.

Installing a dedicated DNS server on the Proxmox Mail Gateway can help in such situations.

Keep in mind that the DNS Blocklists can only count the requests per public IP, i.e. if you have both your internal DNS and Proxmox Mail Gateway natted to the same public IP setting up a recursive DNS server will not help.

If you keep reaching the limit despite having a dedicated recursive server for your Proxmox Mail Gateway you should consider getting a dedicated feed, which is provided by most DNS Blocklist providers for a fee. This also helps keeping this important infrastructure up and running.

We will use the Unbound recursive DNS server.

Installing and using unbound

Simply run

apt install unbound dnsutils

to install the unbound server - the dnsutils package contains dig, which can be used for testing.

Check that unbound is indeed listening on port 53:

# ss -tulnp | grep :53
udp     UNCONN   0        0              127.0.0.1:53             0.0.0.0:*      users:(("unbound",pid=137,fd=5))
udp     UNCONN   0        0                  [::1]:53                [::]:*      users:(("unbound",pid=137,fd=3))
tcp     LISTEN   0        128            127.0.0.1:53             0.0.0.0:*      users:(("unbound",pid=137,fd=6))
tcp     LISTEN   0        128                [::1]:53                [::]:*      users:(("unbound",pid=137,fd=4))

You can verify that DNS resolution works by using the dig utility

# dig a proxmox.com @127.0.0.1 +short 
79.133.36.244

Afterwards you need to configure your Proxmox Mail Gateway installation to use the local dns-server listening on 127.0.0.1 (or optionally ::1) as resolver. In the following article we assume that your domain is yourdomain.example - you need to adapt the posted configuration. On a standard installation this is done by simply placing:

nameserver 127.0.0.1
search yourdomain.example

in /etc/resolv.conf. You can use the GUI for setting the dns-resolver as well under Configuration -> Network/Time -> DNS - Just add 127.0.0.1 as DNS Server 1.

When Proxmox Mail Gateway is running as a Container on Proxmox VE, then you need to edit the container's DNS Settings to use 127.0.0.1 as DNS Server (and adapt your search domain to yourdomain.example)

If you have installed the resolvconf package you should not need to change everything, since the unbound package in Debian brings integration with resolvconf

Should your system use systemd-resolved make sure that

resolvectl status

indicates that 127.0.0.1 is listed as Current DNS Server

Alternatively you can simply disable and stop the service

systemctl disable systemd-resolved
systemctl stop systemd-resolved

After installing you can either reboot you Proxmox Mail Gateway, or restart the services relevant for mail-processing:

systemctl restart pmg-smtp-filter pmgpolicy postfix

Forwarding requests for your internal zone to your internal DNS

In some environments the internally used DNS has all knowledge about your domain, and should be consulted for it, instead of unbound getting the publicly available data via DNS delegation from the root-servers.

You can configure unbound to ask your internal DNS-server (for this example the internal DNS-server has the IP 192.0.2.53) for your internal domains (yourdomain.example and yourseconddomain.example). Create a dedicated config-snippet /etc/unbound/unbound.conf.d/local-stub.conf:

stub-zone:
  name: "yourdomain.example"
  stub-addr: 192.0.2.53
stub-zone:
  name: "yourseconddomain.example"
  stub-addr: 192.0.2.53


Afterwards restart unbound and verify that DNS-requests for yourdomain.example are delegated to 192.0.2.53 (by checking the DNS logs there):

systemctl restart unbound
dig test.yourdomain.example @127.0.0.1


Optional: Using the local unbound only for DNS Blocklist requests

Should your environment require you to use an internal DNS server for all requests, because you have a very modified setup or are employing some other blocking for regulatory reasons you can also try to forward all other requests to your internal DNS Server and only ask the DNS Blocklist zones recursively.

This setup is not recommended for general use, since it increases the complexity which makes debugging harder.

In the example we will use recursive queries for the following domains and forward all other requests to 192.0.2.53:

  • mailspike.net
  • dnsbl.sorbs.net
  • rhsbl.sorbs.net
  • bl.spamcop.net
  • spamhaus.org
  • surbl.org
  • uribl.com
  • dnswl.org

The list is taken from the Spam Assassin Entry on DNS Blocklists. You should enhance the list by all domains you are using in your setup (especially the one's configured for postscreen)

Since unbound cannot do recursive lookups for specific zones if it is forwarding all other requests we will configure 2 unbound instances:

  • one listening on port 5003 for recursive lookups - the DNSBL instance
  • one forwarding requests for the DNSBL domains to port 5003, and all other requests to your internal DNS Server.

For the DNSBL instance - create a config-file which does only include the necessary config-options /etc/unbound/unbound-dnsbl.conf:

#unbound instance listening on port 5003 for DNSBL lookups
include: "/etc/unbound/unbound.conf.d/qname-minimisation.conf"
include: "/etc/unbound/unbound.conf.d/root-auto-trust-anchor-file.conf"

port: 5003
remote-control:
     control-port: 8954

Additionally you need to create a systemd-unit (/etc/systemd/system/unbound-rbl.service

[Unit]
Description=Unbound DNS server for DNSBL lookups
Documentation=man:unbound(8)
After=network.target
Before=nss-lookup.target
Wants=nss-lookup.target
[Service]
Type=simple
Restart=on-failure
EnvironmentFile=-/etc/default/unbound
EnvironmentFile=-/etc/default/unbound-rbl
ExecStartPre=-/usr/lib/unbound/package-helper chroot_setup
ExecStartPre=-/usr/lib/unbound/package-helper root_trust_anchor_update
ExecStart=/usr/sbin/unbound -c /etc/unbound/unbound-rbl.conf -d $DAEMON_OPTS
ExecReload=/usr/sbin/unbound-control -c /etc/unbound/unbound-rbl.conf reload
[Install]
WantedBy=multi-user.target


and enable it with

systemctl enable unbound-rbl
systemctl start unbound-rbl


For the instance listening on port 53 you need to create a config-snippet in /etc/unbound/unbound.conf.d/pmg-dnsbl.conf:

server:
      do-not-query-localhost: no
      # depending on your internal DNS-servers capabilities these options might be necessary
      # harden-dnssec-stripped: no 
      # module-config: "iterator"

forward-zone:
       name: "uceprotect.net"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "mailspike.net"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "sorbs.net"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "bl.spamcop.net"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "spamhaus.org"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "surbl.org"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "uribl.com"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "dnswl.org"
       forward-addr: 127.0.0.1@5003

forward-zone:
       name: "."
       forward-addr: 192.0.2.53

Test the setup by doing lookups to:

  • a testpoint of a DNSBL and verify that the query does not arrive at your internal server
  • a testpoint of an arbitrary address (which should arrive at your internal server):
# dig any test.uribl.com.multi.uribl.com @127.0.0.1 +short # should not show up as query on 192.0.2.53
127.0.0.14
"permanent testpoint"
# dig a proxmox.com @127.0.0.1 +short  #should show up as query on 192.0.2.53
79.133.36.244