Linux Security Guide for Hardening IPv6

Version 6 of Internet Protocol is now 20+ years available. You would think it is widely available now, right? Not exactly. Still many internet providers don’t have it deployed for their customers. Hosting companies are not always eager to deploy it either. Mostly because of lacking knowledge. To get at east more knowledge shared on the security side of IPv6, we have crafted this guide. Hopefully it will be a practical guide for your to configure and tune your configurations.

The Need for a New Version

The addressing scheme for version 4 was thought to sufficient at the time. With the widespread usage of the web and services on the internet in general, this turned out differently. With some trickery, we were able to extend the possibilities, like subnetting and running multiple websites on one single IP address. Last year, the last pools of available IP addresses were depleted and it is time to move forward.

Version 6 of the Internet Protocol promises that address space will never be a problem anymore. Even if we were to give every human, device, and trees, their own address. We will see if that holds true in the next 20 years to come.

Do I Really Need It?

Most guides on the internet tell you a simple thing: disable IPv6 if you don’t need it. That sounds logical and is a good hardening principle. Unfortunately, this advice might also be harmful. For example, the web really benefits if we make the transition to IPv6. Instead of fearing away for some of the known attacks, it is better to build up a good understanding of the subject and take the right measures. Still, if you feel IPv4 will do in your internal network, then use that and disable IPv6. In any case, keep reading and make the world a safer place.

Before we start talking about hardening, it makes sense to do a gentle introduction into IPv6. After all, there are still a lot of people who never used or configured it.

IPv6 Basics

Although the subject is very extensive, with lots of standards and RFCs, here are the basics of the new protocol.

  • No ARP (neighbor discovery instead, based on ICMPv6)
  • No broadcasts (lots of multicast instead)
  • Hierarchical address space (with clear functions and separation)
  • Multiple IP addresses per interface, with different scopes
  • Header is simplified, modular due to extensions and chaining

Neighbor Discovery

Instead of using ARP, multicast is used other to detect other systems and routers. For the discovery of routers there are Router Advertisements and Router Solicitations, for other systems Neighbor Advertisement and Neighbor Solicitation. These advertisements and solicitations help hosts to find their way on the network. Depending on the configuration, the right device(s) will respond to a query. For example the initial task to obtain an IP address with the right prefix.

IPv6 Addresses on First Sight

If you never worked with IPv6 before, the new addresses can be overwhelming. No longer the short IP addresses we are used to, divided by dots. Instead, we get colons and additional letters. To avoid this blog post becoming a book, we will skip on all the details regarding addressing. Instead, let’s have a look at some common addresses:

  • ::1/128 - Localhost (IPv4 alternative: 127.0.0.1)
  • fc00::/7 - Unique Local Addresses (ULAs) (IPv4 alternative: 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16)
  • fe80::/10 - Link-Local Addresses, used on the internal network (IPv4 alternative: 169.254.0.0/16)
  • 2001::1000::/32 - Global addresses (Teredo)
  • 2002::/16 - Global addresses (6to4)

The other addresses are usually external addresses (global), or special purpose addresses. If you want to know more about these address types, use the RIPE IPv6 sheet as a handy reference.

Let’s leave addressing and move towards configuration.

IPv6 Configuration Basics

Linux distributions are all slightly different. In this guide, we will show several examples. For specific configuration of your favorite Linux distribution, have a look at the More Resources section, located at the end.

Before doing any configuration at all, why not first determine what is configured? This commands are also helpful for auditors and security professionals, to understand configurations better. Previously the ifconfig command was used by many distributions, with the ip command being its common replacement. First step is determining what interfaces are configured, and in particular which have a version 6 configuration.

ip -6 addr

This command shows address information per interface, highlighting only the interface specifics of the version 6 family. For example, any link-local IPv6 address ends with a suffix: scope link. Interfaces or addresses with a global static configuration, have the suffix scope global static. Systems configured via SLAAC will show scope global dynamic. When private extension address is used, it will show scope global temporary dynamic, and for expired items: scope global temporary deprecated. As you quickly will see on your environments, every system is differently configured. So it is good to know these types exists.

If your Linux distribution uses systemd, changes are high that you are using the systemd-networkd service. Querying its status can be done with systemctl.

systemctl status systemd-networkd

The status output of systemd will show something like “Gained IPv6LL”, when it received a Link-Local address. As with the ip command, the output depends on the type of address being configured on the interface.

These two tools can already provide some insight. Time to get closer to the in-depth configuration of IPv6 itself.

Manual versus Automatic Configuration

