Why?

Because I’m changing my Internet Service Provider (ISP) from one who provides a static IP address (Plusnet) to one who doesn’t (Virgin Media)^ unless I buy a silly money business product, however I still need to provide various services to friends and family (email, chat etc.) which requires a static IP address.

Conveniently Andrews & Arnold (aka A&A, another ISP) sell a service specifically for this situation, where you get a static IP address in their network that can be associated with a network interface on a machine in your world over the Layer 2 Tunneling Protocol (L2TP).

The Plan

I currently have a static IP address on my home broadband. This allows me to forward specific traffic from the Internet to a server I run in the house, using my OpenWRT router. Easy(ish).

I will replace this with a remote interface (over L2TP), directly to the server, returning my router to a simpler configuration (almost no traffic forwarding), in preparation for replacing it with the Virgin Media one. I will also need to migrate the firewall rules from the router to the server to filter the raw Internet on this interface (but not any others). Sounds OKish..

Caveat: I presently have one forwarding rule for the L2TP traffic (port 1701/udp), which may not be necessary with sufficient tunnelled traffic or keep alives. To Be Experimented With.

Step #0 - buy the L2TP product

This was definitely the easy bit, A&A have a sane and working fully automated sales platform, so on a Sunday morning I completed my purchase in about 10 mins. I particularly like the way they integrate with their payment provider, automatically filling out my details once I have sent them a setup payment.

Step #1 - learn iptables

As I will be migrating firewall rules, and will want them installed before exposing my server to the raw Internet, I looked at this early.

I read the manual for ufw which is the usual firewall tooling recommended by Debian and Ubuntu. Unfortunately it’s designed for systems where you want to firewall everything, and arranging it to only mess with one interface is ugly. I was also unsure the integration into the networking control stack will work for me, I do not use network manager on my server.. next!

I looked at FirewallD which also seemed popular, unfortunately that integrates even more with network manager and is aimed at road warriors.. it may have managed the multi-homing better.. next!

Given I really don’t want anything ‘clever’, and I’ve managed to read and understand what fail2ban is already doing with the underlying iptables kernel configuration, I dove into writing my own ruleset and hooking that in to the INPUT processing. This pretty much worked first time, although I did have a minor struggle with unplanned additional traffic (see below)!

Here’s the final script (hooked into PPP, see below: /etc/ppp/ip-pre-up.d/firewall.sh):

#! /bin/sh
# iptables firewall setup script for L2TP tunnel interface exposed directly to the 'net

# some config..
IFACE=$1
CHAIN=aaisp-in
IPTABLES=/sbin/iptables
#IPTABLES=echo
# services we permit
SSHD="22/tcp"
SMTP="25,465,587/tcp"
IMAP="143,993/tcp"
HTTP="80,443/tcp"
TINC="655/tcp 655/udp"
MTRX="8448/tcp"
# munged together..
ALLOW="$SSHD $SMTP $IMAP $HTTP $TINC $MTRX"

# bail out function
fail() {
    echo "$*" >&2
    exit 1
}

[ -n "$IFACE" ] || fail "no interface on command line"

# first, clean up
# - disconnect INPUT chain from calling our user chain (for current interface)
echo "Dropping existing firewall rules.."
$IPTABLES -D INPUT -i $IFACE -j $CHAIN
sleep 1
# - nuke our user chain
$IPTABLES -F $CHAIN
$IPTABLES -X $CHAIN

# now build our user chain
echo "Creating firewall rules.."
$IPTABLES -N $CHAIN
for s in $ALLOW
do
    PROT=`echo $s |cut -d/ -f2`
    PORT=`echo $s |cut -d/ -f1`
    [ -n "$PORT" ] || fail "missing port number(s): $s"
    $IPTABLES -A $CHAIN -p $PROT -s 0/0 -d 0/0 -m multiport --dports $PORT -j ACCEPT
