Compare commits
8 Commits
c800af9bca
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3b70a47502 | ||
|
|
5674bf339c | ||
|
|
37f946305d | ||
|
|
d39078a78d | ||
|
|
f6b4e66df4 | ||
|
|
238bd37d40 | ||
|
|
b7cf8f185a | ||
|
|
96f5766349 |
@@ -1,12 +1,15 @@
|
|||||||
# syntax=docker/dockerfile:1
|
# syntax=docker/dockerfile:1
|
||||||
|
|
||||||
FROM python:3.9-alpine
|
FROM python:3.9-slim
|
||||||
|
|
||||||
WORKDIR /app
|
WORKDIR /app
|
||||||
|
|
||||||
COPY requirements.txt requirements.txt
|
COPY requirements.txt requirements.txt
|
||||||
RUN pip3 install -r requirements.txt
|
RUN pip3 install -r requirements.txt
|
||||||
|
|
||||||
|
ADD https://github.com/StackExchange/dnscontrol/releases/latest/download/dnscontrol-Linux /usr/local/bin/dnscontrol
|
||||||
|
RUN chmod +x /usr/local/bin/dnscontrol
|
||||||
|
|
||||||
COPY . .
|
COPY . .
|
||||||
|
|
||||||
CMD [ "python3", "daemon.py" ]
|
CMD [ "python3", "daemon.py" ]
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
# Dnscontrol-Sync
|
# Dnscontrol-Sync
|
||||||
|
|
||||||
This images is designed to synchronise changes between Nameserver-Providers (like PowerDNS or Cloudflare-DNS).
|
This image is designed to synchronise changes between Nameserver-Providers (like PowerDNS or Cloudflare-DNS).
|
||||||
|
|
||||||
It uses [dnscontrol](https://github.com/StackExchange/dnscontrol) to pull & push these changes
|
It uses [dnscontrol](https://github.com/StackExchange/dnscontrol) to pull & push these changes
|
||||||
and DNS-NOTIFY for event-driven updates.
|
and DNS-NOTIFY for event-driven updates.
|
||||||
|
|||||||
44
daemon.py
44
daemon.py
@@ -4,6 +4,10 @@ import shutil, os, re, _thread, socket, sys, re, yaml, logging as log
|
|||||||
|
|
||||||
import dnslib as dns
|
import dnslib as dns
|
||||||
|
|
||||||
|
DATA_PATH="/data"
|
||||||
|
APP_PATH="/app"
|
||||||
|
HOSTS_PATH="/app/hosts"
|
||||||
|
|
||||||
config = None
|
config = None
|
||||||
def main(args):
|
def main(args):
|
||||||
setupLogging(True)
|
setupLogging(True)
|
||||||
@@ -11,23 +15,26 @@ def main(args):
|
|||||||
setupEnvironment()
|
setupEnvironment()
|
||||||
|
|
||||||
global config
|
global config
|
||||||
config = readConfig("/data/config.yml")
|
config = readConfig(os.path.join(DATA_PATH, "config.yml"))
|
||||||
|
|
||||||
s = setupSocket(config['socket']['address'], config['socket']['port'])
|
s = setupSocket(config['socket']['address'], config['socket']['port'])
|
||||||
|
|
||||||
startListen(s)
|
startListen(s)
|
||||||
|
|
||||||
def setupEnvironment():
|
def setupEnvironment():
|
||||||
if not os.path.exists('/data'):
|
if not os.path.exists(DATA_PATH):
|
||||||
os.mkdir("/data")
|
os.mkdir(DATA_PATH)
|
||||||
|
|
||||||
(all, some) = copyAllFiles("data/", "/data")
|
(all, some) = copyAllFiles(f"{APP_PATH}/data/", DATA_PATH)
|
||||||
if all:
|
if all:
|
||||||
log.warning("Configuration-files were created!")
|
log.warning("Configuration-files were created!")
|
||||||
log.warning(" Make sure to change them according to your setup")
|
log.warning(" Make sure to change them according to your setup")
|
||||||
elif some:
|
elif some:
|
||||||
log.warning("Some configuration-files were recreated, because they were missing")
|
log.warning("Some configuration-files were recreated, because they were missing")
|
||||||
|
|
||||||
|
if not os.path.exists(HOSTS_PATH):
|
||||||
|
os.mkdir(HOSTS_PATH)
|
||||||
|
|
||||||
def copyAllFiles(src, dst, overwrite=False):
|
def copyAllFiles(src, dst, overwrite=False):
|
||||||
all=True
|
all=True
|
||||||
some=False
|
some=False
|
||||||
@@ -102,13 +109,15 @@ def handleQuery(s, address, dmsg):
|
|||||||
|
|
||||||
log.info(f'{address[0]} |\tNOTIFY for {name}')
|
log.info(f'{address[0]} |\tNOTIFY for {name}')
|
||||||
|
|
||||||
_thread.start_new_thread(updateNsData, (name,))
|
|
||||||
|
|
||||||
response = dmsg.reply() # type: dns.message.Message
|
response = dmsg.reply() # type: dns.message.Message
|
||||||
response.header.aa = 1
|
response.header.aa = 1
|
||||||
sendResponse(s, address, response)
|
sendResponse(s, address, response)
|
||||||
log.debug(f'{address[0]} |\tSent response')
|
log.debug(f'{address[0]} |\tSent response')
|
||||||
|
|
||||||
|
# FIXME: see: data/dnsconfig.js require_glob
|
||||||
|
#_thread.start_new_thread(updateNsData, (name,))
|
||||||
|
updateNsData(name)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
def makeResponseWithRCode(socket, address, dmsg, rcode):
|
def makeResponseWithRCode(socket, address, dmsg, rcode):
|
||||||
@@ -149,14 +158,17 @@ def updateNsData(zone):
|
|||||||
|
|
||||||
log.info(f'{adaptedZone} |\tUpdating NS-Data')
|
log.info(f'{adaptedZone} |\tUpdating NS-Data')
|
||||||
|
|
||||||
dumpFile = f"{adaptedZone}.dump.js"
|
# FIXME: see: data/dnsconfig.js require_glob
|
||||||
|
#dumpFile = f"{adaptedZone}.dump.js"
|
||||||
|
dumpFile = "dump.js"
|
||||||
|
|
||||||
if dumpZoneData(zone, dumpFile) != 0:
|
if dumpZoneData(zone, dumpFile) != 0:
|
||||||
raise Exception("Dumping data failed!")
|
raise Exception("Dumping data failed!")
|
||||||
zone = adaptedZone
|
zone = adaptedZone
|
||||||
|
|
||||||
hasToDelete = True
|
hasToDelete = True
|
||||||
|
|
||||||
adaptFileForRequire(zone, dumpFile)
|
adaptFileForRequire(zone, HOSTS_PATH, dumpFile)
|
||||||
if dnscontrolPush(zone) != 0:
|
if dnscontrolPush(zone) != 0:
|
||||||
raise Exception("Pushing data failed!")
|
raise Exception("Pushing data failed!")
|
||||||
|
|
||||||
@@ -166,7 +178,7 @@ def updateNsData(zone):
|
|||||||
log.warning(f'{adaptedZone} |\tUpdating NS-Data failed!')
|
log.warning(f'{adaptedZone} |\tUpdating NS-Data failed!')
|
||||||
|
|
||||||
if(hasToDelete):
|
if(hasToDelete):
|
||||||
deleteFile(dumpFile)
|
deleteFile(os.path.join(HOSTS_PATH, dumpFile))
|
||||||
|
|
||||||
def adaptZoneName(zone):
|
def adaptZoneName(zone):
|
||||||
if config['zone']['public-suffix'] != "" and zone.endswith(config['zone']['public-suffix']):
|
if config['zone']['public-suffix'] != "" and zone.endswith(config['zone']['public-suffix']):
|
||||||
@@ -176,28 +188,30 @@ def adaptZoneName(zone):
|
|||||||
|
|
||||||
def dumpZoneData(zone, dumpFile):
|
def dumpZoneData(zone, dumpFile):
|
||||||
log.debug(f"{zone} |\tDumping to '{dumpFile}'..")
|
log.debug(f"{zone} |\tDumping to '{dumpFile}'..")
|
||||||
return os.system(f"dnscontrol get-zones --creds /data/creds.json --format=js --out={dumpFile} powerdns POWERDNS {zone}")
|
return os.system(f"dnscontrol get-zones --creds {DATA_PATH}/creds.json --format=js --out={HOSTS_PATH}/{dumpFile} powerdns POWERDNS {zone}")
|
||||||
|
|
||||||
def deleteFile(file):
|
def deleteFile(file):
|
||||||
log.debug(f"Deleting file '{file}'")
|
log.debug(f"Deleting file '{file}'")
|
||||||
os.remove(file)
|
os.remove(file)
|
||||||
|
|
||||||
ignoreLinesRexp = r"^\s*(var|D\(|DnsProvider\(|DefaultTTL\()"
|
ignoreLinesRexp = r"^\s*(var|D\(|DnsProvider\(|DefaultTTL\()"
|
||||||
def adaptFileForRequire(zone, dumpFile):
|
def adaptFileForRequire(zone, path, dumpFile):
|
||||||
log.debug(f"{zone} |\tRewriting file '{dumpFile}'..")
|
log.debug(f"{zone} |\tRewriting file '{dumpFile}'..")
|
||||||
|
|
||||||
with open(dumpFile, 'r') as fin:
|
dumpFilePath = os.path.join(path, dumpFile)
|
||||||
with open(f"{dumpFile}.tmp", 'w+') as fout:
|
|
||||||
|
with open(dumpFilePath, 'r') as fin:
|
||||||
|
with open(f"{dumpFilePath}.tmp", 'w+') as fout:
|
||||||
fout.write(f'D_EXTEND("{zone}",\n')
|
fout.write(f'D_EXTEND("{zone}",\n')
|
||||||
|
|
||||||
for line in fin:
|
for line in fin:
|
||||||
if not re.match(ignoreLinesRexp, line):
|
if not re.match(ignoreLinesRexp, line):
|
||||||
fout.write(line)
|
fout.write(line)
|
||||||
os.replace(f"{dumpFile}.tmp", dumpFile)
|
os.replace(f"{dumpFilePath}.tmp", dumpFilePath)
|
||||||
|
|
||||||
def dnscontrolPush(zone):
|
def dnscontrolPush(zone):
|
||||||
log.debug(f'{zone} |\tPushing..')
|
log.debug(f'{zone} |\tPushing..')
|
||||||
return os.system(f"dnscontrol push --config /data/dnsconfig.js --creds /data/creds.json --domains {zone}")
|
return os.system(f"dnscontrol push --config {DATA_PATH}/dnsconfig.js --creds {DATA_PATH}/creds.json --domains {zone}")
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
sys.exit(main(sys.argv))
|
sys.exit(main(sys.argv))
|
||||||
|
|||||||
@@ -20,4 +20,8 @@ D('example.com', REG_NONE, DnsProvider(cloudflare));
|
|||||||
|
|
||||||
// Include
|
// Include
|
||||||
// (Do not touch unless you know what you are doing!)
|
// (Do not touch unless you know what you are doing!)
|
||||||
require_glob(""); // TODO: determine correct location to include
|
|
||||||
|
// FIXME: As require_glob does not work, we have to find another way to allow multiple files being loaded into dnscontrol
|
||||||
|
// Maybe load 1 seperate file which can be expanded (being careful about thread-safety)
|
||||||
|
//require_glob("/app/hosts/");
|
||||||
|
require("/app/hosts/dump.js");
|
||||||
|
|||||||
Reference in New Issue
Block a user