IPv6 Firewalling

From SixXS Wiki
Jump to: navigation, search

This article is for information regarding firewalls on an IPv6 network.

Please see the SixXS FAQ for some information on what one should filter.

Linux support

Linux Kernel (ip6tables)

There is now an IPv6 stateful inspection kernel module since version 2.6.20 and ip6tables version 1.3.5 [1] [2]; you will need to enable the kernel module named CONFIG_NF_CONNTRACK_IPV6.

Configurable scripts

You don't have to build a specialized script for every firewall; there are configurable ones. If you are familiar with Shorewall (and even if you aren't) you may like 6wall, originally from the LEAF Project, rescued from oblivion and packaged for Debian/Ubuntu by Flavio Visentin. The configuration files are very similar to Shorewall's.

Beginning with Shorewall version 4.2.4, there are two additional packages that provide IPv6 support that can be installed to ipv4 Shorewall in parallel.

shorewall6, is documented at shorewall6

6wall, much like the example script below, is ready for old kernels (up to 2.6.20) that do not support properly IPv6 connection tracking. It relies on the SYN flag to allow new TCP connections, and makes no distinction between new and established connections for UDP and other protocols.

If you are running Linux 2.6.21+, you can apply a patch to add proper connection tracking support.

Using upstart on Ubuntu without additional packages

If you don't feel like using some additional software to manage couple of firewall lines you can use upstart to trigger firewall rules. Rules can be saved in whichever place using ip[6]tables-save, below upstart task is using /etc/default/ip[6]tables: File: /etc/init/iptables.conf

description "Starts and stops firewall by restoring ip[6]tables policies.
Save rules with iptables-save > /etc/default/iptables and ip6tables-save > /etc/default/ip6tables"

start on runlevel [2345] or net-device-up IFACE!=lo
stop on runlevel [!2345]
emits firewall

console output 

pre-start script
 test -f /etc/default/iptables && /sbin/iptables-restore < /etc/default/iptables
 test -f /etc/default/ip6tables && /sbin/ip6tables-restore < /etc/default/ip6tables
end script

post-stop script
 /sbin/iptables -F
 /sbin/ip6tables -F
end script

Save and restore IPv6 firewall rules

To configure firewall rules you can execute commands from below examples and then save them with:

# ip6tables-save > /etc/default/ip6tables.rules

To restore stored firewall rules, run:

# ip6tables-restore < /etc/default/ip6tables.rules

Example script for IPv6 stateless firewall

Note that this example, is exactly that, an example. It doesn't show best practices or what one should do.

#!/bin/sh

# Flush & default
ip6tables -F INPUT
ip6tables -F OUTPUT
ip6tables -F FORWARD
ip6tables -F

# Set the default policy to accept
#ip6tables -P INPUT ACCEPT
#ip6tables -P OUTPUT ACCEPT
#ip6tables -P FORWARD ACCEPT

# Enable the following lines only if a router!
# Enabling IPv6 forwarding disables route-advertisement reception.
# A static gateway will need to be assigned.
#
#echo "1" >/proc/sys/net/ipv6/conf/all/forwarding
#
#End router forwarding rules

# Disable processing of any RH0 packet
# Which could allow a ping-pong of packets
ip6tables -A INPUT -m rt --rt-type 0 -j DROP
ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP
ip6tables -A FORWARD -m rt --rt-type 0 -j DROP

# Allow anything on the local link
ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

# Allow Link-Local addresses
ip6tables -A INPUT -s fe80::/10 -j ACCEPT
ip6tables -A OUTPUT -s fe80::/10 -j ACCEPT

# Allow multicast
ip6tables -A INPUT -d ff00::/8 -j ACCEPT
ip6tables -A OUTPUT -d ff00::/8 -j ACCEPT

# Allow ICMP
ip6tables -A INPUT -p icmpv6 -j ACCEPT
ip6tables -A OUTPUT -p icmpv6 -j ACCEPT
#ip6tables -A FORWARD -p icmpv6 -j ACCEPT

# Disable privileged ports for the outside, except ports 22, 515, and 631
# Specifying an interface (-i ethX) is probably a good idea to specify what is the outside
ip6tables -A INPUT -p tcp --dport 1:21 -j REJECT
ip6tables -A INPUT -p udp --dport 1:21 -j REJECT
ip6tables -A INPUT -p tcp --dport 23:514 -j REJECT
ip6tables -A INPUT -p udp --dport 23:514 -j REJECT
ip6tables -A INPUT -p tcp --dport 516:630 -j REJECT
ip6tables -A INPUT -p udp --dport 516:630 -j REJECT
ip6tables -A INPUT -p tcp --dport 632:1024 -j REJECT
ip6tables -A INPUT -p udp --dport 632:1024 -j REJECT

