Configure Iptables Firewall on a Debian PC

Set up iptables firewall rules for inbound and outbound IPv4 traffic on a Debian PC (no routing).

Show Current Configuration for IPv4

# iptables -t filter -nL

Save Current IPv4 Rules to a Backup File

# iptables-save > /root/iptables.rules.backup

Flush and Delete any Existing Chain Configuration for IPv4

# iptables -F
# iptables -X
# iptables -t nat -F
# iptables -t nat -X
# iptables -t mangle -F
# iptables -t mangle -X

Setup a New Configuration for IPv4 and Apply New Rules

Create a file to store configuration:

# touch /etc/iptables.up.rules

Add iptables rules to /etc/iptables.up.rules. Below are my Debian PC rules as an example.

*filter
##############################
# FLUSH EXISTING CHAIN RULES #
##############################

-F INPUT
-F OUTPUT
-F FORWARD

##############################
# SET DEFAULT CHAIN POLICIES #
##############################

### Default INPUT and OUTPUT are set to ACCEPT because I sometimes 
### have to do "iptables -F" and need all traffic to come and leave
### uninterrupted for troubleshooting. Do iptables-restore afterwards.

-P INPUT ACCEPT
-P FORWARD ACCEPT
-P OUTPUT ACCEPT

#####################
# INPUT CHAIN RULES #
#####################

### My LAN is on 10.10.1.0/24
### My vboxnet is on 10.8.8.0/24
### My eth0 and wlan0 interfaces are bonded (bond0)

### Allow inbound loopback
-A INPUT -i lo -j ACCEPT -m comment --comment "local"
-A INPUT ! -i lo -d 127.0.0.0/8 -j REJECT

### Allow inbound SSH (from LAN and VPN)
-A INPUT -s 10.0.0.0/8 -p tcp -m state --state NEW --dport 22 -j ACCEPT

### Allow inbound Echo ICMP
-A INPUT -p icmp -m icmp --icmp-type 8 -m limit --limit 20/second -j ACCEPT

### Allow inbound NetFlow for Ntop
-A INPUT -s 10.10.1.1/32 -p udp --dport 2055 -j ACCEPT

### Allow inbound Zabbix
-A INPUT -s 10.10.1.17/32 -p tcp --dport 10050 -j ACCEPT

### Stop filling logs
-A INPUT -p udp -m multiport --dport 137,138,139,445 -j DROP
-A INPUT -p udp -d 255.255.255.255 -j DROP

### Allow established and related traffic
-A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT

### Log and reject everything else
-A INPUT -j LOG --log-level 4 --log-prefix "iptables_input "
-A INPUT -j REJECT --reject-with icmp-port-unreachable

#######################
# FORWARD CHAIN RULES #
#######################

-A FORWARD -j LOG --log-level 4 --log-prefix "iptables_forward "
-A FORWARD -j REJECT --reject-with icmp-port-unreachable

######################
# OUTPUT CHAIN RULES #
######################

### Allow outbound loopback
-A OUTPUT -o lo -d 127.0.0.0/8 -j ACCEPT -m comment --comment "local"

### Allow outbound any ICMP
-A OUTPUT -p icmp -m icmp --icmp-type any -j ACCEPT

### Basically, we want only encrypted VPN traffic to be allowed to leave (via tun0).
### However, before that, we need to connect to a VPN gateway.
### Allow outbound to VPN gateway
-A OUTPUT -d vpn.example.com -p udp --dport 443 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT -m comment --comment "VPN GW"

### Access to LAN should be allowed via any interface really.
### Allow outbout to LAN and vboxnet
-A OUTPUT -o bond0 -d 10.10.1.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -o eth0 -d 10.10.1.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -o wlan0 -d 10.10.1.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -o vboxnet0 -d 10.8.8.0/24 -m state --state NEW,RELATED,ESTABLISHED -j ACCEPT

