+++
date = '2024-10-20T14:22:33-03:00'
title = 'Mastering iptables: Basic Firewall Configurations for a Newly Deployed GNU/Linux Server'
description = "A step-by-step guide to configuring iptables firewall rules for a newly deployed GNU/Linux server, covering SSH, HTTP/HTTPS, IPv6, ICMP, and security best practices."
tags = [
    "webserver",
    "GNU/Linux",
    "Basics Series",
]
+++

When setting up a new Linux server, one of the first things you’ll want to do is secure it by configuring a firewall. iptables, a powerful firewall tool available on most Linux distributions, is an excellent choice for managing traffic rules.

In this post, I’ll walk you through setting up some basic iptables configurations for a freshly deployed server. By the end, you’ll have a solid firewall to protect your services while keeping things simple.

## What is iptables?

iptables is a command-line firewall utility that uses policy chains to manage network traffic. Each chain is a list of rules that apply to incoming and outgoing packets. You can filter traffic based on things like IP addresses, ports, and protocols.

Securing a server is a top priority for any sysadmin, and firewalls are one of the most important layers of defense. With iptables, you can:

- [ ] Block unwanted traffic
- [ ] Allow trusted connections (e.g., SSH, HTTP/HTTPS)
- [ ] Protect against DoS attacks and port scanning
- [ ] Log suspicious activity for future analysis

## How a packet flows in iptables

I highly suggest that you read more about the packet flow in iptables before applying firewall rules.