NOTE: REJECT target support for netfilter/IPv6 was first introduced with Linux-kernel v2.6.14.

Example script for IPv6 stateful firewall

Almost as above, but with state. You need a new kernel with the correct modules (see above). This firewall denies everything by default, but allows link-local addresses, multicast and icmpv6. Everything internal is also allowed. The host itself and every client can access the Internet and it also forward (inwards) ssh and bittorrent traffic to a specified host on the subnet.

sixxs is the external interface, br-lan is internal.

# First, delete all:
ip6tables -F
ip6tables -X

# Allow anything on the local link
ip6tables -A INPUT  -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

# Allow anything out on the internet
ip6tables -A OUTPUT -o sixxs -j ACCEPT
# Allow established, related packets back in
ip6tables -A INPUT  -i sixxs -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow the localnet access us:
ip6tables -A INPUT    -i br-lan   -j ACCEPT
ip6tables -A OUTPUT   -o br-lan   -j ACCEPT

# Filter all packets that have RH0 headers:
ip6tables -A INPUT -m rt --rt-type 0 -j DROP
ip6tables -A FORWARD -m rt --rt-type 0 -j DROP
ip6tables -A OUTPUT -m rt --rt-type 0 -j DROP

# Allow Link-Local addresses
ip6tables -A INPUT -s fe80::/10 -j ACCEPT
ip6tables -A OUTPUT -s fe80::/10 -j ACCEPT

# Allow multicast
ip6tables -A INPUT -d ff00::/8 -j ACCEPT
ip6tables -A OUTPUT -d ff00::/8 -j ACCEPT

# Allow ICMPv6 everywhere
ip6tables -I INPUT  -p icmpv6 -j ACCEPT
ip6tables -I OUTPUT -p icmpv6 -j ACCEPT
ip6tables -I FORWARD -p icmpv6 -j ACCEPT

# Allow forwarding
ip6tables -A FORWARD -m state --state NEW -i br-lan -o sixxs -s <subnet-prefix>::/48 -j ACCEPT
ip6tables -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# SSH in
ip6tables -A FORWARD -i sixxs -p tcp -d <subnet-prefix>::5 --dport 22 -j ACCEPT

# Bittorrent
ip6tables -A FORWARD -i sixxs -p tcp -d <subnet-prefix>::5 --dport 33600:33604 -j ACCEPT

# Set the default policy
ip6tables -P INPUT   DROP
ip6tables -P FORWARD DROP
ip6tables -P OUTPUT  DROP

A more sophisticated script for IPv6 stateful firewall

Almost as above, but with state. You need a new kernel with the correct modules, using asuswrt (Merlin Build) load

insmod /lib/modules/2.6.22.19/kernel/net/ipv6/netfilter/nf_conntrack_ipv6.ko

before FW launch.

This firewall denies everything by default, but allows link-local addresses, multicast and icmpv6. Everything internal is also allowed. The host itself and every client can access the Internet and only the sixxs POP is allowed to ping us, nobody else!


 
#!/bin/sh
#
# Test your ipv6 firewall rule set using:
# http://ipv6.chappell-family.com/ipv6tcptest/index.php
# Thank you Tim for providing this test tool.
#
# Ver. 2.0 (RHO and Logging, speciall ICMP Blocking) 
# 29.12.2012  
# 

# Definitions
IP6TABLES='/usr/sbin/ip6tables'

# change LAN and IPv6 WAN interface name according your requirements
WAN_IF='sixxs' 
LAN_IF='br0'  

SUBNETPREFIX='<subnet-prefix::>/48'
MYTUNNEL='Your IP'
SIXXSTUNNEL='Pop IP' 

# First Flush and delete all:
$IP6TABLES -F INPUT
$IP6TABLES -F OUTPUT
$IP6TABLES -F FORWARD

$IP6TABLES -F
$IP6TABLES -X 

# DROP all incomming traffic
$IP6TABLES -P INPUT DROP
$IP6TABLES -P OUTPUT DROP
$IP6TABLES -P FORWARD DROP

# Filter all packets that have RH0 headers:
$IP6TABLES -A INPUT -m rt --rt-type 0 -j DROP
$IP6TABLES -A FORWARD -m rt --rt-type 0 -j DROP
$IP6TABLES -A OUTPUT -m rt --rt-type 0 -j DROP

