Prepare Debian + OpenVPN Server + UFW rules

Hosting your own Mail Server

Company: Personal Project

Part 1 of 2 — In this part we configure the linode VPS to create the gateway, OpenVPN, set up outbound NAT, and add the necessary firewall rules so that all traffic arriving on the Linode VPS is correctly forwarded to Mailcow.

Mailcow Behind a Linode VPS with OpenVPN + pfSense

Part 1 - Prepare Debian + OpenVPN Server + UFW rules

Running a full mail server like Mailcow directly on a public VPS exposes a large attack surface. A much cleaner and more secure approach is to place Mailcow behind a firewall (pfSense) and only expose a single OpenVPN port on a cheap Linode VPS.

In this two-part series we will build exactly that architecture.

Architecture Overview

Internet
   │
   ▼
┌──────────────────────────────┐
│  Linode VPS (Debian 12)      │
│  • OpenVPN Server (UDP 1100) │
│  • UFW + DNAT rules          │
│  • Forwards almost all       │
│    traffic to VPN client     │
└──────────────┬───────────────┘
               │ OpenVPN tunnel
               ▼
┌──────────────────────────────┐
│  pfSense (OpenVPN Client)    │
│  • Receives all forwarded    │
│    ports via the tunnel      │
│  • Protects Mailcow          │
└──────────────┬───────────────┘
               │
               ▼
┌──────────────────────────────┐
│  Mailcow (internal network)  │
└──────────────────────────────┘

The Linode VPS becomes a pure “jump host / port forwarder”. The real mail server never has a public IP.

What We Will Do in Part 1

  1. Prepare a fresh Debian 12 VPS (disable IPv6, install required packages)
  2. Install and configure a modern OpenVPN server on port 1100/UDP
  3. Generate a client configuration that can be imported into pfSense
  4. Set up advanced UFW rules that DNAT almost every port to the connected OpenVPN client

Part 2 will cover the pfSense side, firewall rules, and connecting Mailcow.

Prerequisites

Note: All scripts used in this guide are available in the accompanying GitHub repository:
Github repo - mailcow-openvpn-pfsense 

Step 1 – Prepare Debian 12

We start by hardening the VPS a bit and installing the required packages.

# Download and run the preparation script
curl -O https://raw.githubusercontent.com/tleadley/mailcow-openvpn-pfsense/main/prepare-debian.sh
chmod +x prepare-debian.sh
sudo ./prepare-debian.sh

This script will:

  • Update the system
  • Install OpenVPN, Easy-RSA, and useful tools
  • Disable IPv6 system-wide (recommended when you only use IPv4)
  • Enable IP forwarding
  • Create the necessary OpenVPN directories

Step 2 – Install the OpenVPN Server

Next we set up a clean OpenVPN server listening on UDP port 1100.

curl -O https://raw.githubusercontent.com/tleadley/mailcow-openvpn-pfsense/main/setup-openvpn-server.sh
chmod +x setup-openvpn-server.sh
sudo ./setup-openvpn-server.sh

Key points of this setup:

  • Uses modern cryptography (AES-256-GCM + tls-crypt)
  • Listens on port 1100/UDP (less scanned than 1194)
  • Automatically generates a client configuration named pfsense-client.ovpn
  • The client config is ready to import into pfSense

After the script finishes you will find the client configuration here:

/root/openvpn-clients/pfsense-client.ovpn

Download this file — you will need it in Part 2.


Step 3 – Configure Advanced Port Forwarding with UFW

This is the most important part of the VPS setup. We configure UFW so that:

  • Port 1100 (OpenVPN) is accepted on the VPS itself
  • Almost every other port is DNATed to the OpenVPN client (the pfSense machine)
  • Port 22 is blocked on the public interface (optional but recommended)
curl -O https://raw.githubusercontent.com/tleadley/mailcow-openvpn-pfsense/main/setup-ufw-rules.sh
chmod +x setup-ufw-rules.sh
sudo ./setup-ufw-rules.sh

Important: Run this script after the pfSense OpenVPN client has connected at least once. The script reads the assigned VPN IP from /var/log/openvpn/ipp.txt and builds the DNAT rules dynamically.

What the rules do:

  • DNAT TCP/UDP ports 1–21, 23–1099 and 1101–65535 → OpenVPN client
  • Allow the OpenVPN port (1100) on the VPS
  • Block direct SSH access from the internet
  • Properly masquerade traffic coming from the VPN tunnel

Verification

After everything is running you can check:

Bash
 
# OpenVPN status
systemctl status openvpn-server@server

# See connected clients
cat /var/log/openvpn/ipp.txt

# UFW status
ufw status verbose

# Watch OpenVPN log
journalctl -u openvpn-server@server -f

Security Notes

  • The VPS only needs UDP 1100 open in the Linode Cloud Firewall.
  • All other ports can (and should) be blocked at the Linode firewall level if you want defense in depth.
  • Keep the OpenVPN client configuration private — it contains certificates.
  • Consider adding a second authentication factor (e.g. TLS + username/password) later if needed.

What’s Next?

In Part 2 we will:

  • Import the OpenVPN client configuration into pfSense
  • Configure the necessary firewall rules and NAT on pfSense
  • Point Mailcow at the internal interface
  • Set up reverse proxy / SSL certificates correctly
  • Discuss monitoring and maintenance

Repository

All scripts used in this guide are available here:
https://github.com/tleadley/mailcow-openvpn-pfsense

Feel free to open issues or pull requests if you improve the scripts.


Part 1 complete.
Your Linode VPS is now ready to act as a secure entry point for Mailcow.

Part 2 - pfSense OpenVPN client, gateway, NAT and firewall rules