Farming http requests out to other servers on LAN based on request URL

The scenario is simple:

  1. You have a Turris Omnia as a router acting as a gateway between your LAN and the wider WAN.
  2. The Omnia has one IP address on the broader WAN
  3. You have a one or more domains all pointing to this one IP, perhaps using Dynamic DNS updates.
  4. You’d like incoming HTTP requests to be directed at a LAN server based on the incoming URL

Alas it’s not trivial to do. Nearly trivial but sufficiently not that it’s worth documenting what my day of efforts yielded, so if you’re doing it, it takes minutes and not a day.

Observations of relevance:

  1. The Omnia is running lighttpd to serve up the Foris and LuCI web interfaces already.
  2. Ligttpd can do this but needs mod_proxy installed to do it, that is not installed on the Omnia by default (in Turris OS 3.6.1)
  3. You can’t install that (or anything via LuCI because LuCI is slightly broken in Turris OS 3.6.3). We’ll have to fix it, then install lighttpd-mod-proxy (more on that later in How To)
  4. The lighttpd module mod_fasctgi is already installed on the Turris Omnia, though it’s apparently not actually used by anything. This relevant because of an obscure lighttpd feature/bug, namely the order in which modules are loaded is relevant (feature) and if you load mod_fastcgi before mod_proxy, then mod_proxy won’t work (which is relevant because opkg does exactly that, install mod_proxy so it loads after mod_fastcgi.

Now onto How To:

  1. Fix LuCI:
    a. Check the lighttpd error log. You’ll probably see something like this:
    .
    2017-05-06 10:13:58: (server.c.1295) WARNING: unknown config-key: setenv.set-environment (ignored)
    .
    This is the reason as it happens that LuCi can’t install packages in Turris OS 3.6.3. Easy to fix, just edit /etc/lighttpd/conf.d/luci.conf and change “setenv.set-environment = ( "PATH" => "/usr/bin:/usr/sbin:/bin:/sbin" )” to “setenv.add-environment = ( "PATH" => "/usr/bin:/usr/sbin:/bin:/sbin" )” Then reload lighttpd configs (/etc/init.d/lighttpd reload)

  2. Now install the mod_proxy module:
    a. Go to the Omnia advanced configuration (the LuCI interface at http://192.168.0.1/cgi-bin/luci, using the address or name of your routerm or just click Go to LuCI on the home page of the Omnia web interface.
    b. Go to System > Software
    c. Under Filter: enter lighttpd-mod-proxy and click Find Package
    d. If it’s installed you’ll now see it under “Installed packages” and if not then under “Available packages”, where you can click Install".

  3. This will have installed mod_proxy, which you can see here:

    ls -1 /etc/lighttpd/modules.d/

    30-access.load
    30-accesslog.load
    30-alias.load
    30-cgi.load
    30-fastcgi.load
    30-proxy.load
    30-setenv.load
    This folder really does one thing, namely specify which modules to load and in which order, Look inside one or two of these and in /etc/config/lighttpd/lighttpd.conf, to get familiar with this if you like. It’s not convoluted, lightttpd just loads that file at startup and that file loads these module files and these module files load the modules.

  4. Now you can see the module load order (alphabetic) and that mod_fastcgi is loaded before mod_proxy.Notice that each file is prefixed with a number, 30 in the default install, this number is intended to drive the sorting, which of course it doesn’t if it’s always 30. I had to experiment with sorting at length to discover that mod_fastcgi was the cause of the problem (mod_proxy not working). And I ended up wit this:

    ls -1 /etc/lighttpd/modules.d/

    10-access.load
    11-accesslog.load
    12-alias.load
    13-cgi.load
    14-setenv.load
    20-proxy.load
    21-fastcgi.load
    Notice that I load mod_proxy before mod_fastcgi. There’s a chance of course that the reverse is true, that mod_proxy prevents mod_fastcgi from working. I don’t know, but you could probably remove mod_fastcgi anyhow as it’s not apparently used anywhere. I’m aiming for minimalist interventions mind and am venturing into guesswork here. What I know is that if you load mod_proxy first it’ll work.

  5. Now configure mod_proxy. You can do this in various ways, but to illustrate, this is how I’ve done it to keep things simple:

    cat /etc/lighttpd/conf.d/LAN-servers.conf

    Farm out targetted requests to the server on the LAN handling them

    $HTTP[“host”] == “mydomain1.com” {
    proxy.server = ( “” => ( ( “host” => “192.168.0.10” ) ) )
    }

    $HTTP[“host”] == “mydomain2.com” {
    proxy.server = ( “” => ( ( “host” => “192.168.0.11” ) ) )
    }
    This file is loaded by /etc/lighttpd/lighttpd.conf, as are all *.conf files in this directory. I just named it LAN-servers.conf because I wanted to. You can name whatever you like, it’ll load if it ends in .conf and is in this directory. The order of loading files in this directory is alphabetic again mind you.

  6. Things to note about mod_proxy configuration (above):
    a. These conditions are checked on every http request that arrives.
    b. The operator == could instead be =~ which is an RE match. More flexible pattern matching.
    c. You can test other header items, notably $HTTP["url"] for more fine grained farming out of requests
    d. mod_proxy only lets you specify a server by IP address (not name). You can specify the port too and multiple servers with a farming strategy - check the mod_proxy docs for the wealth of options, here I only want to nominate a server for different domains.

Voila, you’re done. You can now configure the farming out to LAN servers of pretty much any HTTP request really, and keep your Omnia as a high performance router while other devices handle web requests.

1 Like