# Allow anything on the local link
$IP6TABLES -A INPUT  -i lo -j ACCEPT
$IP6TABLES -A OUTPUT -o lo -j ACCEPT

# Allow anything out on the internet
$IP6TABLES -A OUTPUT -o $WAN_IF -j ACCEPT
# Allow established, related packets back in
$IP6TABLES -A INPUT  -i $WAN_IF -m state --state ESTABLISHED,RELATED -j ACCEPT


# Allow the localnet access us:
$IP6TABLES -A INPUT    -i $LAN_IF  -j ACCEPT
$IP6TABLES -A OUTPUT   -o $LAN_IF   -j ACCEPT

# Allow Link-Local addresses
$IP6TABLES -A INPUT -s fe80::/10 -j ACCEPT
$IP6TABLES -A OUTPUT -s fe80::/10 -j ACCEPT

# Allow multicast
$IP6TABLES -A INPUT -d ff00::/8 -j ACCEPT
$IP6TABLES -A OUTPUT -d ff00::/8 -j ACCEPT

# Paranoia setting on ipv6 interface  
$IP6TABLES -I INPUT -i $WAN_IF -p tcp --syn -j DROP
$IP6TABLES -I FORWARD -i $WAN_IF -p tcp --syn -j DROP
$IP6TABLES -I INPUT -i $WAN_IF -p udp  -j DROP
$IP6TABLES -I FORWARD -i $WAN_IF -p udp  -j DROP

# Allow forwarding on ipv6 interface 
$IP6TABLES -A FORWARD -m state --state NEW -i $LAN_IF -o $WAN_IF -s $SUBNETPREFIX -j ACCEPT
$IP6TABLES -A FORWARD -m state --state ESTABLISHED,RELATED -j ACCEPT

# Allow dedicated  ICMPv6 packettypes, do this in an extra chain because we need it everywhere
$IP6TABLES -N AllowICMPs
# Destination unreachable
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 1 -j ACCEPT
# Packet too big
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 2 -j ACCEPT
# Time exceeded
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 3 -j ACCEPT
# Parameter problem
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 4 -j ACCEPT
# Echo Request (protect against flood)
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 128 -m limit --limit 5/sec --limit-burst 10 -j ACCEPT
# Echo Reply
$IP6TABLES -A AllowICMPs -p icmpv6 --icmpv6-type 129 -j ACCEPT
#
# Only the sixxs POP is allowed to ping us (read FAQ this is a requirement) 
#
$IP6TABLES -A INPUT -p icmpv6 -s $SIXXSTUNNEL -d $MYTUNNEL  -j AllowICMPs


# Log 
$IP6TABLES -A INPUT -j LOG --log-prefix "INPUT-v6:"
$IP6TABLES -A FORWARD -j LOG  --log-prefix "FORWARD-v6:"
$IP6TABLES -A OUTPUT -j LOG  --log-prefix "OUTPUT-v6:"

FreeBSD support

FreeBSD supports 3 different firewalls (although two of them are very close):

  1. ipfw6 is the IPv6 version of ipfw and has been part of FreeBSD for a long time.
  2. ipf aka IPFirewall by Darren Reed
  3. pf was started as a fork of ipf. pf was integrated in FreeBSD starting at version 5.3

The FreeBSD Handbook entry on firewalls is a good introduction to the concepts and tools.

pf

To enable pf on FreeBSD, you need to insert the following lines in /etc/rc.conf:

pf_enable="YES"
pflog_enable="YES"
pf_rules="/etc/filters/pf.conf"         # rules definition file for pf
pflog_logfile="/var/log/pflog"  # where pflogd should store the logfile

You can change the path specified in pf_rules of course. The pf.conf(5) manpage give some extensive examples (incl. the complete grammar for rules and configuration).

The most simple firewall rule set is

Skeleton of an pf.conf firewall configuration

... your firewall rule / interfaces etc 

....
 
tun_if = "gif0" # <--- add the tunnel interface 

# don't filter on the loopback interface
set skip on lo0

# scrub incomming packets
scrub in all

..... other rules 

#
# --- ipv6 pf.conf rules  
#


# block in/out on $tun_if
block in log on $tun_if inet6
block out log on $tun_if inet6

# allow heartbeat ping 
pass in log quick on $tun_if inet6 proto { ipv6-icmp } from <your individual net>::1 to <your individual net>::2 keep state

# pass tcp, udp, and icmp6 out on the ipv6 tunnel interface.
pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state

Don't forget to check your configuraition with an external scanner to ensure your firewall settings are correct.

You can also find more information on the web (tutorials, book and so on). You can start with these: Wikipedia page on pf

Simple pf.conf firewall configuration


