15

The aim of this script is to only allow traffic over the VPN, except for localhost<->localhost and incoming SSH traffic. But when I run the script over SSH I am disconnected and forced to restart the vm. What is wrong with my script?

#!/bin/bash
iptables -F

#Allow over VPN
iptables -A INPUT -i tun+ -j ACCEPT
iptables -A OUTPUT -o tun+ -j ACCEPT

#Localhost
iptables -A INPUT -s 127.0.0.1/8 -j ACCEPT
iptables -A OUTPUT -d 127.0.0.1/8 -j ACCEPT

#VPN
iptables -A INPUT -s 123.123.123.123 -j ACCEPT
iptables -A OUTPUT -d 123.123.123.123 -j ACCEPT

#SSH
iptables -A INPUT -p tcp --dport ssh -j ACCEPT

#Default Deny
iptables -A INPUT -j DROP
iptables -A OUTPUT -j DROP
hellodanylo
  • 2,433
Steven
  • 153

2 Answers2

14

Your #SSH rule implies ssh is a one way form of communication, which it is not. Data is being sent forth and back.

The normal way to deal with this, since you can't know the port number on the client side in advance, is to allow connections which are considered "established" or "related" to an established connection. To do this you need:

-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
-A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

Before your DROP rules (and preferably at the top, since the rules are processed in order and these two will apply to most packets).

There's an explanation of how a TCP connection becomes ESTABLISHED here; essentially, the fact of the server replying to the packet allowed by your #SSH INPUT rule makes it so.

goldilocks
  • 87,661
  • 30
  • 204
  • 262
  • 1
    This will not work. Established means packets in both directions for a given TCP connection have been seen. If you only add this rule, the first outbound packet will still be blocked. – hellodanylo Jun 09 '14 at 11:35
  • @SkyDan You're right about what "established" means, but you are wrong about the first output packet being blocked if the connection is -p tcp and was created via an INPUT rule. Try it yourself (replace your output rule with an established, related rule). – goldilocks Jun 09 '14 at 12:05
  • 3
    @SkyDan Here's a reference for that. Notice on the diagram that when the server sends a syn/ack back to the client after receiving the opening syn, the connection is established meaning iptables will let that reply packet through: "Once it has seen one packet(the SYN), it considers the connection as NEW. Once it sees the return packet(SYN/ACK), it considers the connection as ESTABLISHED." -> again: iptables sees the return packet the server wants to send, sets the connection as established, and lets the reply through. – goldilocks Jun 09 '14 at 12:20
  • 1
    Okay, I see why it will work. It's a bit obscure, since the iptables man only talks about seeing packets in both directions, not a word about the TCP handshake packets being an exception. Thanks for the reference! – hellodanylo Jun 09 '14 at 12:24
  • 3
    @SkyDan In fact, the logic does not apply only to tcp -- I was wrong about -p tcp making any difference in this sense, and look at the subsequent explanation for UDP on that page (it's the same). The point is that the server replies without knowing whether iptables will allow it or not, and when iptables receives that reply from the server on the local system, it has now seen traffic in both directions (even though the client has not yet), considers the connection established, and lets the reply out. The "technicality" here hinges on the firewall being in the middle of the two parties. – goldilocks Jun 09 '14 at 12:28
  • 1
    You are correct there. Someone should probably include this information into the iptables man. – hellodanylo Jun 09 '14 at 13:01
12

Output chain is responsible for any packet going out.

Your script only allows outbound packets to tunnel interface, localhost and remote host at 123.123.123.123.

If you are connecting to the server in a way that requires SSH daemon to send packets to the destination other than one of the above, the traffic will not be allowed to go out.

To allow outbound packets from your SSH daemon to the SSH client you need to add the following rule:

iptables -A OUTPUT -p tcp --sport 22 -j ACCEPT

You might also want to add destination IP criteria to the above rule, if you are only connecting from a single location. This rule needs to come before the ultimate 'DROP anything else' rule for the output chain.

hellodanylo
  • 2,433