Pages

Tuesday 24 December 2013

Linux: iptables


Contents

To make a Bittorrent application work with Linux firewall iptables, you need to allow the Bittorrent client application to establish network connections to the network. Also, the Bittorent application need to accept connections from peer clients. In this section, we detail the iptables rules needed to achieve the above. These rules have been tested on a Linux Debian OS with the qBittorrent application. the same should apply to other torrent applications and Linux distributions with minor modifications if not any.




BitTorent ports
  • BitTorent client listens to one TCP port for incoming connections from peer torrent clients. This port is configurable and is normally set according to the standard to one value in the range 6881 to 6889. This range has then been extended from 6881 to 6999. It is however very common among torrent users to set this port outside the specified range in the standard, These users try to avoid Internet Service Providers (ISPs) bandwidth limitations set on these ports. Indeed, ISPs tend to limit Peer to Peer (P2P) applications due to the amount of internet usage they accounts for. 
  • BitTorent client should be allowed for TCP connections to peer torrent clients. For the reasons explained above, it very common for these ports to be outside the range 6881 to 6999.
  • If enabled, the DHT extension (peer2peer tracker) uses various UDP ports negotiated by the peers. At the client the UDP listening port is configurable.
  • The Bittorrent client should be allowed to reach the tracker server at port 6969. Note however, that most of the tracker servers use instead TCP port 80.
The table bellow depicts the list of UDP and TCP used by BitTorrent clients. [source: Wikipedia, List of TCP and UDP port numbers]:
Note that these ports are not registered IANA ports (1024 to 49151).

  Ports TCP   UDP
 6881-6968  TCP  UDP  BitTorrent part of full range ports used
 6969  TCP  BitTorrent Tracker
 6970-6999 TCP  UDP  BitTorrent part of full range ports used
 7000  TCP  Default for Vuze's built in HTTPS Bittorent Tracker
 10000 and above  TCP  UDP  Used by most BitTorrent applications.



Firewall Generic Rules for a BitTorrent application

The following table gives the generic rules you need to implement in your firewall to allow the BitTorrent client to work. In this table BitTorrent client IP is the IP of your network device that is connected to the internet.

 rule  Protocol Src. Port  Dest. Port  Src. IP  Dst. IP  Notes
 1  TCP  1024:65535  80  BitTorrent client IP  ANY Allow HTTP connections to web servers and to BitTorrent trackers.
 2  TCP  1024:65535  6881-65535  BitTorrent client IP  ANY  Allow BitTorrent client TCP connections to other peer clients.
 3  UDP  1024-65535  6881-65535  BitTorrent client IP  ANY Allow BitTorrent client UDP connection to other peer clients
 4  TCP 1024-65535   Configurable TCP port   ANY  BitTorrent client Allow peers to establish TCP connections to your BitTorrent application. 
 5  UDP  1024-65535  Configurable UDP port  ANY  BitTorrent client Allow you BitTorrent client to receive UDP packets from peers.
 6  TCP  1024-65535  53  BitTorrent Client IP  DNS server IP Allow TCP connections to the Domain Name Server (DNS) 



iptables Firewall Rules for a Standalone System

We need to translate the generic rules in the table above to iptables rules. Basically, the to set at the firewall should allow input and output TCP and UDP connections at specified ports and IP addresses. The Generic, iptable to allow output TCP connection is as follows:

iptables -A OUTPUT -o $ETH -p tcp \
   -s $TORRENT_CLIENT_IP --sport $UNPRIVPORTS \
     --dport $UNPRIVPORTS  \
    -m state --state NEW -j ACCEPT

iptables -A OUTPUT -o $ETH -p tcp ! --syn \
  -s $TORRENT_CLIENT_IP  --sport $UNPRIVPORTS \
   --dport $UNPRIVPORTS -j ACCEPT

iptables -A INPUT -i $ETH -p tcp ! --syn \
  -d $TORRENT_CLIENT_IP  --dport $UNPRIVPORTS \
  --sport $UNPRIVPORTS -j ACCEPT

In the above:

 -A OUTPUT Appends the rule to the 'output' chain.  
-o $ETH 
Specifies the network device name the rule applies applies to. If no device is specified, the rule is applied to all network devices.  

Generaly, the output network device is eth0. You may check your network devices on your machine using the command "ip add"

root@nemo:~# ip add
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 16436 qdisc noqueue state UNKNOWN
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc pfifo_fast state UP qlen 1000
link/ether 00:02:a5:22:8c:07 brd ff:ff:ff:ff:ff:ff
inet 192.168.153.51/24 brd 192.168.153.255 scope global eth0
inet6 fe80::202:a5ff:fe22:8c07/64 scope link
valid_lft forever preferred_lft forever