#
# --- Simple pf.conf for ipv4 and ipv6 (aiccu)
#

# Macros
ext_if = "vr0" # macro for external interface (tun0 or pppoe0 for PPPoE)
int_if = "vr1"  # macro for internal interface
tun_if = "gif0" # macro for aiccu tunnel interface
localnet = $int_if:network
mytunnelstart = 2001:dead:beef:fdb8::1
mytunnelendpoint = 2001:dead:beef:fdb8::2

# don't filter on the loopback interface
set skip on lo0

# scrub incomming packets
scrub in all

#
# --- ipv4 rule set
#

nat on $ext_if from $localnet to any -> ($ext_if)
block log all
pass from {lo0, $localnet } to any keep state

#
# --- ipv6 rule set
#

# keep alive rules
pass out log proto 41 from ($ext_if) to  [PoP IPv4 Endpoint] keep state
pass in log proto 41 from [PoP IPv4 Endpoint] to ($ext_if) keep state

# block in/out on $tun_if
block in log on $tun_if inet6
block out log on $tun_if inet6

# allow heartbeat ping
pass in log quick on $tun_if inet6 proto { ipv6-icmp } from $mytunnelstart to $mytunnelendpoint keep state

# pass tcp, udp, and icmp6 out on the ipv6 tunnel interface.
pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state


Please modify interfaces according your hardware requirements.

Check configuration


router# pfctl -sr
No ALTQ support in kernel
ALTQ related functions disabled
scrub in all fragment reassemble
block drop in log on gif0 inet6 all
block drop out log on gif0 inet6 all
pass in log quick on gif0 inet6 proto ipv6-icmp from 2001:dead:beef:dfb0::1 to 2001:dead:beef:dfb0::2 keep state
pass out log quick on gif0 inet6 proto tcp all flags S/SA keep state
pass out log quick on gif0 inet6 proto udp all keep state
pass out log quick on gif0 inet6 proto ipv6-icmp all keep state

The sample shown above is from an ipv6 injector firewall according. Implemented on an dedicated FreeBSD instance after an NAT Firewall.


ipfw6

Your main source of information will be the following page in the handbook ipfw page

ipfw6 is the IPv6 version of ipfw. It can be enabled in /etc/rc.conf with these lines:

firewall_flags=""               # Flags passed to ipfw when type is a file
firewall_myservices=""          # List of TCP ports on which this host
                                #  offers services
firewall_allowservices=""       # List of IPs which has access to
                                #  $firewall_myservices
firewall_trusted=""             # List of IPs which has full access to this host
firewall_logdeny="NO"           # Set to YES to log default denied incoming
                                #  packets.
firewall_nologports="135-139,445 1026,1027 1433,1434" # List of TCP/UDP ports
                                #  for which denied incoming packets are not
                                #  logged.

More information within the default rc file in /etc/default/rc.conf and in the /etc/rc.firewall6 script.

ipf

Main page in the handbook on ipf is here

To enable ipf on FreeBSD, you need to insert the following lines in /etc/rc.conf:

ipfilter_enable="YES"
ipnat_enable="YES"
ipmon_enable="YES"
ipfs_enable="NO"
ipfilter_rules="/etc/filters/adsl.rules"
ipnat_rules="/etc/filters/nat.rules"
ipmon_flags="-Ds"
ipfilter_flags="6"

IPnat is not required for IPv6 support of course.

OpenBSD support

For the sake of completeness a brief view on OpenBSD.


pf

pf on OpenBSD is enabled per default.

You can also find more information on the web (tutorials, book and so on). You can start with these: Wikipedia page on pf

We use the same pf.conf as already shown in the FreeBSD section.

Simple pf.conf firewall configuration

Check the minor syntax changes in pf. This work with OpenBSD 5.7.


#
# --- Simple pf.conf for ipv4 and ipv6 (aiccu)
#

# Macros
ext_if = "vr0" # macro for external interface (tun0 or pppoe0 for PPPoE)
int_if = "vr1"  # macro for internal interface
tun_if = "gif0" # macro for aiccu tunnel interface
localnet = $int_if:network
mytunnelstart = 2001:dead:beef:fdb8::1
mytunnelendpoint = 2001:dead:beef:fdb8::2

# don't filter on the loopback interface
set skip on lo0

# scrub incomming packets
match on $tun_if scrub (no-df)


#
# --- ipv4 rule set
#

nat on $ext_if from $localnet to any -> ($ext_if)
block log all
pass from {lo0, $localnet } to any keep state

#
# --- ipv6 rule set
#

