Use skipper to reverse proxy your network

rp

I got tired of paying $20/month to cloud providers for hosting my side projects, especially when I have 3 raspberry pis sitting idle. I moved my projects in-house this past weekend.

Traefik has been useful for serving multiple projects, but I realize what I really want is a reverse proxy directly on the router. My router has ARM architecture, 50 MB disk space, and <500 MB RAM, so traefik is out of the question.

I could use HAProxy or nginx, but the thought of maintaining their config files really dampens my spirits; infrequent config changes always causes dozens of search queries for reminders of how to update config.

Caddy 2 came very close to being a straightforward solution, but I specifically need to route based on HTTP headers, and I hit a wall at the somewhat sparsely detailed quick-start guide (it seems possible, but it's hard to tell).

Finally, I was pleasantly surprised by skipper, whose "eskip" config is as straightforward as it gets. With the help of the docs, I was able to install it on the router and route traffic in ~30 minutes.

# Suggested setup

OpenWRT

To use the router as a reverse proxy server, it will need to be equipped with ssh. This guide uses OpenWRT, but other software might also do the trick.

Alternatively, if using the router is too much trouble, you can port forward to a dedicated reverse proxy machine.

If you choose this option then replace all instances of "router" with "dedicated skipper server"

Machines with unique hostnames

I don't assume that my servers will always have the same LAN ip, especially if I need to replace the router for some reason.

Hostnames are static and useful for contextual routing, so each of my backend severs gets a specific hostname:

Personally I like to use random Halo Monitor names as hostnames

# machine: backbend server
echo 'noble-operator' | sudo tee /etc/hostname

# Install Skipper

Select the appropriate architecture from the releases page and install it on the router.

# machine: router
curl -L https://github.com/zalando/skipper/releases/download/v0.11.98/skipper-v0.11.98-linux-arm7.tar.gz -o skipper.tar.gz
tar -zxvf skipper.tar.gz
cd skipper-v0.11.98-linux-arm7/

Need curl in OpenWRT?

Run opkg update; opkg install curl

Then save the following to routes.eskip (it's only a simple ping/pong health check for now).

// machine: router
// routes.eskip
ping:
    Host("ping")
    -> status(200)
    -> inlineContent("pong\n")
    -> <shunt>;

# Run skipper

This guide will assume you serve skipper from port 12345, so replace that with whichever port you choose.

# machine: router
./skipper -routes-file=routes.eskip -address 0.0.0.0:12345 -metrics-flavour=prometheus -enable-connection-metrics

Then run this curl command:

# machine: router
curl -H Host:ping http://127.0.0.1:12345

If you got a pong back, it's working!

# Open the gates

And now for the magic hack that opens up this proxy to the world, port-forward your desired ports (usually 80/443) to the router itself. I guess this is hardly port "forwarding," per se, now it's more like port "consuming."

If you're using a dedicated reverse proxy machine inside the LAN (and not the router), then port forward to that instead.

pf

Then test it from the internet (I use my phone, disconnected from wifi, to make sure there's no network shenanigans):

# machine: something that is not on your LAN
curl -H Host:ping http://1.2.3.4 # replace 1.2.3.4 with your public IP

If all is right, you should get another pong.

# Add routes

Now you're ready to start routing.

// machine: router
// routes.eskip
ping:
    Host("ping")
    -> status(200)
    -> inlineContent("pong\n")
    -> <shunt>;

example:
    Host("example.com")
    ->preserveHost("true")
    -> "http://noble-operator";

This example forwards example.com to the server with the noble-operator hostname.

Now you can explore the docs and skipper's many features, such as metrics, ratelimiting, filters, and protcols.