Time to make changes to the system and set up IPv6. The first choice to make is if we want to use automatic configuration, or do it by hand. The first option simplifies things a lot. Using manual configuration gives you more control regarding the configuration. It also counters a few known attacks, so for specifically hardening purposes it is the best bet. The best option for your environment? It really depends on your networking architecture and what type of machines are in it.

Automatic Configuration

Two common ways for providing addresses to new and existing clients, is using SLAAC (Stateless address auto-configuration) or DHCPv6. First question you might ask yourself, is which one to use. Unfortunately, the answer depends on your environment, used devices and their support for IPv6.

A quick dive into both:

SLAAC

  • Uses Multicast ICMPv6
  • Provides IPv6 prefixes
  • Router advertisement provide addressing, routing, MTU and other options.
  • Privacy issue: uses MAC address (which can be countered with privacy extensions turned on)

DHCPv6

When using the stateful configuration of DHCPv6:

  • Client/Server
  • Uses multicast UDP
  • Provides addressing, routing, NTP, SIP, DNS, other options

The stateless DHCPv6 configuration will use SLAAC with the flag O=1 (Other Config) set, followed by extra configuration options via DHCPv6.

System Configuration on Linux Systems

Time to apply some configuration!

Arch Linux

When using netctl, use the following line and add it to your interface configuration file:

IP6=stateless

If you are using NetworkManager, you should receive automatically an IPv6 address, when advertised. When using the newer systemd-networkd, reload with:systemctl restart systemd-networkd

systemctl restart systemd-networkd

CentOS

Change the related network configuration file, with the right interface. For example /etc/sysconfig/network-scripts/ifcfg-eth0.

IPV6INIT=yes
IPV6_AUTOCONF=yes
IPV6_DEFROUTE=yes
IPV6_PEERDNS=yes
IPV6_PEERROUTES=yes
IPV6_FAILURE_FATAL=no
Debian and Ubuntu

Change the /etc/network/interfaces file.

iface eth0 inet6 auto

Note: newer versions of Debian and Ubuntu most likely don’t use this file anymore

Manual Configuration

If you don’t want to use SLAAC or DHCPv6, it is time to make some serious changes. First step is turning of auto-configuration. Add the following lines to your /etc/sysctl.conf

net.ipv6.conf.all.autoconf=0

Systems running Red Hat distribution like RHEL, might need another change in their network configuration file, which is usually /etc/sysconfig/network.

NETWORKING_IPV6=yes
IPV6_AUTOCONF=no

On a regular basis people are advised to also disable the router advertisements (RA). This is done with the sysctl key net.ipv6.conf.all.accept_ra sysctl. This is actually not advised. As a starter, these RAs help hosts determining if a system is the same subnet, in other words if they are on-link or off-link. Also the configuration of DNS and packet size (MTU) is arranged with RAs. For neighbor discovery (the IPv6 equivalent of ARP), timeouts, and other settings are determined. So in other words, if you decide to disable RA, make sure you really know what you are doing. You might find trouble later on, which was simply caused by ignoring RAs.

Here are some tips for the most common Linux distributions to get you started. Note that some of these configurations can change over time, as systemd is gaining in popularity.

Arch Linux and Fedora 21+

Systems using systemd, need to be configured via systemd-networkd. Change file /etc/systemd/network/50-static.network.

[Match]
Name=eth0

[Network]
Address=192.168.1.2/24
Gateway=192.169.1.1
Address=fe80::2/64
Gateway=fe80::1

Reload network configuration:

systemctl restart systemd-networkd

IPV6 Configuration for Debian and Ubuntu

Change your /etc/network/interfaces file

iface eth0 inet6 static
  address aaaa:bbbb::abcd:abcd
  netmask 64
  mtu 1280
  gateway aaaa:bbbb::abcd::1

CentOS and Red Hat (RHEL)

Change the related interface in the directory /etc/sysconfig/network-scripts, starting with ifcfg-*.

IPV6INIT=yes
IPV6ADDR=fe80::100/64
IPV6_DEFAULTGW=fe80::1/64
DNS1=fe80::10/64
DNS2=fe80::20/64

Reload configuration (older versions of CentOS/RHEL)

service network restart

Reload configuration (CentOS/RHEL)

nmcli reload  
nmcli con down "System eth0"  
nmcli con up "System eth0"

Testing Connectivity

For most common utilities there is an IPv6 variant available. For ping this is ping6 and provides similar functionality.

Ping utility ping6 showing invalid argument error for connection

If you get a “connect: Invalid argument” error with ping6, you might wonder why that is. Because of the routing and dual stack involved, you need to specify the interface.

