Hardening a self-hosted VPN server: firewall, key rotation and what to log

A self-hosted VPN server is a piece of infrastructure you want to be paranoid about. If it gets compromised, the attacker owns your tunnel, and every machine behind it. Whether you are running WireGuard or IPsec, the defensive work is the same: lock down the host, rotate keys, log what you need and nothing more. This guide walks through those steps on a Windows VPS with a dedicated Vietnam IPv4, running as the server. The principles apply to Linux too, but the commands here target a Windows Server environment (using PowerShell and RDP).
This is written for legitimate business use: your own team, your own services, your own traffic. Cross-border teams between China and Vietnam (for example, a company with staff in Shenzhen and an office in Ho Chi Minh City) need a stable, always-on VPN server that is hardened because it is public-facing. A VPS in Vietnam with a local IPv4 means domestic routing inside Vietnam and a clean IP for your tunnel. Every hop beyond Vietnam crosses international transit, that is how the internet works.
- Key takeaways
- Lock SSH and RDP before configuring anything else: disable password auth, use keys.
- nftables ruleset: only the VPN port, nothing else.
- fail2ban on the Windows VPS (or Linux) to rate-limit connection attempts.
- Rotate VPN peer keys every 60 to 90 days; revoke old keys immediately on staff change.
- Log only connection events (handshake, interface up/down, auth failure). Set a 30-day retention by script or event log policy.
Prerequisites
- A Windows VPS (Windows Server 2022 or 2025 recommended) with Administrator access via RDP and a dedicated Vietnam IPv4. For the alternative, use a Linux VPS, the hardening steps are equivalent.
- Your VPN software installed: WireGuard for simplicity, or a built-in Windows VPN (L2TP/IPsec, SSTP) if the client supports it. This guide assumes WireGuard, but the hardening steps are software-agnostic.
- SSH (if you enable it for fallback) or RDP for administration.
- PowerShell with administrative privileges for all commands.
Why hardening matters for a cross-border VPN
A VPN server on a public IPv4 is scanned constantly. Tens of thousands of probes per day is normal. The attacker does not care whether you are in Hanoi, Guangzhou, or Kunming, they scan the entire internet. A hardened server stops the vast majority of automated attacks. Manual attacks are harder, but logging tells you when they happen.
For teams running a China to Vietnam VPS setup, the VPN server sits in a Vietnam datacenter (often at Viettel IDC, VNPT IDC, FPT Telecom, or CMC Telecom, these are the four major telecoms and data centre operators in Vietnam). The server carries traffic between your team inside China and your systems in Vietnam. If it gets broken into, the attacker sees that traffic or pivots into your internal network. Hardening is not optional.
The same logic applies for a Vietnam VPS for China business: it is a shared resource your team relies on. Treat it like a production server, because it is one.
对运行在中国和越南之间的自建VPN服务器,必须强制控制防火墙、SSH认证、密钥轮换和日志策略。任何漏洞都可能暴露整个团队的业务流量。
For any self-hosted VPN server running between China and Vietnam, you must enforce firewall, SSH authentication, key rotation, and logging policy. A single weakness can expose your whole team's traffic.
Step 1, Lock down SSH and RDP
If you open RDP to the world, you are asking for trouble. Thousands of bots try common usernames (Administrator, admin, user) and weak passwords every minute. Start here.
1a. Disable password authentication
On Windows, RDP does not have a native "key only" mode like SSH, but you can enforce Network Level Authentication (NLA) which requires the client to authenticate before the desktop session loads. By default, NLA is on in modern Windows Server. Verify it:
Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name UserAuthentication
If the value is 1, NLA is enabled. If 0, set it:
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name UserAuthentication -Value 1
Next, change the default RDP port from 3389 to a non-standard port. This stops the dumbest scans (not determined attackers, but reduces log noise by 90%):
New-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Control\Terminal Server\WinStations\RDP-Tcp" -Name PortNumber -Value 33456 -PropertyType DWORD
Restart-Computer
If you also run SSH on the Windows VPS (e.g., via OpenSSH Server), disable password auth there too. Edit C:\ProgramData\ssh\sshd_config:
PasswordAuthentication no
PubkeyAuthentication yes
Restart the SSH service:
Restart-Service sshd
Then generate an ED25519 key pair on your client (ssh-keygen -t ed25519) and append the public key to the server's authorized_keys file.
1b. Use a firewall to restrict who can reach RDP and SSH
Do not leave administration ports open to the entire internet. If your team connects from a fixed set of IPs (your office in Shenzhen, your home in Guangzhou, your staff in Kunming), restrict RDP and SSH to those IPs only. This is your first and most effective layer of defense.
Step 2, Build a minimal nftables ruleset (or Windows Firewall)
On Linux, nftables is the modern replacement for iptables. On Windows, you use the built-in Windows Firewall with Advanced Security. The principle is identical: deny all inbound by default, allow only what you need.
2a. Windows Firewall rules (PowerShell)
Create rules that allow only your VPN port and nothing else. For WireGuard, the default port is UDP 51820. Replace YOUR_VPN_PORT and ALLOWED_IP_RANGES with your actual values:
# Block all inbound by default (set the default inbound action to Block)
Set-NetFirewallProfile -Profile Domain,Public,Private -DefaultInboundAction Block
# Allow the VPN port (WireGuard example)
New-NetFirewallRule -DisplayName "WireGuard VPN" -Direction Inbound -Protocol UDP -LocalPort 51820 -Action Allow
# Allow RDP only from specific IPs (replace with your team's IPs)
New-NetFirewallRule -DisplayName "RDP from team" -Direction Inbound -Protocol TCP -LocalPort 33456 -RemoteAddress 203.0.113.0/24,198.51.100.0/24 -Action Allow
# Allow SSH only from specific IPs (if used)
New-NetFirewallRule -DisplayName "SSH from team" -Direction Inbound -Protocol TCP -LocalPort 22 -RemoteAddress 203.0.113.0/24,198.51.100.0/24 -Action Allow
Verify the rules:
Get-NetFirewallRule -DisplayName "WireGuard VPN" | Format-List
2b. Linux alternative: nftables
If you run the VPN server on a Debian 12 or Ubuntu 24.04 VPS, use nftables. Create /etc/nftables.conf:
#!/usr/sbin/nft -f
flush ruleset
table inet filter {
chain input {
type filter hook input priority 0; policy drop;
# Allow loopback
iif lo accept
# Allow established/related connections
ct state established,related accept
# Allow your VPN port (UDP 51820)
udp dport 51820 accept
# Allow SSH from your team IPs
tcp dport 22 ip saddr { 203.0.113.0/24, 198.51.100.0/24 } accept
# Drop everything else (policy drop handles this)
}
chain forward {
type filter hook forward priority 0; policy drop;
# Allow forwarding for VPN traffic
iif tun0 accept
oif tun0 accept
}
}
Apply and enable:
nft -f /etc/nftables.conf
systemctl enable nftables
Verify:
nft list ruleset
Step 3, Rate-limit with fail2ban
Even with key-only auth, repeated connection attempts fill logs and waste CPU. fail2ban reads the logs and blocks the offending IP at the firewall for a defined time.
On Windows, fail2ban does not natively run, but you can install it via WSL or use the built-in Windows Firewall with a PowerShell script that parses Event Log (Security) for failed logons and adds a block rule. A simpler alternative: install Win2ban (an open-source port) or use the Windows Event Viewer + scheduled tasks approach. For this guide, I recommend running the VPN server on Windows Server and pairing it with a separate Linux VPS as a hardened jump box that handles SSH access and fail2ban, a common pattern for cross-border teams.
On Linux, install fail2ban and configure it to watch SSH and your VPN service logs:
apt install fail2ban
cp /etc/fail2ban/jail.conf /etc/fail2ban/jail.local
Edit jail.local:
[sshd]
enabled = true
maxretry = 3
bantime = 3600
findtime = 600
Start and verify:
systemctl enable fail2ban
systemctl start fail2ban
fail2ban-client status sshd
Expected output shows the number of banned IPs.
Step 4, Rotate and revoke peer keys on a schedule
Key rotation is the discipline most teams skip until there is a problem. For WireGuard, each peer has a private/public key pair. If a laptop is lost or a staff member leaves, you must revoke that key immediately. If a key is rotated every 60 days, the window of exposure after a leak is 60 days max.
4a. Generating and rotating WireGuard keys
On the server, each peer config has a [Peer] section. To rotate a key:
# On the client, generate a new key pair
wg genkey | tee client_private.key | wg pubkey > client_public.key
# On the server, replace the old public key with the new one
# Edit /etc/wireguard/wg0.conf (Linux) or the equivalent config on Windows
# [Peer]
# PublicKey = <new_client_public_key>
# AllowedIPs = 10.0.0.2/32
Reload the configuration:
wg setconf wg0 /etc/wireguard/wg0.conf
wg show
On Windows, WireGuard has a GUI, right-click the tunnel and reload. The handshake will renegotiate.
4b. Revoking a key
If a staff member leaves, remove their [Peer] block entirely from the server config and reload. Their connection stops immediately. Do not leave orphan peers in the config, each unused key is a potential backdoor.
Set a calendar reminder every 60 to 90 days. A simple PowerShell or cron job can email you a list of peer keys and their last handshake time. If a key has not handshaked in 30 days, revoke it.
Step 5, Log what you need, on purpose
Do not log everything by default. Too much log data is noisy, costly to store, and creates liability if the server is ever compromised (logs may contain decrypted traffic fragments). Decide on purpose what to keep.
What to log
- Authentication events: successful and failed logons (RDP, SSH). This is critical for detecting brute force or credential theft. Windows Event Log (Security) captures this. On Linux,
/var/log/auth.logorjournalctl -u ssh. - VPN interface up/down events: a restart or disconnect that happens outside a scheduled window may indicate a restart or attack. WireGuard logs handshake events to
dmesgorjournalctl. - Connection start and end times per peer: useful for auditing who was connected when. WireGuard's
wg showshows latest handshake and transfer totals.
What NOT to log
- Raw packet payloads (TCP dump of VPN traffic). This logs everything and violates user privacy. Never enable this unless required by a formal security investigation with a defined scope.
- Every DNS query made through the tunnel. Too high volume, too little value for most teams.
Retention policy
Set a retention period. 30 days is standard for compliance-oriented teams. After that, rotate and archive to cold storage or delete. On Windows, configure Event Log to 30 days:
wevtutil sl "Security" /rt:30
wevtutil sl "System" /rt:30
On Linux, configure logrotate for /var/log/auth.log (or use systemd journal with MaxRetentionSec=30day):
journalctl --vacuum-time=30d
FAQ
I use a Windows VPS. Can I still run WireGuard?
Yes. WireGuard has an official Windows client and server build. Configure it through the GUI or via a config file. The hardening steps (RDP port change, Windows Firewall, NLA) are identical.
Do I need a Linux VPS for fail2ban?
Not strictly, but it is easier. On Windows you can use Event Log filters and scheduled tasks to replicate fail2ban behaviour. Many teams keep a lightweight Linux jump box (a cheap VNLite plan) in front of the Windows VPS. That Linux box handles SSH, fail2ban, and the nftables firewall, while the Windows VPS runs the VPN and other services.
How often should I rotate VPN keys?
Every 60 to 90 days, or immediately after a staff member leaves or a device is lost. Rotating on a schedule means the worst case leak window is bounded. Automate the reminder with a calendar or a script that emails you the list of peer handshake times.
What if my team's IPs change frequently (remote workers, mobile)?
Then you cannot restrict RDP/SSH by IP. Use a second authentication factor: either a separate VPN tunnel to a small jump box (which itself is restricted), or a tool like Duo Security or Windows Hello for Business. The main VPN port (WireGuard) remains open to any IP, but that port only accepts authenticated VPN connections, so it is your first line.
Why not just use a cloud provider's managed VPN?
A self-hosted VPN on a Vietnam VPS gives you a Vietnam IPv4, which matters when your users are in Vietnam (domestic routing) or when your business requires local presence. It also gives you full control over logging, key material, and the firewall, no third party touches your keys.
Should I log every connection?
Only connection events (handshake, interface up/down). Do not log traffic content. A log of who connected when is enough for most audits and troubleshooting. Set a 30-day retention and purge old events.
Related articles
- Set up a WireGuard VPN on your VPS in 10 minutes
- Hardening SSH on a new VPS: keys, ports, fail2ban, and CrowdSec
- Set up nftables firewall on a VPS to replace iptables
- Why a Vietnam IPv4 matters when your users are in Vietnam
自建VPN服务器的安全加固
加固内容包括防火墙规则、SSH加固、禁用密码登录、密钥轮换与吊销,以及日志留存策略。全部为防御性配置。密钥吊销这一步最容易被忽略。


