iptables/ip6tables one file script

wlanboy

Content Contributer
I want to start a thread about how a one file script for iptables/ip6tables should look like. What default settings are ok and how different services like webserver, mailservers, etc can be unlocked.

Why unlocked? Because like word filters I do not prefer blacklists:

Everytime I read a post about someone telling how good his blacklist is I just start smiling. You cannot maintain a list of all bad things that should be filtered. In my opinion only a whitelist of allowed things can really secure your services.

So back to iptables and ip6tables. More and more providers do add ipv6 ips to their vps and a lot of linux services do support ipv6 by default. So if someone like fail2ban he should add ip6tables rules too.

iptables is part of the kernel so you just have to setup rules to "activate" iptables. This can be done by adding a file to /etc/init.d/:


sudo nano /etc/init.d/iptables

But be carefull to auto start this service by default. Because if the firewall is active and you did not unlock your ssh port you can lockout yourself.

So do only call this command if you know your rules are working!


sudo chmod +x /etc/init.d/iptables

You can run this script with:


sh /etc/init.d/iptables start
sh /etc/init.d/iptables stop

If you cannot connect to your vps one of the rules might be too restrictive. Restart your vps and everything is fine (as long as the script is not started automatically).

Content of the script:


#!/bin/bash
# ---------------------------------------------------------------------
# Linux iptables init script, Copyright (c) 2013 GPL
# --------------------------------------------------------------------
### BEGIN INIT INFO
# Provides:          firewall
# Required-Start:    $remote_fs $syslog
# Required-Stop:     $remote_fs $syslog
# Default-Start:     2 3 4 5
# Default-Stop:      0 1 6
# Short-Description: Start daemon at boot time
# Description:       Enable iptables and ip6tables rules
### END INIT INFO

# iptables lookup
iptables=`which iptables`
ip6tables=`which ip6tables`

# setup network device
# device="eth0" #// KVM
device="venet0" #// OpenVZ
yourexternalid="55.55.55.55" #// Change this value!
yourvpnsubnet="10.1.1.0/24" #// Change this value!

# stop if iptables is not found
test -f "$iptables" || exit 0
test -f "$ip6tables" || exit 0

case "$1" in
  start)
    echo "setting up iptables and ip6tables"

    # flush tables
    iptables -F
    iptables -t nat -F
    iptables -t mangle -F
    iptables -X
    iptables -t nat -X
    iptables -t mangle -X
    
    ip6tables -F
    #ip6tables -t nat -F
    #ip6tables -t mangle -F
    ip6tables -X
    #ip6tables -t nat -X
    #ip6tables -t mangle -X    

    # set default policies
    iptables -P INPUT DROP
    iptables -P OUTPUT DROP
    iptables -P FORWARD DROP
    
    ip6tables -P INPUT DROP
    ip6tables -P OUTPUT DROP
    ip6tables -P FORWARD DROP    

    # MY_REJECT chain
    iptables -N MY_REJECT
    ip6tables -N MY_REJECT6

    # MY_DROP chain
    iptables -N MY_DROP
    iptables -A MY_DROP -j DROP
    
    ip6tables -N MY_DROP6
    ip6tables -A MY_DROP6 -j DROP    

    # drop invalid packages
    iptables -A INPUT -m state --state INVALID -j DROP
    iptables -A OUTPUT -m state --state INVALID -j DROP
    
    ip6tables -A INPUT -m state --state INVALID -j DROP
    ip6tables -A OUTPUT -m state --state INVALID -j DROP
    
    # allow loopback device traffic
    iptables -A INPUT -i lo -j ACCEPT
    iptables -A OUTPUT -o lo -j ACCEPT
    
    ip6tables -A INPUT -i lo -j ACCEPT
    ip6tables -A OUTPUT -o lo -j ACCEPT

    # set tracking of connection
    iptables -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
    iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

    ip6tables -A OUTPUT -m state --state NEW,ESTABLISHED,RELATED -j ACCEPT
    ip6tables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
    
    # allow ICMP ping
    iptables -A INPUT -p icmp --icmp-type echo-reply -j ACCEPT
    iptables -A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
    iptables -A OUTPUT -p icmp --icmp-type destination-unreachable -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type source-quench -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type echo-request -j ACCEPT
    iptables -A OUTPUT -p icmp --icmp-type echo-request -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT
    iptables -A INPUT -p icmp --icmp-type parameter-problem -j ACCEPT

ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

    # allow HTTP
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 80 -j ACCEPT

