Using iptables to block traffic that's not protected by IPSec
When I talk about my IPSec setup, I often say that I use GRE over IPSec (or 'an IPSec based GRE tunnel'). However, this is not really what is going on; a more accurate but more opaque description is that I have a GRE tunnel that is encrypted and protected by IPSec. The problem, and the reason that the difference matters, is that there is nothing that intrinsically ties the two pieces together, unlike something where you are genuinely running X over Y such as 'forwarding X11 over SSH'. In the X11 over SSH case, if SSH is not working you do not get anything. But in my case if IPSec isn't there for some reason my GRE tunnel will cheerfully continue working, just without any protection against either eavesdropping or impersonation.
In theory this is undoubtedly not supposed to happen, since you (I)
designed your GRE setup to work in conjunction with IPSec. Unfortunately
in practice there are any number of ways for IPSec to go away on
you, possibly without destroying the GRE tunnel in the process.
Your IPSec IKE daemon probably removes the IPSec security policies
that reject unencrypted traffic when it shuts down, for example,
and if you're manually configuring IPSec with
setkey you can do
all sorts of fun things like accidentally leaving a '
command in a control file that only (re)loads keys and is no longer
used to set up the security policies.
The obvious safety method is to add some iptables rules that block
unencrypted GRE traffic. If you are like me, you'll start out by writing
iptables -A INPUT -p esp -j ACCEPT iptables -A INPUT -p gre -j DROP
This doesn't work. As far as I can tell, the Linux IPSec system
effectively re-injects the decrypted packets into the IP stack,
where they will be seen in their unencrypted state by iptables rules
(as well as by
tcpdump, which can be both confusing and alarming).
The result is that after the re-injection the ipfilters rules see
a plain GRE packet and drop it.
Courtesy of this netfilter mailing list message, it turns out that what you need is to match packets that will be or have been processed by IPSec. This is done with a policy match:
iptables -A INPUT -m policy --dir in --pol ipsec -j ACCEPT iptables -A INPUT -p gre -j DROP # and for outgoing packets: iptables -A OUTPUT -m policy --dir out --pol ipsec -j ACCEPT iptables -A OUTPUT -p gre -j DROP
Reading the iptables-extensions manpage suggests that I should add
at least '
--proto esp' to the policy match for extra paranoia.
I've tested these rules and they work. They pass GRE traffic that is protected by IPSec, but if I remove the IPSec security policies that force IPSec for my GRE traffic these iptables rules block the unprotected GRE traffic as I want.
(Extension to non-GRE traffic is left as an exercise to the reader. I have a simple IPSec story in that I'm only using it to protect GRE and I never want GRE traffic to flow without IPSec to any destination under any circumstances. Note that there are potentially tricky rule ordering issues here and you probably want to always put this set of rules at the end of your processing.)