### Multiports allow outbound traceroute via VPN
-A OUTPUT -o tun0 -p udp -m multiport --dport 33434:33523 -j ACCEPT

### Allow outbound WHOIS via VPN
-A OUTPUT -o tun0 -p tcp --dport 43 -j ACCEPT

### Allow outbound DNS via VPN
-A OUTPUT -o tun0 -p udp --dport 53 -j ACCEPT
-A OUTPUT -o tun0 -p tcp --dport 53 -j ACCEPT

### Allow outbound NTP via VPN
-A OUTPUT -o tun0 -p udp --dport 123 -j ACCEPT

### Allow outbound rsync via VPN
-A OUTPUT -o tun0 -p tcp --dport 873 -j ACCEPT

### Multiports allow non-encrypted outbound POP3 and IMAP via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 110,143 -j ACCEPT

### Multiports allow outbound SMTP(S), IMAPS and POP3S via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 25,465,587,993,995 -j ACCEPT

### Multiports allow outbound OpenLDAP(S) via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 389,636 -j ACCEPT

### Multiports allow outbound NFS and RPCBIND via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 111,2049 -j ACCEPT
-A OUTPUT -o tun0 -p udp -m multiport --dport 111,2049 -j ACCEPT

### Allow outbound Squid proxy via VPN
-A OUTPUT -o tun0 -p tcp --dport 3128 -j ACCEPT

### Multiports allow outbound various HTTP(S) via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 80,443 -j ACCEPT
-A OUTPUT -p tun0 -p tcp -m multiport --dport 3000,3001 -j ACCEPT
-A OUTPUT -p tun0 -p tcp -m multiport --dport 8080,8443 -j ACCEPT
-A OUTPUT -o tun0 -p tcp -m multiport --dport 943,1080,8140,8834 -j ACCEPT

### Multiports allow outbound SSH via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 22,2212 -j ACCEPT

### Multiports allow outbound RDP via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 3389 -j ACCEPT

### Multiports allow outbound Zabbix via VPN
-A OUTPUT -o tun0 -p tcp -m multiport --dport 10051,38088 -j ACCEPT

### Multiports allow outbound P2P (custom ports) via VPN
-A OUTPUT -o tun0 -p udp -m multiport --sport 63001:64000 -j ACCEPT
-A OUTPUT -o tun0 -p tcp -m multiport --sport 63001:64000 -j ACCEPT

### Allow established and related VPN traffic
-A OUTPUT -o tun0 -m state --state ESTABLISHED,RELATED -j ACCEPT

### Log if required and reject everything per interface
#-A OUTPUT -j LOG --log-level 4 --log-prefix "iptables_output "
-A OUTPUT -o bond0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -o eth0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -o wlan0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -o tun0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -o vboxnet0 -j REJECT --reject-with icmp-port-unreachable
-A OUTPUT -j REJECT --reject-with icmp-port-unreachable
COMMIT

Apply changes:

# iptables-restore < /etc/iptables.up.rules

Make Iptables Rules for IPv4 to Start on Boot

Create a startup file:

# cat > /etc/network/if-pre-up.d/iptables << EOL
#!/bin/bash
/sbin/iptables-restore < /etc/iptables.up.rules
EOL

Make it executable:

# chmod 0755 /etc/network/if-pre-up.d/iptables

Iptables Rules for IPv6

These are my rules for IPv6 (which my ISP does not currently use). Simply. Drop. Everything.

# cat /etc/ip6tables.up.rules
*filter
##############################
# FLUSH EXISTING CHAIN RULES #
##############################

-F INPUT
-F OUTPUT
-F FORWARD

##############################
# SET DEFAULT CHAIN POLICIES #
##############################

-P INPUT DROP
-P FORWARD DROP
-P OUTPUT DROP

COMMIT

In this case it is also worth disabling IPv6 in the /etc/sysctl.conf:

net.ipv6.conf.lo.disable_ipv6 = 0
net.ipv6.conf.all.disable_ipv6 = 1
net.ipv6.conf.default.disable_ipv6 = 1

However, I suggest to leave IPv6 on for loopback.

Make Iptables IPv6 Rules to Start on Boot

Create a startup file:

# cat > /etc/network/if-pre-up.d/ip6tables << EOL
#!/bin/bash
/sbin/ip6tables-restore < /etc/ip6tables.up.rules
EOL

Make it executable:

# chmod 0755 /etc/network/if-pre-up.d/ip6tables

8 thoughts on “Configure Iptables Firewall on a Debian PC

  1. When I update firewall rules with:

    iptables-restore < /etc/iptables.up.rules

    current ssh connection to Debian machine hang, and I cannot extablish new ssh connections.
    I have a debian 7.6 on a Hetzner dedicated server.
    Tnx in advance, best regards.

    • What’s the SSH server port you’re using? Is it a standard TCP port 22? Have you enabled it on iptables?

      Can you post the output of:

      # netstat -nltp | grep ssh
      # cat /etc/iptables.up.rules
  2. Hi, I used your tutorial to secure my debian wheezy vps and today I stumbled upon this https://www.rosehosting.com/blog/securing-your-ubuntudebian-based-vps-using-iptablesnetfilter-firewall and this https://www.rosehosting.com/blog/blocking-abusive-ip-addresses-using-iptables-firewall-in-debianubuntu/ iptables articles so I wondered if there will be any issues/interference with your firewall script and the one shown on the articles?

    Im reall not sure what exactly am I doing but I like to easily block some abusing IPs and continue to do that in the future.

    Thanks

    • You shouldn’t be applying iptables rules unless you understand what they do. Otherwise you may lock yourself out of your own server.

      The iptables rules (script) that are provided on this article are adapted to my particular case. These may or may not work for you. All depends on the services you use and access you want to allow. For example, some people tend to set the default OUTPUT chain policy to ALLOW and do not configure any rules. I like to have control over what is allowed to leave my PC/server.

      Assuming you use the standard SSH port 22, you can configure incoming iptables rules something like this:

      # iptables -P INPUT ACCEPT
      # iptables -A INPUT -p tcp --dport 22 -j ACCEPT
      # iptables -A INPUT -j DROP

      The above sets the default INPUT chain policy to ACCEPT, allows TCP connections to port 22 and drops everything else including any abusing IPs. If you want to drop just some specific IPs (say 10.1.2.3), you can do it this way:

      # iptables -A INPUT -s 10.1.2.3/32 -j DROP
  3. In the past I used iptables-persistent on a Debian 6 (squeeze) v-server. Now I tried the same on a new Debian 7 (wheezy) root server but following the instructions I used in the past it no longer works. When I follow your instructions with my rules it works perfectly well. Does it mean iptables-persistent has become obsolete?

    What me confuses is the fact that even the Debian wiki describes it, less detailed, but much similar to you. On the other hand the wiki still mentions iptables-persistent. Why do they, if it’s no longer needed?

    • Meanwhile it seems I’ve got it worked out. It looks like iptables-persistent is still a valid option. The reason it did not work was that I used iptables-save instead of ip6tables-save to store my ip v6 tables to rules.v6.
      However, I still see a difference in the handling of the startup files during the boot process: while your startup file is started before the network interfaces are started, iptables-persistent is started by one of the defined run levels which leaves me a little bit uncertain, because I don’t know if at this point the interfaces are already started or not. I guess they are which leaves a potential security hole, right?

    • I just checked iptables-persistent on a Debian Wheezy VM. I see that the default runlevel is set to 2, and iptables-persistent service is started with the highest priority S01 at that runlevel. Network interface is initialised in runlevel S, which is run first except when booting in emergency. I don’t see any security holes here. Do you?

Leave a Reply to Marco Ingrosso Cancel reply

Your email address will not be published. Required fields are marked *