Using mod_proxy to convert a legacy IPv4 web site to IPv6

In preparation for the impending doom of IPv4, and in order to put my money where my mouth is [fr], I decided to make this blog natively accessible through IPv6.

The first problem I ran into is that my blog runs in a FreeBSD jail. Jails are a fine way to run a virtualized environment but they only support IPv4 at the moment. So I had the following options:

  • implement IPv6 in jails myself (this wouldn’t have happened overnight);
  • wait for someone else to;
  • abandon the idea;
  • find a workaround.

Finally I found a workaround, using Apache mod_proxy as a separate server to provide a IPv6 frontend (a configuration called reverse proxy).
Here’s the relevant snippet from a dedicated Apache setup:


NameVirtualHost *:80
<VirtualHost *:80>
  ServerAdmin webmaster@example.com
  ServerName signal.eu.org
  ProxyRequests Off
  <Proxy *>
    Order deny,allow
    Allow from all
  </Proxy>
  ProxyPass             /       http://signal.eu.org/
  ProxyPassReverse      /       http://signal.eu.org/
</VirtualHost>

The <VirtualHost> section wrapping is used to limit the proxying to a strictly-defined site; this avoids having random people access random sites through your machine (let’s leave that kind of bad behaviour to poor virused, zombied Windows users). The ServerAdmin and ServerName and optionally ServerAlias lines should match the destination IPv4 server’s configuration.

The lines after that are pretty much textbook, copied verbatim from the mod_proxy documentation.

The really interesting part is the ProxyPass directive. It tells Apache that any access below the site root should be proxied to the address obtained by appending it to the URL listed at right. So if someone accesses http://signal.eu.org/blog/, Apache sees that /blog/ is under / and proxies to http://signal.eu.org/blog/. This is exactly what we want.

There are two little tricks at work here. Did you notice we didn’t say a word about IPv6 or IPv4 in that configuration? We’re using the fact that Apache knows about IPv6 (assuming you compiled it with IPv6 enabled). The *:80 in the NameVirtualHost and VirtualHost directives match any IPv6 or IPv4 address the server happens to be configured with. That’s for the first trick. The second trick is that we rely on the operating system to provide Apache with just the IPv4 from signal.eu.org when proxying (here, I just put that address in the /etc/hosts file). This lets Apache know where to proxy to. Apache shouldn’t see the IPv6 address for the site or it would try to proxy to itself: we would have a loop. If for some reason you have to let both IPv4 and IPv6 addresses visible to Apache on the “regular” site name, you can use a different, local name such as v4.signal.eu.org, resolving only to the IPv4 address. In that case, just change the domain names after http:// to v4.signal.eu.org, and add a ServerAlias v4.signal.eu.org directive on the destination server (thanks to Samuel for pointing this out).

The ProxyPassReverse directive is probably not useful; it’s used by the proxy to rewrite redirect URLs, for example, but no such thing is needed here when the IPv6 site uses exactly the same URLs as the IPv4 site.

The nice thing is that you can have several <VirtualHost> sections corresponding to different sites, thanks to the NameVirtualHost directive at the top. My configuration currently proxies 3 web sites and their aliases. Another nice thing is that you need not change a single configuration line in the destination IPv4 servers, except if you need the “v4” trick.

There a few more details to be aware of regarding security.

From the destination server’s point of view, the access comes from the IPv4 address of the proxy. So if you limit access based on IPv4 addresses and you give special privileges to local network addresses, a common setup on intranets, you will have to update your access policy.

On a related note, the destination server’s access logs will not show the accessing IPv6 addresses in its logs by default. However, the proxy module kindly sets headers X-Forwarded-For, X-Forwarded-Host and X-Forwarded-Server with relevant information. So we only need to log these on the destination server using the %{X-Forwarded-For}i syntax, to keep it all in one place. Alternatively, we can keep the proxy logs which contain the client address, but they may be difficult to cross-reference with the destination server’s.

Note that, although I made this setup to work around a limitation of FreeBSD jails, it works in any configuration where your destination IPv4 server can not be easily upgraded to handle IPv6 clients. So this kind of setup is sure to become common in the forthcoming years with legacy applications…

4 thoughts on “Using mod_proxy to convert a legacy IPv4 web site to IPv6”

  1. OB: You can’t use simple NAT since we’re talking about converting IPv4 IPv6. So you need more than NAT in any case. Doing IPv4 IPv6 “on the wire” would be a bit tough on the hardware (or even kernel, if that’s what you had in mind) and I don’t see this usable outside of a lab, at least because of logging requirements, not to mention virtual host handling and various other HTTP tricks we might need to pull. So application-level proxying looks like the logical answer, and that’s what we’re doing.

Comments are closed.