LinuxQuestions.org

LinuxQuestions.org (/questions/)
-   Linux - Networking (https://www.linuxquestions.org/questions/linux-networking-3/)
-   -   Iptables: how to redirect locally-generated packets to a remote server? (https://www.linuxquestions.org/questions/linux-networking-3/iptables-how-to-redirect-locally-generated-packets-to-a-remote-server-797173/)

briwood 03-22-2010 10:13 PM

Iptables: how to redirect locally-generated packets to a remote server?
 
I'm trying to workaround a limitation in a server application. The limitation is that I can only connect to a LOCAL mysql database. I am trying to fool the server in to using a remote mysql database. I was hoping to do this by simply forwarding 3306 to another server on the same subnet.

To that end I've set up iptables rules to forward all connections to port 3306 to a non-standard mysql port on a remote server. This works, except that I need to deal with the loopback interface in a special way and I'm stuck.


Code:

iptables -t nat -A PREROUTING -p tcp --dport 3306 -j DNAT --to 128.XXX.XXX.XXX:3197
iptables -A FORWARD -p tcp -d 128.XXX.XXX.XXX --dport 3197 -j ACCEPT
iptables -t nat -A POSTROUTING  -j MASQUERADE

Since locally-generated packets will never hit the PREROUTING rule, you'll need to setup a near identical rule using OUTPUT to make it work. Here is what I've tried:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.XXX.XXX.XXX:3197
With all of these rules in place, I can connect to another server and n telnet to 3306 on this server and this results in a response from mysql on the remote server. BUT from this server I cannot 'telnet localhost 3306' and get a mysql connection. When I try that, the telnet connection hangs open with no response.

Here are what my chains look like:

Code:

# iptables -L
Chain INPUT (policy ACCEPT)
target    prot opt source              destination       

Chain FORWARD (policy ACCEPT)
target    prot opt source              destination       
ACCEPT    tcp  --  anywhere            mysql.example.com tcp dpt:embrace-dp-s

Chain OUTPUT (policy ACCEPT)
target    prot opt source              destination       

Chain RH-Firewall-1-INPUT (0 references)
target    prot opt source              destination     

iptables -L -t nat -v
Chain PREROUTING (policy ACCEPT 131 packets, 61868 bytes)
 pkts bytes target    prot opt in    out    source              destination       
    1    60 DNAT      tcp  --  any    any    anywhere            anywhere            tcp dpt:mysql to:128.XXX.XXX.XXX:3197

Chain POSTROUTING (policy ACCEPT 23 packets, 1470 bytes)
 pkts bytes target    prot opt in    out    source              destination       
  204 13161 MASQUERADE  all  --  any    any    anywhere            anywhere           
Chain OUTPUT (policy ACCEPT 826 packets, 53871 bytes)
 pkts bytes target    prot opt in    out    source              destination       
    0    0 DNAT      tcp  --  any    lo      anywhere            anywhere            tcp dpt:mysql to:128.XXX.XXX.XXX:3197

Thanks for any help!

zhjim 03-23-2010 03:55 AM

Quote:

Originally Posted by briwood (Post 3908336)
Since locally-generated packets will never hit the PREROUTING rule, you'll need to setup a near identical rule using OUTPUT to make it work. Here is what I've tried:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.XXX.XXX.XXX:3197

I doubt that local packages traverse the -t nat table. Try the command without the -t option.

Or to just be sure where things are growing dim use something like this.
Code:

iptables -I OUTPUT -j LOG --log-prefix "Out: " --log-tcp-options
do this for all the chains.

win32sux 03-23-2010 04:04 AM

Quote:

Originally Posted by zhjim (Post 3908736)
I doubt that local packages traverse the -t nat table.

They do. See chapter 6 of Oskar Andreasson's famous tutorial.

Do an "iptables -nvL -t nat" on your box to see the packet counters with your own eyes. :)

briwood 03-23-2010 10:17 AM

Yes in fact in table 3-2 at http://www.faqs.org/docs/iptables/tr...goftables.html it says:

source of packet = local process
table=nat
chain=output:
Quote:

This chain can be used to NAT outgoing packets from the firewall itself.
Quote:

Originally Posted by win32sux (Post 3908745)
Do an "iptables -nvL -t nat" on your box to see the packet counters with your own eyes. :)