ping6 -I enp0s3 fe80::800:27ff:fe00:1

An alternative is leaving out the -I parameter and append % (e.g. %eth0) to the address.

ping6 fe80::800:27ff:fe00:1%enp0s3

Another tricky item is the usage of Link-Local addresses on Linux, and the related name resolving. During testing, you may receive the error “unknown host”. Even if you are certain the hostname and IP address are correct. If you encounter this, ping the IPv6 address manually instead. Use a tool like dig or host, to check if the AAAA records are properly configured. These are the equivalent of A records for IPv4 addresses.

Troubleshooting

To find why things don’t work as expected, start using the ip command as shown before. This will provide you the interface details. Other useful sources are dmesg for kernel related messages. Check your syslog and systemd logging.

Screenshot of systemd showing IPv6 message that no routers are found

This system won't be automatically configured as no routers provide IPv6 router advertisements

IPv6-only or Not?

Another decision to make is the communication protocol for the machine. If no IPv4 mappings are needed, a possibility is to go to IPv6-only. Change this particular sysctl key and add it to /etc/sysctl.conf to persist after a system reboot.

net.ipv6.bindv6only=1

**Relevant RFC: **RFC 3493 - Basic Socket Interface Extensions for IPv6

IPv6 Hardening on Linux

Besides the specific configuration type, we can apply additional layers of system hardening to our IPv6 network configuration.

As discussed before, disabling router advertisements can be good for hardening. At the same time, it may result in a lot more tweaking. If you decided to go for it, then add the following lines to your /etc/sysctl.conf file.

net.ipv6.conf.all.accept_ra=0  
net.ipv6.conf.default.accept_ra=0

Enable IPv6 Privacy Extensions

When clients receive an address through SLAAC, it is created by combining an advertised prefix with the (physical) MAC address of the network card. If you rather don’t create addresses based upon MAC addresses, enable the privacy extensions in IPv6. Add the following lines to /etc/sysctl.conf.

# Enable the IPv6 Privacy Extensions: do not use MAC address  
net.ipv6.conf.all.use_tempaddr=2  
net.ipv6.conf.default.use_tempaddr=2

Configuration with NetworkManager

When using NetworkManager, ensure that it also is configured properly.

/etc/NetworkManager/NetworkManager.conf

[connection]
ipv6.ip6-privacy=2

Configuration with systemd-networkd

For systemd-networkd, open up your configuration file (e.g. /etc/systemd/network/wired.network) and locate the [Network] section. Define IPv6PrivacyExtensions and set to true.

[Network]
DHCP=yes
IPv6PrivacyExtensions=true

Rate Limiting

To protect against denial of service (DoS) attacks, limiting resources is usually a good counter measure. At the same time caution is advised, as sometimes changes can actually increase the chance of an accidental denial of service by legitimate usage. So in all cases check the business purpose of your machine and what a typical network load is expected.

  • net.ipv6.icmp.ratelimit - limit in time (ms), default 1000

At this moment, IPv4 has more sysctl keys available than IPv6. The main reason for this is simply that not all features are ported yet.

net.ipv4.icmp_echo_ignore_all = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_errors_use_inbound_ifaddr = 0
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.icmp_msgs_burst = 50
net.ipv4.icmp_msgs_per_sec = 1000
net.ipv4.icmp_ratelimit = 1000
net.ipv4.icmp_ratemask = 6168

Firewalling with IPv6

Firewalls are still a common hardening measure. Its effectiveness to filter out network traffic is high, while rule sets are usually fairly easy to set up. To counter some of the attacks on Linux systems, we can filter out some of the bad traffic involved with known IPv6 attacks.

ip6tables

At this moment, ip6tables is still the common firewall to be used. It has proven its stability over the years and helps with filtering out the good and bad traffic.

nftables

Most likely nftables will replace iptables and ip6tables in the upcoming years. It does simplify rule sets a lot. Another benefit of nftables is that the ip and ip6 protocol families can be merged into inet, to further simplify things. No more double rules!

Allow several types of IPv6 ICMP packets:

nft add rule ip6 traffic-filter input icmpv6 type { nd-neighbor-solicit, echo-request, nd-router-advert, nd-neighbor-advert } accept

What to Filter?

Most networks will be using a dual-stack implementation for IP traffic. Therefore it is important to ensure that both IP versions are filtered. It would be a shame if you tightened your firewall, just to find out that via IPv6 everything is available.

