Building a "what's my ip" service with Caddy and Nix
I got tired of the services I used to check my current public IP address dropping off the face of the internet, so I made my own very minimal one using the Caddy web server, and deployed it with Nix
- posted:
It’s often useful to be able to determine your “public” IP address1. Once upon a time this would have simply been the IP address assigned to your device’s network interface, but in 2022 (unless you’re at a hackercamp or possibly a University campus) you are almost certainly behind some form of NAT (Network Address Translation), allowing multiple devices on a network to share a single public IP address.
This means that, by itself, your device has no way of knowing what its public IP is. The easiest way to find out is to query a web server that will tell you the IP you are appearing as. There are rather a lot of these such services. Heck, you can even ask some search engines. But well, hosting your own things is just cooler. So…
What’s my IP, Caddy?
This would be a pretty simple custom web server. In fact, here is a bare minimum one, written in Go:
|
|
But why do that when we could just use something off the shelf? We’ll be using Caddy, a modern web server that features zero configuration automatic HTTPS courtesy of Let’s Encrypt.
Here is the entire Caddy config (“Caddyfile”) to serve a HTTPS-secured “what’s my IP”
site on the domain ip.example.com
:
ip.example.com {
respond "{remote_host}"
}
respond
tells Caddy to just respond with a string, and {remote_host}
is one of the
config placeholders supported by the Caddyfile, which expands to the IP address of the
requesting host. It’s that easy.
Bonus: deploying with Nix
You could install Caddy with a package manager and dump the above Caddyfile exerpt in
/etc/Caddyfile
on any old Linux distro, but instead we’re going to be using Nix. Nix
(/NixOS) is a functional programming language, package “manager” and Linux distribution,
all rolled into one project.
It’s quite a different approach to how you may have done similar things before, but once you get a bit familiar you can define how to build, configure and run any software, all using the same language. Here is the definition of our “whatsip” service in Nix:
|
|
This is not the place (nor am I qualified) to give an introduction to nix, but in brief
we do two broad things here. Firstly, we define a single configuration option for a
service that we’re calling “whatsip” - a list of hostnames. Secondly, we conditionally
set a chunk of NixOS configuration, depending on whether the hostnames
option we just
defined is not empty.
This chunk enables the Caddy server, and sets its config - which is just the Caddyfile as a multiline string embedded in the Nix file. This allows us to use Nix’s string interpolation to take the list of hostnames from our config option, concatenate them together separated by commas and inject them into the Caddyfile.
This snippet forms a NixOS “module” - which, saved in a file, is then imported into a wider NixOS configuration for a whole system:
|
|
When this configuration is evaluated and a NixOS system built from it, Nix will have
downloaded Caddy, configured systemd to run it and have built a Caddyfile with our
hostnames and respond
directive.
Double bonus: NixOS config merging
A not-immediately-obvious coda concerning NixOS modules: you could be forgiven for
thinking that the above configuration would not play nicely with additional
services.caddy.config
occurences. Consider the above, but in the main
configuration.nix
for our system we already had a website configured using Caddy:
|
|
Here you might expect that services.caddy.config
in configuration.nix
might override
the services.caddy.config
we have in whatsip.nix
, or vice versa. But, the NixOS
modules system is smart and will automatically merge configurations, in this case
concatenating our two Caddy config strings into one final Caddyfile. Which is exactly
what we want - declarations of different services/websites can live in different places
of your Nix configuration, neatly split up into separate logical modules. Such is the
power of Nix ❄️.
-
That is, the ip address that you appear as to the rest of the internet ↩︎