Initial Version

This commit is contained in:
Knut Ahlers 2018-05-09 17:23:04 +02:00
commit 8d7e928ddb
Signed by: luzifer
GPG key ID: DC2729FDD34BE99E
10 changed files with 240 additions and 0 deletions

2
.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
blacklist
named.stubs

10
Corefile Normal file
View file

@ -0,0 +1,10 @@
. {
hosts /etc/bind/blacklist {
fallthrough
}
forward . 127.0.0.1:1053
errors
log
}

42
Dockerfile Normal file
View file

@ -0,0 +1,42 @@
FROM python:3-alpine as builder
COPY . /src
WORKDIR /src
RUN set -ex \
&& apk --no-cache add make \
&& pip install -r requirements.txt \
&& python build_stubs.py \
&& make blacklist
# ------
FROM alpine:latest
ENV DNSMASQ_HOSTSFILE=/etc/bind/blacklist \
DNSMASQ_POLL=60
LABEL maintainer Knut Ahlers <knut@ahlers.me>
COPY build.sh /usr/local/bin/
RUN set -ex \
&& apk --no-cache add \
bash \
bind \
bind-tools \
&& /usr/local/bin/build.sh
COPY --from=builder /src/named.stubs /etc/bind/
COPY --from=builder /src/blacklist /etc/bind/
COPY named.conf /etc/bind/
COPY Corefile /etc/
COPY docker-entrypoint.sh /usr/local/bin/
EXPOSE 53/udp 53
HEALTHCHECK --interval=30s --timeout=5s \
CMD dig +short @localhost health.server.test A || exit 1
ENTRYPOINT ["/usr/local/bin/docker-entrypoint.sh"]

6
Makefile Normal file
View file

@ -0,0 +1,6 @@
default:
blacklist:
curl -sSfL https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts | awk '/^(#.*|0.0.0.0.*|)$$/' > blacklist
# Add health check response
echo "0.0.0.0 health.server.test" >> blacklist

22
build.sh Executable file
View file

@ -0,0 +1,22 @@
#!/bin/bash
set -euxo pipefail
# Install build utilities
apk --no-cache add curl
# Get latest versions of tools using latestver
COREDNS_VERSION=$(curl -sSfL 'https://lv.luzifer.io/catalog-api/coredns/latest.txt?p=version')
DUMB_INIT_VERSION=$(curl -sSfL 'https://lv.luzifer.io/catalog-api/dumb-init/latest.txt?p=version')
[ -z "${COREDNS_VERSION}" ] && { exit 1; }
[ -z "${DUMB_INIT_VERSION}" ] && { exit 1; }
# Install tools
curl -sSfL https://github.com/coredns/coredns/releases/download/v${COREDNS_VERSION}/coredns_${COREDNS_VERSION}_linux_amd64.tgz | \
tar -x -z -C /usr/local/bin
curl -sSfLo /usr/local/bin/dumb-init https://github.com/Yelp/dumb-init/releases/download/v${DUMB_INIT_VERSION}/dumb-init_${DUMB_INIT_VERSION}_amd64
chmod +x /usr/local/bin/dumb-init
# Cleanup
apk --no-cache del curl

124
build_stubs.py Executable file
View file