done
# permit established connections (that we have made from inside)
$IPTABLES -A $CHAIN -m conntrack --ctstate ESTABLISHED -j ACCEPT
# default log then reject after all allows
$IPTABLES -A $CHAIN -j LOG --log-prefix "_FIREWALL_ "
$IPTABLES -A $CHAIN -j REJECT

# connect current interface to user chain
$IPTABLES -A INPUT -i $IFACE -j $CHAIN

# done!
exit 0

As you can see, I run a few TCP services, plus a tinc VPN (TCP & UDP). The original plan was not to have the default route via the new interface, so I wouldn’t need to allow any other traffic apart from the well-known services. Unfortunately this didn’t work (routing issues), so I now include the additional ESTABLISHED rule which allows Internet things to reply to any packets my server sends out (like DNS!).

Currently I’m logging rejected packets, with a prefix that allows me to dump the logs into a separate file (/var/log/firewall.log). YMMV.

Step #2 - hook up the L2TP & PPP

A&A provide a guide for Debian which was quite helpful, but doesn’t really reflect my use case. I also read the manuals for the software used and it turns out there are better ways to acheive some things - might poke their wiki with an update at some point :)

required packages

# aptitude install xl2tpd pppd

static routes for the tunnel

This ensures that even if the default route changes (it will) then tunnel traffic always goes out over the underlying network (/etc/network/interfaces):

# The primary network interface
# - additional static routes for l2tp.aa.net.uk so it remains routed via gateway even
#   after xl2tpd/pppd brings up a new default route via the tunnel
auto eno1
iface eno1 inet static
        address 192.168.0.5
        broadcast 192.168.0.255
        netmask 255.255.255.0
        gateway 192.168.0.1
        up /sbin/ip route add 90.155.53.19/32 via 192.168.0.1
        up /sbin/ip route add 194.4.172.12/32 via 192.168.0.1

xl2tpd tunnel client

Here we provide the L2TP daemon with connection details for A&A, and a useful option that automatically starts connection attempts. We also ask it to retry effectively forever if the connection fails. This avoids the ugly cron job hack in the original guide to start the connection (/etc/xl2tpd/xl2tpd.conf):

[lac aaisp]
lns = l2tp.aa.net.uk
autodial = yes
redial = yes
redial timeout = 15
max redials = 9999
require authentication = no
pppoptfile = /etc/ppp/peers/lac.aaisp

Note that this needs working DNS to locate the tunnel server. At this point we have a default route to the Internet via the router, so we can resolve names (I happen to run a full recursive resolver on this system, which is paranoia on my part and you may be happy to simply forward requests to your ISP!)

pppd remote interface

Almost the final part of the puzzle, this configures pppd to negotiate an IP address and create a new network interface associated with it (/etc/ppp/peers/lac.aaisp):

# PPP outbound config for AAISP/L2TP tunnel
# - do not require peer to authenticate with us
# - replace default route via this interface when up
# - call the new interface 'aaisp-l2tp'
# - include our AAISP credentials
noauth
replacedefaultroute
defaultroute
ifname aaisp-l2tp
name <insert your A&A L2TP identity here>
password <insert your A&A L2TP password here>

Note that this replaces the default route on the server (previously to my gateway at 192.168.0.1) so that connecting clients get replies from the same IP they sent packets to. I tried configuring without adjusting the default route, but things get messy with trying to associate incoming and outgoing packets in the network core and force non-default specific routing.. Maybe later when I’ve read another manual! I also have other interfaces on this server..

pppd will automatically run the firewall setup script (see above) as this interface comes up, providing the interface name as the first argument. Note that as I only run one ppp connection, I don’t need to filter on interface name in the script to avoid firewalling other things. YMMV.

Fire up the beast!

This is the easy bit:

# systemctl start xl2tpd; tail -f /var/log/syslog

..and watch the magic happen :)


^ Also why: because they are the only alternative to a terrible aluminium POTS line that will never go faster than 50/1.5, and there is zero sign of Openreach putting actual fibre along my road. Le sigh..