# keep alive rules
pass out log proto 41 from ($ext_if) to  [PoP IPv4 Endpoint] keep state
pass in log proto 41 from [PoP IPv4 Endpoint] to ($ext_if) keep state

# block in/out on $tun_if
block in log on $tun_if inet6
block out log on $tun_if inet6

# allow heartbeat ping
pass in log quick on $tun_if inet6 proto { ipv6-icmp } from $mytunnelstart to $mytunnelendpoint keep state

# pass tcp, udp, and icmp6 out on the ipv6 tunnel interface.
pass out log quick on $tun_if inet6 proto { tcp udp ipv6-icmp} keep state


Please modify interfaces according your hardware requirements.

Simple pf.conf router firewall configuration

Configuration

Cable Internet Provider

AICCU heartbeat tunnel

PC Engines ALIX / OpenBSD 5.7

Consult OpenBSD and pf documentation prior to any changes on your firewall configuration.



#       $OpenBSD: pf.conf,v 1.1 2015/11/01 21:12:47 tom Exp $

## Macros

# The internal interface (connected to the local network)
int_if="{ vether0 vr1 vr2 }"
# The external interfaces (connected to the ipv4 and ipv6 network)
ext_if="{ vr0 }"
sixxs_if="{ gif0 }"

## don't forget to modify your parameters
Your_IPv6="2001:dead:beaf:ab4::2"
PoP_IPv6="2001:dead:beaf:ab4::1"
sixxsgw="xx.yy.zz.dd" # replace by your PoP IPv4

## Options
set loginterface egress
set optimization aggressive

# scrub incomming packets
match on egress scrub  (no-df random-id max-mss 1440)

# Set the default policy to return RSTs or ICMPs for blocked traffic
set block-policy drop

# Ignore the loopback interface entirely
set skip on lo0

anchor "ftp-proxy/*"

pass in quick on $int_if inet proto tcp to any port ftp \
divert-to 127.0.0.1 port 8021

## Translation rules

# NAT traffic on the interface in the default egress interface group (to
# which the interface out of which the default route goes is assigned) from the
# local network
match out on egress inet from !(egress:network) to any nat-to (egress:0)
#####

## Queueing
queue limits on $ext_if bandwidth 100M max 100M
queue sixxsping parent limits bandwidth 5K
queue priorityack parent limits bandwidth 100K
queue std parent limits bandwidth 17M default

## Filtering ruleS

# Default deny rule. which all blocked packets logged.
block log all
block in log on $ext_if all
antispoof log for egress
# Pass all traffic to and from the local network, using quick so that later
# rules are not evaluated if a packet match this. Some rulesets would restrict
# local traffic much further
pass quick on $int_if all
antispoof quick for "{ lo $int_if }"


# Permit all traffic going out, keep state so that replies are
# automatically passed many rulesets would have many rules here,
# restricting traffic in an out on  the external (egress) interface.
# (keep state is not needed on the newest version of pf)
pass out quick
pass out on $ext_if proto tcp from $ext_if to any flags S/SA keep state  queue priorityack set prio (1,7)

#pass in  on $ext_if proto tcp from any to $ext_if flags S/SA keep state queue priorityack set prio (1,7)

## ipv6

# Allow sixxs regular pings to our inbound interface
# used for tunnel keep alive

pass out log proto 41 from $ext_if to  $sixxsgw  keep state # set queue sixxsping
pass in log proto 41 from $sixxsgw to $ext_if keep state # set queue sixxsping

#
#
#
pass in log on $sixxs_if inet6 proto { ipv6-icmp } from $PoP_IPv6 to $Your_IPv6

# Allow outgoing ipv6 traffic
# pass tcp, udp, and icmp6 out on the external (Internet) interface.
pass out on gif0 inet6 proto { tcp udp ipv6-icmp}



Check firewall

Check if the AICCU tunnel heartbeat is accepted.


# tcpdump -n -e -ttt -i pflog0
tcpdump: WARNING: snaplen raised from 116 to 160
tcpdump: listening on pflog0, link-type PFLOG
Feb 21 17:20:42.904336 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request
Feb 21 17:21:41.925066 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request
Feb 21 17:22:40.944487 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request
....
Feb 21 17:23:39.963511 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request
Feb 21 17:24:38.983139 rule 13/(match) pass in on gif0: 2001:dead:beef:fdb8::1 > 2001:dead:beef:fdb8::2: icmp6: echo request

As shown above, inbound icmp6 is allowed from your PoP IPv6. If you see frequent inbound requests the is up and running.

Make an additional test using Tim Chappel's free online IPV6 scanner.