The output shows in our case two devices. the loopback device noted 'lo' and the 'eth0' network device with IP address 192.168.153.51. Thus we have:

ETH = eth0
TORRENT_CLIENT_IP = 192.168.153.51 
-p tcp   Specifies the TCP protocol that the rule applies to.   
-s $TORRENT_CLIENT_IP   The IP of the network device.
--sport $UNPRIVPORTS  
The source port allowed to establish a TCP connection. $UNPRIVPORTS are the unregistered private ports range 1024 to 65535.  Therefore:

UNPRIVPORTS = 1024:65535

 --dport $UNPRIVPORTS The destination port. 
 -m state --state NEW Rules in iptables are applied on a packet by packet basis. The 'state' filter option here is a performance enhancement that allows to bypass rule checking to packets that belongs to an established TCP or UDP connection. the option '--state NEW' is equivalent to the SYN TCP packet or the first UDP packet.

The rule bellow is necessary in case the state gets initialized (lost) in an ongoing TCP or UDP connection.

iptables -A OUTPUT -o $ETH -p tcp ! --syn \
  -s $TORRENT_CLIENT_IP  --sport $UNPRIVPORTS \
   --dport $UNPRIVPORTS -j ACCEPT 

 -j ACCEPT If the rule matches the received packet then accept it. 


Related Links

This wiki shows how to save iptables rules to a file and then use it to restore the rules at boot time. This is a handy way for iptables rules to survive a reboot.

You'll learn here how to configure programs to start up automatically at login. You may use that to start your torrent application without further action at boot time. This is useful if your BitTorrent client runs on a dedicated machine that you access remotely via the Torrent's application Web User Interface (Web UI).

A list of Internet service providers (ISPs) that are known to cause trouble for BitTorrent clients and the reason why.

The documentation of qBittorent.

You can use qBittorrent application through a Web User Interface (UI) only. In that case, your desktop do not need X server. This link describes how to achieve this, you need however to recompile the qBittorrent application.



Example
Bellow is an example of a script for iptables rules for the qBittorent application. The qBittorent application runs on HOME_DEBIAN (192.168.153.51) on the same subnet HOME_HP (192.168.153.21) is accessing the WEB UI of the qBittorent on port 5901.

#!/bin/bash

IPT="/sbin/iptables"


if [ "$1" = "status" ]
then
$IPT -L -v -n
exit 0
fi

ENABLE_LOGGING="1"
UDP_TORRENT="0"
ACCEPT_ICMP="1"
CONNECTION_TRACKING="1"
UNPRIVPORTS="1024:65535"
TORRENT_PORTS="6881"
TORRENT_PORTS_RANGE="6881:65535" 
SSH_PORTS="1020:65535"


HOMELAN="192.168.153.0/24"
HOMELAN_ADDR="192.168.153.1"
HOME_HP="192.168.153.21"
HOME_DEBIAN="192.168.153.51"
ETH="eth0"

ANY_ADDR="0.0.0.0/0"
LOOPBACK="127.0.0.0/8"

############################################
# Remove any existing rules from all chains#
############################################
$IPT --flush
$IPT -t nat --flush
$IPT -t mangle --flush

$IPT -X
$IPT -t nat -X
$IPT -t mangle -X

echo 1 > /proc/sys/net/ipv4/tcp_syncookies
echo 1 > /proc/sys/net/ipv4/icmp_echo_ignore_broadcasts
echo M1
###########################
# Reset the default policy#
###########################
$IPT --policy INPUT   ACCEPT
$IPT --policy OUTPUT  ACCEPT
$IPT --policy FORWARD ACCEPT
$IPT -t nat --policy PREROUTING  ACCEPT
$IPT -t nat --policy OUTPUT ACCEPT
$IPT -t nat --policy POSTROUTING ACCEPT
$IPT -t mangle --policy PREROUTING ACCEPT
$IPT -t mangle --policy OUTPUT ACCEPT

if [ "$1" = "stop" ]
then
echo "Firewall completely stopped!  WARNING: THIS HOST HAS NO FIREWALL RUNNING."
exit 0
fi

##############################################
# Unlimited traffic on the loopback interface#
##############################################

$IPT -A INPUT  -i lo -j ACCEPT
$IPT -A OUTPUT -o lo -j ACCEPT


#################################
# Set the default policy to drop#
#################################

iptables --policy INPUT DROP
iptables --policy FORWARD DROP
iptables --policy OUTPUT DROP

#$IPT -t nat --policy PREROUTING  DROP
#$IPT -t nat --policy OUTPUT DROP
#$IPT -t nat --policy POSTROUTING DROP

#$IPT -t mangle --policy PREROUTING DROP
#$IPT -t mangle --policy OUTPUT DROP

