Icanhazip: A simple IP address tool survived a deluge of users (2021)
blog.apnic.net
blog.apnic.net
nginx config example:
location /ip {
add_header Content-Type "application/json";
return 200 '{"host":"$server_name","ip":"$remote_addr","port":"$remote_port","server_ip":"$server_addr","server_port":"$server_port
"}\n';
}Which will return something like this in JSON format:
{"host":"www.example.org","ip":"199.200.200.14","port":"7990","server_ip":"199.200.100.229","server_port":"443"}
This is all done 100% within NGINX, and I include a lot of stuff you probably don't want or care about. Other web servers probably have similar capabilities.With the parent comment example, all you need is nginx, available as a package on all distros, and a single static config file. With an auto-updating package manager and a service manager, one never needs to write anything else apart from that static config file.
Or did you mean something else with "actually maintainable"?
So that's not a problem. Besides the majority of nginx setups don't rely on too fancy things; most of the stuff that gets changed in updates that affect config files are the fancier nginx modules.
If you're following that approach then having auto updates enabled is kind of irrelevant/meaningless; at some point you're going to be running a very old (and unsupported) version of your distro with all that that implies.
Maybe. I have more faith in a Rust library to maintain API backward compatibility than a Linux distro, is what I'm saying.
.. Ain't 65536 a capital B.
Edit: @toast0: valid point, thanks for the correction.
Chances are you'll never run into that limit for this service, because even if there's a ton of users behind the same IP due to NAT, they don't need to hold long sessions, and you can disable http keep-alive and close connections right away. Chances are you'll have some ip diversity in 400k requests/second though.
With that in mind + the general advancing of hw since then, it wouldn't be too suprising if nginx could handle that load easily. It'd just get more difficult if you wanted to put anything else (ie. metrics) on that endpoint since afaict nginx doesn't have anything to monitor those things if you don't have the Enterprise version of their tools.
In any case you might be misreading the setup for the parent here - the recommendation is just that if you're doing this in the context of a webapp, that this sort of behavior is trivially easy to bounce back in JSON using nginx.
If your use case calls for 400k/reqs a second on what your IP is and you haven't yet figured out how to build out your infrastructure(or pay someone to do it for you), you have bigger problems than figuring our what your public IP is.
example.com {
handle /ip {
header Content-Type application/json
respond `{"host":"{host}","ip":"{remote_host}","port":"{remote_port}"}`
}
}
Though the usefulness of the host (client specified it), remote port (it's random), and server ip/port (it's static and not useful for the client) are questionable.I learned this from: https://news.ycombinator.com/item?id=36383258
https://developers.cloudflare.com/fundamentals/get-started/r...
sni=plaintext
There was a great trial CF ran for a long period with a previous version of encrypted SNI (now ECH). One could get encrypted SNI with any site using CF via modified OpenSSL. But they stopped this experiment. Meanwhile ECH was still not ready.If anyone is getting something other than "sni=plaintext" for a CF domain besides crypto.cloudflare.com, then please let us know how.
https://whatismyip.akamai.com/advanced?debug
https://ipv4.whatismyip.akamai.com
DNS-based
drill whoami.akamai.net
The second one might be useful to verify one is not using local, ISP-provided DNS, or to see whether a DoH provider effectively geolocates its users, e.g., Cloudflare.CNAME for whatismyip.akamai.com is a1524.g.akamai.net
Probably no certitifcate that lists whatismyip.akamai.com
dig ip @dns.toys +shortSend a request to stackexchange.com or any of its subdomains without a user-agent header.
I’m not affiliated with any of these services, but my goto has been ip4.me, ip6.me, and ip6only.me because they’re short and memorable and because they acknowledge the IPv4/IPv6 split. The first two domains give you your v4 and v6 IP respectively and the latter only resolves over IPv6 (useful to ensure your IPv6 is off when using a VPN). You can tack on /api to any of them to get a plaintext response.
I made wgetip.com back in 2008 to solve this. icanhaz must've been pretty suboptimal back then to have issues with traffic. wgetip still gets about 3M requests per hour. All from a single $5 droplet.
curl https://ip4.me/api/
works for meFor wgetip.com that is not needed of course. But I haven't found a way to get the IPv4 address there...
Check your local DNS configuration and your spelling, something is likely hijacking NXDOMAINs.
They have ipv4.icannazip.com and ipv6.icannazip.com where they will return that.
Even though nobody asked, I have to mention this every time there is a discussion about IPv6 and IP address data. According to domain name records, even if a site supports both IPv4 and IPv6 it defaults to IPv6. I would love to know why.
I have come across a lot of people saying IPinfo doesn't support IPv6. The internet as an ecosystem doesn't completely support IPv6, but we do. So, mentioning it constantly in every discussion is my current approach.
Newer is better? Good clients use the 'happy eyeballs' (rfc 8305) mechanism, which is roughly, open v6, wait a bit, open v4, use whichever connection comes back first. There's a bias towards v6 because often it's a more direct connection, and may work better, but it was also common to have non working AAAA records, so a delayed fallback is preferable to no fallback as used to be common.
Very easy URL to relay, "do you see the chicken? let me know what the blue numbers are below it" - and most people seem to get a kick out of it!
No need for a /ip, just doing:
$ curl ip.wtf
... will do the right thing, or if your user-agent isn't curl send a header of "Accept: text/plain" and you'll get the plain text version (see https://ip.wtf/about for more).
A New Future for Icanhazip
https://news.ycombinator.com/item?id=27415537 (2 years ago, 253 comments)
With @(dns server ip here) you can query a different dns server to check what their answer is.
With +trace you can go through the complete dns chain from the root servers on to check for issues in the chain.
Sorry if this doesn’t help you or you knew this already.
dig -4 +short myip.opendns.com @resolver1.opendns.com
$ dig +short TXT o-o.myaddr.l.google.com @ns1.google.com
"A.B.C.D"
$ dig +short TXT o-o.myaddr.l.google.com
"172.253.195.202"
"edns0-client-subnet A.B.C.0/24"
$ dig +short TXT o-o.myaddr.l.google.com @9.9.9.9
"162.244.55.23"
$ dig +short TXT o-o.myaddr.l.google.com @1.1.1.1
"172.71.221.175"
The opendns implementation returns nothing if you try something similar to the last three.Akamai:
dig +short whoami.akamai.net @ns1-1.akamaitech.net
Google: dig +short o-o.myaddr.l.google.com txt @ns1.google.com
Cloudflare: dig +short whoami.cloudflare ch txt @1.1.1.1
OpenDNS: dig -4 +short myip.opendns.com @resolver1.opendns.com
Note that if you leave off the `@service.authoritative.nameserver` portion of all of the above, you get the IP address of the recursive resolver that your machine is configured to use. If you pass this to a service like https://ipinfo.io/, you can use this to figure out what company's DNS service is currently configured, be it your ISPs, or an internet company like Cloudflare/Google/Quad9.Arguably, that is the only valid reason to be using an IP address echoer via DNS, as if you truly wanted your own IP, you could fetch it far easier from a service like icanhazip.com.
Personally, Akamai's offering is by far the most useful and convenient--it's an A record, so you won't accidentally forget to add "txt" to your dig invocation, and has a very easy to remember name. Google's works too, though it requires remembering the strange domain name. Cloudflare tries to be cute and uses the Chaosnet DNS class, which while being an incredibly cool reference to internet history, unfortunately is not propagated by most public recursive resolvers. And like the parent comment mentions, OpenDNS somehow blocks requests that pass through a recursive resolver.
> And like the parent comment mentions, OpenDNS somehow blocks requests that pass through a recursive resolver.
https://web.archive.org/web/20131126131155/https://ndkyez8kh...
DNS is hard to implement, but for responding to a very specific type of query, should be easy?
Here is an implementation which uses parallel STUN queries to report address reliably as fast as possible: https://github.com/Snawoot/myip
But unfortunately I don't have it in a random docker/kubernetes containers or random internal servers, and these are the contexts where I personally usually want to check my external IP. They usually have curl though.
I might not have permission or it wouldn't be reasonable to install something. And I can link it to customers or give them a simple command to run, and all it returns is the actual info I need from them.
curl ifconfig.meYou got the short end of the stick for $8 reimbursement!
Everyone was so nice and I learned so much. I owe them all a lot of drinks. :)
$ curl -v icanhazip.com
> GET / HTTP/1.1
> Host: icanhazip.com
> User-Agent: curl/8.2.1
> Accept: */*
curl is being curl here: protocol, host, shortest possible user-agent string, nothing extra. Let's see the reply: < HTTP/1.1 200 OK
Okay. < Date: Tue, 01 Aug 2023 06:59:21 GMT
I am not sure, is it really necessary? I will use NTP if I need to know the current GMT. RFC doesn't state this header as mandatory. < Content-Type: text/plain
< Content-Length: 14
Okay, nice to know. < Connection: keep-alive
Really? What's a use case here? Do I need to be reminded of my IP again in a few seconds? Or is it in case my IP will quickly change? Oh, never mind... < Access-Control-Allow-Origin: *
< Access-Control-Allow-Methods: GET
Now, this is a bit ridiculous. Why would the fetch-based browser app rely on a third-party service to determine the client's IP? < Set-Cookie: [250 bytes of total abomination]
Why, oh why? Why do I need to receive this and keep it somewhere? All I want is to haz IP! Can I only haz IP? < Server: cloudflare
Okay, a little vanity never killed nobody. < CF-RAY: 7efc32adfef5c21e-VIE
I know what Cloudflare Ray is. The question is: why do I need it here? < alt-svc: h3=":443"; ma=86400
Good to know, maybe, but to be honest - this is redundant too. xxx.xx.xx.xx [my IP address, masked for privacy reasons]
At last! Now I can do my thing with the IP I just haz.I will not rant here about extra bytes transferred, extra bandwidth congested, extra electricity burned, and so on. Sapienti sat. Two side notes: it replies with HTTP 1.1 on HTTP 1.0 request, and it still puts alt-svc header into https reply.
I rest my case.
It was conceived this way to be used in scripts. I don't see why you might want to -v here except to prove a non existing point that you made up.
``` addEventListener("fetch", event => { event.respondWith(handleRequest(event.request)) })
async function handleRequest(e) { const connectingIP = e.headers.get("cf-connecting-ip"); return new Response(`${connectingIP}\n`, {status: 200}) } ```
[1]: formatting gets messed up: https://pastebin.com/gSysKd5k
Now I sometimes use AI image generation tools to really make it stand out. It's been a lot of fun. A lot of the tech folks in my area are using it now.
I made a shell script with:
curl -s 'http://icanhazip.com/' | cat -vCloudflare is at the top of the list of custodians I would want if in a similar position, glad it’s found a new steward.
My similar setup with nginx (http://4a.si/ip) only returns $remote_addr without the newline, making it ugly for using with `curl`.
they'd soon learn
icanhazip is part of internet lore, and passing it on to Cloudflare is a very noble thing to do! Cloudflare, please stay true to the idealistic principles of simplicity and availability for the service.
So he did not solve that Chinese block?
No follow up?
Can someone please explain how does returning a few hundred bytes of plaintext response can cost thousands of dollars? Either I'm really bad at estimating things or there's some hidden cost somewhere I'm not seeing.
Also why could it not work fine behind a Cloudflare free tier?
30 billion times 350 bytes (size of my response) is equal to 10.5 terabytes (base-10) a day. 84 terabits per day, 84000/3600/24 ~= 1 gigabit if my math is right. A fully saturated 1 gigabit pipe of worldwide internet costs a lot. So, there.
My take. I suspect Cloudflare benefited allowing that traffic. It's another data point/tool in their toolbox against the baddies.
Yeah sorry, I would not pay $30k/mo so y'all could track your external IP addresses...
And that's if you truly expect to saturate it. If you just want to take the risk and go for oversold fiber, you can pay some nominal price for "unmetered bandwidth" (with the caveat that if you're saturating the pipe, your host will probably make you pay for it eventually).
From there traffic doubled, and he moved to Cloudflare Workers. And then traffic 30xed.
I suspect it's more about request count (>300k per second) than raw number of bits.
Eh, how would that even work? You have to serve the requests anyway.