zx23 blog

DIY “What Is My IP” Service

Today a need arose for a service that would tell us our public IP address. This is such a common question to ask the Internet that Google and DuckDuckGo provide the answer for you directly in the search results, saving you an extra click and TCP connection or two to one of the many websites offering the same information. (Interestingly, neither Yahoo nor Bing have this feature, but thats a different story).

Instead of relying on one search engine or 3rd party site, we looked at how we can build something of our own to solve this problem. And what we came up with was rather obvious - make use of the return directive in nginx rewrite module, which can return any given status code and text to the client.

Excerpt from the rewrite module doc concerning the use of return directive:

1
2
3
4
5
Syntax:     return code [text];
return      code URL;
return      URL;
Default:    -
Context:    server, location, if

So, based on that, we arrive at the following location block:

1
2
3
4
location /ip {
    add_header Content-Type text/plain;
    return 200 '$remote_addr';
}

And here’s the output it produces:

1
2
% curl example.net/ip
10.10.10.10

Neat isn’t it? Hey but what if the consumer of this data is a machine instead of a human? Can we make the output more machine-friendly? Yes we can, this location returns a JSON. Oh my! Or, more accurately, 01001111 01101000 00100000 01101101 01111001 00100001, as its your computer yelling that.

1
2
3
4
location /ip.json {
    add_header Content-Type application/json;
    return 200 '{\n"ip": "$remote_addr"\n}\n';
}

Don’t just take our word for it, see it for yourself:

1
2
3
4
% curl example.net/ip.json
{
    "ip": "10.10.10.10"
}

And then we find the list of nginx variables and we’re on our way of building something resembing httpbin, but without the overhead of a high-level programming language.

Now, nginx is our primary webserver for most websites, but we also have Varnish in front for caching. So, can we return client’s IP directly from Varnish and save one network hop up to nginx? Yes we can, but with a small caveat - we loose the cool Guru Meditation error page if we only want to return the IP address alone.

So here’s the VCL that will do this for your:

1
2
3
4
5
6
7
8
9
10
sub vcl_recv {
    if (req.url == "/ip") {                                                                                                                            
        error 200 client.ip;                                                                                                                      
    }
}

sub vcl_error {
    synthetic obj.response;                                                                                                                                 
    return (deliver);                                                                                                                                       
}

Load that VCL and see it working. Note how the response doesn’t include a trailing new line character, so our shell prompt character, %, ends up on the same line. Thats due to a limitation in VCL, type IP doesn’t support appending with the + operator; if you try this you’ll get the following error from the VCL complier: Operator + not possible on type IP.

1
2
% curl lab.zx23.net/ip
10.10.10.10%

The exercise of writing a VCL to return the IP as JSON is left to the reader. Happy meditation.

Comments