Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I have a PC behind a router with port forwarding setup Port 22 for SSH which works perfectly - I can login and everything. What I am trying to do is let this PC connect to a VPN and only allow traffic on local network or via the VPN connection (for internet access). This is also working great. As soon as the VPN connection is broken, the PC has no connection to internet. Now I am doing this with IPTABLES, the problem is that I can not seem to get incoming SSH to work from external sources via the router port forwarding. I am thou able to SSH to the pc from within the local network.

Here is what I have tried:

# Allow traffic to VPN SERVER
sudo iptables -A INPUT -s $REMOTE_IP -j ACCEPT

# Allow ssh traffic
iptables -A INPUT -i eth1 -p tcp --dport 22 -m state --state NEW,ESTABLISHED -j ACCEPT    
iptables -A OUTPUT -o eth1 -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT

# Allow local traffic.
sudo iptables -A INPUT -s 10.0.0.0/8 -j ACCEPT
sudo iptables -A INPUT -s 172.16.0.0/12 -j ACCEPT
sudo iptables -A INPUT -s 192.168.0.0/16 -j ACCEPT

# Disallow everything else.
sudo iptables -A INPUT ! -i tun+ -j DROP

# Allow traffic from VPN SERVER.
sudo iptables -A OUTPUT -d $REMOTE_IP -j ACCEPT

# Allow local traffic.
sudo iptables -A OUTPUT -d 10.0.0.0/8 -j ACCEPT
sudo iptables -A OUTPUT -d 172.16.0.0/12 -j ACCEPT
sudo iptables -A OUTPUT -d 192.168.0.0/16 -j ACCEPT

# Disallow everything else.
sudo iptables -A OUTPUT ! -o tun+ -j DROP

sudo openvpn --config client.cfg --auth-user-pass client.cred --daemon

Here is my iptables -vL -n output: (Replaced the vpn server with XX.XX.XX.XX)

Chain INPUT (policy ACCEPT 25674 packets, 4792K bytes)
 pkts bytes target     prot opt in     out     source               destination         
78848   11M ACCEPT     all  --  *      *       XXX.XXX.XXX.XXX      0.0.0.0/0           
 3176  318K ACCEPT     tcp  --  eth1   *       0.0.0.0/0            0.0.0.0/0           tcp dpt:22 state NEW,ESTABLISHED 
    0     0 ACCEPT     all  --  *      *       10.0.0.0/8           0.0.0.0/0           
    0     0 ACCEPT     all  --  *      *       172.16.0.0/12        0.0.0.0/0           
 2517  231K ACCEPT     all  --  *      *       192.168.0.0/16       0.0.0.0/0           
   35 12374 DROP       all  --  !tun+  *       0.0.0.0/0            0.0.0.0/0           

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination         

Chain OUTPUT (policy ACCEPT 32187 packets, 4374K bytes)
 pkts bytes target     prot opt in     out     source               destination         
 3681 2443K ACCEPT     tcp  --  *      eth1    0.0.0.0/0            0.0.0.0/0           tcp spt:22 state ESTABLISHED 
70697   10M ACCEPT     all  --  *      *       0.0.0.0/0            XXX.XXX.XXX.XXX      
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            10.0.0.0/8          
    0     0 ACCEPT     all  --  *      *       0.0.0.0/0            172.16.0.0/12       
   27  5787 ACCEPT     all  --  *      *       0.0.0.0/0            192.168.0.0/16      
 2265  150K DROP       all  --  *      !tun+   0.0.0.0/0            0.0.0.0/0

And yes if I do a ifconfig I only have a eth1 and not a eth0 so it is not that.

Here is also the output of netstat -rn

Kernel IP routing table
Destination     Gateway         Genmask         Flags   MSS Window  irtt Iface
0.0.0.0         XX.XXX.242.128  128.0.0.0       UG        0 0          0 tun0
0.0.0.0         192.168.1.1     0.0.0.0         UG        0 0          0 eth1
XX.XXX.193.107  192.168.1.1     255.255.255.255 UGH       0 0          0 eth1
XX.XXX.242.0    0.0.0.0         255.255.255.0   U         0 0          0 tun0
128.0.0.0       XX.XXX.242.128  128.0.0.0       UG        0 0          0 tun0
192.168.1.0     0.0.0.0         255.255.255.0   U         0 0          0 eth1
share|improve this question

2 Answers 2

As penguin359 correctly said, the problem is that the return packets will be routed over the VPN instead of via your local router, which is where the incoming connection came from. SNAT on the router is one solution, but if that's not feasible, you can use advanced routing on your PC.

You'll need to add these advanced routing rules in addition to your existing iptables rules:

ip rule add from 192.168.1.X table 128
ip route add table 128 to 192.168.1.0/24 dev eth1
ip route add table 128 default via 192.168.1.1

Replace 192.168.1.X with the LAN IP address of your PC. 192.168.1.1 is your LAN's router and 192.168.1.0/24 is your LAN's subnet.

The first command creates a rule which causes any packet with a source address of 192.168.1.X to use a special routing table which has number 128. The next two commands create routing table 128, in which the default gateway is your router (and not your VPN server like in the main routing table).

The only packets which will have a source address of 192.168.1.X will be packets responding to incoming connections from your router (i.e. port-forwarded SSH connections) and packets destined for your LAN. These are exactly the packets which you want to not use your VPN. All other outgoing packets will have a different source address and will use the main routing table which routes them over your VPN.

share|improve this answer
    
EPIC. THANK YOU! –  Dakatine Sep 30 '13 at 17:19

To clarify, while the VPN is running, SSH from external sources is broken, but prior to running the VPN, SSH from all sources was working. The problem comes down to the routing table. As you show above, the default route (0.0.0.0) is going to tun0. I don't understand what's with the funny netmask of 128.0.0.0, but that would cause any external address starting with 1-126 to use tun0. It doesn't matter where the incoming packets come from for SSH, the outgoing packets will only go out where they match in the routing table. I've done fancy configs like this myself. The standard solution I use for this kind of problem is to use incoming SNAT rules which change the source address of the incoming packet to the internal IPv4 address of the router. This will cause it to appear like an internal connection to the PC and it will happily route it back to the router since it resides on a local destination in the routing table above. The router will then reverse the SNAT and send it back out int the wild to be sniffed and prodded by all the nefarious hackers out there. This is SNAT must exist as an iptables rule on the router, not the PC inside the network. If you are just using something like a stock Linksys WRT router, you might have to install OpenWRT or similar on it to get this kind of control over the firewall/NAT rule.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.