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.