You can check those well made diagrams by [nerdalert on Github](https://gist.github.com/nerdalert/a1687ae4da1cc44a437d)

## Basic iptables Commands

Let’s start with a quick overview of essential iptables commands:

### Show current rules

```bash
iptables -L
```

### Flush (delete) all existing rules

```bash
iptables -F
```

### Save iptables rules

If you are running Debian, you need to install the `iptables-persistent` package, and then you can use the following commands:

```bash
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
```

Restore iptables rules:

```bash
iptables-restore < /etc/iptables/rules.v4
ip6tables-restore < /etc/iptables/rules.v6
```

## Setting Up iptables for a New Server

Now, let’s walk through some basic rules you can apply to secure your server right from the start. We’ll allow essential services like SSH and HTTP/HTTPS while blocking everything else.

### Step 1: Clear Existing Rules

Before setting up new rules, let’s clear any pre-existing configurations:

```bash
iptables -F
```

This ensures you’re starting with a clean slate.

Be careful with the following commands, since you can get locked you of your own server! That happens to every sysadmin sooner or later. The easiest way to be sure that it doesn't happen is to do it physically, or remotely via a KVM access.

### Step 2: Set Default Policies

Next, we’ll define the default behavior for incoming, outgoing, and forwarded traffic. By default, it’s a good idea to block all incoming traffic and allow outgoing traffic:

```bash
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
```

This means any incoming connection will be dropped unless you explicitly allow it. Outgoing traffic, on the other hand, is allowed by default.

### Step 3: Allow SSH Traffic (Port 22)

You’ll need SSH access to manage your server, so we’ll allow traffic on port 22. It’s important to only allow connections from trusted IP addresses, but for simplicity, we’ll start by allowing all connections on SSH:

```bash
iptables -A INPUT -p tcp --dport 22 -j ACCEPT
```

The `-A` is for "Append", this inserts the rule at the bottom, you can insert at the top with `-I` flag.

It is a good idea to always add comments to your rules, so in the future you can troubleshoot it faster.

In iptables, the syntax to add comments is as follows:

```bash
iptables -m comment --comment "Your comments here"
```

If you want to restrict SSH access to a specific IP (for example, your home IP), use this instead:

```bash
iptables -A INPUT -p tcp -s <your-ip-address> -m comment --comment "Allow SSH from my home IP" --dport 22 -j ACCEPT
```

### Step 4: Allow HTTP and HTTPS Traffic (Ports 80 and 443)

If you’re running a web server, you’ll want to allow HTTP and HTTPS traffic:

```bash
iptables -A INPUT -m comment --comment "Allow HTTP" -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow HTTPS" -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow QUIC" -p udp --dport 443 -j ACCEPT
```

This allows visitors to access your website over both HTTP (port 80) and HTTPS (port 443).

The 443/udp port is for QUIC protocol, QUIC is a transport layer protocol developed by Google, designed for faster, more secure internet connections by combining features of TCP, TLS, and HTTP/2 with lower latency and improved performance. I highly suggest that you search more about this, maybe I write an article about it in the future.

### Step 5: Allow Loopback Traffic

Your server needs to communicate with itself via the loopback interface. Let’s make sure traffic on lo is allowed:

```bash
iptables -A INPUT -m comment --comment "Allow loopback traffic" -i lo -j ACCEPT
```

### Step 6: Drop Invalid Packets

Invalid packets can often indicate an attack or misconfiguration, so it’s good practice to drop them:

```bash
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
```

### Step 7: Allow Established and Related Connections

To allow responses to outgoing connections (like when your server requests updates), you’ll need to allow established and related connections:

```bash
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
```

This ensures your server can accept responses to any requests it makes.

### Step 8: Don't forget IPv6 rules

For IPv6, the syntax is the same, you mostly need to change the command from `iptables` to `ip6tables`

The main difference is the ICMP protocol, you really need to allow ICMPv6 to your server function properly.

Here is a good starting point, where we explicitly accept ICMPv6 control packets, while dropping all others.

```bash
ip6tables -A INPUT -p icmpv6 --icmpv6-type echo-request -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 --icmpv6-type echo-reply -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 --icmpv6-type neighbor-solicitation -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 --icmpv6-type neighbor-advertisement -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type packet-too-big -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type time-exceeded -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type destination-unreachable -j ACCEPT
ip6tables -A INPUT -p icmpv6 --icmpv6-type router-advertisement -j ACCEPT
ip6tables -A INPUT -p icmpv6 -j DROP
```

### Step 9: Save Your iptables Rules

Once you’ve added all your rules, don’t forget to save them. This ensures they persist after a reboot:

```bash
iptables-save > /etc/iptables/rules.v4
ip6tables-save > /etc/iptables/rules.v6
```

### Step 10: Testing Your Configuration

After setting up iptables, it’s a good idea to test it by attempting to connect to your server. You should be able to access SSH, HTTP, and HTTPS services while all other traffic is blocked.

## Summary

In summary, this is a good starting point for your rules, remember to save and test everything afterwards!

### IPv4

```bash
iptables -P INPUT DROP
iptables -P FORWARD DROP
iptables -P OUTPUT ACCEPT
iptables -A INPUT -p tcp -s <your-ip-address> -m comment --comment "Allow SSH from my home IP" --dport 22 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow HTTP" -p tcp --dport 80 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow HTTPS" -p tcp --dport 443 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow QUIC" -p udp --dport 443 -j ACCEPT
iptables -A INPUT -m comment --comment "Allow loopback traffic" -i lo -j ACCEPT
iptables -A INPUT -m conntrack --ctstate INVALID -j DROP
iptables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables-save > /etc/iptables/rules.v4
```

### IPv6

```bash
ip6tables -P INPUT DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT ACCEPT
ip6tables -A INPUT -p tcp -s <your-ip-address> -m comment --comment "Allow SSH from my home IP" --dport 22 -j ACCEPT
ip6tables -A INPUT -m comment --comment "Allow HTTP" -p tcp --dport 80 -j ACCEPT
ip6tables -A INPUT -m comment --comment "Allow HTTPS" -p tcp --dport 443 -j ACCEPT
ip6tables -A INPUT -m comment --comment "Allow QUIC" -p udp --dport 443 -j ACCEPT
ip6tables -A INPUT  -m comment --comment "Allow loopback traffic" -i lo -j ACCEPT
ip6tables -A INPUT -m conntrack --ctstate INVALID -j DROP
ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
ip6tables-save > /etc/iptables/rules.v6
```

## Conclusion

Congratulations! You’ve successfully configured basic iptables rules for your newly deployed Linux server. By blocking all incoming traffic except for trusted services and connections, you’ve added a crucial layer of security to your server.
