A simple, local, wildcarding DNS server
- map wildcarded domains...
- ...to local IP
- ...to loopback
- ...to a specific target
- recursively resolve domains...
- ...via servers specified for that domain
- ...via a specified
resolv.conf
-formatted file- so you can use this as your primary local DNS resolver
go get github.com/miekg/dns
go build
install=$HOME/bin
case "$(uname)" in
Darwin) GOOS=darwin ;;
*) GOOS=linux ;;
esac
docker run --rm \
-v $(pwd):/localdns \
-v ${install}:/out \
-w /localdns \
-e GOOS=$GOOS \
-e GOARCH=amd64 \
-e GOBIN=/out \
golang:1.3.3-cross \
go get -v ./...
All configuration is via environment variables.
-
CNAMES=src:target,src2:target2,...
*.src
will return a CNAME totarget
localdns
will also look uptarget
s A record(s)- no default
-
SELF=name,name2,...
*.name
will point to local, non-loopback IP address(es)- no default
-
IFACE=name%iface1%iface2...
*.name
will point to IP addresses assigned to interface(s)iface1
...- no default
-
LOOP=name,name2,...
*.name
will point to 127.0.0.1- no default
-
SERVERS=name/name2/server,server2,...
- use
server
for upstream resolving - if
name/
is present, only useserver
for names ending inname
- otherwise
server
is used unconditionally - if any
server
matches (including unconditionally),RESOLV
will not be used - no default (will fall through to
RESOLV
)
- use
-
RESOLV=filename
- use
filename
as theresolv.conf
-formatted source for upstream servers - default
/etc/resolv.conf
won't work if using as primary DNS server
- use
-
ADDR=ip:port
- listen on
port
, bindingip
- no default
- listen on
-
PORT=port
- listen on
port
, not bound to a specific interface - default
9753
- listen on
sudo env \
CNAMES=nginx.nginx.dev.docker:example.dev \
SELF=me \
IFACE=ether%enp0s25 \
LOOP=dev \
SERVERS=docker/127.0.0.1:5553,int.vpc/10.10.1.51,int.vpc/10.10.1.50 \
RESOLV=/etc/resolv.conf.upstream \
PORT=53 \
localdns
$ dig @localhost +noall +answer my.machine.dev
my.machine.dev. 60 IN A 127.0.0.1
$ dig @localhost +noall +answer an.arbitrarily.long.name.me
an.arbitrarily.long.name.me. 60 IN A 192.168.30.40
an.arbitrarily.long.name.me. 60 IN A 172.17.42.1
$ dig @localhost +noall +answer bhaskell.example.dev
bhaskell.example.dev. 60 IN CNAME nginx.nginx.dev.docker.
nginx.nginx.dev.docker. 22 IN A 172.17.0.26
$ dig @localhost +noall +answer google.com
google.com. 193 IN A 74.125.228.7
google.com. 193 IN A 74.125.228.2
google.com. 193 IN A 74.125.228.8
...
$ dig @localhost +noall +answer reflect.ether
reflect.ether. 60 IN A 10.50.50.54
reflect.ether. 60 IN AAAA fe80::d7db:9dbf:f031:761c
sudo
is only needed when listening on a "privileged" port (<= 1024
), and
can be replaced by capabilities on Linux:
setcap cap_net_bind_service=+ep `which localdns`
I've been a huge fan of dnsmasq for a long time, but I needed an extra feature: arbitrary CNAME mapping.
From the dnsmasq man page,
under the --cname=<cname>,<target>
option:
Return a CNAME record which indicates that <cname> is really <target>. There
are significant limitations on the target; it must be a DNS name which is known
to dnsmasq from /etc/hosts (or additional hosts files), from DHCP, from
--interface-name or from another --cname. If the target does not satisfy this
criteria, the whole cname is ignored.