Here it is again:
Code:

# iptables -nvL -t nat
Chain PREROUTING (policy ACCEPT 53 packets, 24986 bytes)
 pkts bytes target    prot opt in    out    source              destination       
    1    60 DNAT      tcp  --  *      *      0.0.0.0/0            0.0.0.0/0          tcp dpt:3306 to:128.xxx.xxx.xxx:3197

Chain POSTROUTING (policy ACCEPT 4 packets, 258 bytes)
 pkts bytes target    prot opt in    out    source              destination       
  194 12506 MASQUERADE  all  --  *      *      0.0.0.0/0            0.0.0.0/0         

Chain OUTPUT (policy ACCEPT 197 packets, 12704 bytes)
 pkts bytes target    prot opt in    out    source              destination       
    0    0 DNAT      tcp  --  *      lo      0.0.0.0/0            0.0.0.0/0          tcp dpt:3306 to:128.xxx.xxx.xxx:3197

That -t nat OUTPUT rule is:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.xxx.xxx.xxx:3197
Without that -t nat OUTPUT rule:
Code:

# telnet localhost 3306
Trying 127.0.0.1...
telnet: connect to address 127.0.0.1: Connection refused
telnet: Unable to connect to remote host: Connection refused

With that -t nat OUTPUT rule:
Code:

[root@adm-10-cms ~]# telnet localhost 3306
Trying 127.0.0.1...

(hangs...^C)


hmm...

briwood 03-23-2010 10:47 AM

Looking at http://tinyurl.com/yhqr5no I believe that the problem is that once I do the DNAT in -t nat, the packet will not traverse the FORWARD chain. So I think what needs to be done is to resend the packet back to this host so that it originates from eth0 and gets processed by those rules. I tried:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 169.xxx.xxx.xxx:3306
(169.xxx is the ip of this machine.)

...but that's not quite it.

nimnull22 03-23-2010 10:56 AM

Quote:

Originally Posted by briwood (Post 3908336)
...

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.XXX.XXX.XXX:3197
With all of these rules in place, I can connect to another server and n telnet to 3306 on this server and this results in a response from mysql on the remote server. BUT from this server I cannot 'telnet localhost 3306' and get a mysql connection. When I try that, the telnet connection hangs open with no response.

Thanks for any help!

Can you please, explain, what do you mean by: "BUT from this server I cannot 'telnet localhost 3306' and get a mysql connection"

You can easily telnet to different port and then in iptables use REDIRECT to turn packets to localhost ANY port.

briwood 03-23-2010 11:19 AM

Quote:

Originally Posted by nimnull22 (Post 3909131)
Can you please, explain, what do you mean by: "BUT from this server I cannot 'telnet localhost 3306' and get a mysql connection"

The server where I'm modifying iptables is adm-10-cms:

Code:

[root@adm-10-cms ~]# telnet localhost 3306
Trying 127.0.0.1...

(hangs...^C)

Connecting from a different server:
Code:

[bwood@dev-10-cms ~]$ telnet 169.xxx.xxx.xxx 3306
Trying 169.xxx.xxx.xxx...
Connected to adm-10-cms.example.com (169.xxx.xxx.xxx).
Escape character is '^]'.
8
5.0.67-log��VR{4/1EG,�!u:^K=/ph;.qr^]
telnet> quit
Connection closed.
[bwood@dev-10-cms ~]$

As you can see I get a response from the remote mysql server when I initiate the connection from dev-10-cms, but I do not when I initiate the connection from adm-10-cms. I need to be able to initate the connection from adm-10-cms.

(Mysql is not running on adm-10-cms. I am definitley getting a response from the 128.xxx box mentioned above.)

eantoranz 03-23-2010 11:20 AM

If fails when you telnet to localhost port 3306, right?

I'd _guess_ it's because you do a DNAT but probably the source IP address that's set on those packets is still set to 127.0.0.1, isn't that right?