###############
#Stealth Scans#
###############
# Unclean
#$IPT -A INPUT -m unclean -j DROP
# All of the bits are cleared
$IPT -A INPUT -p tcp --tcp-flags ALL NONE -j DROP
# SYN and FIN are both set
$IPT -A INPUT -p tcp --tcp-flags SYN,FIN SYN,FIN -j DROP
# SYN and RST are both set
$IPT -A INPUT -p tcp --tcp-flags SYN,RST SYN,RST -j DROP
# FIN and RST are both set
$IPT -A INPUT -p tcp --tcp-flags FIN,RST FIN,RST -j DROP
# FIN is the only bit set, without the expected accompanying ACK
$IPT -A INPUT -p tcp --tcp-flags ACK,FIN FIN -j DROP
# PSH is the only bit set, without the expected accompanying ACK
$IPT -A INPUT -p tcp --tcp-flags ACK,PSH PSH -j DROP
# URG is the only bit set, without the expected accompanying ACK
$IPT -A INPUT -p tcp --tcp-flags ACK,URG URG -j DROP

##########################################
#Enable for Debuging purposes            #
##########################################
if [ "$ACCEPT_ICMP" = "1" ]; then
  iptables -A INPUT -p icmp -j ACCEPT
  iptables -A FORWARD -p icmp -j ACCEPT
  iptables -A OUTPUT -p icmp -j ACCEPT
fi
##########################################
#Connection State to bypass rule checking#
##########################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
  iptables -A INPUT -m state --state ESTABLISHED -j ACCEPT
  iptables -A FORWARD -m state --state ESTABLISHED -j ACCEPT
  iptables -A OUTPUT -m state --state ESTABLISHED -j ACCEPT
fi


###############################################
# Refuse spoofed packets pretending to be from#
# the external interface's IP address         #
###############################################
$IPT -A INPUT  -i $ETH -s $HOME_DEBIAN -j DROP

############################################################
# Refuse packets claiming to be from the loopback interface#
############################################################
$IPT -A INPUT  -i $ETH -s $LOOPBACK -j DROP



####################################################
#Allow DNS lookup                                  #
####################################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
  iptables -A OUTPUT -o $ETH -p udp \
   -s $HOME_DEBIAN --sport $UNPRIVPORTS \
     -d 195.170.0.1 --dport 53 \
    -m state --state NEW -j ACCEPT
fi

iptables -A OUTPUT -o $ETH -p udp \
  -s $HOME_DEBIAN --sport $UNPRIVPORTS \
  -d 195.170.0.1 --dport 53 -j ACCEPT

iptables -A INPUT -i $ETH -p udp \
  -d $HOME_DEBIAN --dport $UNPRIVPORTS \
  --sport 53 -s 195.170.0.1 -j ACCEPT

####################################################
#Allow TCP connection from LANA to STB for upgrade #
####################################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
  iptables -A OUTPUT -o $ETH -p tcp \
   -s $HOME_DEBIAN --sport $UNPRIVPORTS \
     --dport 80 \
    -m state --state NEW -j ACCEPT
fi

iptables -A OUTPUT -o $ETH -p tcp \
  -s $HOME_DEBIAN --sport $UNPRIVPORTS \
   --dport 80 -j ACCEPT

iptables -A INPUT -i $ETH -p tcp ! --syn \
  -d $HOME_DEBIAN --dport $UNPRIVPORTS \
  --sport 80 -j ACCEPT


####################################################
#Allow TCP connection to torrents clients          #
####################################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
  iptables -A OUTPUT -o $ETH -p tcp \
   -s $HOME_DEBIAN --sport $UNPRIVPORTS \
     --dport $TORRENT_PORTS_RANGE  \
    -m state --state NEW -j ACCEPT
fi

iptables -A OUTPUT -o $ETH -p tcp \
  -s $HOME_DEBIAN --sport $UNPRIVPORTS \
   --dport $TORRENT_PORTS_RANGE -j ACCEPT

iptables -A INPUT -i $ETH -p tcp ! --syn \
  -d $HOME_DEBIAN --dport $UNPRIVPORTS \
  --sport $TORRENT_PORTS_RANGE -j ACCEPT

if [ "$CONNECTION_TRACKING" = "1" ]; then
  iptables -A OUTPUT -o $ETH -p udp \
   -s $HOME_DEBIAN --sport $UNPRIVPORTS \
     --dport $TORRENT_PORTS_RANGE  \
    -m state --state NEW -j ACCEPT
fi

iptables -A OUTPUT -o $ETH -p udp \
  -s $HOME_DEBIAN --sport $UNPRIVPORTS \
   --dport $TORRENT_PORTS_RANGE -j ACCEPT

