newspaint

Documenting Problems That Were Difficult To Find The Answer To

Monthly Archives: February 2019

How to DNAT a Port on Localhost in Linux

Let’s say you want to take all traffic destined for http://localhost/ and send it to another IP address, e.g. 10.0.9.4 through your eth0 to another host on your LAN.

The obvious thing to do is to set up a DNAT rule in your NAT iptables, e.g.:

*nat
:PREROUTING ACCEPT [0:0]
:POSTROUTING ACCEPT [0:0]
:OUTPUT ACCEPT [0:0]

# set all traffic sent towards localhost interface destined for port 80
#   to have a new destination address of 10.0.9.4
-A OUTPUT -o lo+ -p tcp --dport 80 -j DNAT --to-destination 10.0.9.4:80

# masquerade such traffic so that it appears to come from the eth0 interface
#   when leaving the host
-A POSTROUTING -o eth0 -p tcp -s 127.0.0.0/8 -j MASQUERADE

Right, that takes care of changing the destination address (DNAT) and the source address while in transit to the new destination (MASQUERADE).

But we still have a problem. Linux won’t route this traffic. Why? The answer was found in this forum post:

$ sysctl -w net.ipv4.conf.eth0.route_localnet=1

This value is documented as:

Do not consider loopback addresses as martian source or destination while routing. This enables the use of 127/8 for local routing purposes.

default FALSE

You have to set this sysctl value for every interface for which you want to be able to DNAT packets out of from localhost.

Systemd Listening on Port

You perform a netstat and discover that systemd is listening on a port that it shouldn’t be occupying:

me@server:~$ sudo netstat -apn
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address  Foreign Address  State   PID/Program name
tcp        0      0 0.0.0.0:993    0.0.0.0:*        LISTEN  1/systemd
tcp        0      0 0.0.0.0:143    0.0.0.0:*        LISTEN  1/systemd

What can be done about this? You can discover which sockets have been configured by running:

me@server:~$ sudo systemctl list-sockets
LISTEN       UNIT            ACTIVATES
...
0.0.0.0:143  dovecot.socket  dovecot.service
0.0.0.0:993  dovecot.socket  dovecot.service
[::]:143     dovecot.socket  dovecot.service
[::]:993     dovecot.socket  dovecot.service
...

In this case we see that “dovecot.socket” is the unit responsible for the binding of these ports.

In the short term you may want to kill the socket to release the port:

me@server:~$ sudo systemctl kill dovecot.socket

… but long term you may want to delete the unit and prevent the ports from being bound in the first place:

me@server:~$ sudo mv /lib/systemd/system/dovecot.socket