mirror of
https://github.com/luzifer-docker/personal-dns.git
synced 2024-12-20 12:41:18 +00:00
Initial Version
This commit is contained in:
commit
8d7e928ddb
10 changed files with 240 additions and 0 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
|||
blacklist
|
||||
named.stubs
|
10
Corefile
Normal file
10
Corefile
Normal file
|
@ -0,0 +1,10 @@
|
|||
. {
|
||||
hosts /etc/bind/blacklist {
|
||||
fallthrough
|
||||
}
|
||||
|
||||
forward . 127.0.0.1:1053
|
||||
|
||||
errors
|
||||
log
|
||||
}
|
42
Dockerfile
Normal file
42
Dockerfile
Normal 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
6
Makefile
Normal 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
22
build.sh
Executable 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
124
build_stubs.py
Executable 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
8
docker-entrypoint.sh
Executable 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
15
named.conf
Normal 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
8
named.stubs.j2
Normal 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
3
requirements.txt
Normal file
|
@ -0,0 +1,3 @@
|
|||
requests
|
||||
dnspython
|
||||
jinja2
|
Loading…
Reference in a new issue