TTR Networks

Intermediate · OpenVPN on Debian: users, routing and revocation · section 6 of 11

Many users, and a LAN behind one

✓ Lab-tested 21 October 2026 on Debian 13.7, OpenVPN 2.6.14, easy-rsa 3.2.2, nftables 1.1.3

With several users connected, the status file is the server's view of who's on: each user's certificate name, where they're connecting from, their VPN address and their traffic. OpenVPN rewrites it every 10 seconds (the status line in server.conf).

Fixed addresses per user

Each user's file in ccd (named after their certificate) sets a fixed address with ifconfig-push. A fixed address is what makes per-user firewall rules possible: alice is always 10.8.0.10, so a rule can allow, say, only alice to reach the payroll server.

A whole network behind one user

The branch router connects as a user like any other, but it has a LAN behind it, 10.20.0.0/24. Reaching it takes two settings, and the difference between them confuses almost everyone:

  • route 10.20.0.0 255.255.255.0 in server.conf tells the server's kernel to send 10.20.0.0/24 into tun0.
  • iroute 10.20.0.0 255.255.255.0 in ccd/branch tells OpenVPN itself which user owns that network inside the tunnel. Without it, OpenVPN wouldn't know which of its users to hand the packets to, and would drop them.

You need both: route gets the packets into the tunnel, iroute gets them to the right user. The status file shows the result: 10.20.0.0/24 belongs to branch. The branch router also needs IP forwarding turned on, and the server pushes the route to other users, so they can reach the branch too.

The branch PC and the office host can now reach each other. But alice can't reach the branch PC, and that's the next section's subject.

The server's status file, three users connected (real output)
root@vpn:~# cat /run/openvpn-server/status-server.log
TITLE,OpenVPN 2.6.14 aarch64-unknown-linux-gnu [SSL (OpenSSL)] [LZO] [LZ4] [EPOLL] [PKCS11] [MH/PKTINFO] [AEAD] [DCO]
TIME,2026-10-21 05:19:12,1792559952
HEADER,CLIENT_LIST,Common Name,Real Address,Virtual Address,Virtual IPv6 Address,Bytes Received,Bytes Sent,Connected Since,Connected Since (time_t),Username,Client ID,Peer ID,Data Channel Cipher
CLIENT_LIST,alice,198.51.100.21:43892,10.8.0.10,,5857,5290,2026-10-21 05:18:05,1792559885,UNDEF,0,0,AES-256-GCM
CLIENT_LIST,branch,198.51.100.23:57361,10.8.0.20,,4619,4736,2026-10-21 05:19:08,1792559948,UNDEF,2,2,AES-256-GCM
CLIENT_LIST,bob,198.51.100.22:57709,10.8.0.11,,4613,4766,2026-10-21 05:19:08,1792559948,UNDEF,1,1,AES-256-GCM
HEADER,ROUTING_TABLE,Virtual Address,Common Name,Real Address,Last Ref,Last Ref (time_t)
ROUTING_TABLE,10.8.0.20,branch,198.51.100.23:57361,2026-10-21 05:19:08,1792559948
ROUTING_TABLE,10.20.0.0/24,branch,198.51.100.23:57361,2026-10-21 05:19:08,1792559948
ROUTING_TABLE,10.8.0.11,bob,198.51.100.22:57709,2026-10-21 05:19:08,1792559948
ROUTING_TABLE,10.8.0.10,alice,198.51.100.21:43892,2026-10-21 05:18:57,1792559937
GLOBAL_STATS,Max bcast/mcast queue length,0
GLOBAL_STATS,dco_enabled,0
END

root@vpn:~# ip route | grep tun0
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.1 
10.20.0.0/24 via 10.8.0.2 dev tun0 
  • Lines 5–7: Each user: certificate name, real address, fixed VPN address, traffic and connection time.
  • Line 10: iroute at work: the branch LAN belongs to the user branch.
  • Line 19: route at work: the server's kernel sends the branch LAN into tun0.
The branch, and who can reach it (real output)
root@branch:~# ip -br addr show tun0; ip route | grep tun0
tun0             UNKNOWN        10.8.0.20/24 
10.8.0.0/24 dev tun0 proto kernel scope link src 10.8.0.20 
10.10.0.0/24 via 10.8.0.1 dev tun0 

root@office:~# ping -c 4 -W 2 10.20.0.10
PING 10.20.0.10 (10.20.0.10) 56(84) bytes of data.
64 bytes from 10.20.0.10: icmp_seq=1 ttl=62 time=0.328 ms
From 10.10.0.254 icmp_seq=2 Redirect Host(New nexthop: 10.10.0.1)
64 bytes from 10.20.0.10: icmp_seq=2 ttl=62 time=0.290 ms
64 bytes from 10.20.0.10: icmp_seq=3 ttl=62 time=0.414 ms

--- 10.20.0.10 ping statistics ---
3 packets transmitted, 3 received, +1 errors, 0% packet loss, time 2029ms
rtt min/avg/max/mdev = 0.290/0.344/0.414/0.051 ms

root@alice:~# ping -c 2 -W 1 10.20.0.10
PING 10.20.0.10 (10.20.0.10) 56(84) bytes of data.

--- 10.20.0.10 ping statistics ---
2 packets transmitted, 0 received, 100% packet loss, time 1003ms


root@branchpc:~# ping -c 2 -W 1 10.10.0.20
PING 10.10.0.20 (10.10.0.20) 56(84) bytes of data.
64 bytes from 10.10.0.20: icmp_seq=1 ttl=61 time=0.330 ms
64 bytes from 10.10.0.20: icmp_seq=2 ttl=61 time=0.624 ms

--- 10.10.0.20 ping statistics ---
2 packets transmitted, 2 received, 0% packet loss, time 1025ms
rtt min/avg/max/mdev = 0.330/0.477/0.624/0.147 ms
  • Line 2: The branch router's fixed VPN address.
  • Lines 6–15: The office reaches the branch PC (after lanrtr's harmless redirect).
  • Lines 17–21: alice can't: that's traffic from one VPN user to another. The next section explains.
  • Lines 24–31: The branch PC reaches the office.

Check your understanding

The branch router is connected and the server has route 10.20.0.0 255.255.255.0, but nothing reaches the branch LAN. The status file shows no entry for 10.20.0.0/24. What's missing?

  • iroute 10.20.0.0 255.255.255.0 in the branch user's ccd file.
  • A second route line.
  • client-to-client.
  • A return route on lanrtr.