Self-hosted public IP API
There are various sites and services around the interwebs you can use to find out your public IP address. Both Google and DuckDuckGo (and probably others) return your address when you search for it (try it) but there are times when you may want to find it out via an API, without using a browser, or just for fun.
Two parts are required, DNS resolution, and the API endpoint.
API endpoint
To serve up the response I am using an Nginx service with the following server entry:
server {
server_name ip.example.com ip6.example.com ip4.example.com;
location / {
add_header Cache-Control no-store;
if ($http_accept = 'application/json') {
add_header Content-Type application/json;
return 200 "{ \"ip\": \"$remote_addr\" }";
}
default_type text/plain;
return 200 $remote_addr;
}
}
The server_name
ensures that we can answer requests to the 3 subdomains, one
for generic entries which will return IP6 or IP4, and then one for each IP
version that will return an appropriate response.
The location block also allows for both a JSON and a plain text response which
means a browser will see simple text but a programmatic client can extract
JSON. Very handy. Either way the response grabs the built in Nginx variable
$remote_addr
which will be the public IP address we are after, either IP4 or
IP6.
DNS resolution
To resolve the endpoints above we want an entry for each of the 3 server names:
ip.example.com
, ip4.example.com
& ip6.example.com
.
The IP version endpoints will need a corresponding A or AAAA record for IP4 and IP6 respectively:
;; BIND zone file extract
ip CNAME example.com ;;
ip4 A x.x.x.x ;; IP4 address of nginx
ip6 AAAA x:x:x:x:x:x:x:x ;; IP6 address of nginx
This enables any client to use the generic endpoint ip.example.com
and
dual-stack clients to use an endpoint other than their default resolution bias.
This works because CNAME records are used by both IP4 & IP6 and the
ip4.example.com
and ip6.example.com
records can only be resolved by IP4 and
IP6 capably clients respectively.
So now the following is possible (from a dual-stack network):
$ ifconfig |grep inet
inet6 ::1 prefixlen 128
inet6 fe80::1%lo0 prefixlen 64 scopeid 0x2
inet 127.0.0.1 netmask 0xff000000
inet6 fe80::2a16:adff:fe76:a485%lagg0 prefixlen 64 scopeid 0x4
inet6 2001:44b8:dead:beef:2a16:adff:fe76:a485 prefixlen 64 autoconf
inet 10.0.1.197 netmask 0xffffff00 broadcast 10.0.1.255
$ curl -4 ip.example.com
14.201.108.199
$ curl -6 ip.example.com
2001:44b8:dead:beef:2a16:adff:fe76:a485
$ curl ip4.example.com
14.201.108.199
$ curl -H 'Accept: application/json' ip.example.com
{ "ip": "2001:44b8:dead:beef:2a16:adff:fe76:a485" }