@ -0,0 +1,124 @@
#!/usr/bin/env python
import random
import re
import requests
import jinja2
import dns.resolver
BLACKLIST_FILE = 'https://raw.githubusercontent.com/StevenBlack/hosts/master/hosts'
IANA_TLD_LIST = 'https://data.iana.org/TLD/tlds-alpha-by-domain.txt'
INTERNIC_ROOT_FILE = 'https://www.internic.net/domain/named.root'
OPENNIC_ROOT = '75.127.96.89'
OPENNIC_FILTER = ["..", "opennic.glue."]
IANA_FILTERS = ['arpa.']
roots = None
def get_generic_ip(fqdn):
res = dns.resolver.Resolver()
ans = res.query(fqdn, 'A')
return ans.rrset.items[0].to_text()
def get_iana_tlds():
tlds = requests.get(IANA_TLD_LIST).text.split("\n")
return [t.lower()+'.' for t in tlds if len(t) > 0 and t[0] != "#"]
def get_internic_roots():
global roots
if roots is not None:
return roots
named_file = requests.get(INTERNIC_ROOT_FILE).text
roots = []
for line in named_file.split("\n"):
match = re.search(r"\s+A\s+([0-9]+\.[0-9]+\.[0-9]+\.[0-9]+)$", line)
if match is None:
continue
roots.append(match.group(1))
return roots
def get_iana_zone_master(zone):
query = dns.message.make_query(zone, dns.rdatatype.NS)
ans = dns.query.tcp(query, random.choice(get_internic_roots()))
glue = {i.name.__str__(): i.items[0].address
for i in ans.additional
if i.rdtype == dns.rdatatype.A}
auth = [i.to_text() for i in ans.authority[0].items]
return [glue[i] for i in auth if i in glue]
def opennic_ip_from_master(master):
res = dns.resolver.Resolver()
res.nameservers = [OPENNIC_ROOT]
ans = res.query(master, 'A')
return [i.to_text() for i in ans.rrset.items]
def get_opennic_zone_master(zone):
res = dns.resolver.Resolver()
res.nameservers = [OPENNIC_ROOT]
ans = res.query('{}.opennic.glue.'.format(zone.strip('.')), 'CNAME')
masters = [t.strip('"')
for t in ans.rrset.items[0].to_text().split(" ")
if t.startswith('ns')]
masters.append('ns0.opennic.glue.')
master_ips = [opennic_ip_from_master(m) for m in masters]
return [item for sublist in master_ips for item in sublist]
def get_opennic_tlds():
res = dns.resolver.Resolver()
res.nameservers = [OPENNIC_ROOT]
ans = res.query('tlds.opennic.glue.', 'TXT')
return [t.strip('"')+'.' for t in ans.rrset.items[0].to_text().split(" ")]
def main():
entries = {
"opennic.glue.": [OPENNIC_ROOT],
}
iana_tlds = get_iana_tlds()
if len(iana_tlds) == 0:
raise Exception("No IANA TLDs found")
for tld in get_iana_tlds():
if tld in IANA_FILTERS:
continue
print("Working on IANA TLD '{}'...".format(tld))
entries[tld] = get_iana_zone_master(tld)
opennic_tlds = get_opennic_tlds()
if len(opennic_tlds) == 0:
raise Exception("No OpenNIC TLDs found")
for tld in get_opennic_tlds():
if tld in OPENNIC_FILTER:
continue
print("Working on OpenNIC TLD '{}'...".format(tld))
entries[tld] = get_opennic_zone_master(tld)
with open('named.stubs', 'w') as f:
f.write(render_corefile(entries))
def render_corefile(entries):
template = jinja2.Template(open("named.stubs.j2").read())
return template.render(entries=entries)
if __name__ == '__main__':
main()

8
docker-entrypoint.sh Executable file
View file

@ -0,0 +1,8 @@
#!/usr/local/bin/dumb-init /bin/bash
set -euxo pipefail
# Start bind in background
named -p 1053 -c /etc/bind/named.conf -g &
# Start coredns to filter blacklist
coredns -conf /etc/Corefile

15
named.conf Normal file
View file

@ -0,0 +1,15 @@
options {
directory "/var/bind";
allow-recursion {
127.0.0.1/32;
};
listen-on port 1053 { 127.0.0.1; };
pid-file "/var/run/named/named.pid";
allow-transfer { none; };
};
include "/etc/bind/named.stubs";

8
named.stubs.j2 Normal file
View file

@ -0,0 +1,8 @@
#{% for tld, to in entries.items() %}
zone "{{tld}}" in {
type static-stub;
server-addresses { {{ to | join('; ') }}; };
};
#{% endfor %}
# vim: set ft=named:

3
requirements.txt Normal file
View file

@ -0,0 +1,3 @@
requests
dnspython
jinja2