Another option is to filter out traffic from unexpected ranges. With the earlier mentioned guide from RIPE, you can quickly find out what you want to allow, block the rest of the traffic. Also apply DHCPv6 filtering and restrict access of UDP ports 546 and 547.

Additional reads:

IPv6 sysctl: Keys and Values Explained

With IPv6 enabled on your Linux system, a lot of additional sysctl keys will be listed. Let’s break them up and understand each section.

IPv6 Neighbor Configuration

net.ipv6.neigh.[interface].[key]

The neighbor configuration settings are applied to an interface. So you the choice to define a particular interface with specific settings. If you want to apply them all interfaces, change your /etc/sysctl.conf and override a setting to the all and default, to make them applicable for all interfaces.

IPv6 Routing

net.ipv6.route.gc_elasticity = 9
net.ipv6.route.gc_interval = 30
net.ipv6.route.gc_min_interval = 0
net.ipv6.route.gc_min_interval_ms = 500
net.ipv6.route.gc_thresh = 1024
net.ipv6.route.gc_timeout = 60

This first batch of keys within routing, determine the garbage collecting. This process cleans out old entries, usually to free up memory and increase performance. If your machine has a very particular role requiring many simultaneous connections or sessions, consider diving into tweaking these. Otherwise, leave them on their default value.

net.ipv6.route.max_size = 4096
net.ipv6.route.min_adv_mss = 1220
net.ipv6.route.mtu_expires = 600

The max_size defines the maximum routes in the kernel. Then there is min_adv_mss, the minimum advertised MSS (maximum segment size). It depends on the MTU (maximum transmission unit) of the first hop. Be careful with adjusting this, as it can introduce unexpected network behavior. Last there is the mtu_expires, which defines how long items for the MTU will be cached (in seconds).

Generic IPv6 Settings

Sysctl: net.ipv6.xfrm6_gc_thresh

Garbage collector threshold. Unless you have specific tuning requirements for your machine, keep it at the default value (e.g. 32768).

Flow labels

Flow labels help marking a connection. Normally this is done with the combination of details regarding sender and receiver, port numbers and sequence numbers. Due to fragmentation, encryption and other reasons, it may be hard to know to which previous flow a new incoming packet belongs. Flow labels solve this problem.

Normally the defaults (1) for both net.ipv6.flowlabel_consistency and net.ipv6.flowlabel_state_ranges will do.

Relevant RFC: RFC6437 - IPv6 Flow Label Specification

Firewall Marks

If you are using firewall marks, this boolean on net.ipv6.fwmark_reflect will define if the fwmark property will be set to zero, or the value to which fwmark it is applicable. For most users keeping this the default value will be fine.

ICMP Rate Limiting

The setting net.ipv6.icmp.ratelimit limits the amount of ICMP packets, expressed in miliseconds. Default value is 1000.

IPv6 Fragmentation

net.ipv6.ip6frag_high_thresh = 4194304
net.ipv6.ip6frag_low_thresh = 3145728
net.ipv6.ip6frag_secret_interval = 600
net.ipv6.ip6frag_time = 60

These keys deal with fragmentation of packets. The time determines how long one can stay in memory, before being thrown out. If you want to apply some hardening, you can lower this, depending on the type of traffic you are serving. The ip6frag_secret_interval setting defines the interval (and lifetime) of a hash secret. This secret is used to ensure fragments can be identified quickly as being from a source we already communicated with. All other unknown fragments might be malicious, with the goal to exhaust system resources.

Common IPv6 Actions and Commands

Disable IPv6

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

Use disable_ipv6 and activate changes with sysctl -p. Change your /etc/sysctl.conf file as well. This way changes remain active after a system reboot. You can also disable IPv6 on a single network interface. In that case define the sysctl setting only for that particular interface.

Other ways include disabling it in the kernel, like disabling the related kernel modules. Add this to your modprobe configuration, which is usually stored in /etc/modprobe.d/.

install ipv6 /bin/true

Delete an IP Address

ip -6 addr del <address> dev <interface>

Deleting an address from an interface can be done with the ip command. Note that when auto-configuration is used, addresses may reappear.

Preference for IPv6

Already using IPv6 within your network? In that case you might prefer AAAA records for systems, instead of A records. You can instruct the resolver to use that instead in your resolver configuration (usually /etc/resolv.conf).

options inet6

As always, test before you deploy.

Package Managers and IPv6

Most network services are nowadays aware of the existence of both IPv4 and IPv6. Depending on your configuration, your package manager may need some configuration to work properly as well.

APT (Debian / Ubuntu)

Use the settings Acquire::ForceIPv4 and Acquire::ForceIPv6 to specify what protocol version you want to use.