iptables -A INPUT -i $ETH -p udp \
  -d $HOME_DEBIAN --dport $UNPRIVPORTS \
  --sport $TORRENT_PORTS_RANGE -j ACCEPT


#####################################
# Allow SSH incoming access to debian #
# Allow SSH incoming accessto debian #
#####################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
 iptables -A INPUT -i $ETH -p tcp \
   -s $HOME_HP --sport $SSH_PORTS \
    -d $HOME_DEBIAN --dport 22 \
    -m state --state NEW -j ACCEPT
fi

iptables -A INPUT -i $ETH -p tcp \
  -s $HOME_HP --sport $SSH_PORTS \
   -d $HOME_DEBIAN --dport 22 -j ACCEPT

iptables -A OUTPUT -o $ETH -p tcp ! --syn \
  -d $HOME_HP --dport $SSH_PORTS \
  -s $HOME_DEBIAN --sport 22 -j ACCEPT


if [ "$CONNECTION_TRACKING" = "1" ]; then
 iptables -A INPUT -i $ETH -p tcp \
   -s $HOME_HP --sport $SSH_PORTS \
    -d $HOME_DEBIAN --dport 5901 \
    -m state --state NEW -j ACCEPT
fi

iptables -A INPUT -i $ETH -p tcp \
  -s $HOME_HP --sport $SSH_PORTS \
   -d $HOME_DEBIAN --dport 5901 -j ACCEPT

iptables -A OUTPUT -o $ETH -p tcp ! --syn \
  -d $HOME_HP --dport $SSH_PORTS \
  -s $HOME_DEBIAN --sport 5901 -j ACCEPT



##################################
# Allow VNC connection
##################################

if [ "$CONNECTION_TRACKING" = "1" ]; then
 iptables -A INPUT -i $ETH -p tcp \
   -s $HOME_HP --sport $UNPRIVPORTS \
    -d $HOME_DEBIAN --dport 5900 \
    -m state --state NEW -j ACCEPT
fi

iptables -A INPUT -i $ETH -p tcp \
  -s $HOME_HP --sport $UNPRIVPORTS \
   -d $HOME_DEBIAN --dport 5900 -j ACCEPT

iptables -A OUTPUT -o $ETH -p tcp \
  -d $HOME_HP --dport $UNPRIVPORTS \
  -s $HOME_DEBIAN --sport 5900 -j ACCEPT


##################################
# Allow peer torrents connections 
##################################
if [ "$CONNECTION_TRACKING" = "1" ]; then
 iptables -A INPUT -i $ETH -p tcp \
    --sport $UNPRIVPORTS \
    -d $HOME_DEBIAN --dport $TORRENT_PORTS \
    -m state --state NEW -j ACCEPT
fi

iptables -A INPUT -i $ETH -p tcp \
   --sport $UNPRIVPORTS \
   -d $HOME_DEBIAN --dport $TORRENT_PORTS -j ACCEPT

iptables -A OUTPUT -o $ETH -p tcp ! --syn \
  --dport $TORRENT_PORTS \
  -s $HOME_DEBIAN --sport $TORRENT_PORTS -j ACCEPT

if [ "$UDP_TORRENT" = "1" ]; then 
if [ "$CONNECTION_TRACKING" = "1" ]; then
 iptables -A INPUT -i $ETH -p udp \
    --sport $UNPRIVPORTS \
    -d $HOME_DEBIAN --dport $TORRENT_PORTS \
    -m state --state NEW -j ACCEPT
fi

iptables -A INPUT -i $ETH -p udp \
   --sport $UNPRIVPORTS \
   -d $HOME_DEBIAN --dport $TORRENT_PORTS -j ACCEPT

iptables -A OUTPUT -o $ETH -p udp  \
  --dport $TORRENT_PORTS \
  -s $HOME_DEBIAN --sport $TORRENT_PORTS -j ACCEPT
fi




#drop everything and Log it
iptables -A INPUT -j LOG --log-prefix IN
iptables -A INPUT -j DROP
iptables -A OUTPUT -j LOG --log-prefix OUT
iptables -A OUTPUT -j DROP

#if [ "$ENABLE_LOGGING" = "1" ]; then
#for i in $STBLANS;do
#   iptables -A CDNCHAIN -s $i -j LOG --log-prefix STB_INVALID:
#done
#   iptables -A CDNCHAIN -s $STBLAN2 -j LOG --log-prefix STB_INVALID:
#   iptables -A CDNCHAIN -s $ANY_ADDR -j LOG --log-prefix UNKNOW_INVALID:
#fi

#for i in $STBLANS;do
#iptables -A CDNCHAIN -s $i -j DROP
#iptables -A CDNCHAIN -s $STBLAN2 -j DROP
#done
#iptables -A CDNCHAIN -s $ANY_ADDR -j DROP