ip6tables -A INPUT -i $device -m state --state NEW -p tcp --dport 80 -j ACCEPT

    # allow HTTPS
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 443 -j ACCEPT

    ip6tables -A INPUT -i $device -m state --state NEW -p tcp --dport 443 -j ACCEPT

    # allow SMTP
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 25 -j ACCEPT

    # allow SMTPS
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 465 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 587 -j ACCEPT

    # allow POP3
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 110 -j ACCEPT

    # allow POP3S
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 995 -j ACCEPT

    # allow IMAP
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 143 -j ACCEPT

    # allow IMAPS
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 993 -j ACCEPT

    # allow NNTP
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 119 -j ACCEPT

    # allow DNS queries
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 53 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p udp --dport 53 -j ACCEPT

    # allow FTP
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 21 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 20 -j ACCEPT

    # allow SSH default port and changed port
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 22 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 8000 -j ACCEPT

    # allow MYSQL
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 3306 -j ACCEPT

# allow MongoDB
iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 27019 -j ACCEPT

    # allow NTP
    #iptables -A INPUT -i $device -m state --state NEW -p udp --dport 123 -j ACCEPT

    # allow IRC server
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 6667 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 6668 -j ACCEPT
    ip6tables -A INPUT -i $device -m state --state NEW -p tcp --dport 6669 -j ACCEPT
    
    # allow znc
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 9000 -j ACCEPT

    # allow polipo
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 8080 -j ACCEPT

    # allow rabbitmq
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 4369 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 5672 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 15672 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 55672 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 53958 -j ACCEPT
    iptables -A INPUT -i $device -m state --state NEW -p tcp --dport 58508 -j ACCEPT

# allow smb connections for 199.199.199.199
iptables -A INPUT -i eth0 -p tcp -s 199.199.199.199 --sport 137:139 -j ACCEPT
iptables -A INPUT -i eth0 -p udp -s 199.199.199.199 --sport 137:139 -j ACCEPT
iptables -A INPUT -i eth0 -p tcp -s 199.199.199.199 --sport 445 -j ACCEPT
iptables -A INPUT -i eth0 -p udp -s 199.199.199.199 --sport 445 -j ACCEPT
    
    # allow OPENVPN and enable ip forwarding
    iptables -A INPUT -i tun0 -j ACCEPT
    iptables -A FORWARD -i tun0 -j ACCEPT
    iptables -A FORWARD -o $device -i tun0 -j ACCEPT
    iptables -A FORWARD -o tun0 -i $device -m state --state RELATED,ESTABLISHED -j ACCEPT
    iptables -A INPUT -i $device -p udp --dport 1194 -m state --state NEW -j ACCEPT
    iptables -t nat -A POSTROUTING -s $yourvpnsubnet -j SNAT --to $yourexternalid

    # allow PPTPD and enable ip forwarding
    iptables -A INPUT -p gre -j ACCEPT
    iptables -A OUTPUT -p gre -j ACCEPT
    iptables -A INPUT -p tcp --sport 1723 -s $yourexternalid -j ACCEPT
    iptables -A OUTPUT -p tcp --dport 1723 -d $yourexternalid -j ACCEPT    
    
    # set default policies REJECT
    iptables -A INPUT -j MY_REJECT
    iptables -A OUTPUT -j MY_REJECT
    
    ip6tables -A INPUT -j MY_REJECT6
    ip6tables -A OUTPUT -j MY_REJECT6    
    ;;

  stop)
    echo "cleaning iptables and ip6tables rules"
    # flush tabelles
    iptables -F
    iptables -t nat -F
    iptables -t mangle -F
    iptables -X
    iptables -t nat -X
    iptables -t mangle -X
    
    ip6tables -F
    #ip6tables -t nat -F
    #ip6tables -t mangle -F
    ip6tables -X
    #ip6tables -t nat -X
    #ip6tables -t mangle -X    
    
    # set default policies
    iptables -P INPUT ACCEPT
    iptables -P OUTPUT ACCEPT
    iptables -P FORWARD ACCEPT
    
    ip6tables -P INPUT ACCEPT
    ip6tables -P OUTPUT ACCEPT
    ip6tables -P FORWARD ACCEPT    
    ;;

  restart)
    echo "restart iptables and ip6tables"
    $0 stop
    $0 start
    ;;

  status)
    echo "rule list of iptables and ip6tables"
    iptables -L -vn
    ip6tables -L -vn
    echo "nat table"
    iptables -t nat -L -vn
    #ip6tables -t nat -L -vn
    echo "mangle table"
    iptables -t mangle -L -vn
    #ip6tables -t mangle -L -vn
    ;;

  *)
    echo "syntax of firewal"
    echo "Syntax: $0 {start|stop|status}"
    exit 1
    ;;