IPv6 Security Testing Utilities

There are several packages which focus specifically on testing IPv6 and some known weaknesses. Here is a quick overview if you want to test them in your lab.

THC-IPv6

The THC-IPv6 toolkit helps attacking several weaknesses in the IPv6 protocol, including ICMP6.

  • 6to4test - Check if IPv4 address has dynamic 6to4 tunnel setup
  • alive6 - Detects systems listening to an address
  • denial6 - Denial of Service (DoS) tests againsts a specified target
  • detect-new-ip6 - Detect new IPv6 devices joining the network
  • dos-new-ip6 - Detect new IPv6 devices and causing Denial of Service (DoS)
  • exploit6 - Test for known IPv6 vulnerabilities
  • fake_mipv6 - Take away mobile IP when IPSEC is not used
  • firewall6 - Firewall testing utility
  • flood_advertise6 - Flood utility with neighbor advertisements
  • flood_router6 - Flood utility router advertisements
  • implementation6 - Perform implementation checks
  • parasite6 - Spoofing utility for ICMP neighbor solicitations/advertisements
  • redir6 - Spoof traffic via ICMP6
  • rsmurf6 - Remote ICMP flood attack utility
  • smurf6 - Local ICMP flood attack utility
  • thcping6 - Custom ICMP packets
  • trace6 - Alternative tool for traceroute6 (with ICMP6 echo request and TCP-SYN)

Other great tools:

  • soca
  • nc6
  • ndisc6 (ICMPv6 Neighbor Discovery tool)
  • Scapy (package manipulation)
  • SI6 Networks IPv6 Toolkit
  • Nmap
  • Evil FOCA

More IPv6 Resources

There are many resources available regarding IPv6. To help you find more specific details for your platform, we have collected some.

IP version 6 has a nice collection of Request For Comments (RFC):

  • RFC 1981 - Path MTU discovery for IP version 6
  • RFC 2460 - Internet protocol, version 6 (IPv6) specification
  • RFC 2461 - Neighbor discovery for IP version 6 (IPv6)
  • RFC 2462 - IPv6 stateless address autoconfiguration
  • RFC 2464 - Transmission of IPv6 packets over Ethernet networks
  • RFC 2465 - Management Information Base for IPv6: Textual Conventions and General Group
  • RFC 2472 - PPPv6 (Red Hat only)
  • RFC 2710 - Multicast Listener Discovery (MLD)
  • RFC 3041 - Privacy Extensions
  • RFC 3056 - Connection of IPv6 Domains via IPv4 Clouds
  • RFC 3315 - Stateful DHCPv6
  • RFC 3484 - Default Address selection
  • RFC 3493 - Basic Socket Interface Extensions for IPv6
  • RFC 3596 - DNS Extensions to support IPv6
  • RFC 3810 - Multicast Listener Discovery Version 2 (MLDv2)
  • RFC 3971 - SEcure Neighbor Discovery (SEND)
  • RFC 4007 - IPv6 Scoped Address Architecture
  • RFC 4193 - Unique Local IPv6 Unicast Addresses
  • RFC 4213 - Transition Mechanisms for IPv6 Host and Routers
  • RFC 4291 - IPv6 Addressing Architecture
  • RFC 4443 - Internet Control Message Protocol (ICMPv6) for the Internet Protocol Version 6 (IPv6) Specification
  • RFC 4890 - Recommendations for Filtering ICMPv6 Messages in Firewalls
  • RFC 5942 - IPv6 Subnet Model: The Relationship between Links and Subnet Prefixes
  • RFC 6104 - Rogue IPv6 Router Advertisement Problem Statement
  • RFC 6105 - IPv6 Router Advertisement Guard
  • RFC 6106 - IPv6 Router Advertisement Options for DNS Configuration
  • RFC 6437 - IPv6 Flow Label Specification
  • RFC 6980 - Security Implications of IPv6 Fragmentation with IPv6 Neighbor Discovery
  • RFC 7381 - Enterprise IPv6 Deployment Guidelines

Operating Systems

Other

Found this guide helpful, or have other tips to secure IPv6 configurations on Linux? Let it know!

Relevant commands in this article

Like to learn more about the commands that were used in this article? Have a look, for some there is a cheat sheet available:

Feedback

Small picture of Michael Boelen

This article has been written by our Linux security expert Michael Boelen. With focus on creating high-quality articles and relevant examples, he wants to improve the field of Linux security. No more web full of copy-pasted blog posts.

Discovered outdated information or have a question? Share your thoughts. Thanks for your contribution.

Mastodon icon