Try snatting or masquerading all traffic to the real mysql server so that it sees your IP address on that network interface, otherwise the mysql server will get 127.0.0.1 as the source address of those packets and won't be able to reply back.

nimnull22 03-23-2010 11:30 AM

Use REDIRECT rule in iptables to turn local port <ANY> to local port <ANY>.

briwood 03-23-2010 11:39 AM

Quote:

Originally Posted by nimnull22 (Post 3909176)
Use REDIRECT rule in iptables to turn local port <ANY> to local port <ANY>.

Are you saying to do what I tried in #5:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 169.xxx.xxx.xxx:3306
but to leave out the "--dport 3306" and remove the ":3306"?

briwood 03-23-2010 11:42 AM

Quote:

Originally Posted by eantoranz (Post 3909165)
If fails when you telnet to localhost port 3306, right?

I'd _guess_ it's because you do a DNAT but probably the source IP address that's set on those packets is still set to 127.0.0.1, isn't that right?

Try snatting or masquerading all traffic to the real mysql server so that it sees your IP address on that network interface, otherwise the mysql server will get 127.0.0.1 as the source address of those packets and won't be able to reply back.

I guess I could try that as a test. In reality I only want mysql connections to go to the real mysql server. Http and other connections should still come to adm-10-cms.

nimnull22 03-23-2010 11:55 AM

wait, wait, you confuse me.
nat -A OUTPUT - is located BEFORE router. So, you need to do things different way.

You need to connect to port 3306 on local and remote host - no problems.
But you need separate connections. In that case you can choose port 3306 for local and 4444 for remote server.
Now your iptables rules should be next:
iptables -t nat -A OUTPUT -p tcp -o lo --dport 4444 -j DNAT --to 169.xxx.xxx.xxx:3306

Now packets to port 3306 will not match any rules and go to local host as it suppose to be.
But packets to 4444 will match our rule and NAT will change destination.

eantoranz 03-23-2010 12:04 PM

Sure... just add filters to the rule in -t nat POSTROUTING that will match only traffic you care to masquerade/snat.

And just to make sure: it fails when you try to connect to telnet localhost 3306 (which is supposed to end up connecting to a remote mysql server), is that right?

eantoranz 03-23-2010 12:09 PM

But I'm seeing in your rules that you are masquerading all traffic that's going out... so the problem is not what I'm describing. Let me read it again to see what's going on.

eantoranz 03-23-2010 12:13 PM

can you do a tcpdump on your network interface that connects to the nerwork where the remote mysql server is?

something like:
tcpdump -i blah -p tcp and port 3197 -n -v

and then try to do the telnet and see what addresses/ports are showing up?

briwood 03-23-2010 12:54 PM

Thanks eantoraz,

First I setup my rules again:

Code:

[root@adm-10-cms ~]# iptables -F
[root@adm-10-cms ~]# iptables -F -t nat
[root@adm-10-cms ~]# iptables -X
[root@adm-10-cms ~]# iptables -t nat -A PREROUTING -p tcp --dport 3306 -j DNAT --to 128.xxx.xxx.xxx:3197
[root@adm-10-cms ~]# iptables -A FORWARD -p tcp -d 128.xxx.xxx.xxx --dport 3197 -j ACCEPT
[root@adm-10-cms ~]#  iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.xxx.xxx.xxx:3197
[root@adm-10-cms ~]#
[root@adm-10-cms ~]# iptables -t nat -A POSTROUTING  -j MASQUERADE

Here's tcpdump of a a successfull connection: 'telnet adm-10-cms 3306' initiated from dev-10-cms

Code:

[root@adm-10-cms ~]# tcpdump -i eth0 -p tcp and port 3197 -n -v
tcpdump: listening on eth0, link-type EN10MB (Ethernet), capture size 96 bytes
10:27:20.745788 IP (tos 0x10, ttl  63, id 52105, offset 0, flags [DF], proto: TCP (6), length: 60) 169.xxx.xxx.xxx.48658 > 128.xxx.xxx.xxx.embrace-dp-s: S, cksum 0x5f9a (correct), 3997077676:3997077676(0) win 5840 <mss 1460,sackOK,timestamp 1904696101 0,nop,wscale 7>
10:27:20.746836 IP (tos 0x0, ttl  63, id 0, offset 0, flags [DF], proto: TCP (6), length: 60) 128.xxx.xxx.xxx.embrace-dp-s > 169.xxx.xxx.xxx.48658: S, cksum 0x3d23 (correct), 4207250702:4207250702(0) ack 3997077677 win 5792 <mss 1380,sackOK,timestamp 2221807272 1904696101,nop,wscale 2>
10:27:20.747693 IP (tos 0x10, ttl  63, id 52106, offset 0, flags [DF], proto: TCP (6), length: 52) 169.xxx.xxx.xxx.48658 > 128.xxx.xxx.xxx.embrace-dp-s: ., cksum 0x8209 (correct), ack 1 win 46 <nop,nop,timestamp 1904696104 2221807272>
10:27:20.748452 IP (tos 0x8, ttl  63, id 64331, offset 0, flags [DF], proto: TCP (6), length: 112) 128.xxx.xxx.xxx.embrace-dp-s > 169.xxx.xxx.xxx.48658: P 1:61(60) ack 1 win 1448 <nop,nop,timestamp 2221807274 1904696104>
10:27:20.748957 IP (tos 0x10, ttl  63, id 52107, offset 0, flags [DF], proto: TCP (6), length: 52) 169.xxx.xxx.xxx.48658 > 128.xxx.xxx.xxx.embrace-dp-s: ., cksum 0x81ca (correct), ack 61 win 46 <nop,nop,timestamp 1904696105 2221807274>
10:27:25.749780 IP (tos 0x8, ttl  63, id 64333, offset 0, flags [DF], proto: TCP (6), length: 52) 128.xxx.xxx.xxx.embrace-dp-s > 169.xxx.xxx.xxx.48658: F, cksum 0x68c6 (correct), 61:61(0) ack 1 win 1448 <nop,nop,timestamp 2221812275 1904696105>
10:27:25.752042 IP (tos 0x10, ttl  63, id 52108, offset 0, flags [DF], proto: TCP (6), length: 52) 169.xxx.xxx.xxx.48658 > 128.xxx.xxx.xxx.embrace-dp-s: F, cksum 0x5ab3 (correct), 1:1(0) ack 62 win 46 <nop,nop,timestamp 1904701109 2221812275>
10:27:25.752594 IP (tos 0x8, ttl  63, id 64335, offset 0, flags [DF], proto: TCP (6), length: 52) 128.xxx.xxx.xxx.embrace-dp-s > 169.xxx.xxx.xxx.48658: ., cksum 0x5536 (correct), ack 2 win 1448 <nop,nop,timestamp 2221812278 1904701109>

8 packets captured
8 packets received by filter
0 packets dropped by kernel

When I try to capture the unsuccessful connection from localhost I don't get any output. I do:

Code:

[root@adm-10-cms ~]# tcpdump -i lo -p tcp and port 3197 -n -v
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes

then:

Code:

[root@adm-10-cms ~]# telnet localhost 3306
Trying 127.0.0.1...

Nothing appears in the tcpdump console, so I ^C and get:

Code:

0 packets captured
0 packets received by filter
0 packets dropped by kernel

Next I try to listen on 3306 on the lo interface, but nothing comes across when I telnet localhost 3306

Code:

[root@adm-10-cms ~]# tcpdump -i lo -p tcp and port 3306 -n -v
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes

0 packets captured
0 packets received by filter
0 packets dropped by kernel

Trying the same thing to 3307 (no service there) I get this tcpdump output:

Code:

[root@adm-10-cms ~]# tcpdump -i lo -p tcp and port 3307 -n -v
tcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes
10:37:06.874458 IP (tos 0x10, ttl  64, id 10105, offset 0, flags [DF], proto: TCP (6), length: 60) 169.xxx.xxx.xxx.47587 > 127.0.0.1.opsession-prxy: S, cksum 0xd07d (correct), 2294676478:2294676478(0) win 32792 <mss 16396,sackOK,timestamp 2478038151 0,nop,wscale 7>
10:37:06.874670 IP (tos 0x10, ttl  64, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 127.0.0.1.opsession-prxy > 127.0.0.1.47587: R, cksum 0x62f9 (incorrect (-> 0x663a), 0:0(0) ack 2294676479 win 0

2 packets captured
4 packets received by filter
0 packets dropped by kernel

For the last test I remove the -t nat OUTPUT rule and listen on 3306 while attempting the same telnet localhost 3306. This yields expected output.

Code:

[root@adm-10-cms ~]#  iptables -t nat -D OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.xxx.xxx.xxx:3197

[root@adm-10-cms ~]# tcpdump -i lo -p tcp and port 3306 -n -vtcpdump: listening on lo, link-type EN10MB (Ethernet), capture size 96 bytes
10:50:19.579767 IP (tos 0x10, ttl  64, id 18940, offset 0, flags [DF], proto: TCP (6), length: 60) 169.xxx.xxx.xxx.34570 > 127.0.0.1.mysql: S, cksum 0xf892 (correct), 3120938229:3120938229(0) win 32792 <mss 16396,sackOK,timestamp 2478830857 0,nop,wscale 7>
10:50:19.580301 IP (tos 0x10, ttl  64, id 0, offset 0, flags [DF], proto: TCP (6), length: 40) 127.0.0.1.mysql > 127.0.0.1.34570: R, cksum 0xa39c (incorrect (-> 0xa6dd), 0:0(0) ack 3120938230 win 0

2 packets captured
4 packets received by filter
0 packets dropped by kernel


eantoranz 03-23-2010 01:49 PM

With the rules you set in place, do the tcpdump on -i lo and try the telnet localhost 3306 and tell us what comes out.

eantoranz 03-23-2010 01:52 PM

And is the counter of the -t nat OUTPUT rule you set increasing when you do the telnet?

eantoranz 03-23-2010 02:04 PM

Oh, I saw you tried the tcpdump listening on -i lo so skip that test for now. Tell me about the counter, because that traffic must be going somewhere, right?

eantoranz 03-23-2010 02:29 PM

I would think that the problem here is that there's code on the network stack doing checkups after OUTPUT that when looking at the packet (source address: 127.0.0.1, dest addres: something not in loopback) drops it for not being "consistent".... perhaps someone knows about this? And perhaps could sysconf be used to disable such checkup?

briwood 03-23-2010 03:10 PM

Quote:

Originally Posted by eantoranz (Post 3909367)
With the rules you set in place, do the tcpdump on -i lo and try the telnet localhost 3306 and tell us what comes out.

When I do

Code:

[root@adm-10-cms ~]# tcpdump -i lo -v
and then 'telnet localhost 3306' in a different console on the same server nothing is captured.

The counter is incrementing. I see:

Code:

[root@adm-10-cms ~]# iptables -L -nvx -t nat
...snip...
Chain OUTPUT (policy ACCEPT 108 packets, 7372 bytes)
    pkts      bytes target    prot opt in    out    source              destination       
      6      360 DNAT      tcp  --  *      lo      0.0.0.0/0            0.0.0.0/0          tcp dpt:3306 to:128.XXX.XXX.XXX:3197
...snip...

I do another telnet localhost and then I see it's at 8:

Code:

Chain OUTPUT (policy ACCEPT 108 packets, 7372 bytes)
    pkts      bytes target    prot opt in    out    source              destination       
      8      480 DNAT      tcp  --  *      lo      0.0.0.0/0            0.0.0.0/0          tcp dpt:3306 to:128.XXX.XXX.XXX:3197

I really appreciate all your help thus far!

briwood 03-23-2010 03:16 PM

Quote:

Originally Posted by eantoranz (Post 3909418)
I would think that the problem here is that there's code on the network stack doing checkups after OUTPUT that when looking at the packet (source address: 127.0.0.1, dest addres: something not in loopback) drops it for not being "consistent".... perhaps someone knows about this? And perhaps could sysconf be used to disable such checkup?

What do you think of my theory here:
http://www.linuxquestions.org/questi...21#post3909121

Since this is a locally-generated packet it is never going to hit this rule

Code:

iptables -A FORWARD -p tcp -d 128.xxx.xxx.xxx --dport 3197 -j ACCEPT
which does the actual forwarding. I *think* that this rule:

Code:

iptables -t nat -A OUTPUT -p tcp -o lo --dport 3306 -j DNAT --to 128.xxx.xxx.xxx:3197
only rewrites the destination of the packet. We need the forward rule above to actually send the packet on...

eantoranz 03-23-2010 05:22 PM

Well, FORWARD is not touched by packets that are going out from OUTPUT so don't worry cause it doesn't affect you.

I think it's because of the source address inconsistency, I think. Check out this article I just found (a little dated, by the way):

http://lists.netfilter.org/pipermail...er/040104.html

So, how about other tricks? Why do you need locally generated connections on local port 3306 to get connected to a remote host in the first place? In case it's a _must_, wouldn't a SSH tunnel (or a simpler approach) work for you?

In case you want to try the ssh runnel trick, remove the OUTPUT rule doing the DNAT to remote:3197 and run this command on that same host:

ssh -nNT -L 3306:remote-server:3197 user@localhost

After the connection is established (you will know because after a few seconds the command won't return), try telnet localhost 3306

Maybe there are simpler approaches but _at least_ I bet that one will work.

eantoranz 03-23-2010 05:30 PM

It _has_ to be the IP address inconsistency.

With all your rules in place, try to do a telnet to your local IP address on the intranet (instead of localhost). That way, it did work with your DNAT trick.

eantoranz 03-23-2010 05:31 PM

At least, it did work for me.

briwood 03-23-2010 07:44 PM

Quote:

Originally Posted by eantoranz (Post 3909624)
wouldn't a SSH tunnel (or a simpler approach) work for you?

Thought of that. The remote mysql server doesn't allow ssh and I don't have control over that server.

Quote:

(or a simpler approach)
Is there a non ssh option for setting up a simple tunnel to 3197 on the remote server?

Will try telnet to ipaddr tomorrow and report back. If that works, I may look into snat.

Thanks.;

eantoranz 03-24-2010 09:13 AM

The ssh tunnel as I told you to use it yesterday requires no ssh on the mysql server but on the host you are working instead (the one where you want local connections to port 3306 to be sent to a remote server port 3197).

briwood 03-24-2010 10:20 AM

Thanks again for all the help!

You are right. Telnetting to anything other than localhost works:

Code:

[root@adm-10-cms ~]# telnet 169.xxx.xxx.xxx 3306
Trying 169.xxx.xxx.xxx...
Connected to adm-10-cms.example.com (169.xxx.xxx.xxx).
Escape character is '^]'.
8
5.0.67-log��Mw.}OP%e,�!QDAYz{|{v^R`^]
telnet> quit
Connection closed.
[root@adm-10-cms ~]# telnet adm-10-cms 3306
Trying 169.xxx.xxx.xxx...
Connected to adm-10-cms.example.com (169.xxx.xxx.xxx).
Escape character is '^]'.
8
5.0.67-log��q%])SE@R,�![SnO!Sbo+%s:Connection closed by foreign host.
[root@adm-10-cms ~]# telnet localhost 3306
Trying 127.0.0.1...
(hangs...^C)

So if you snatted the localhost packets correctly, this could probably be made to work from localhost via the loopback interface.

Thanks for enlightening me on the ssh tunnel. I assumed that you needed sshd running on the target server. Since you don't I'm pursuing this option since it is way simpler. Always good to know more about iptables though!

fredericgermain 02-16-2016 09:35 PM

I think your problem is really solved here (I mean, solved the iptables way) :

http://unix.stackexchange.com/questi...s-to-127-0-0-1

you need to activate local routing on your outbound interface. if eth0 :

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

it seems to be like security feature

hope it helps some people coming to this question !


All times are GMT -5. The time now is 02:54 AM.