[ipsec, iptables, ebtables] How to avoid leakage of packets addressed to/from private IP space30 Jul 2015
I've been asked by SEED4.ME VPN to investigate the package leakaging issue in their environment raised after delivering new VPN features for Apple devices.
We've agreed that we do want not only to solve this particular problem with Apple IPsec, but to build a fence system which can completely eliminate any packet leaking problems in future caused by any bug, feature, misconfiguration etc.
I hope results of this small project can be useful for other engineers facing similar challenges.
Problem: packet leakage
Packets belonging to private IP address space shall not appear in the network interfaces which do not belong to private networks.
Typical example is VPS hosted in the cloud. If it sends packets addressed to private IP address space through its external NIC, it usually means that something wrong is happening. More providers started to implement BCP38 RFC for defeating Denial of Service Attacks which employ IP Source Address Spoofing. They block such packets and complain to VPS administrators.
This article describes Castle Approach to this problem. An idea is simple:
- Leaked packets are filtered by Linux firewall (netfilter iptables)
- If packets are not filtered by Linux firewall, they are filtered by Linux ethernet bridging firewall (netfilter ebtables). In the same time, they are logged and became visible to the monitoring system
Ebtables is used as last line of defense. It has additional benefit — usually ebtables rules are simple (if they even exist!) and iptables rules can be very complex, managed with sophisticated tools etc. As a result, it's much easier to make a mistake managing iptables than ebtables 1.
IPsec: dropping packets without a matched policy
Create a drop rule in filter FORWARD, that drops incoming packets for SNATed IPs without a matching policy: 2
iptables -A FORWARD -d <src> -m policy --pol none --dir out -j DROP
<src> is your IPSEC VPN network. E.g.,
This is our first line of defense. Don't forget to test that it really works (e.g., by adding logging)!
This step really depends on your networking configuration. Example above is valid for IPsec VPN implementation based on xfrm.
Ebtables as last line of defense
ebtables is a filtering tool for a Linux-based bridging firewall. It works in the link layer.
We will use it to log and drop unwanted packets. Presence of ebtables logs is a signal to administrator that something wrong has happened — packets were not filtered in the higher layers and reached link layer.
Filtering by IP with ebtables: goal setting
Our goal is to drop all the packets addressed to private networks from your external network interface. Filtering mechanism should also support exceptions. In other words, it shall be possible to allow packets addressed to certain (exceptional) private networks to flow through external network interface.
We will suppose that your network interface is called eth0.
Step 1: Enable network bridge functionality
ebtables works with network bridges. It means we need to have one. First, make sure your kernel supports network bridges. Set "networking -> 802.1d Ethernet Bridging" to either yes or module.
Second, check that bridge device can be created:
# ip link add name br0 type bridge # ifconfig br0
Step 2: Enable ebtables filtering
Then install ebtables utility. Installation command for Ubuntu:
apt-get install ebtables
Check that command line
ebtables -L shows you an empty output for all three chains — FORWARD, OUTPUT and INPUT.
Step 3: Create a bridge and add physical network interface to it
We need to have a bridge with only one network card connected (eth0). It means that you need to configure bridge IP address to be the same as it is currently set for your NIC. And change routing.
It is done through network interfaces configuration.
E.g., in Ubuntu you need to change
/etc/network/interfaces file in the following manner:
auto br0 iface eth0 inet manual up ip link set eth0 up up ip link set dev eth0 promisc on down ip link set eth0 promisc off down ip link set eth0 down # The local network bridge iface br0 inet static bridge_ports eth0 bridge_stp off # disable Spanning Tree Protocol bridge_waitport 0 # no delay before a port becomes available bridge_fd 0 # no forwarding delay address <IP address of your host> netmask <netmask of your host> gateway <default gateway> metric 1 post-up ip link set dev br0 promisc on post-up /usr/local/bin/ebtables-filter.sh br0 post-down ebtables -F
Most of the parameters are obvious. Several comments:
- We set promiscuous mode for
br0. This mode is activated after bringing the interfaces up. This step seems to be unnecessary in the recent kernels (4.1.0 and later).
- Filtering is activated by the script
ebtables-filter.shwhich is launched after bridge interface came up.
Step 4: Filtering
/usr/local/bin/ebtables-filter.sh is relatively simple:
It instructs ebtables to drop all packets which are coming from or addressed to private networks. In order to support exceptions we set acceptance rules in the beginning — the packets which are coming from or to these networks will not be dropped. Every chain has a different logging prefix which makes it easier for the reader of log files to understand where the packet was caught.
Step 5: Test your setup
We are done. Now it's time to test your setup. If any packet is dropped, it is also logged into you logging system (the same way iptables logs their packets if requested to do so) and you can easily monitor these logs in your network monitoring platform.
If you have any questions, comments, improvements ideas, don't hesitate to comment under this post!