esac

exit 0
There are different ways to handle this. You can use different files, you can try a lot of things. I do prefer configuration parameters and the "use as few files as possible" approach.

If someone wants to add some services or does have some recommendation just post them. I will add them to the template.
 
Last edited by a moderator:

acd

New Member
1. Never returns false because $iptables and $ip6tables are empty when `which iptables` cannot find the binary. You can correct this by quoting "$iptables" "$ip6tables":


iptables=`which iptables`
ip6tables=`which ip6tables`
...
# stop if iptables is not found
test -f $iptables || exit 0
test -f $ip6tables || exit 0
2. Old ovz kernels (RHEL5.x's 2.6.18 based) often won't take stateful IPv6 in a container. The easiest way around this is match --syn & accept ! --syn on those systems.

3. Consider using iptables-restore & ip6tables-restore and a config file to store your rules.  This allows them to be quickly saved and restored with iptables-{save,restore}

4. You don't explicitly allow ICMPV6 (-p ipv6-icmp) in, though you do ipv4 icmp?

5. I generally explicitly drop ip6tables -m rt  --rt-type 0 --rt-segsleft 0 -j DROP

6. On recent kernels/ip[6]tables versions, -m state --state is obsoleted and should be replaced by -m conntrack --ctstate. But that often doesn't work on 2.6.18.

7. I usually set my default "firewall stop" policy to DROP; If you need to get in w/ the firewall down, use the console (if your provider has one) or VNC (if your provider offers it).

8. Not sure why you have these commented out. They can be important if any rules were added there.


    #ip6tables -t nat -F
    #ip6tables -t mangle -F
    ...
    #ip6tables -t nat -X
    #ip6tables -t mangle -X  
9. I usually prefer my fw script to run when my interfaces come up or very nearly. Consider calling the firewall in a post-up/pre-down script.

best regards,

-tw
 

wlanboy

Content Contributer
best regards,

-tw
Thank you alot for your comments.

To 1: You are right. Never ever tested what happens when I search for something that is not present e.g. "which ipables". Fixed

To 2: Ok. Mind providing an RHEL compatible version?

To 3: I considered this. It's a nice feature of iptables. But this is about "one file to do all".

To 4: Added the lines. Good point to avoid the hassle between "-p icmp" and "-p ipv6-icmp"

To 5: Ok. You should explain more about the reasons why you add this drop. (IPv6 Type 0 Routing Header issue)

To 6: You are right. This script is for OpenVZ. You see that on the OpenVPN section too. E.g. not using "iptables -t nat -A POSTROUTING -s $yourvpnsubnet -o $device -j MASQUERADE" to ensure everything is working.

To 7: You are right. But for newcomers it better to clear all rules and to allow everything. To restore the state before they started the script.

To 8: A lot of OpenVZ providers do not add the proper ip6tables modules. This is all about avoiding "can't initialize ip6tables table `nat': Table does not exist (do you need to insmod?)". Difference between "not adding" and "comment something out".

To 9: You are right. But like point 3 - this would include to edit more than one file.

Looking forward to your version of this script.
 
Last edited by a moderator:

peterw

New Member
# allow OPENVPN and enable ip forwarding
iptables -A INPUT -i tun0 -j ACCEPT
iptables -A FORWARD -i tun0 -j ACCEPT
iptables -A FORWARD -o $device -i tun0 -j ACCEPT
iptables -A FORWARD -o tun0 -i $device -m state --state RELATED,ESTABLISHED -j ACCEPT
iptables -A INPUT -i $device -p udp --dport 1194 -m state --state NEW -j ACCEPT
iptables -t nat -A POSTROUTING -s $yourvpnsubnet -j SNAT --to $yourexternalid
Great tutorial. Thank you for the OpenVPN rules!
 
Last edited by a moderator:

acd

New Member
This is approximately what your ipv6 rules would look like with my usual additions. I didn't include my usual allows for link-local since you folks probably don't trust link-local on a VPS (where I use it, this is not a problem).

Code:
# flush tables
ip6tables -F
#ip6tables -t nat -F
#ip6tables -t mangle -F
ip6tables -X
#ip6tables -t nat -X
#ip6tables -t mangle -X

ip6tables -P INPUT DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -P FORWARD DROP

# MY_REJECT chain
ip6tables -N MY_REJECT6
ip6tables -A MY_REJECT6 -j REJECT

# MY_DROP chain
ip6tables -N MY_DROP6
ip6tables -A MY_DROP6 -j DROP

# drop packets with routing header type 0 and any remaining segments (more than 0)
# deprecating RFC: http://www.ietf.org/rfc/rfc5095.txt
# amplification attack: http://www.secdev.org/conf/IPv6_RH_security-csw07.pdf
ip6tables -A INPUT -m rt --rt-type 0 -j DROP
ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP
ip6tables -A FORWARD -m rt --rt-type 0 -j DROP

# allow loopback device traffic
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

# allow non-syn IPv6 TCP traffic (connection already established).
# The kernel will drop everything else for us anyway.
ip6tables -A INPUT -p tcp ! --syn -j ACCEPT

# allow ICMP (and thus SLAAC, etc)
ip6tables -A INPUT -p ipv6-icmp -j ACCEPT

# allow DHCPv6 configuration
ip6tables -A INPUT -p udp --sport 547 --dport 546 -j ACCEPT

# allow DNS reply
ip6tables -A INPUT -p udp --sport 53 --dport 32000:65535 -j ACCEPT

# allow HTTP
ip6tables -A INPUT -i $device -p tcp --syn --dport 80 -j ACCEPT

# allow HTTPS
ip6tables -A INPUT -i $device -p tcp --syn --dport 443 -j ACCEPT

# allow IRC server
ip6tables -A INPUT -i $device -p tcp --syn --dport 6669 -j ACCEPT

# set default policies REJECT
ip6tables -A INPUT -j MY_REJECT6
 

acd

New Member
Sorry I'm late getting back to you. I would just use similar forward rules to ipv6, except no nat.  So just pass everything through to known good IPs on your VPN and drop the rest, and from VPN to internet, passall. Roughly:



ip6tables -X
ip6tables -N goodvpnipv6

ip6tables -A FORWARD -i tun0 -j ACCEPT
ip6tables -A FORWARD -o tun0 -j goodvpnipv6
ip6tables -A FORWARD -o tun0 -j DROP

ip6tables -A goodvpnipv6 -d an:ip:addr:in:your::nets -j ACCEPT
ip6tables -A goodvpnipv6 -d an:ip:to::drop -j DROP
Making sure those IPv6s are routed properly to vpn clients is outside of the scope of this script. I don't support any kind of NAT/NPT for ipv6; it's best you figure that out on your own if you need it.
 
Top