Redirecting traffic to another machine with Linux's iptables

August 22, 2007

Let us suppose, as a not entirely hypothetical example, that you have added an A record for your subdomain name that points to one of your login servers, so that people can do 'ssh subdomain' and have it work. Let us further suppose that this login server is not your web server and you now want to make http://subdomain/ also do something useful, instead of giving connection refused errors.

One way to do this is to use iptables on the login server to redirect any connections to its port 80 off to the actual web server machine. Assuming that W is the IP address of your web server, what you need on your login server is:

  1. echo 1 >/proc/sys/net/ipv4/ip_forward

    Without IP forwarding enabled, the kernel will just drop our redirected packets instead of (re)routing them as we want.

  2. iptables -t nat -A PREROUTING -p tcp --dport 80 -j DNAT --to-destination W

    This sends the traffic off by rewriting the destination of attempts to connect to the login server's port 80 to be attempts to connect to the web server. Because it is in the PREROUTING table it does not affect connections made on the login server itself.

  3. iptables -t nat -A POSTROUTING -p tcp -d W --dport 80 -j MASQUERADE

    This rewrites the origin of connections to the web server's port 80 to appear to come from the login server. (Using -j SNAT --to-source L where L is the IP address of the login server is equivalent but longer, and the difference is unimportant for machines with permanently up interfaces.)

The last step is necessary because we need to make reply packets from the web server go through the login server so that it can reverse the transformation. Otherwise when a client connects to port 80 on the login server, the web server will see a connection from the client to it and send reply packets directly back to the client, where the client will ignore them because as far as it is concerned it connected to the login server, not the web server.

(Well, technically the client sends RSTs to the web server instead of completely ignoring the packets, assuming that no firewalls intervene.)

The drawback of dealing with the situation this way is that the web server will see (and log) all of this traffic as coming from the login server instead of from its real origin. This may or may not matter to you.

If the packets were already going through the login server on the way back (perhaps it is also your PPP server), you wouldn't need the third step but you'd want to be more specific in the second step, so that only packets to port 80 on the login server itself are affected. (Otherwise you would be creating a not so transparent proxy, where all websites are your web server.)

(To answer an obvious question: one reason to not just do this on your firewall is if you want even internal attempts to use the URL http://subdomain/ to do something useful.)


Comments on this page:

By Dan.Astoorian at 2007-08-23 12:41:10:

The drawback of dealing with the situation this way is that the web server will see (and log) all of this traffic as coming from the login server instead of from its real origin. This may or may not matter to you.

If you or your users have any resources which are restricted based on IP address of the user's browser (i.e., "allow from subnet" directives in your Apache config), it matters to you.

IMHO, allowing http to the bare domain name is really just a convenience, and users really ought to be redirected via HTTP to the correct sitename; i.e., redirect http://subdomain/ to http://www.subdomain/, so that traffic is not going through your login server unnecessarily.

There's more than one way to accomplish this:

  • Instead of redirecting via NAT, your login server can run a stub webserver which does nothing but issue HTTP redirects to the correct URL. (cks: I actually set up a crude one of these at CS about five years ago, although the firewall prevents it from actually being reachable. It's still there on the old login server, though it no longer serves a useful purpose.) Since this is an entirely userspace solution, it's perhaps simplest to set up, but is not particularly efficient.

  • Alternatively, you can use iptables as you've described to NAT the initial HTTP connection, and additionally define a name-based virtual host for "subdomain" (and "subdomain.domain," etc.) on the target webserver which simply performs a redirect to http://www.subdomain/.

--Dan

By cks at 2007-08-23 20:43:11:

If you are cautious, you do not want to use a name-based virtual host for the redirection; instead you want to use an IP-based virtual host. The problem with a name-based virtual host is that the Host: header in the request is under the control of the client, so an attacker who knew what was going on could connect to the login server but put the web server's name in the Host: header, bypassing the redirection and letting them at access-restricted things.

From 79.199.126.231 at 2009-08-08 04:06:42:

Did you mean (a) instead of (b) ?

(a) iptables -t nat -A PREROUTING ..

(b) iptables -t nat -D PREROUTING ..

By cks at 2009-08-08 12:17:57:

You are totally correct; I did mean -A instead of -D. What an embarrassing error, and thank you for pointing it out. I've now corrected it in the entry.

(I wonder how I made the error originally. Maybe I accidentally copied the commands from when I was tearing down my test NAT.)

Written on 22 August 2007.
« The dilemma of website facing
The excessive cleverness of some people's reverse DNS »

Page tools: View Source, View Normal, Add Comment.
Search:
Login: Password:
Atom Syndication: Recent Comments.

Last modified: Wed Aug 22 23:22:37 2007
This dinky wiki is brought to you by the Insane Hackers Guild, Python sub-branch.