Learn

iptables IPv6

This article is a quick-start guide for ip6tables - the IPv6 version of iptables, covering the basics of installing, configuring, viewing, editing, and persistence. It’s primarily intended for those already familiar with iptables for IPv4. 

For those unfamiliar with iptables, it is a command-line tool used for firewalling, NAT, and traffic accounting purposes. Policy chains are created to handle these various functions, acting on traffic coming into or going out of the host running it. It is most frequently used to firewall traffic coming into the host, such as permitting anyone on the internet to access a webpage but denying SSH or SQL access to all except for a set of trusted hosts.

To read the man page for ip6tables you can visit https://linux.die.net/man/8/ip6tables.

Summary of key concepts

This article covers the following areas, primarily using Ubuntu OS as a reference in our examples.

Installation How to install the package.
Creating Rules for a Web server Example Creating rules for a web server.
Persistent Configuration How to ensure that configuration survives reboot.
Operations How to view, edit, delete, and restore rules.
Recommendations Best practices and cautions.
Conclusion Wrapping up.

{{banner-13="/design/banners"}}

Installation

Debian/Ubuntu

Update the apt cache and install the iptables package, which includes v4 and v6 commands:

$ sudo apt-get update && sudo apt-get install iptables
Hit:1 http://mirrors.linode.com/ubuntu bionic InRelease
Get:2 http://mirrors.linode.com/ubuntu bionic-updates InRelease [88.7 kB]
Get:3 http://mirrors.linode.com/ubuntu bionic-backports InRelease [74.6 kB]

Get:38 http://download.zerotier.com/debian/bionic bionic/main i386 Packages [5,104 B]
Get:39 http://debian.drdteam.org stable/multiverse i386 Packages [5,162 B]
Fetched 17.4 MB in 6s (2,801 kB/s)
Reading package lists... Done
Reading package lists... Done
Building dependency tree
Reading state information... Done
iptables is already the newest version (1.6.1-2ubuntu2).

After reading this article, you might wish to refer to this community-authored Ubuntu iptables how-to guide at https://help.ubuntu.com/community/IptablesHowTo.

CentOS

Installing on CentOS/RPM based systems is a little different. Starting with CentOS 7, iptables is still used, but configuring rules is now performed with firewalld as a frontend/wrapper. To switch back to iptables, you must remove firewalld and install iptables:

$ sudo yum remove firewalld                                 # remove
$ sudo yum install iptables-services                        # install
$ sudo systemctl start iptables                             # start v4
$ sudo systemctl start ip6tables                            # start v6

From here, the article uses Ubuntu as the reference, but most of the commands are identical after installing and starting the services. One exception is persistence. Here instead you do this:

$ sudo service iptables save   # v4: saves /etc/sysconfig/iptables
$ sudo service ip6tables save  # v6: savesto /etc/sysconfig/ip6tables

By default, on reboot, iptables will not be running, so you must enable the iptables service to ensure that it starts on boot (it will load any saved configuration):

$ sudo systemctl enable iptables  # v4: enable service; reboot -> enabled
$ sudo systemctl enable ip6tables # v6: enable service; reboot -> enabled

Creating rules for a web server example

Let’s create some ip6tables rules for a hypothetical web server. Note that for IPv6 to work fully, we must ensure certain kinds of ICMPv6 messages are permitted. Here we will permit all ICMPv6 messages, but for further reading on ip6tables rules for specific ICMPv6 message types, please see: https://resources.sei.cmu.edu/tools/downloads/vulnerability-analysis/assets/IPv6/ip6tables_rules.txt.

Service Protocol Port(s) Notes
Web Service TCP 80, 443 This server needs to permit connections from any source to our web server ports. Web servers listen on TCP ports 80 and 443, so we must permit those.
Management TCP 22 To manage the web server, we need to gain SSH access on TCP port 22.
Troubleshooting and other functions ICMPv6 N/A For pings, traceroutes, and other functions, we need to permit ICMP.

Other requirements

We must not forget that the machine’s loopback interface is also affected by the ip6tables configuration. The machine must be able to communicate freely over the loopback interface because it is used for process-to-process communication. This is the first rule we will add.

We must also permit ESTABLISHED and RELATED traffic, which is traffic coming in related to any outbound session initiated from our server and any traffic related to an existing session initiated from the outside. A rule like this is usually entered near the very top of the list because the majority of traffic will usually be processed by it.

