Created
April 1, 2021 01:02
-
-
Save nijave/39353bcf4d7fec7bc27a1ad596b16344 to your computer and use it in GitHub Desktop.
A simple opendkim key rotator
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #!/usr/bin/env python3 | |
| # Note: many os-related methods in python3.5 don't accept | |
| # pathlib.Path and require manual conversion to string, first | |
| import os | |
| import random | |
| import re | |
| import shutil | |
| import subprocess | |
| from pathlib import Path | |
| KEY_SIZE = 2048 | |
| opendkim_user_group = ("opendkim", "opendkim") | |
| opendkim_dir = Path("/etc/opendkim") | |
| kt = open(str(opendkim_dir / "KeyTable"), "r").read().strip() | |
| st = open(str(opendkim_dir / "SigningTable"), "r").read().strip() | |
| key_table = [ | |
| dict(zip(("domain_record", "domain", "selector", "key_path"), key.split()[0:1] + key.split()[1].split(":"))) | |
| for key in kt.splitlines() | |
| ] | |
| sorted(key_table, key=lambda x: x["domain"]) | |
| new_table = [] | |
| for key in key_table: | |
| selector = "k" + hex(random.getrandbits(3*8))[2:] | |
| key_path = opendkim_dir / "keys" / key["domain"] | |
| if not key_path.is_dir(): | |
| key_path.mkdir() | |
| output = subprocess.check_output( | |
| ["opendkim-genkey", "-n", str(KEY_SIZE), "-s", selector, "-d", key["domain"]], | |
| cwd=str(key_path), | |
| ).decode() | |
| if output: | |
| print(output) | |
| shutil.chown(str(key_path / ("%s.txt" % selector)), *opendkim_user_group) | |
| os.chmod(str(key_path / ("%s.txt" % selector)), 0o644) | |
| shutil.chown(str(key_path / ("%s.private" % selector)), *opendkim_user_group) | |
| os.chmod(str(key_path / ("%s.private" % selector)), 0o600) | |
| new_table.append( | |
| {**key, **{ | |
| "selector": selector, | |
| "domain_record": "%s._domainkey.%s" % (selector, key["domain"]), | |
| "key_path": key_path / ("%s.private" % selector), | |
| }} | |
| ) | |
| print("New KeyTable:") | |
| kt_data = "\n".join([ | |
| "%s %s:%s:%s" % (key["domain_record"], key["domain"], key["selector"], key["key_path"]) | |
| for key in new_table | |
| ]) | |
| print(kt_data) | |
| print() | |
| with open(str(opendkim_dir / "KeyTable"), "w") as kt_file: | |
| kt_file.write(kt_data) | |
| print("New SigningTable") | |
| st_data = "\n".join([ | |
| "*@%s %s" % (key["domain"], key["domain_record"]) | |
| for key in new_table | |
| ]) | |
| print(st_data) | |
| print() | |
| with open(str(opendkim_dir / "SigningTable"), "w") as st_file: | |
| st_file.write(st_data) | |
| print("DNS Records:") | |
| for key in new_table: | |
| """ | |
| This is designed to print out DNS records in a human copy/pastable way. | |
| This might not work for other use cases where a machine should consume | |
| the records. | |
| """ | |
| fn = str( | |
| re.sub(r'\.private$', '.txt', str(key["key_path"])) | |
| ) | |
| pk = open(fn, "r").read().strip() | |
| # Strip comments | |
| pk = re.sub(r'\s*; .+$', '', pk) | |
| # Remove extra quotes around record | |
| pk = re.sub(r'(?<!\\)"', '', pk) | |
| # Compact space | |
| pk = re.sub(r'[\s]+', ' ', pk) | |
| # Un-escape quotes | |
| pk = pk.replace(r'\"', '"') | |
| pk = pk.strip() | |
| pk = pk.replace("( ", '`').replace(" )", '`') | |
| print(key["domain_record"], pk.split(" ", 1)[1]) | |
| print() | |
| print("Restart opendkim after adding new DNS records") | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment