sslip.io is a DNS server that maps specially-crafted DNS A records to IP addresses (e.g. "127-0-0-1.sslip.io" maps to 127.0.0.1). It is similar to, and inspired by the now-defunct xip.io.
If you'd like to use sslip.io as a service, refer to the website (nip.io) for more information. This README targets developers; the website targets users.
git clone https://github.com/cunnie/sslip.io.git
cd sslip.io
go mod tidy
sudo go run .
# sudo is required to bind to privileged port 53In another window:
dig @localhost 192.168.0.1.sslip.io +short
# should return "192.168.0.1"go mod tidy
go generate
go install github.com/onsi/ginkgo/v2/ginkgo
go get github.com/onsi/gomega/...
# exclude subdir "spec/", used for testing sslip.io service not code
~/go/bin/ginkgo -r -p --skip-package=spec .We can customize our nameserver and address records (NS, A, and AAAA). This can
be done with a combination of the -ns, -a, and -aaaa flags.
For example, let's say we're the DNS admin for pivotal.io and our email is dns-admin@pivotal.io, and we'd like to have a subdomain, "xip.pivotal.io", that does sslip.io-style lookups (e.g. "127.0.0.1.xip.pivotal.io" would resolve to "127.0.0.1"). Let's say we have two servers that we've set aside for this purpose:
- ns-ip-0.pivotal.io, 10.8.8.8 (IPv4)
- ns-ip-1.pivotal.io, fc88:: (IPv6)
First, we delegate the subdomain "xip.pivotal.io" to our two nameservers, and then we run the following command run on each of the two servers:
# after we've cloned our repo & cd'ed into it
go run . \
-ns=.=ns-ip-0.pivotal.io \
-ns=.=ns-ip-1.pivotal.io \
-a=ns-ip-0.pivotal.io=10.8.8.8 \
-aaaa=ns-ip-1.pivotal.io=fc88:: \
-soa=ns-ip-0.pivotal.io,dns-admin.pivotal.io,20261024,900,900,604800,180 \
-blocklistURL=file:///dev/null \
-ptr-domain=xip.pivotal.ioNote: These nameservers are not general-purpose nameservers; for example, they won't look up google.com. They are not recursive. Don't ever configure a client to use these nameservers.
Probably the easiest way to run the nameserver is with the official Docker image, cunnie/sslip.io-dns-server. Using our DNS servers from the previous example, we run the following, setting our port to 5553 so we can run unprivileged:
docker run \
-it \
--rm \
-p 5553:53/udp \
cunnie/sslip.io-dns-server \
-ns=.=ns-ip-0.pivotal.io \
-ns=.=ns-ip-1.pivotal.io \
-a=ns-ip-0.pivotal.io=10.8.8.8 \
-aaaa=ns-ip-1.pivotal.io=fc88:: \
-soa=ns-ip-0.pivotal.io,dns-admin.pivotal.io,20261024,900,900,604800,180 \
-blocklistURL=file:///dev/null \
-ptr-domain=xip.pivotal.ioFrom our machine, we look up the DNS NS records for "127.0.0.1.com", and we see the expected reply:
dig ns 127.0.0.1.com @localhost -p 5553
...
;; ANSWER SECTION:
127.0.0.1.com. 604800 IN NS ns-ip-1.pivotal.io.
127.0.0.1.com. 604800 IN NS ns-ip-0.pivotal.io.
;; ADDITIONAL SECTION:
ns-ip-0.pivotal.io. 604800 IN A 10.8.8.8
ns-ip-1.pivotal.io. 604800 IN AAAA fc88::The Docker image is multi-platform, supporting both x86_64 architecture as well as ARM64 (AWS Graviton, Apple M1–M6).
-portoverrides the default port, 53, which the server binds to. This can be especially useful when running as a non-privileged user, unable to bind to privileged ports (<1024) ("listen udp :53: bind: permission denied"). For example, to run the server on port 9553:go run main.go -port 9553. To query,dig @localhost 127.0.0.1.sslip.io -p 9553-nssets NS records asdomain=nameserver. If the domain is., it sets the nameservers returned for every query, e.g.-ns=.=ns1.example.com -ns=.=ns2.example.com. If you're running your own nameservers, you probably want to set this; otherwise no NS records are returned. Don't forget to set address records for your nameservers with the-aand-aaaaflags (see below). For any other domain,-nsdelegates that domain and its subdomains to the nameserver, often used to acquire wildcard certificates from Let's Encrypt via the DNS-01 challenge, e.g.-ns=_acme-challenge.73-189-219-4.xip.nono.io=ns-437.awsdns-54.com-asets an A (IPv4) record ashostname=ipv4, e.g.-a=ns1.example.com=10.8.8.8. Repeat the flag to set many addresses for a single host, e.g.-a=ns1.example.com=1.1.1.1 -a=ns1.example.com=8.8.8.8-aaaasets an AAAA (IPv6) record ashostname=ipv6, e.g.-aaaa=ns1.example.com=fc::8888-soasets the SOA record returned for every domain asmname,rname,serial,refresh,retry,expire,minimum, e.g.-soa=ns1.example.com,dns-admin.example.com,20261024,900,900,604800,180. Unlike the other record flags, there's only one SOA, and it always has a default:ns-01.nip.io.,dns.nip.io.,20261024,900,900,604800,180-mxsets an MX record asdomain=preference,host, e.g.-mx=example.com=10,mail.example.com -mx=example.com=20,mail2.example.com. Without it, the MX record is the queried hostname with a preference of 0-txtsets a TXT record asdomain=text; the surrounding double quotes are optional, e.g.-txt=example.com="v=spf1 mx -all". Text longer than 255 bytes is split into multiple strings within the record-cnamesets a CNAME record asdomain=target, e.g.-cname=www.example.com=example.com. A domain can have only one CNAME-blocklistURLoverrides the default block list, (https://raw.githubusercontent.com/cunnie/sslip.io/main/etc/blocklist.txt). The blocklist is not a show-stopper: if the DNS server can't download the blocklist, it logs a message and continues to serve DNS queries-ptr-domainthe domain to use for PTR records. For example, if you set-ptr-domain=ip.example.comand then do a reverse-lookup (PTR record), e.g.dig -x 127.0.0.1, the result will be127-0-0-1.ip.example.com. Best practice: make sure the forward lookup matches the reverse lookup, e.g.dig 127-0-0-1.ip.example.comshould return an A record of127.0.0.1.-publiccontrols whether public addresses are resolved. If unsure, set-public=false, which means only private IP addresses will resolve, e.g.10.9.9.30,192.168.0.1,169.254.169.254. Let's say you run a bank, and your nip.io-style subdomain isip.wellsfargo.com, you don't a phisher scammer to spin up a VM with a public IP address and get a certificate for that IP address's hostname52-0-56-137.ip.wellsfargo.comand use it to phish your customers.-public=falseprevents that scenario.-quietsuppresses logging of each DNS response, which can save you money if your cloud provider charges you to retain logs-dnstapenables dnstap logging of queries that return an A or AAAA record; its argument is the destination socket, either a Unix socket path (e.g./var/run/dnstap.sock) or a TCP address (e.g.127.0.0.1:6000)
In the record flags (-ns, -a, -aaaa, -soa, -mx, -txt, -cname),
names are case-insensitive, and the trailing dot is optional. You can repeat
-ns, -a, -aaaa, -mx, and -txt to set several records for the same
domain.
- It binds to both UDP and TCP.
- It returns the same SOA record for every domain (see
-soa). - There are no SRV records
-
spec/contains the tests for the production nameservers. To ensure the production nameservers are running properly:DOMAINS=nip.io,sslip.io ginkgo -r -p spec/
-
To test the code (if you've made changes) without testing the production nameservers:
ginkgo -r -p --skip-package=speck8s/document_root_nip.io/contains the HTML content of the nip.io website.
- The late, great Roopinder Singh (nip.io), Sam Stephenson (xip.io), and the other DNS developers out there
- The contributors (@normanr, @jpambrun come to mind) who improved sslip.io
- Let's Encrypt for bumping our rate limits many, many times