Finally, we will deny ALL other traffic as a security best practice. This is our catch-all term applied at the end.

5-tuple pseudocode: What are our objectives, in simple terms?

When familiarizing yourself with iptables syntax, it is very important to consider in simple terms what your objectives are before attempting to compose or enter any commands. This practice helps ensure that rule composition and ordering are correct before making your change; after all, firewall issues could cut off your SSH management session and/or disrupt user traffic in a way that is time-consuming to correct.

In our case, our objectives are:

  • From SRC ANY to DST loopback then ACCEPT
  • From ESTABLISHED or RELATED then ACCEPT
  • From SRC ANY to DST ICMP then ACCEPT
  • From SRC ANY to DST TCP 22 then ACCEPT
  • From SRC ANY to DST TCP 80 then ACCEPT
  • From SRC ANY to DST TCP 443 then ACCEPT
  • From SRC ANY to DST ANY then DROP

Entering the configuration

sudo ip6tables -A INPUT -i lo -j ACCEPT
sudo ip6tables -A INPUT -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
sudo ip6tables -A INPUT -p icmp -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport ssh -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport http -j ACCEPT
sudo ip6tables -A INPUT -p tcp --dport https -j ACCEPT
sudo ip6tables -A INPUT -j DROP

Viewing the configuration

Here we have left out our ICMP rule because we will later show you how to insert this rule in a specific location.

$ sudo ip6tables -L -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target   prot opt in   out     source       destination
    0     0 ACCEPT   all      lo   any     anywhere     anywhere
   11  3955 ACCEPT   all      any  any     anywhere     anywhere     ctstate RELATED,ESTABLISHED
    0     0 ACCEPT   tcp      any  any     anywhere     anywhere     tcp dpt:ssh
    0     0 ACCEPT   tcp      any  any     anywhere     anywhere     tcp dpt:http
    0     0 ACCEPT   tcp      any  any     anywhere     anywhere     tcp dpt:https
   13  1160 DROP     all      any  any     anywhere     anywhere

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target   prot opt in   out     source       destination

Chain OUTPUT (policy ACCEPT 28 packets, 3931 bytes)
 pkts bytes target   prot opt in   out     source       destination

Persistent configuration

We must ensure that ip6tables rules are saved every time after editing and then restored after rebooting. A common way to do this through editing /etc/network/interfaces is to add pre-up and post-down commands or scripts to run. These would call the ip6tables-save and ip6tables-restore commands. Refer to the Ubuntu community how-to linked above if you wish to explore this more advanced method.

The most elegant solution is to simply use the (Ubuntu) iptables-persistent package. Let’s install it and let it save our rules so that they persist on reboot:

$ sudo apt-get install iptables-persistent
Reading package lists... Done
Building dependency tree
Reading state information... Done
The following additional packages will be installed:
  netfilter-persistent
The following NEW packages will be installed:
  iptables-persistent netfilter-persistent
0 upgraded, 2 newly installed, 0 to remove and 203 not upgraded.
Need to get 13.1 kB of archives.
After this operation, 81.9 kB of additional disk space will be used.
Do you want to continue? [Y/n] y

Follow the prompts, which will ask you if you want to save your IPv4 and IPv6 rules. Generally, you will choose Yes here unless you wish NOT to save them, e.g., if you have misconfigured any rules.

If you selected Yes and saved both your IPv4 and IPv6 rules at the prompts, they will now persist across a reboot instead of being lost.

Additional changes

If you make further changes to your rules, consider whether and when to update the “on-boot” configuration. If your new in-memory rules are not yet fully tested, you may want to avoid updating the on-boot configuration, so that in the event of a major problem with iptables filtering, a reboot will ensure that a known-good state is restored.

Let’s say you have made a rule change in memory and are certain that you want to save it to the on-boot configuration. Let’s use iptables-persistent to update that configuration. For this, you need to actually become root: sudo alone is not sufficient with the default permissions set by the iptables-persistent package:

$ sudo su
<depending on your configuration, you may need to enter a password here>
# iptables-save > /etc/iptables/rules.v4
# ip6tables-save > /etc/iptables/rules.v6

Let’s verify the on-boot configuration; here, only only v6 rules are shown:

$ cat /etc/iptables/rules.v6
<snip>
<snip>
<snip>
# Generated by ip6tables-save v1.6.1 on Mon Aug  1 20:37:22 2022
*filter
:INPUT ACCEPT [0:0]
:FORWARD ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]
-A INPUT -i lo -j ACCEPT
-A INPUT -m conntrack --ctstate RELATED,ESTABLISHED -j ACCEPT
-A INPUT -p icmp -j ACCEPT
-A INPUT -p tcp -m tcp --dport 22 -j ACCEPT
-A INPUT -p tcp -m tcp --dport 80 -j ACCEPT
-A INPUT -j DROP
COMMIT
# Completed on Mon Aug  1 20:37:22 2022

Manually save to another location (e.g., backup, version control, creating files for exporting to another system, etc). As an example, we will save to the home directory:

#
# Save all rules to files in your home directory.
# To view the files, use ‘cat ~/iptables_rules*’.
#
$ sudo iptables-save > ~/iptables_rules_v4
$ sudo ip6tables-save > ~/iptables_rules_v6

{{banner-9="/design/banners"}}

Operations

View rules

Run ip6tables with option -L for list and -v for verbose. In this example, there are no rules specified; the default configuration is to ACCEPT.

$ sudo ip6tables -L -v
Chain INPUT (policy ACCEPT 28M packets, 11G bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain FORWARD (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source               destination

Chain OUTPUT (policy ACCEPT 25M packets, 5963M bytes)
 pkts bytes target     prot opt in     out     source               destination

Edit rules

Earlier in the article, we saw output that was missing our ICMP ACCEPT rule. Let’s see how we can insert this new rule in the correct location. First we get the rule line numbers, like this:

$ sudo ip6tables -L INPUT -v --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source    destination
1        0     0 ACCEPT     all      lo     any     anywhere  anywhere
2       17  5069 ACCEPT     all      any    any     anywhere  anywhere             ctstate RELATED,ESTABLISHED
3        0     0 ACCEPT     tcp      any    any     anywhere  anywhere   tcp dpt:ssh
4        0     0 ACCEPT     tcp      any    any     anywhere  anywhere   tcp dpt:http
5        0     0 ACCEPT     tcp      any    any     anywhere  anywhere   tcp dpt:https
6        8   832 DROP       all      any    any     anywhere  anywhere

We want to insert our ICMP rule after our RELATED/ESTABLISHED rule on line number 2 (see the first column of output for line numbers).

To insert our new rule specifically at line number 3, we enter -I for insert into our INPUT chain at line number 3:

$ sudo ip6tables -I INPUT 3 -p icmp -j ACCEPT

To verify, we use the same line-numbers command as before and here follow with grep to shorten the output to the ICMP rule plus the line before (-B 1) and the line after (-A 1). You can see that the previous line 3 is now line 4:

$ sudo ip6tables -L INPUT -v --line-numbers | grep icmp -B 1 -A 1
2       25  7561 ACCEPT     all   any    any   anywhere  anywhere             ctstate RELATED,ESTABLISHED
3        0     0 ACCEPT     icmp  any    any   anywhere  anywhere
4        0     0 ACCEPT     tcp   any    any   anywhere  anywhere   tcp dpt:ssh

{{banner-10="/design/banners"}}

Delete Rules

As an example, let’s delete the line that permits HTTPS access on TCP port 443. After the previous example, this rule is unchanged, except it is line number 6 now. To delete line 6 we do -D for delete, INPUT chain, and in this case we want to target line number 6 - the command to delete looks like this:

sudo ip6tables -D INPUT 6

Now let’s enter the full sequence of commands (see comments inline):

#
# Find the line number to delete:
#
$ sudo ip6tables -L INPUT -v --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source               destination
1       42  8475 ACCEPT     all      lo     any     anywhere  anywhere
2       25  7561 ACCEPT     all      any    any     anywhere  anywhere             ctstate RELATED,ESTABLISHED
3        0     0 ACCEPT     icmp     any    any     anywhere  anywhere
4        0     0 ACCEPT     tcp      any    any     anywhere  anywhere tcp dpt:ssh
5        0     0 ACCEPT     tcp      any    any     anywhere  anywhere tcp dpt:http
6        0     0 ACCEPT     tcp      any    any     anywhere  anywhere tcp dpt:https
7      366 37232 DROP       all      any    any     anywhere  anywhere
#
# Delete it - line 6: <...> tcp dpt:https
#
$ sudo ip6tables -D INPUT 6
#
# Verify your change - any/any/tcp dpt:https is now gone.
#
$ sudo ip6tables -L INPUT -v --line-numbers
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
num   pkts bytes target     prot opt in     out     source   destination
1       42  8475 ACCEPT     all      lo     any     anywhere anywhere
2       25  7561 ACCEPT     all      any    any     anywhere anywhere             ctstate RELATED,ESTABLISHED
3        0     0 ACCEPT     icmp     any    any     anywhere anywhere
4        0     0 ACCEPT     tcp      any    any     anywhere anywhere    tcp dpt:ssh
5        0     0 ACCEPT     tcp      any    any     anywhere anywhere    tcp dpt:http
6      374 38064 DROP       all      any    any     anywhere anywhere

Flushing

You may misconfigure your IPv4 or IPv6 rules and need to quickly disable the ip6tables firewalling. For example, if you implement some complex connection tracking or logging, and this adversely impacts user traffic or system stability, you need to recover quickly. The simplest way to do this is to flush all the rules. 

For example, consider this before state:

$ sudo ip6tables -L INPUT -v
Chain INPUT (policy ACCEPT 0 packets, 0 bytes)
 pkts bytes target     prot opt in     out     source   destination
   71 14612 ACCEPT     all      lo     any     anywhere anywhere
   15  4295 ACCEPT     all      any    any     anywhere anywhere             ctstate RELATED,ESTABLISHED
    0     0 ACCEPT     tcp      any    any     anywhere anywhere      tcp dpt:ssh
    0     0 ACCEPT     tcp      any    any     anywhere anywhere      tcp dpt:http
    0     0 ACCEPT     tcp      any    any     anywhere anywhere      tcp dpt:https
  500 50880 DROP       all      any    any     anywhere anywhere

Next, we flush the rules:

#
# Flush all IPv6 rules.
#
$ sudo ip6tables -F

And here’s how it looks after:

#
# Chain is emptied of rules - now the default policy ACCEPT applies.
#
$ sudo ip6tables -L INPUT -v
Chain INPUT (policy ACCEPT 2 packets, 208 bytes)
 pkts bytes target     prot opt in     out     source               destination
$

At this point, the server is wide open to all connections, so it’s imperative that you quickly restore a known-good configuration as soon as possible. See the section on persistence for how best to manage on-boot (known-good) configurations; restoring from here is likely the better option for both test and production environments. 

Restoring configuration

You can restore the known-good configuration like this (assuming files saved in /etc/iptables/rules.v[4|6] - adjust your path to suit):

$ sudo su
<depending on your configuration, you may need to enter a password here>
# iptables-restore < /etc/iptables/rules.v4
# ip6tables-restore < /etc/iptables/rules.v6

Recommendations

  • Start in a Test Environment: Read the man page for ip6tables, try the examples given, and familiarize yourself in a test environment. 
  • Beware of Losing Access: When modifying iptables rules, whether in an IPv4 or IPv6 environment, you risk cutting off network connectivity if you make a mistake in configuration. It is best to ensure that you make rules persistent only after testing them, or you could disrupt your access to the server and be unable to fix the issue. 
  • Work on Each IP Address Family Independently at First: For example, if you first develop, test, and persist your IPv4 rules, then you know that no matter what changes you make to IPv6 rules, you can still access the server via IPv4.
  • Test a Known-Good Configuration First: If you are transitioning to IPv6 only and want to remove IPv4 addressing from the server, it is recommended that you fully test and persist a known-good ip6tables configuration before doing so.
  • Investigate Remote Access: Many VM hosting providers offer remote console access via a web control panel, so even if you cut off your IP access, you can still access the machine to fix any issues. It is still good practice to exercise extreme caution when making changes to your configuration: It’s a habit you should insist on because you may find yourself in a situation where the server does not have console access or the console is down for maintenance.
  • Use Pseudocode and Intention Statements: Familiarize yourself with the concepts of crafting and composing individual rules and rule chains using pseudocode and “5-tuple’’ intention statements. Prepare iptables/ip6tables configurations in a text editor before applying them on the CLI.

{{banner-sre="/design/banners"}}

Conclusion

You should come away from this article with an understanding of the fundamentals of deploying and working with ip6tables. Hopefully you’re already familiar with iptables (IPv4) and find that not much differs other than the command name. Good luck securing your IPv6 perimeters!

What's Next?