66

I have a service running on 127.0.0.1 with port 2222. I need to forward all requests to 192.168.2.2:2222 (outside IP) only from subnet 192.168.1.0/24 to 127.0.0.1:2222.

I'm trying to use this, but it's not working.

$ iptables -t nat -I PREROUTING -p tcp -d 192.168.1.0/24 --dport 2222 -j DNAT --to-destination 127.0.0.1:2222

How can I get this to work?

UPD: Edit address scheme.

imz -- Ivan Zakharyaschev
  • 15,113
  • 15
  • 61
  • 123
SimWhite
  • 785
  • 1
  • 7
  • 9
  • We need some clarification. Where is the traffic coming from? Where is the traffic originally going to? Where should the traffic be going to? As I read it, you want traffic from 192.168.1.0/24 to 127.0.0.1:2222 to be redirected to 12.23.34.45:2222. But Warren's answer assumes you want traffic from 192.168.1.0/24 to 12.23.34.45:2222 to be redirected to 127.0.0.1:222 – phemmer Jan 29 '14 at 13:37
  • 2
    Traffic comes from 192.168.1.0/24 subnet to 192.168.2.2:2222 and should be translated to service on 127.0.0.1:2222. I'm corrected address scheme. – SimWhite Jan 29 '14 at 14:27
  • 1
    You want a rule that allows traffic to port `2222` on the loopback interface from subnet `192.168.1.0/24`? That isn't just a single rule type of setup. See here: http://www.debuntu.org/how-to-redirecting-network-traffic-to-a-new-ip-using-iptables/ – slm Jan 29 '14 at 14:35
  • Yes. As I understand I need to add masq rule? IP forwarding is already enabled of course. – SimWhite Jan 29 '14 at 14:47
  • Why not run it on a "real" IP, and filter out traffic comming from unwanted sources? That is what firewalls are for, essentially... – vonbrand Feb 03 '14 at 01:04

3 Answers3

95

The iptables rule you are using will work, but there is one additional change you need to make:

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

(replacing eth0 with the nic 192.168.2.2 resides on)

By default this value is 0, which instructs the kernel to not route external traffic destined to 127.0.0.0/8. This is just for security as such traffic is not normal.

phemmer
  • 70,657
  • 19
  • 188
  • 223
  • 19
    This info is surprisingly hard to find. – Wren T. May 16 '14 at 04:06
  • Yes, very valuable hard-to-find information! Thank you! But I don't have `route_localnet`. Have there been other names for it? (in linux 2.6.30) `ls /proc/sys/net/ipv4/conf/lan/`: accept_redirects arp_accept arp_filter arp_notify disable_policy force_igmp_version log_martians medium_id proxy_arp secure_redirects shared_media accept_source_route arp_announce arp_ignore bootp_relay disable_xfrm forwarding mc_forwarding promote_secondaries rp_filter send_redirects tag – imz -- Ivan Zakharyaschev Mar 31 '15 at 22:39
  • I see, [the patch for `route_localnet`](http://patchwork.ozlabs.org/patch/163683/) is more recent (June 7, 2012) than my kernel ( 2.6.30-std-def-alt15 #1 SMP Mon Dec 14 08:45:48 UTC 2009). Ok, I'll simply achieve the wanted port-forwarding (from outside to inside) with a forwarding process like `netcat` (`nc`), `xinetd`, or the port-forwarding options of `ssh` (the latter is ineffective and stupid, of course, but I mention it, because, well, this possibility is there). – imz -- Ivan Zakharyaschev Mar 31 '15 at 22:50
  • 4
    My 2 cents: you may enable this parameter for all interfaces with `sysctl -w net.ipv4.conf.all.route_localnet=1` – Dmitriusan Nov 10 '17 at 12:39
  • Like others, this info was hard to come by. Thanks, if I'd found this earlier, would have saved me a tonne of effort. – Stephen Simpson Feb 12 '19 at 10:40
  • Thank you very much, surprisingly route_localnet didn't exist by default or commented in `systctl.conf` – anjarwidi83 Apr 10 '22 at 17:40
7

You can redirect to localhost but not to loopback (127.0.0.0/8). Loopback is a loophole. You have to redirect to one of your real interfaces. Try using REDIRECT.

iptables -t nat -A PREROUTING ..... -j REDIRECT --to-port 222

dresende
  • 247
  • 1
  • 4
  • This is probably the best answer without the sysctl voodo... – Lester Cheung Jun 03 '16 at 02:30
  • 2
    Still doesn't help if port 222 is only being listened on localhost (the rule won't change destination address) – sanmai Dec 20 '16 at 04:35
  • 4
    In that case, use `-j DNAT --to-destination w.x.y.z:222` – dresende Dec 21 '16 at 00:58
  • 4
    Thanks @dresende, `-j DNAT --to-destination 127.0.0.1:` works great!
    This is the full command I used to forward connection to a MariaDB server listening on port 1186: `iptables -t nat -A PREROUTING -i eth0 -p tcp --dport 1186 -j DNAT --to-destination 127.0.0.1:1186`
    – maximus Jul 13 '20 at 09:14
5

What if the correct answer with route_localnet doesn't work?..

If your kernel doesn't include the patch for route_localnet, then... upgrade the kernel!

Or there are other ways to forward the traffic coming to one interface to another port on another interface (in particular, to localhost) by running a process that would listen on the external interface and forward the traffic.

netcat (nc), xinetd, and ssh (and perhaps more) are all examples of programs able to do this (although choosing ssh would be strange and ineffective).

I've written a configuration for xinetd for this. Now this service is automatically brought up:

# cat /etc/xinetd.d/z-from-outside 
# default: off
# description: Forward connections to the z port.
service z-from-outside
{
    disable         = no
    socket_type     = stream
    type        = UNLISTED
    wait            = no
    user            = nobody
    bind        = vaio.ob
    port        = 7070
    redirect    = localhost 7070
}
# 

(vaio.ob is the name of this host on the external network interface.)

After a service xinetd reload, let's check that it is listening:

# lsof -i -P | fgrep 7070
xinetd    556      root    6u  IPv4 1797906      0t0  TCP vaio.ob:7070 (LISTEN)
sshd    27438 tun_zzoom    4u  IPv4 1059100      0t0  TCP localhost.localdomain:7070 (LISTEN)
# 

And indeed, connections do go through!

imz -- Ivan Zakharyaschev
  • 15,113
  • 15
  • 61
  • 123