iptables/ip6tables one file script

Discussion in 'Tutorials and Guides' started by wlanboy, Jun 29, 2013.

  1. wlanboy

    wlanboy Content Contributer

    2,126
    1,169
    May 16, 2013
    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: Nov 17, 2013
    souen, vRozenSch00n, Kyn_DH and 6 others like this.
  2. HalfEatenPie

    HalfEatenPie The Irrational One Retired Staff

    2,890
    1,386
    Mar 25, 2013
    HalfEatenPie
    Fantastic!  This is just fantastic!  

    Thanks for this great script/tutorial!  
     
  3. acd

    acd New Member

    176
    71
    May 16, 2013
    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
     
  4. wlanboy

    wlanboy Content Contributer

    2,126
    1,169
    May 16, 2013
    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: Jun 29, 2013
  5. peterw

    peterw New Member

    800
    189
    Jun 14, 2013
    Great tutorial. Thank you for the OpenVPN rules!
     
    Last edited by a moderator: Jul 1, 2013
  6. acd

    acd New Member

    176
    71
    May 16, 2013
    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
    
     
  7. peterw

    peterw New Member

    800
    189
    Jun 14, 2013
    Thank you. Can you show me the ip6tables rules for openvpn service too?
     
  8. acd

    acd New Member

    176
    71
    May 16, 2013
    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.
     
  9. wlanboy

    wlanboy Content Contributer

    2,126
    1,169
    May 16, 2013
    Updated the rules for smb mounts (limited to one ip) on request.