+++ /dev/null
-#!/usr/bin/python3
-#
-# ssh-based server to allow hosts to update their own TLS-related RRs
-# intended to be used like
-# command="/srv/tls/bin/addrecord hepworth.siccegge.de" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOIlLx3R+Q5LgBZbJ6USuzam/uAEQITl6vzOn/ylk4fq christoph@mitoraj
-
-import os
-import sys
-import re
-import datetime
-import fcntl
-
-import yaml
-
-class Addrecord:
- def __init__(self, host):
- self._host = host
- self._ports = {'www': [443],
- 'smtp': [25, 587, 465],
- 'imap': [993, 143],
- 'pop': [110, 995],
- 'xmpp': [5222, 5269],
- }
- self._tlsa_re = re.compile('TLSA 3 1 1 [0-9a-f]{64}')
- self._acme_re = re.compile(r'[a-zA-Z0-9_-]{43}')
- with open('inventory.yaml') as inv:
- self._inventory = yaml.load(inv)
-
-
- def tlsa(self):
- host = input("Hostname: ")
- service = input("Service: ")
- value = input("Value: ")
- if host not in self._inventory[self._host][service]:
- sys.stderr.write("Not authorized to update entries for service '%s' on '%s'\n"
- % (service, host))
- return 1
-
- if re.fullmatch(self._tlsa_re, value) is None:
- sys.stderr.write("Not a valid TLSA record: '%s'\n" % value)
- return 2
-
- records = []
- for port in self._ports[service]:
- records.append("{0:<35s}\t{1}\n".format("_%d._tcp.%s" % (port, host),
- value))
- self._update_records('tlsa', records)
- print("OK")
- return 0
-
-
- def acme(self):
- host = input("Hostname: ")
- value = input("Value: ")
- allowed_hosts = set()
- for value in self._inventory[self._host].values():
- allowed_hosts = allowed_hosts.union(value)
-
- if host not in allowed_hosts:
- sys.stderr.write("Not authorized to update entries for host '%s'\n" % host)
- return 1
-
- if re.fullmatch(self._acme_re, value) is None:
- sys.stderr.write("Not a valid ACME challenge record: '%s'\n" % value)
- return 2
-
- records = [ "{0:<35s}\t{1}\n".format("_acme-challenge.%s" % host, value) ]
- self._update_records('acme', records)
- print("OK")
- return 0
-
-
- def _update_records(self, sort, records):
- to_remove = [ i.split()[0] for i in records ]
-
- with open('%s/%s.m4' % (sort, self._host), 'r') as oldzone:
- fcntl.flock(oldzone, fcntl.LOCK_EX)
- lines = oldzone.readlines()
- lines = [ line for line in lines if line == '\n' or line.split()[0] not in to_remove ]
-
- lines.append('\n')
- lines.append("; Last updated %s by %s\n" % (datetime.datetime.utcnow().isoformat(),
- self._host))
- lines = lines + records
-
- with open('%s/%s.m4.new' % (sort, self._host), 'w') as newzone:
- newtext = ''.join(lines)
- newtext = re.sub(r'\n[\n]+', '\n\n', newtext)
- newtext = re.sub(r'\n;.*\n\n;', '\n;', newtext)
- newzone.write(newtext)
-
- os.rename('%s/%s.m4.new' % (sort, self._host),
- '%s/%s.m4' % (sort, self._host))
- fcntl.flock(oldzone, fcntl.LOCK_UN)
-
-
-def main():
- command = os.environ['SSH_ORIGINAL_COMMAND']
- host = sys.argv[1]
- addrecord = Addrecord(host)
-
- if command == 'acme':
- return addrecord.acme()
- elif command == 'tlsa':
- return addrecord.tlsa()
-
-
-if __name__ == '__main__':
- sys.exit(main())
+++ /dev/null
-example.com:
- www:
- - www.example.com
- - nginx.example.net
- smtp:
- - mx1.example.com
-
-example.org:
- smtp:
- - mx2.example.com
- xmpp:
- - example.com
- - conference.example.com
- - example.org
\ No newline at end of file
--- /dev/null
+#!/usr/bin/python3
+#
+# ssh-based server to allow hosts to update their own TLS-related RRs
+# intended to be used like
+# command="/srv/tls/bin/addrecord hepworth.siccegge.de" ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOIlLx3R+Q5LgBZbJ6USuzam/uAEQITl6vzOn/ylk4fq christoph@mitoraj
+
+import os
+import sys
+import re
+import datetime
+import fcntl
+
+import yaml
+
+class Addrecord:
+ def __init__(self, host):
+ self._host = host
+ self._ports = {'www': [443],
+ 'smtp': [25, 587, 465],
+ 'imap': [993, 143],
+ 'pop': [110, 995],
+ 'xmpp': [5222, 5269],
+ }
+ self._tlsa_re = re.compile('TLSA 3 1 1 [0-9a-f]{64}')
+ self._acme_re = re.compile(r'[a-zA-Z0-9_-]{43}')
+ with open('inventory.yaml') as inv:
+ self._inventory = yaml.load(inv)
+
+
+ def tlsa(self):
+ host = input("Hostname: ")
+ service = input("Service: ")
+ value = input("Value: ")
+ if host not in self._inventory[self._host][service]:
+ sys.stderr.write("Not authorized to update entries for service '%s' on '%s'\n"
+ % (service, host))
+ return 1
+
+ if re.fullmatch(self._tlsa_re, value) is None:
+ sys.stderr.write("Not a valid TLSA record: '%s'\n" % value)
+ return 2
+
+ records = []
+ for port in self._ports[service]:
+ records.append("{0:<35s}\t{1}\n".format("_%d._tcp.%s" % (port, host),
+ value))
+ self._update_records('tlsa', records)
+ print("OK")
+ return 0
+
+
+ def acme(self):
+ host = input("Hostname: ")
+ value = input("Value: ")
+ allowed_hosts = set()
+ for value in self._inventory[self._host].values():
+ allowed_hosts = allowed_hosts.union(value)
+
+ if host not in allowed_hosts:
+ sys.stderr.write("Not authorized to update entries for host '%s'\n" % host)
+ return 1
+
+ if re.fullmatch(self._acme_re, value) is None:
+ sys.stderr.write("Not a valid ACME challenge record: '%s'\n" % value)
+ return 2
+
+ records = [ "{0:<35s}\t{1}\n".format("_acme-challenge.%s" % host, value) ]
+ self._update_records('acme', records)
+ print("OK")
+ return 0
+
+
+ def _update_records(self, sort, records):
+ to_remove = [ i.split()[0] for i in records ]
+
+ with open('%s/%s.m4' % (sort, self._host), 'r') as oldzone:
+ fcntl.flock(oldzone, fcntl.LOCK_EX)
+ lines = oldzone.readlines()
+ lines = [ line for line in lines if line == '\n' or line.split()[0] not in to_remove ]
+
+ lines.append('\n')
+ lines.append("; Last updated %s by %s\n" % (datetime.datetime.utcnow().isoformat(),
+ self._host))
+ lines = lines + records
+
+ with open('%s/%s.m4.new' % (sort, self._host), 'w') as newzone:
+ newtext = ''.join(lines)
+ newtext = re.sub(r'\n[\n]+', '\n\n', newtext)
+ newtext = re.sub(r'\n;.*\n\n;', '\n;', newtext)
+ newzone.write(newtext)
+
+ os.rename('%s/%s.m4.new' % (sort, self._host),
+ '%s/%s.m4' % (sort, self._host))
+ fcntl.flock(oldzone, fcntl.LOCK_UN)
+
+
+def main():
+ command = os.environ['SSH_ORIGINAL_COMMAND']
+ host = sys.argv[1]
+ addrecord = Addrecord(host)
+
+ if command == 'acme':
+ return addrecord.acme()
+ elif command == 'tlsa':
+ return addrecord.tlsa()
+
+
+if __name__ == '__main__':
+ sys.exit(main())
--- /dev/null
+example.com:
+ www:
+ - www.example.com
+ - nginx.example.net
+ smtp:
+ - mx1.example.com
+
+example.org:
+ smtp:
+ - mx2.example.com
+ xmpp:
+ - example.com
+ - conference.example.com
+ - example.org
\ No newline at end of file