diff --git a/README.md b/README.md index 5b5a2b8..f6b0b3f 100644 --- a/README.md +++ b/README.md @@ -5,21 +5,20 @@ automation for cert renewal with local hooks given a service: * start letsencrypt's certbot "manually", getting ownership proof data - * write proof into nginx's serving path - * log into the firewall, allow http for the given service - * enable http for the given service + * turn up a custom nginx site for the proof + * log into the firewall, allow http to the given service + * enable http for the given service in nginx * instruct let's encrypt to check the proof - * get new keys + * new expiration date on certs * disable http for the service * log into firewall, block http for the given service - * set permissions and ownership on new keys * perform service specific hooks * jellyfin/plex: generate a pkcs12 key and put it in the right place + * set permissions and ownership on new keys All secrets are GPG encrypted and one password prompt allows for script access to all secrets necessary. State: - * running for all services, no known bugs at this time diff --git a/update_cert.py b/ssl-update.py similarity index 92% rename from update_cert.py rename to ssl-update.py index b929438..293eb6a 100755 --- a/update_cert.py +++ b/ssl-update.py @@ -18,7 +18,8 @@ import subprocess import sys import time -supported_services = ['git', 'plex', 'jellyfin', 'photoprism', 'nextcloud'] +supported_services = [ + 'git', 'plex', 'jellyfin', 'photoprism', 'nextcloud', 'read'] restart_delay = { 'plex': 10 @@ -29,22 +30,22 @@ pfx_key_path = { 'jellyfin': '/etc/letsencrypt/live/jellyfin.drheck.dev/jellyfin.pfx', } +# Cert owning user if different than the name of the service users = { - 'jellyfin': 'jellyfin', 'git': 'gitea', - 'plex': 'plex', - 'photoprism': 'photoprism', - 'nextcloud': 'nextcloud', + 'read': 'http', } +# systemd service names that don't match the service name +# service : systemd_service systemd_services = { 'git': 'gitea', 'plex': 'plexmediaserver', + 'read': 'kavita', } cert_files = ['privkey1.pem', 'fullchain1.pem', 'chain1.pem', 'cert1.pem'] - router = 'danknasty' router_user = 'luke131' router_key = '/root/.ssh/id_autofirewall' @@ -61,7 +62,9 @@ def firewall_mod(state, service, decrypt_pp): log.info(f'env for fw: ssl_service: {service}') log.info(f'cmd to connect to firewall: "{" ".join(cmd)}"') p = pexpect.spawnu(' '.join(cmd)) - res = p.expect([f'Enter passphrase for key "{router_key}":', + p.logfile = sys.stderr + log.info(f'key string: {router_key}') + res = p.expect([f'''Enter passphrase for key ['"]{router_key}['"]:''', pexpect.TIMEOUT, pexpect.EOF]) if res > 0: sys.exit('Couldnt send decryption key to ssh.') @@ -149,8 +152,8 @@ def restart(service): cmd = ['/usr/bin/systemctl', 'status', systemd_service] log.info(f'cmd to show status of service: "{" ".join(cmd)}"') p = subprocess.run(cmd, capture_output=True) - stderr = p.stdout.decode('UTF-8') - sys.stderr.write(p.stdout) + stderr = p.stderr.decode('UTF-8') + sys.stderr.write(stderr) sys.exit(1) log.info(f'{service} has restarted OK') @@ -175,6 +178,7 @@ def pfx_gen(service): '-certfile', f'/etc/letsencrypt/live/{service}.drheck.dev/chain.pem'] log.info(f'cmd to encrypt private key: "{" ".join(cmd)}"') p = pexpect.spawnu(' '.join(cmd)) + p.logfile = sys.stderr res = p.expect(['Enter Export Password:', pexpect.EOF, pexpect.TIMEOUT]) if res > 0: sys.exit('Failed to run openssl to generate ' @@ -196,7 +200,8 @@ def pfx_gen(service): def main(args): - logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING")) + # logging.basicConfig(level=os.environ.get("LOGLEVEL", "WARNING")) + logging.basicConfig(level=os.environ.get("LOGLEVEL", "INFO")) log.info(f'program start: {sys.argv}') if len(args) != 1: @@ -232,9 +237,9 @@ def main(args): fqdn = f'{service}.drheck.dev' log.info(f'fqdn: {fqdn}') # Dry run: - cmd = ['/usr/bin/certbot', '--dry-run', 'certonly', '--manual', '-d', fqdn] + # cmd = ['/usr/bin/certbot', '--dry-run', 'certonly', '--manual', '-d', fqdn] # Real run: - # cmd = ['/usr/bin/certbot', 'certonly', '--manual', '-d', fqdn] + cmd = ['/usr/bin/certbot', 'certonly', '--manual', '-d', fqdn] log.info(f'certbot cmd: "{" ".join(cmd)}"') cb = pexpect.spawnu(' '.join(cmd)) res = cb.expect( @@ -319,12 +324,21 @@ def main(args): log.info(f'live keypath: {live}') log.info(f'archive keypath: {archive}') - user = users[service] + if service in globals(): + log.info(f'{service} has a service specific function to run') + eval(f'{service}()') + log.info(f'{service} specific work complete.') + + user = service + if service in users: + user = users[service] log.info(f'service {service} has user {user}') uid = pwd.getpwnam(user).pw_uid gid = pwd.getpwnam(user).pw_gid - log.info(f'uid: {uid} gid: {gid}') + # chown after custom service in case pfx or other key is generated + + log.info(f'uid: {uid} gid: {gid}') os.chown(live, uid, gid) log.info(f'live keypath chmodded') os.chown(archive, uid, gid) @@ -334,11 +348,6 @@ def main(args): log.info(f'{cert_file} chowned to service user') log.info(f'chmodded new keys from certbot') - if service in globals(): - log.info(f'{service} has a service specific function to run') - eval(f'{service}()') - log.info(f'{service} specific work complete.') - restart(service)