OpenVPN’s default behavior is full network access: once a client connects, it can reach anything routable through the tunnel. That’s not what you want for a contractor VPN, a support tunnel, or any profile that only needs a handful of internal services. Restricting a client to specific IPs takes three pieces working together: a client-config-dir route, a script that runs on connect, and iptables rules that actually enforce it.
Prerequisites
A working OpenVPN server
Root or sudo access to the server
Basic knowledge of networking and firewall concepts
Server configuration
Edit /etc/openvpn/server.conf:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# DNS setup
push "dhcp-option DNS 10.8.0.1"
push "redirect-gateway def1 bypass-dhcp"
# This helps prevent DNS leaks on Windows
push "block-outside-dns"
# Client configuration directory
client-config-dir /etc/openvpn/ccd
route 10.8.0.0 255.255.255.0
# Logging
log-append /var/log/openvpn.log
status /var/log/openvpn-status.log
# Enable running external scripts
script-security 2
client-connect /etc/openvpn/client-connect.sh
Enforcing the restriction with a client-connect script
Pushed routes tell the client which networks to send through the tunnel; they don’t stop the server from forwarding traffic anywhere else. The actual enforcement happens in iptables, applied by a script that runs every time a client connects.
#!/bin/bash# Read the allowed IPsALLOWED_IPS=$(cat /etc/openvpn/allowed_ips.txt)# Create iptables rules for each allowed IPfor IP in$ALLOWED_IPS;do
iptables -A FORWARD -i tun+ -d$IP-j ACCEPT
done# Drop all other forward traffic from tun interfaces
iptables -A FORWARD -i tun+ -j DROP
1
chmod +x /etc/openvpn/client-connect.sh
Managing rules directly with iptables
If you’d rather manage rules by hand instead of through the connect script:
1
2
3
4
5
6
7
# Allow all traffic for other VPN clientssudo iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.0/24 -j ACCEPT
# Rules for a specific client (10.8.0.5)sudo iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.5 -d 93.184.216.34 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.5 -p udp --dport 53 -j ACCEPT
sudo iptables -A FORWARD -i tun0 -o eth0 -s 10.8.0.5 -j DROP
1
2
3
4
5
6
# View current rulessudo iptables -L-v-n# Delete a specific rulesudo iptables -L--line-numberssudo iptables -D CHAIN_NAME RULE_NUMBER
#!/bin/bashBACKUP_DIR="/path/to/backup/directory"TIMESTAMP=$(date +"%Y%m%d_%H%M%S")BACKUP_FILE="$BACKUP_DIR/iptables_backup_$TIMESTAMP.rules"
iptables-save >"$BACKUP_FILE"echo"Backup saved to $BACKUP_FILE"
Per-client configuration
To give one client its own IP and routes, create a file in ccd named after the client:
1
sudo nano /etc/openvpn/ccd/client1
1
2
3
4
5
# Assign a specific IP to the client
ifconfig-push 10.8.0.200 255.255.255.255
# Push specific routes to this client
push "route 192.168.1.0 255.255.255.0"
sudo systemctl status openvpn
# orsudo service openvpn status
1
2
3
4
5
6
7
8
9
10
11
# Check routing table
netstat -r
ip route
# Check tun interface
ifconfig tun0
ip addr show tun0
# Check OpenVPN connections
ss -anp | grep openvpn
netstat -anp | grep openvpn
1
tail-f /var/log/openvpn.log
What this buys you, and what it doesn’t
Pushed routes and iptables rules restrict where traffic can go, but they’re enforced on the server, not the client: a compromised or misconfigured client is still authenticated, just unable to reach much. That’s the right trade-off for a contractor or support tunnel. It isn’t a substitute for per-client certificates and proper key management, which this setup assumes you’ve already solved.