Iptables: access connected openvpn client from the LAN with the VPN server

iptablesopenvpnroutingtun

I have what is essentially a routing problem, and I'm not familiar enough with routing and iptables to effectively troubleshoot and set up my network needs.

What's working

I have an openVPN network set up and working; clients can connect to a LAN through the internet.

What I'd like to have happen

…when a clients connects to the VPN on a given subnet.

  • The client should be accessible from the VPN network

Bonus Points if the rules can do one or more of the following:

  • the client should not be able to initiate connections to the VPN
  • the client should show up in our DNS

Topology

Our network topology looks something like this:

   ______        ____________________
 /        \     /                    \
| internet |---| client (10.8.8.0/24) |
 \________/     \____________________/
     |
   ______
 /        \
| gateway  |
 \________/
     |
-----LAN------ (10.10.10.0/24)
|    |   |   |
             L_____VPN Server `VPN1` 10.10.10.2 (fictional name/subnet)

Current Settings

Our gateway has the following route defined:

10.8.8.0    255.255.255.0   10.10.10.2  LAN & WLAN

On VPN1, iptables has the following rules

# Flush the filter and nat tables
iptables -t filter -F
iptables -t nat -F

iptables -A INPUT -i tun+ -j ACCEPT
iptables -A FORWARD -i tun+ -j ACCEPT

iptables -A INPUT -i eth0 -j ACCEPT -d 10.8.8.0/24
iptables -A FORWARD -i eth0 -j ACCEPT
iptables -t nat -A POSTROUTING -s 10.10.10.0/16 -d 10.8.8.0/24 -o tun+ -j MASQUERADE

What is currently happening

Running the command mtr 10.8.8.1 (the connected client's IP on the VPN) shows that the current route is bouncing back and forth between VPN1 and the gateway.

Best Answer

After another grueling round of iptables online learning madness, I discovered the solution.

First, however, there is an invalid assumption regarding iptables. My initial approach to the rules was that when a packet was received, it would run through the INPUT and OUTPUT chains. Not so; the minute a rule matches a packet, it leaves the table. Since the filter table is assumed unless specified (e.g. "-t nat"), most of the rules listed are run on the filter table.

Regarding Chains

  • INPUT : run on packets destined for the server
  • OUTPUT : run on packets originating from the server
  • FORWARD : everything else - if a rule matches here, and the "jump" (I like to think if -j as "job" ;) is ACCEPT, the packet will be routed appropriately

A breakdown of the rules

This is a description of the rules under Current Settings above

iptables -t filter -F
iptables -t nat -F

These rules simply flush the filter and nat tables. Note that there are more tables and a more thorough way of clearing iptables rules.

iptables -A INPUT -i tun+ -j ACCEPT

This rule does nothing, because:

  • it is run on traffic destined for VPN1, not another network
  • there is no policy set for incoming traffic, so it is allowed by default

moving on...

iptables -A FORWARD -i tun+ -j ACCEPT

This rule allows traffic coming from 10.8.8.0/24 to be routed. Rules in the nat table run on packets that match this rule.

iptables -A INPUT -i eth0 -j ACCEPT -d 10.8.8.0/24

This rule also has no effect on the desired routing, since 10.8.8.0/24 traffic is not destined for the VPN1 server.

iptables -A FORWARD -i eth0 -j ACCEPT

This rule allows traffic from 10.10.10.0/16 to be routed.

iptables -t nat -A POSTROUTING -s 10.10.10.0/16 -d 10.8.8.0/24 -o tun+ -j MASQUERADE

This rule causes the traffic destined for the VPN from 10.10.10.0/16 to look like it came from VPN1, effectively causing VPN1 to look like a gateway.

What's wrong?

The rules should be "OK" as is to get traffic from one network to the next. There isn't any real protection in place - e.g. a default "DROP" policy, etc, but that isn't the point of the question.

If iptables is set up so that it can route the traffic over the VPN, what would cause it to be sent back over eth0 to the gateway? If VPN1 did not know about 10.8.8.0/24. If the VPN server wasn't aware of that network, it would be treated as internet traffic and sent back to the gateway.

The fix

The solution was to tell the VPN server about the network (this is an openvpn server). There are two ways to do this; if the server is only serving one network, it is a simple configuration setting:

server 10.8.8.0 255.255.255.0

In my case, I had the server route set up, and I needed it to know about an additional network. The configuration looked more like this:

server 10.5.5.0 255.255.255.0
route 10.8.8.0 255.255.255.0

That's it! Once VPN1 had a route to the network, the FORWARD chain is able to route the traffic.

A better iptables setup

After flushing iptables, a better config would look something like this:

# Forward established traffic so that (in the above case) VPN1 doesn't
# drop responses from the client, A.K.A. "the magic"
iptables -t filter -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

iptables -t filter -A FORWARD     -s 10.10.10.0/16 -d 10.8.8.0/24  -j ACCEPT
iptables -t nat    -A POSTROUTING -s 10.10.10.0/16 -d 10.8.8.0/24  -j MASQUERADE

# Drop everything else that wants to be forwarded
iptables -P FORWARD DROP

Notice that there are no explicit rules for traffic coming from 10.8.8.0/24, which means that by default the traffic won't reach the 10.10.10.0/16 network - except for traffic in response to traffic sent from 10.10.10.0/16. Now that iptables is set up, clients can be assigned an IP in the VPN config, and added to DNS for a complete solution.

Related Topic