mirror of
https://github.com/cunnie/sslip.io.git
synced 2025-09-27 03:55:56 +08:00

Previously we blocked by CIDRs, not IPs, but that was flawed: of the 746 CIDRs, 744 of them were /32 — in other words, IP addresses. And matching CIDRs is computationally expensive: consuming 4.8% of the CPU for each query. We switched to a string-indexed map instead to accelerate matching. - Fivefold increase in blocklist lookup speed, dropping from consuming 4.8% of the CPU to 0.96% - Added a new member, `xip.BlocklistIPs` - All blocked sites are IPv4. I have never gotten a takedown for an IPv6 site - I wanted to maintain backwards-compatiblity with my blocklist file; I didn't want to be forced to coordinate updating that simultaneously with a deploy of this code, hence the automated "/32" conversion from a CIDR to an IP address - I cleaned up the test blocklist file (`blocklist-test.txt`); it's easier to read & understand - I added profiling from before, `profile/cpu-cidr.prof`, and after, `profile/cpu-ip.prof`, the change.
368 lines
20 KiB
HTML
368 lines
20 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="en">
|
||
|
||
<head>
|
||
<meta charset="utf-8">
|
||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1">
|
||
<!-- The above 3 meta tags *must* come first in the head; any
|
||
other head content must come *after* these tags -->
|
||
<title>Welcome to nip.io / sslip.io</title>
|
||
<meta name="description" content="nip.io / sslip.io">
|
||
<meta name="author" content="Brian Cunnie"><!-- cute Green Lock icon -->
|
||
<link rel="shortcut icon" type="image/x-icon" href="https://sslip.io/img/favicon.ico"><!-- Latest
|
||
compiled and minified CSS -->
|
||
<link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.7/dist/css/bootstrap.min.css" rel="stylesheet"
|
||
integrity="sha384-LN+7fdVzj6u52u30Kp6M/trliBMCMKTyK833zpbD+pXdCLuTusPj697FH4R/5mcr" crossorigin="anonymous">
|
||
</head>
|
||
|
||
<body>
|
||
<div class="container">
|
||
<div class="alert alert-info" role="alert" style="margin-top: 20px; margin-bottom: 10px; text-align: center;">
|
||
<strong>This service is dedicated to the late, great Roopinder Singh, who created & ran nip.io</strong>
|
||
</div>
|
||
<div style="padding-top: 10px;">
|
||
<h3 id="sslip.io">nip.io & sslip.io</h3>
|
||
<p>Operational Status: <a href="https://github.com/cunnie/sslip.io/actions/workflows/nameservers.yml"><img
|
||
src="https://github.com/cunnie/sslip.io/actions/workflows/nameservers.yml/badge.svg"
|
||
alt="GitHub Actions"></a> <sup><a href="#status" class="alert-link">[Status]</a></sup>
|
||
<img
|
||
src="https://img.shields.io/endpoint?url=https://gist.githubusercontent.com/cunnie/67dc2a78c9ac6032db05027727c63ea1/raw/qps.json"
|
||
alt="Queries / second">
|
||
</p>
|
||
<p><em>nip.io</em> and <em>sslip.io</em> are a DNS (<a
|
||
href="https://en.wikipedia.org/wiki/Domain_Name_System">Domain Name System</a>)
|
||
service that, when queried with a hostname with an embedded IP address, returns that IP address. It was inspired
|
||
by <a href="http://xip.io">xip.io</a>, which was created by <a href="https://github.com/sstephenson">Sam
|
||
Stephenson</a>.</p>
|
||
<p>Here are some examples (the domains nip.io and sslip.io are interchangeable):</p>
|
||
<table class="table">
|
||
<thead>
|
||
<tr class="header">
|
||
<th>Hostname / URL</th>
|
||
<th>IP Address</th>
|
||
<th>Notes</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td>
|
||
<a href="https://52.0.56.137.sslip.io">https://52.0.56.137.sslip.io</a>
|
||
</td>
|
||
<td>52.0.56.137</td>
|
||
<td>dot separators, sslip.io website mirror (IPv4)</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>
|
||
<a href="https://52-0-56-137.sslip.io">https://52-0-56-137.sslip.io</a>
|
||
</td>
|
||
<td>52.0.56.137</td>
|
||
<td>dash separators, sslip.io website mirror (IPv4)</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>www.192.168.0.1.sslip.io</td>
|
||
<td>192.168.0.1</td>
|
||
<td>subdomain</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>www.192-168-0-1.sslip.io</td>
|
||
<td>192.168.0.1</td>
|
||
<td>subdomain + dashes</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>
|
||
<a href="https://www-78-46-204-247.sslip.io">https://www-78-46-204-247.sslip.io</a>
|
||
</td>
|
||
<td>78.46.204.247</td>
|
||
<td>dash prefix, sslip.io website mirror (IPv4)</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>--1.sslip.io</td>
|
||
<td>::1</td>
|
||
<td>IPv6 — always use dashes, never dots</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td>
|
||
<a href="https://2a01-4f8-c17-b8f--2.sslip.io">https://2a01-4f8-c17-b8f--2.sslip.io</a>
|
||
</td>
|
||
<td>2a01:4f8:c17:b8f::2</td>
|
||
<td>sslip.io website mirror (IPv6)</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td>
|
||
<a href="https://334B3513.nip.io">https://334B3513.nip.io/</a>
|
||
</td>
|
||
<td>51.75.53.19</td>
|
||
<td>sslip.io website mirror (hexadecimal notation)</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<h3 id="branding">Branding / White Label / Custom Domains</h3>
|
||
<p>sslip.io can be used to brand your own site (you don’t need to use the sslip.io domain). For example, say you
|
||
own the domain “example.com”, and you want your subdomain, “xip.example.com” to have xip.io-style features. To
|
||
accomplish this, set the following three DNS servers as NS records for the subdomain “xip.example.com”</p>
|
||
<table class="table">
|
||
<thead>
|
||
<tr class="header">
|
||
<th>hostname</th>
|
||
<th>IP address</th>
|
||
<th>Location</th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr class="odd">
|
||
<td><code>ns-do-sg.sslip.io.</code></td>
|
||
<td>146.190.110.69<br>
|
||
2400:6180:0:d2:0:1:da21:d000</td>
|
||
<td>Singapore</td>
|
||
</tr>
|
||
<tr class="even">
|
||
<td><code>ns-hetzner.sslip.io.</code></td>
|
||
<td>5.78.115.44<br>
|
||
2a01:4ff:1f0:c920::</td>
|
||
<td>USA</td>
|
||
</tr>
|
||
<tr class="odd">
|
||
<td><code>ns-ovh.sslip.io.</code></td>
|
||
<td>51.75.53.19<br>
|
||
2001:41d0:602:2313::1</td>
|
||
<td>Poland</td>
|
||
</tr>
|
||
</tbody>
|
||
</table>
|
||
<p>Let’s test it from the command line using <code>dig</code>:</p>
|
||
<pre><code>dig @ns-ovh.sslip.io. 169-254-169-254.xip.example.com +short</code></pre>
|
||
<p>Yields, hopefully: <sup><a href="#timeout" class="alert-link">[connection timed out]</a></sup></p>
|
||
<pre><code>169.254.169.254</code></pre>
|
||
<h3 id="server">But I Want My Own DNS Server!</h3>
|
||
<p>If you want to run your own DNS server, it's simple: you can compile from <a
|
||
href="https://github.com/cunnie/sslip.io">source</a> or you can use one of our <a
|
||
href="https://github.com/cunnie/sslip.io/releases">pre-built binaries</a>. In the following example, we
|
||
install & run
|
||
our server within a docker container:</p>
|
||
<pre>
|
||
docker run -it --rm fedora
|
||
curl -L https://github.com/cunnie/sslip.io/releases/download/3.2.7/sslip.io-dns-server-linux-amd64 -o dns-server
|
||
chmod +x dns-server
|
||
./dns-server 2> dns-server.log &
|
||
dnf install -y bind-utils
|
||
dig @localhost 127-0-0-1.nip.io +short # returns "127.0.0.1"</pre>
|
||
<h3 id="tls">TLS</h3>
|
||
<p>You can acquire TLS certificates for your externally-accessible hosts from certificate authorities (CAs) such
|
||
as Let's Encrypt. The easiest mechanism to acquire a certificate would be to use the <a
|
||
href="https://letsencrypt.org/docs/challenge-types/#http-01-challenge">HTTP-01 challenge</a>. It requires, at
|
||
a
|
||
minimum, a web server running on your machine. The <a href="https://caddyserver.com/">Caddy</a> web server is
|
||
one
|
||
of the most popular examples. For example, if you had a webserver with the IP address 52.0.56.137, you could
|
||
obtain a TLS certificate for "52.0.56.137.nip.io", or "www.52.0.56.137.nip.io", or
|
||
"prod.www-52-0-56-137.nip.io".</p>
|
||
<div class="alert alert-success" role="alert">
|
||
<b>Let's Encrypt Rate Limits</b> If your request for a "nip.io" certificate is <a
|
||
href="https://letsencrypt.org/docs/rate-limits/">rate-limited</a>, please open a <a
|
||
href="https://github.com/cunnie/sslip.io/issues/new/choose">GitHub issue</a> and we'll request a rate-limit
|
||
increase.
|
||
</div>
|
||
<p>If you have procured a wildcard certificate for your branded / white label / custom nip.io-style subdomain,
|
||
you may install it on your machines for TLS-verified connections.</p>
|
||
<div class="alert alert-success" data-role="alert">
|
||
<p>When using a TLS wildcard certificate in conjunction with your branded nip.io style subdomain, you must
|
||
<b>use dashes not dots</b> as separators. For example, if you have the TLS certificate for
|
||
<i>*.xip.example.com</i>, you could browse to https://www-52-0-56-137.xip.example.com/ but not
|
||
https://www.52.0.56.137.xip.example.com/.
|
||
</p>
|
||
</div>
|
||
<h3 id="experimental">Experimental Features</h3>
|
||
<p>Experimental features can change; don't depend on them.</p>
|
||
<h4 id="whatismyip">Determining Your External IP Address via DNS Lookup</h4>
|
||
<p>You can use sslip.io's DNS servers (<code>ns.nip.io</code>) to determine your public IP address by querying
|
||
the <code>TXT</code> record of <code>ip.nip.io</code>:</p>
|
||
<pre>
|
||
dig @ns.nip.io txt ip.nip.io +short # sample reply "2607:fb90:464:ae1e:ed60:29c:884c:4b52"
|
||
dig @ns.nip.io txt ip.nip.io +short -4 # forces IPv4 lookup; sample reply "172.58.35.231"
|
||
dig @ns.nip.io txt ip.nip.io +short -6 # forces IPv6 lookup; sample reply "2607:fb90:464:ae1e:ed60:29c:884c:4b52"</pre>
|
||
<div class="alert alert-success" role="alert">
|
||
When querying for your IP address, always <b>include the nip.io name server</b> (e.g. <i>@ns.nip.io</i>).
|
||
If omitted, you won't get your IP address; instead, you'll get the IP address of your upstream name server.
|
||
</div>
|
||
<p>This feature was inspired by Google's DNS lookup, i.e. <code>dig txt o-o.myaddr.l.google.com @8.8.8.8
|
||
+short</code>. There are also popular HTTP-based services for determining your public IP address:</p>
|
||
<ul>
|
||
<li>
|
||
<a href="http://icanhazip.com/">icanhazip.com</a> (<a
|
||
href="https://major.io/2021/06/06/a-new-future-for-icanhazip/">backstory</a>)
|
||
</li>
|
||
<li>
|
||
<a href="http://ipify.org/">ipify.org</a>
|
||
</li>
|
||
<li>
|
||
<a href="http://ip4.me/">ip4.me</a>
|
||
</li>
|
||
<li>
|
||
<a href="http://curlmyip.org/">curlmyip.org</a>
|
||
</li>
|
||
<li>
|
||
<a href="http://ifconfig.co/">ifconfig.co</a>
|
||
</li>
|
||
<li>
|
||
<a href="http://ipinfo.io/">ipinfo.io</a> (commercial)
|
||
</li>
|
||
</ul>
|
||
<p>A big advantage of using DNS queries instead of HTTP queries is bandwidth: querying
|
||
<code>ns.nip.io</code> requires a mere 594 bytes spread over 2 packets; Querying <a
|
||
href="https://icanhazip.com/">https://icanhazip.com/</a> requires 8692 bytes spread out over 34 packets—over
|
||
14 times
|
||
as much! Admittedly bandwidth usage is a bigger concern for the one hosting the service than the one using the
|
||
service.
|
||
</p>
|
||
<h4 id="version">Determining The Server Version of Software</h4>You can determine the server version of the
|
||
nip.io software by querying the TXT record of <code>version.status.nip.io</code>:
|
||
<pre>
|
||
dig @ns-ovh.nip.io version.status.nip.io txt +short
|
||
"4.1.0"
|
||
"2025/06/22-17:49:11-0700"
|
||
"b879e43"
|
||
</pre>
|
||
<p>The first number, ("4.1.0"), is the version of the nip.io DNS software, and is most relevant. The other two
|
||
numbers are the date compiled and the most recent git hash, but those values can differ across servers due to
|
||
the
|
||
manner in which the software is deployed.</p>
|
||
<h4 id="metrics">Server Metrics</h4>You can retrieve metrics from a given server by querying the TXT records of
|
||
<code>metrics.status.nip.io</code>
|
||
<pre>
|
||
dig @ns-ovh.nip.io metrics.status.nip.io txt +short
|
||
"Uptime: 1168705"
|
||
"Blocklist: 2025-07-22 04:30:18-07 3,722,2"
|
||
"Queries: 2619971786 (2241.8/s)"
|
||
"TCP/UDP: 2949450/2617181176"
|
||
"Answer > 0: 934226491 (799.4/s)"
|
||
"A: 827459036"
|
||
"AAAA: 8761271"
|
||
"TXT Source: 76"
|
||
"TXT Version: 66"
|
||
"PTR IPv4/IPv6: 2274/13"
|
||
"NS DNS-01: 143712"
|
||
"Blocked: 278944"
|
||
</pre>
|
||
<h5>Explanation of Metrics</h5>
|
||
<dl>
|
||
<dt>Uptime</dt>
|
||
<dd>The time since the DNS server has been started, in seconds</dd>
|
||
<dt>Blocklist</dt>
|
||
<dd>
|
||
The first value ("2023-10-04 07:37:50-07") is the date the blocklist was last downloaded. The following three
|
||
numbers are the number of CIDR matches that are blocked (e.g. 86.106.104.0/24), the number of IP addresses
|
||
that are blocked (e.g. 212.64.214.54), and the number of strings that are blocked (e.g. "raiffeisen" is a
|
||
string that is blocked if it appears in the queried hostname). The blocklist can be found <a
|
||
href="https://github.com/cunnie/sslip.io-blocklist/blob/main/blocklist.txt">here</a>
|
||
</dd>
|
||
<dt>Queries</dt>
|
||
<dd>This consists of two numbers: The first is the raw number of DNS queries that the server has responded to
|
||
since starting operation, and the second is the first number divided by the uptime (i.e. queries/second)</dd>
|
||
<dt>TCP/UDP</dt>
|
||
<dd>This is the number of queries received on the TCP protocol versus the UDP protocol. The sum should equal
|
||
the number of queries. DNS typically uses the UDP protocol</dd>
|
||
<dt>Answer > 0</dt>
|
||
<dd>This consists of two numbers: the first is the number of queries we responded to with at least one record
|
||
in the answer section, and the second is the first number divided by the uptime (i.e. queries/second). Note
|
||
that the number of responses with an answer record is typically a fourth the size of the overall responses.
|
||
This is normal. One reason for this disparity is that often both the IPv4 (A) and IPv6 (AAAA) records will be
|
||
checked, but only one reply will have a record in the answer section . For example, browsing to
|
||
"127.0.0.1.nip.io" generates two lookups, one with an answer (IPv4), and one without (IPv6). Another reason
|
||
is that lookups follow a chain, e.g. looking up "127.0.0.1.nip.io" may generate up to four queries for A
|
||
records ("1.nip.io", "0.1.nip.io", "0.0.1.nip.io" and "127.0.0.1.nip.io"), only the last of which
|
||
returns a record in the answer section. Pro-tip: if you want to shave milliseconds off name resolution, use
|
||
dashes not dots in your hostname (e.g. "10-9-9-30.nip.io" instead of "10.9.9.30.nip.io")</dd>
|
||
<dt>A</dt>
|
||
<dd>The number of responses which included an A (IPv4) record in the answer section since starting operation
|
||
(e.g. "dig 127.0.0.1.nip.io")</dd>
|
||
<dt>AAAA</dt>
|
||
<dd>The number of responses which included an AAAA (IPv6) record in the answer section since starting operation
|
||
(e.g. "dig --1.nip.io aaaa")</dd>
|
||
<dt>TXT Source</dt>
|
||
<dd>The number of responses which included a TXT record of the querier's IP address since starting operation
|
||
(e.g. "dig @ns.nip.io ip.nip.io txt")</dd>
|
||
<dt>TXT Version</dt>
|
||
<dd>The number of responses which included a TXT record of the DNS's servers version since starting operation
|
||
(e.g. "dig @ns-hetzner.sslip.io version.status.nip.io txt")</dd>
|
||
<dt>PTR IPv4/IPv6</dt>
|
||
<dd>This consists of two numbers; the first is the number of responses to IPv4 PTR queries
|
||
(<code>1.0.0.127.in-addr.arpa.</code> → <code>127-0-0-1.sslip.io.</code>), the second, IPv6 PTR queries</dd>
|
||
<dt>NS DNS-01</dt>
|
||
<dd>The number of responses which included a delegation of the NS (name server) to satisfy a certificate
|
||
authority's DNS-01 challenge. This lookup is used for generating wildcard certificates from Let's Encrypt and
|
||
other certificate authority. Technically this is not a "successful" query in that we don't return a record in
|
||
the ANSWER section, but we do return an NS record in the AUTHORITY section. (e.g. "dig @ns-ovh.nip.io
|
||
_acme-challenge.192.168.0.1.nip.io. soa")</dd>
|
||
<dt>Blocked</dt>
|
||
<dd>The number of responses which were blocked because they were either spam or phishing sites. A blocked
|
||
response is an A or AAAA record to a website which notifies the user that the site they're trying to visit has
|
||
been blocked because it's unsafe.</dd>
|
||
</dl>
|
||
<div class="alert alert-success" role="alert">
|
||
<b>Developers: disable indexing of your staging site to avoid being blocked</b> (we block by disabling
|
||
resolution of your nip.io hostname); disable indexing by either including the <code>X-Robots-Tag:
|
||
noindex</code> in your HTTP headers or include a <code>robots.txt</code> at the root of your website with the
|
||
following contents:<code><br>
|
||
User-agent: *<br>
|
||
Disallow: /</code>
|
||
</div>
|
||
<h3 id="related">Related Services</h3>
|
||
<ul>
|
||
<li>
|
||
<a href="http://xip.io/">xip.io</a>: the inspiration for sslip.io. Sadly, this appears to be no longer
|
||
maintained after <a href="https://twitter.com/sstephenson/status/1388146129284603906">Sam Stephenson left
|
||
Basecamp</a>.
|
||
</li>
|
||
<li>
|
||
<a href="http://nip.io">nip.io</a>: formerly a separate service, but now incorporated into sslip.io. The
|
||
service previously used PowerDNS combined with a backend written in elegant Python.
|
||
</li>
|
||
<li>
|
||
<a href="https://letsencrypt.org/">Let's Encrypt</a>: A Certificate Authority providing TLS certificates;
|
||
they have never failed to increase our rate limits when asked. If you can, <a
|
||
href="https://www.abetterinternet.org/donate/">donate</a>.
|
||
</li>
|
||
</ul>
|
||
<hr>
|
||
<h4 id="footnotes">Footnotes</h4>
|
||
<p><a id="status"><sup>[Status]</sup></a> A status of “build failing” rarely means the system is failing. It’s
|
||
more often an indication that when the servers were last checked (currently every six hours), the CI (continuous
|
||
integration) <a href="https://ci.nono.io/teams/main/pipelines/sslip.io">server</a> had difficulty reaching one
|
||
of the three sslip.io name servers. That’s normal. <sup><a href="#timeout" class="alert-link">[connection timed
|
||
out]</a></sup></p>
|
||
<p><a id="timeout"><sup>[connection timed out]</sup></a></p>
|
||
<p>DNS runs over <a href="https://en.wikipedia.org/wiki/User_Datagram_Protocol">UDP</a> which has no guaranteed
|
||
delivery, and it’s not uncommon for the packets to get lost in transmission. DNS clients are programmed to
|
||
seamlessly query a different server when that happens. That’s why DNS, by fiat, requires at least two name
|
||
servers (for redundancy). From <a href="https://tools.ietf.org/html/rfc1034">IETF (Internet Engineering Task
|
||
Force) RFC (Request for Comment) 1034</a>:</p>
|
||
<blockquote>
|
||
<p>A given zone will be available from several name servers to insure its availability in spite of host or
|
||
communication link failure. By administrative fiat, we require every zone to be available on at least two
|
||
servers, and many zones have more redundancy than that.</p>
|
||
</blockquote>
|
||
</div>
|
||
</div><!-- /.container -->
|
||
<!--
|
||
Bootstrap core JavaScript ================================================== -->
|
||
<!--
|
||
Placed at the end of the document so the pages load faster -->
|
||
<!-- jQuery
|
||
(necessary for Bootstrap's JavaScript plugins) -->
|
||
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
|
||
<!-- Latest compiled and minified JavaScript -->
|
||
|
||
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
|
||
<!-- Google Analytics -->
|
||
<!-- Google tag (gtag.js) -->
|
||
<script async src="https://www.googletagmanager.com/gtag/js?id=G-M32C798MGY"></script>
|
||
<script>
|
||
window.dataLayer = window.dataLayer || [];
|
||
function gtag() { dataLayer.push(arguments); }
|
||
gtag('js', new Date());
|
||
gtag('config', 'G-M32C798MGY');
|
||
</script>
|
||
</body>
|
||
|
||
</html> |