12

I have a Linux gateway performing NAT for my home network. I have another network which I'd like to transparently forward packets to, but only to/from specific IP/ports (ie. not a VPN). Here's some example IP and ports to work with:

Source          Router          Remote Gateway     Remote Target
192.168.1.10 -> 192.168.1.1 ->  1.2.3.4        ->  192.168.50.50:5000

I'd like the Source machine to be able to talk to specific ports on Remote Target as if it were directly routable from Router. On the Router, eth0 is the private network and eth1 is internet-facing. Remote Gateway is another Linux machine which I can ssh into and it can route directly to Remote Target.

My attempt at a simple solution is to setup ssh port forwarding on Router, such as:

ssh -L 5000:192.168.50.50:5000 1.2.3.4

This works fine for Router, which can now connect locally to port 5000. So "telnet localhost 5000" will be connected to 192.168.50.50:5000 as expected.

Now I want to redirect traffic from Source and funnel through the now-established ssh tunnel. I attempted a NAT rule for this:

iptables -t nat -D PREROUTING -i eth0 -p tcp -s 192.168.1.10 --dport 5000 -d 1.2.3.4 -j DNAT --to-destination 127.0.0.1:5000

and since the Router is already my NAT gateway, it already has the needed postrouting rule:

-A POSTROUTING -s 192.168.1.0/24 -o eth1 -j MASQUERADE

Most Q&A on this site or elsewhere seem to deal with forwarding server ports or hairpin NAT, both of which I have working fine elsewhere, neither of which apply to this situation. I certainly could DMZ forward Remote Target ports through Remote Gateway, but I don't want the ports internet-accessible, I want them accessible only through the secure SSH tunnel.

The best answer I can find relates to Martian packet rejection in the Linux kernel:

iptables, how to redirect port from loopback?

I've enabled logging of martians and confirmed that the kernel is rejecting these packets as martians. Except that they aren't: I know exactly what these packets are for, where they're from and where they're going (my ssh tunnel).

The "roundabout" solution presented there is applicable to that original question, but does not apply for my case.

However, while writing/researching this question, I have worked around my problem by using SSH source IP binding like so:

ssh -L 192.168.1.1:5000:192.168.50.50:5000 1.2.3.4
iptables -t nat -D PREROUTING -i eth0 -p tcp -s 192.168.1.10 --dport 5000 -d 1.2.3.4 -j DNAT --to-destination 192.168.1.1:5000

Since I'm not using loopback, this gets around Martian rejection.

I still post the question here for two reasons:

  1. In hope that someone trying to do something similar in the future might find this in their searches and this workaround might help them.
  2. I still prefer the idea of keeping my ssh port forwards connection bound only to loopback and being able to route to them through iptables. Since I know exactly what these packets are and where they are going, shouldn't there some way for me to flag them as such so that Linux martian filtering doesn't reject them? All my searching on this topic leads to rp_filter, which didn't help at all in my testing. And even if it did work, it isn't specific to the exact packets I am trying to allow.

ps. Originally posted to serverfault, as that's where I found several related answers when trying to solve this problem. Initially I assumed serverfault was all things Unix/Linux and didn't realize there was a separate Exchange, but according to this meta-post, this exchange is perhaps a better fit for this question. I'm interested in contributing my question and workaround to general search to save someone else the hours of searching I did only to come up with dead ends, as well as hopefully having someone answer the loopback/martian part of my question that still remains open to me.

  • On further reading of the Meta post on UL vs SF, I also notice the request from Michael to not crosspost. I've only crossposted this because it was specifically marked off-topic at SF, so if it's more appropriate and possible to just move the original question and close this one, that would be great too. – Mark Nov 23 '15 at 13:53
  • does putting net.ipv4.conf.default.rp_filter=0 and net.ipv4.conf.all.rp_filter=0 in your /etc/sysctl.conf and doing "sudo sysctl -p" solves your problem? – Rui F Ribeiro Nov 23 '15 at 14:21
  • I tried applying those both directly, ie. 'sysctl net.ipv4.conf.default.rp_filter=0', also including one for my specific private interface. I have confirmed they are set via 'sysctl -a', however, martian packets to 127.0.0.1 are still being rejected. And even if this worked, opening up all of my interfaces to martians is NOT what I want. Nor do I even want to open a single (albiet private) interface. I know exactly which martian packets I want to allow and am wishing/hoping for an iptables NAT/mangle expression to allow me to accomplish this. – Mark Nov 23 '15 at 15:06
  • You might want net.ipv4.conf.default.rp_filter=2 and some other iptables rule – Rui F Ribeiro Nov 23 '15 at 15:38
1

The issue with doing a DNAT to 127.0.0.1:5000 is that when the remote side responds, these packets return into the routing engine as if they were locally originated (from 127.0.0.1) but they have an outside destination address. SNAT/MASQUERADE matching the outside interface would have caught them and rewritten them, but the routing decisions that have to be made for the packets to arrive at that interface come first, and they disallow these packets which are bogus by default. The routing engine can't guarantee you'll remember to do that rewrite later.

The thing that you should be able to do instead is reject any outside connections to 192.168.1.1:5000 at iptables INPUT other than those coming from 192.168.1.10 using the ! argument before the -s source address specification. If you use TCP reset as the rejection mechanism (-j REJECT --reject-with tcp-reset, instead of the default ICMP destination unreachable), it will be largely identical to the situation where nothing was even listening on that address:port combination as far as the outside world is concerned.

0

I would use openVPN to create a tunnel from Router to RemoteGateway.

Then I would, on Router, add a route:

route add -host RemoteTarget gw RemoteGateway-VPNaddress

use simple iptables rule whereever you like to limit which ports you allow Source to get to.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.