> For the complete documentation index, see [llms.txt](https://docs.glesys.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.glesys.com/products/compute/guides-for-server-management/set-up-a-firewall-on-debian-9-using-iptables.md).

# Set up a firewall on Debian 9 using iptables

***

Having a functional firewall on the server is an essential step in security work. In this guide, we will create a basic firewall that you can later extend with your own rules. The guide assumes a newly installed Cloud VM running Debian 9. If you want to set up a firewall on Debian 11 or newer, we have a guide for that here: "[Set up a firewall on Debian 11 or newer using nftables](/products/compute/guides-for-server-management/set-up-a-firewall-on-debian-11-or-newer-using-nftables.md)".

In this guide, we show you how to configure a firewall using `iptables`, which is typically included by default in older Debian releases.

## Install the firewall service

To get started, you need to install the packages `iptables-persistent` and `netfilter-persistent` if you haven’t done so already. These allow you to save your rules and have them applied automatically at boot.

Start by updating the package repository:

{% code title="Command" %}

```
apt update
```

{% endcode %}

Installera `iptables` och `netfilter-persistent` med följande kommando:

{% code title="Command" %}

```
apt install netfilter-persistent iptables-persistent
```

{% endcode %}

During the installation, you will be asked whether you want to save the generated rules. Choose **yes** here. We will edit the rules shortly thereafter.

## Create firewall rules

To get started as quickly as possible, we’ll copy and paste pre‑written firewall rules. Later in the guide, we’ll show you how to add new rules.

To add the firewall rules, edit the files `/etc/iptables/rules.v4` and `/etc/iptables/rules.v6` for IPv4 and IPv6 respectively.

Open the `rules.v4` file in your text editor (use `sudo` if you’re not root).

{% code title="Command" %}

```
nano /etc/iptables/rules.v4
```

{% endcode %}

The file that opens contains something similar to this:

{% code title="/etc/iptables/rules.v4" %}

```
# Generated by iptables-save v1.6.0 on Fri Oct 26 12:20:01 2018
*raw
:PREROUTING ACCEPT [446:35987]
:OUTPUT ACCEPT [318:52605]
COMMIT
# Completed on Fri Oct 26 12:20:01 2018
...
```

{% endcode %}

Replace this with the following configuration. Be sure to insert your server’s IP address wherever it says \<THE SERVER IPv4 ADDRESS>:

{% code title="Modified /etc/iptables/rules.v4" %}

```
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
#default
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -s <THE SERVER IPv4 ADDRESS>/32 -j ACCEPT
-A OUTPUT -d <THE SERVER IPv4 ADDRESS>/32 -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A OUTPUT -p icmp -j ACCEPT
# TRACEROUTE
-A INPUT -p udp -m udp --dport 33434:33523 -j ACCEPT
-A OUTPUT -p udp -m udp --dport 33434:33523 -j ACCEPT
#DNS OUT
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
#NTP OUT (time sync)
-A OUTPUT -p udp -m udp --dport 123 -j ACCEPT
# SSH IN
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# allow http(s) in
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
# allow http()s out
-A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -j DROP
-A OUTPUT -p tcp -j REJECT --reject-with tcp-reset
COMMIT
```

{% endcode %}

Save and close the file.

Restart `netfilter-persistent` to load the configuration (use `sudo` if you're not root):

{% code title="Command" %}

```
systemctl restart netfilter-persistent
```

{% endcode %}

Verify that iptables has loaded the configuration:

{% code title="Command" %}

```
iptables -L
```

{% endcode %}

You should get an output similar to this:

{% code title="Output" %}

```
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     all  --  <THE SERVER IPv4 ADDRESS>-static.glesys.net  anywhere
ACCEPT     udp  --  anywhere             anywhere             udp dpts:33434:33523
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
DROP       all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             <THE SERVER IPv4 ADDRESS>-static.glesys.net
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     udp  --  anywhere             anywhere             udp dpts:33434:33523
ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain
ACCEPT     udp  --  anywhere             anywhere             udp dpt:ntp
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
REJECT     tcp  --  anywhere             anywhere             reject-with tcp-reset
```

{% endcode %}

We have now saved the rules in the file `/etc/iptables/rules.v4`, allowing iptables to use them.

Using the rules above, we permit incoming traffic for `traceroute`, `ICMP (PING)`, `SSH`, `HTTP`, and `HTTPS`, and outgoing traffic for `traceroute`, `HTTP`, and `HTTPS`.

Thanks to `iptables-persistent` and `netfilter-persistent`, these settings will be loaded each time you reboot the server.

### Adding rules for IPv6

If your server also has an IPv6 address, add the rules in the same way as above, but edit the file `/etc/iptables/rules.v6`. Be sure to replace \<THE SERVER IPv6 ADDRESS> with your actual IP address.

{% code title="/etc/iptables/rules.v6" %}

```
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
#default
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A OUTPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -i lo -j ACCEPT
-A OUTPUT -o lo -j ACCEPT
-A INPUT -s <THE SERVER IPv6 ADDRESS>/128 -j ACCEPT
-A OUTPUT -d <THE SERVER IPv6 ADDRESS>/128 -j ACCEPT
-A INPUT -p ipv6-icmp -j ACCEPT
-A OUTPUT -p ipv6-icmp -j ACCEPT
# TRACEROUTE
-A INPUT -p udp -m udp --dport 33434:33523 -j ACCEPT
-A OUTPUT -p udp -m udp --dport 33434:33523 -j ACCEPT
#DNS OUT
-A OUTPUT -p udp -m udp --dport 53 -j ACCEPT
#NTP OUT (time sync)
-A OUTPUT -p udp -m udp --dport 123 -j ACCEPT
# SSH IN
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
# allow http(s) in
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 443 -j ACCEPT
# allow http()s out
-A OUTPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A OUTPUT -p tcp -m tcp --dport 443 -j ACCEPT
-A INPUT -j DROP
-A OUTPUT -p tcp -j REJECT --reject-with tcp-reset
COMMIT
```

{% endcode %}

Restart `netfilter-persistent` to load the configuration (use `sudo` if you're not root):

{% code title="Command" %}

```
systemctl restart netfilter-persistent
```

{% endcode %}

### Add rules using the command line

You can add new rules either by editing `/etc/iptables/rules.v4` or by inserting the rule directly from the command line.

In this example, we add a rule that allows outbound traffic on `port 25 (SMTP)` via the command line.

Enter the following (add `sudo` if you're not root):

{% code title="Command" %}

```
iptables -A OUTPUT -p tcp --dport 25 -j ACCEPT
```

{% endcode %}

Verify that the rule was added by running:

{% code title="Command" %}

```
iptables -L
```

{% endcode %}

In the output you receive, you can see that the rule has been added (see the last line):

{% code title="Output" %}

```
Chain INPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  one.one.one.one      anywhere
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     udp  --  anywhere             anywhere             udp dpts:33434:33523
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:ssh
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
DROP       all  --  anywhere             anywhere

Chain FORWARD (policy ACCEPT)
target     prot opt source               destination

Chain OUTPUT (policy ACCEPT)
target     prot opt source               destination
ACCEPT     all  --  anywhere             anywhere             ctstate RELATED,ESTABLISHED
ACCEPT     all  --  anywhere             anywhere
ACCEPT     all  --  anywhere             one.one.one.one
ACCEPT     icmp --  anywhere             anywhere
ACCEPT     udp  --  anywhere             anywhere             udp dpts:33434:33523
ACCEPT     udp  --  anywhere             anywhere             udp dpt:domain
ACCEPT     udp  --  anywhere             anywhere             udp dpt:ntp
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:http
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:https
REJECT     tcp  --  anywhere             anywhere             reject-with tcp-reset
ACCEPT     tcp  --  anywhere             anywhere             tcp dpt:smtp
```

{% endcode %}

Save the firewall settings by running the following command:

{% code title="Command" %}

```
iptables-save > /etc/iptables/rules.v4
```

{% endcode %}

When the server restarts, the settings will be loaded from `/etc/iptables/rules.v4`.

## Summary

By following this guide—either by pasting your firewall rules directly into the configuration file or manually via commands—you’ll have a solid basic firewall setup. You’ll still need to add individual rules for each service you want to allow access to.

***Note:*** When working with firewalls, be careful not to lock yourself out of the server by blocking SSH traffic (port 22 by default). If you lose access due to firewall settings, you may need to connect through the console to regain access. Once you’re connected via the console, you can modify your firewall rules to re‑allow SSH (or permit all traffic). If your saved firewall rules already allow SSH access, another option is simply to reboot the server. See our guide on how to reach your server via [console mode](/products/compute/kvm-virtual-machines/how-tos/connect-to-the-vm-console.md).


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.glesys.com/products/compute/guides-for-server-management/set-up-a-firewall-on-debian-9-using-iptables.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
