From ab8e0423fc01e4f1aa3c3c040b0c4180f1580a9d Mon Sep 17 00:00:00 2001 From: Alexandre Aubin Date: Sun, 28 Mar 2021 22:08:20 +0200 Subject: [PATCH] Refactor the madness --- README.md | 5 +- config.json.j2 | 26 +++++--- dynette.cron.py | 164 +++++++++++++++++++++--------------------------- dynette.rb | 2 +- 4 files changed, 91 insertions(+), 106 deletions(-) diff --git a/README.md b/README.md index 82a1bc7..97e3ba3 100644 --- a/README.md +++ b/README.md @@ -1,15 +1,12 @@ YunoHost DynDNS Server ====================== - -**Note: Tested on Debian wheezy and YunoHost 2.4 (should work on Ubuntu)** - Setup quickly ------------------------------- + You can use the dynette_ynh package for YunoHost https://github.com/YunoHost-Apps/dynette_ynh - Web subscribe server deployment ------------------------------- diff --git a/config.json.j2 b/config.json.j2 index a1dc0f3..fde7eb9 100644 --- a/config.json.j2 +++ b/config.json.j2 @@ -1,9 +1,21 @@ { - "database_url": "postgres://{{ db_user }}:{{ db_password }}@localhost/{{ db_name }}", - "domains" : [ {{ subdomains }} ], - "subs_url" : [ https://{{ dynette_domain }} ], - "ns0" : {{ ns0 }}, - "ns1" : {{ ns1 }}, - "rname" : {{ rname }}, - "master_key" : {{ master_key }} + "database_url" : "postgres://{{ db_user }}:{{ db_password }}@localhost/{{ db_name }}", + "dyndns_domains" : [ {{ subdomains }} ], + "base_url" : "https://{{ dynette_domain }}", + "ns0" : "{{ ns0 }}", + "ns1" : "{{ ns1 }}", + "rname" : "{{ rname }}", + "master_key" : "{{ master_key }}", + "allowed_operations": { + "." : ["A", "AAAA", "TXT", "MX", "CAA"], + "*." : ["A", "AAAA"], + "pubsub." : ["A", "AAAA", "CNAME"], + "muc." : ["A", "AAAA", "CNAME"], + "vjud." : ["A", "AAAA", "CNAME"], + "xmpp-upload." : ["A", "AAAA", "CNAME"], + "_xmpp-client._tcp." : ["SRV"], + "_xmpp-server._tcp." : ["SRV"], + "mail._domainkey." : ["TXT"], + "_dmarc." : ["TXT"] + } } diff --git a/dynette.cron.py b/dynette.cron.py index 380ad9d..0028aca 100755 --- a/dynette.cron.py +++ b/dynette.cron.py @@ -1,48 +1,26 @@ -#!/usr/bin/python - -### Configuration ### - -import json - -with open('config.json') as config_file: - config = json.load(config_file) - -postgresql_dsn = "dbname=dynette user=dynette password=myPassword" -conf_file = '/etc/bind/named.conf.local' # Include this filename in '/etc/bind/named.conf' -zone_dir = '/var/lib/bind/' # Do not forget the trailing '/' -subs_urls = config_file["subs_urls"] # 127.0.0.1 if you install subscribe server locally -ns0 = config_file["ns0"] # Name servers -ns1 = config_file["ns1"] -rname = config_file["rname"] # Responsible person (https://tools.ietf.org/html/rfc1035#section-3.3.13) -master_key = config_file["master_key"] - -allowed_operations = { - '.' : ['A', 'AAAA', 'TXT', 'MX', 'CAA'], - '*.' : ['A', 'AAAA'], - 'pubsub.' : ['A', 'AAAA', 'CNAME'], - 'muc.' : ['A', 'AAAA', 'CNAME'], - 'vjud.' : ['A', 'AAAA', 'CNAME'], - 'xmpp-upload.' : ['A', 'AAAA', 'CNAME'], - '_xmpp-client._tcp.' : ['SRV'], - '_xmpp-server._tcp.' : ['SRV'], - 'mail._domainkey.' : ['TXT'], - '_dmarc.' : ['TXT'] -} - - -### Script ### +#!/usr/bin/python3 import os import sys import psycopg2 from urllib import urlopen +import json +with open('config.json') as config_file: + config = json.load(config_file) +conf_file = '/etc/bind/named.conf.local' # Include this filename in '/etc/bind/named.conf' +zone_dir = '/var/lib/bind/' # Do not forget the trailing '/' +database = config_file["dabase_url"] +base_url = config_file["base_url"] +ns0 = config_file["ns0"] +ns1 = config_file["ns1"] +rname = config_file["rname"] # Responsible person (https://tools.ietf.org/html/rfc1035#section-3.3.13) +master_key = config_file["master_key"] +dyndns_domains = config_file['dyndns_domains'] +allowed_operations = config_files["allowed_operations"] -# Bind configuration -lines = ['// Generated by Dynette CRON'] - -with psycopg2.connect(postgresql_dsn) as postgresql_connection: +with psycopg2.connect(database) as postgresql_connection: with postgresql_connection.cursor() as psql: # look in the job queue if we have tasks to handle need_rewrite = False @@ -69,70 +47,68 @@ with psycopg2.connect(postgresql_dsn) as postgresql_connection: # job could be added just after we read them all psql.execute("DELETE FROM jobqueues;") - # Loop through Dynette servers - for url in subs_urls: + # Init zone for dyndns domains that don't already exists + for domain in dyndns_domains: + if os.path.exists(zone_dir + domain +'.db'): + continue - lines.extend([ - 'key dynette. {', - ' algorithm hmac-md5;', - ' secret "'+ master_key +'";', - '};', - ]) + db_lines = f""" +$ORIGIN . +$TTL 10 ; 10 seconds +{domain}. IN SOA {ns0}. {rname}. ( + 18 ; serial + 10800 ; refresh (3 hours) + 3600 ; retry (1 hour) + 604800 ; expire (1 week) + 10 ; minimum (10 seconds) - # Get available DynDNS domains - domains = json.loads(str(urlopen(url +'/domains').read())) - for domain in domains: +$TTL 3600 ; 1 hour + NS {ns0}. + NS {ns1}. - # Create zone database if not present - if not os.path.exists(zone_dir + domain +'.db'): - db_lines = [ - '$ORIGIN .', - '$TTL 10 ; 10 seconds', - domain+'. IN SOA '+ ns0 +'. '+ rname +'. (', - ' 18 ; serial', - ' 10800 ; refresh (3 hours)', - ' 3600 ; retry (1 hour)', - ' 604800 ; expire (1 week)', - ' 10 ; minimum (10 seconds)', - ' )', - '$TTL 3600 ; 1 hour', - ' NS '+ ns0 +'.', - ' NS '+ ns1 +'.', - '', - '$ORIGIN '+ domain +'.', - ] - with open(zone_dir + domain +'.db', 'w') as zone: - for line in db_lines: - zone.write(line + '\n') +$ORIGIN {domain}. +""" + with open(zone_dir + domain +'.db', 'w') as zone: + zone.write(db_lines) - lines.extend([ - 'zone "'+ domain +'" {', - ' type master;', - ' file "'+ zone_dir + domain +'.db"; ', - ' update-policy {', - ' grant dynette. wildcard *.'+ domain +'. ANY;', - ]) + # Update /etc/bind/named.conf.local + # that provide permissions for all regirested subdomains on allowed zone file elements + # and registers the key for bind9, later used to authenticate nsupdate from the clients + lines = ['// Generated by Dynette CRON'] + lines.append(f""" +key dynette. { + algorithm hmac-md5; + secret "{master_key}"; +}; +""") + for domain in dyndns_domains: + lines.append(f""" +zone "{domain}" { + type master; + file {zone_dir}{domain}.db; + update-policy { + grant dynette. wildcard *.{domain}. ANY;""") - # Get registered sub-domains - result = json.loads(str(urlopen(url +'/all/'+ domain).read())) - for entry in result: - for subd, type in allowed_operations.items(): - if subd == '.': subd = '' - lines.append(' grant '+ entry['subdomain'] +'. name '+ subd + entry['subdomain'] +'. ' + ' '.join(type) +';') + # Get registered sub-domains + registered_subdomains = json.loads(str(urlopen(base_url +'/all/'+ domain).read())) + for entry in registered_subdomains: + for subd, types in allowed_operations.items(): + if subd == '.': + subd = '' + all_types = ' '.join(types) + lines.append(f" grant {entry['subdomain']}. name {subd}{entry['subdomain']}. {all_types};") - lines.extend([ - ' };', - '};' - '', - ]) + lines.append(f""" + }; +};""") - for entry in result: - lines.extend([ - 'key '+ entry['subdomain'] +'. {', - ' algorithm ' + entry['key_algo'] + ';', - ' secret "'+ entry['public_key'] +'";', - '};', - ]) + for entry in registered_subdomains: + lines.append(f""" +key {entry['subdomain']}. { + algorithm {entry['key_algo']}; + secret {entry['public_key']}; +}; +""" # update bind9 zone if need_rewrite: diff --git a/dynette.rb b/dynette.rb index 49a88f9..ca4ecb6 100755 --- a/dynette.rb +++ b/dynette.rb @@ -21,7 +21,7 @@ rescue => err end DataMapper.setup(:default, ENV['DATABASE_URL'] || config['database_url']) -DOMAINS = config['domains'] +DOMAINS = config['dyndns_domains'] ALLOWED_IP = ["127.0.0.1"] ###############