Today we will learn about loopback addresses that can be reached from the outside via routing. This is useful for running services on a router
In a previous post, I talked about the loopback interface and how we can locally bind services to any address in the range 127.0.0.1-127.255.255.254
. This is useful if 127.0.0.1
is already in use on a particular port.
The main advantages of loopback addresses are:
- Adding additional addresses without the need to add extra interfaces
- Better availability of services, as long as you have a route to the loopback address, the service will stay up.
- Can hide the presence of services to an attacker probing the addresses of connected interfaces.
Adding a loopback address
sudo ip address add 10.32.16.8 dev lo
Accessing it from the outside
We can't yet ping it from the outside. Why? Because none of the outside hosts have a route to it.
So let's connect to my router and add a route
sudo ip route add to 10.32.16.8 via $DESKTOP_IP
Now any host on my network should be able to reach 10.32.16.8
providing it uses the above router as its default gateway.
Restricting Access
On my router, we can add iptables
rules to restrict forwarding to the address.
A simple example will be that only hosts on the eth1
interface can access the loopback address.
sudo iptables -I FORWARD ! -i eth1 -d 10.32.16.8 -j DROP
We can also do this by source address if desired.
What about locking it down on my desktop?
There are a number of ways we may wish to lock it down on my desktop (the host on which I created the loopback address)
First let's create a chain for it.
sudo iptables -N LOOPBACK_ADDR
sudo iptables -I INPUT --dst 10.32.16.8 -j LOOPBACK_ADDR
Give a default action for the chain
sudo iptables -A LOOPBACK_ADDR -j DROP
We can add rules to whitelist traffic to the loopback address
Restrict to eth1 interface
sudo iptables -I LOOPBACK_ADDR -i eth0 -j ACCEPT
Restrict access on the loopback address only to a particular port
sudo iptables -I LOOPBACK_ADDR -p tcp --dport 80 -j ACCEPT
Allow ICMP ping
sudo iptables -I LOOPBACK_ADDR -p icmp --icmp-type echo-request -j ACCEPT
NAT + Docker
I frequently use loopback addresses as the external endpoint for services running inside Docker containers
For example, we want a Ghost blog to be accessible on 10.32.16.8:80
We can use the following command
$ docker run -p 10.32.16.8:80:2368 docker.io/ghost:latest
We don't want any services on the host itself to be accessible on that address. The rules for redirecting traffic to the docker container are in the nat
iptable, which is evaluated before the filter
table (which as the name suggests, is for filtering traffic to the host).;
So if we run
$ sudo iptables -I INPUT -d 10.32.16.8 -j DROP
We will still be able to access the Ghost blog at that address, but won't, for example, be able to access the hosts ssh service on that address.
Better availability of services
Suppose we have a Router with the following interfaces:
lo 127.0.0.1/8
lo 10.53.53.53/32
eth0 10.10.0.1/24
eth1 10.10.1.1/24
We want to run a DNS server on the Router. Normally, we would have to do one of the following:
- Bind the DNS server to
0.0.0.0
(all interfaces) - Have two listening sockets for
eth0
andeth1
respectively
Both are kind of disadvantageous. With the first, it's generally bad security to bind to all interfaces, and with the second, with DHCP we will need to give different dns server addresses for each interface.
Instead, we bind the DNS server to the loopback address. Now as long as we are able to access the router, we will be able to access the DNS server at the address 10.53.53.53
There is an additional security benefit here, we can make our DNS server somewhat hidden. A rogue host on the lan may try probing 10.10.0.1
as its the default gateway address, but they won't find the DNS server because it's listening on the loopback address. There is basically no way to know there's a DNS server running unless you know to try 10.53.53.53
Another neat trick
Keeping with the example of the LAN DNS. Suppose we have a DNS server running on a LAN host, as well as a couple of additional DNS servers to serve as backup.
All the DNS servers have a loopback address of 10.53.53.53
We have as follows:
10.10.0.53 - primary dns server
10.10.1.53 - backup dns server
On the Router we do the following
sudo ip route add to 10.53.53.53 via 10.10.0.53 metric 1
sudo ip route add to 10.53.53.53 via 10.10.1.53 metric 2
I wish to perform maintenance on the primary server and thus need to fallback to the backup.
We run the following command on the router
sudo ip route del to 10.53.53.53 via 10.10.0.53 metric 1
Now connections to 10.53.53.53
will route to 10.10.1.53 (the backup server) rather than the primary 10.10.0.53
.
The cool thing is I can change the DNS server for my lan without having to tell everyone to change their DNS server
Loopback source addresses
Remember, any destinations you connect to need to have the loopback address inside their routing table.
For this example, we will work on my Router because that is the default gateway on my network (therefore any hosts on my network will already have a route to any loopback address on the router)
Let's create a new loopback address on the router
sudo ip address add 192.0.2.69/32 dev lo
Now we run the following command
sudo ip route change 10.10.0.0/24 src 192.0.2.69
Now whenever the router connects to anything in the network 10.10.0.0/24
its source address will show up as 192.0.2.69
To prove it, on the router, let's connect to the https server on my raspberry pi
telnet 10.10.0.80 443
At the same time, on the RasPi we look at our socket statistics
$ ss -lt -o state established
Recv-Q Send-Q Local Address:Port Peer Address:Port
0 0 10.10.0.80:https 192.0.2.69:51556