2013-06-16 01:06:25 +02:00
|
|
|
#!/usr/bin/python
|
|
|
|
|
2013-06-17 10:40:53 +02:00
|
|
|
### Configuration ###
|
2013-06-16 01:06:25 +02:00
|
|
|
|
2018-04-09 18:22:31 +02:00
|
|
|
postgresql_dsn = "dbname=dynette user=dynette password=myPassword"
|
2013-06-17 10:08:26 +02:00
|
|
|
conf_file = '/etc/bind/named.conf.local' # Include this filename in '/etc/bind/named.conf'
|
2013-08-07 12:40:49 +02:00
|
|
|
zone_dir = '/var/lib/bind/' # Do not forget the trailing '/'
|
2016-04-26 12:38:04 +02:00
|
|
|
subs_urls = ['https://dyndns.yunohost.org'] # 127.0.0.1 if you install subscribe server locally
|
2016-04-26 10:12:47 +02:00
|
|
|
ns0 = 'ns0.yunohost.org' # Name servers
|
|
|
|
ns1 = 'ns1.yunohost.org'
|
|
|
|
rname = 'hostmaster@yunohost.org' # Responsible person (https://tools.ietf.org/html/rfc1035#section-3.3.13)
|
2013-06-17 10:08:26 +02:00
|
|
|
|
|
|
|
allowed_operations = {
|
2014-05-10 00:58:20 +02:00
|
|
|
'.' : ['A', 'AAAA', 'TXT', 'MX'],
|
2017-07-20 17:04:37 +02:00
|
|
|
'*.' : ['A', 'AAAA'],
|
2017-04-21 02:40:49 +02:00
|
|
|
'pubsub.' : ['A', 'AAAA', 'CNAME'],
|
|
|
|
'muc.' : ['A', 'AAAA', 'CNAME'],
|
|
|
|
'vjud.' : ['A', 'AAAA', 'CNAME'],
|
2013-06-17 10:08:26 +02:00
|
|
|
'_xmpp-client._tcp.' : ['SRV'],
|
2017-04-21 02:40:49 +02:00
|
|
|
'_xmpp-server._tcp.' : ['SRV'],
|
2017-07-20 17:04:37 +02:00
|
|
|
'mail._domainkey.' : ['TXT'],
|
|
|
|
'_dmarc.' : ['TXT']
|
2013-06-17 10:08:26 +02:00
|
|
|
}
|
|
|
|
|
2013-06-16 09:45:02 +02:00
|
|
|
|
2013-06-17 10:40:53 +02:00
|
|
|
### Script ###
|
|
|
|
|
|
|
|
import os
|
|
|
|
import json
|
2018-04-09 18:22:31 +02:00
|
|
|
import psycopg2
|
2013-06-17 10:40:53 +02:00
|
|
|
from urllib import urlopen
|
2016-04-26 11:57:30 +02:00
|
|
|
|
2018-04-09 18:22:31 +02:00
|
|
|
|
2016-04-26 11:57:30 +02:00
|
|
|
# Get master key
|
|
|
|
master_key_path = os.path.join(os.path.dirname(__file__), 'master.key')
|
|
|
|
master_key = open(master_key_path).read().rstrip()
|
|
|
|
|
2016-04-26 12:03:18 +02:00
|
|
|
# Bind configuration
|
2013-06-17 10:40:53 +02:00
|
|
|
lines = ['// Generated by Dynette CRON']
|
2016-04-26 12:03:18 +02:00
|
|
|
|
2018-04-09 18:22:31 +02:00
|
|
|
with psycopg2.connect(postgresql_dsn) as postgresql_connection:
|
|
|
|
with postgresql_connection.cursor() as psql:
|
|
|
|
# Loop through Dynette servers
|
|
|
|
for url in subs_urls:
|
|
|
|
|
2013-06-16 09:45:02 +02:00
|
|
|
lines.extend([
|
2018-04-09 18:22:31 +02:00
|
|
|
'key dynette. {',
|
|
|
|
' algorithm hmac-md5;',
|
|
|
|
' secret "'+ master_key +'";',
|
|
|
|
'};',
|
2013-06-16 09:45:02 +02:00
|
|
|
])
|
|
|
|
|
2018-04-09 18:22:31 +02:00
|
|
|
# Get available DynDNS domains
|
|
|
|
domains = json.loads(str(urlopen(url +'/domains').read()))
|
|
|
|
for domain in domains:
|
|
|
|
|
|
|
|
# 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')
|
|
|
|
|
|
|
|
lines.extend([
|
|
|
|
'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) +';')
|
|
|
|
|
|
|
|
lines.extend([
|
|
|
|
' };',
|
|
|
|
'};'
|
|
|
|
'',
|
|
|
|
])
|
|
|
|
|
|
|
|
for entry in result:
|
|
|
|
lines.extend([
|
|
|
|
'key '+ entry['subdomain'] +'. {',
|
|
|
|
' algorithm ' + entry['key_algo'] + ';',
|
|
|
|
' secret "'+ entry['public_key'] +'";',
|
|
|
|
'};',
|
|
|
|
])
|
|
|
|
|
|
|
|
# look in the job queue if we have tasks to handle
|
|
|
|
need_rewrite = False
|
|
|
|
need_bind9_cache_flush = False
|
|
|
|
|
|
|
|
# DataMapper convert table names to lower cases and add a "s" at the
|
|
|
|
# end
|
|
|
|
# consume all available tasks at once to merge them and avoir doing
|
|
|
|
# useless jobs
|
|
|
|
for task in psql.execute("SELECT task FROM jobqueues ORDER BY id ASC;"):
|
|
|
|
task = task[0]
|
|
|
|
if task == "conf_rewrite":
|
|
|
|
need_rewrite = True
|
|
|
|
elif task == "bind9_cache_flush":
|
|
|
|
need_bind9_cache_flush = True
|
|
|
|
|
|
|
|
# we have consume all the jobs, flush it
|
|
|
|
# because we are in a SQL transaction we won't have situation where a
|
|
|
|
# job could be added just after we read them all
|
|
|
|
psql.execute("DELETE FROM jobqueues;")
|
|
|
|
|
|
|
|
# update bind9 zone
|
|
|
|
if need_rewrite:
|
|
|
|
# Backup old Bind configuration file.
|
|
|
|
os.system('cp '+ conf_file +' '+ conf_file +'.back')
|
|
|
|
|
|
|
|
# Write Bind configuration file.
|
|
|
|
with open(conf_file, 'w') as zone:
|
|
|
|
zone.write('\n'.join(lines) + '\n')
|
|
|
|
|
|
|
|
# Restore ownership
|
|
|
|
os.system('chown -R bind:bind '+ zone_dir +' '+ conf_file)
|
|
|
|
|
|
|
|
# Reload Bind
|
|
|
|
if os.system('/usr/sbin/rndc reload') == 0:
|
|
|
|
exit(0)
|
|
|
|
else:
|
|
|
|
os.system('cp '+ conf_file +' '+ conf_file +'.bad')
|
|
|
|
os.system('cp '+ conf_file +'.back '+ conf_file)
|
|
|
|
os.system('/usr/sbin/rndc reload')
|
|
|
|
print("An error occured ! Please check daemon.log and your conf.bad")
|
|
|
|
exit(1)
|
|
|
|
|
|
|
|
# flush bind9 cache (mostly because we got a hmac-sha512 key migration
|
|
|
|
if need_bind9_cache_flush:
|
|
|
|
os.system('/usr/sbin/rndc flush')
|
|
|
|
os.system('/usr/sbin/rndc reload')
|