ssl-update/update_cert.py
2022-07-02 14:24:53 -04:00

133 lines
4.1 KiB
Python
Executable File

#!/usr/bin/env python3
"""Automation for cert renewal.
assumptions:
* router is danknasty
* generate key in /root/.ssh/id_autofirewall on server
* firewall has config from `authorized_keys` file
* firewall sshd config contains: `AcceptEnv ssl_service state`
* firewall has `ssl-update.sh` copied to /usr/local/bin and chmod +x
"""
import getpass
import os
import pathlib
import pexpect
import subprocess
import sys
services = ['git', 'plex', 'jellyfin', 'photoprism']
def rmdir(directory):
directory = pathlib.Path(directory)
for item in directory.iterdir():
if item.is_dir():
rmdir(item)
else:
item.unlink()
directory.rmdir()
def main(args):
# Get SSH decryption password
cmd = ['/usr/bin/su', '-l', 'luke', '/usr/bin/pass', 'show', 'ssh/autofirewall']
p = subprocess.run(cmd, capture_output=True)
decrypt_pp = p.stdout.decode('UTF-8').strip()
if not decrypt_pp:
sys.exit('Couldnt get decryption passpharase')
# Start Certbot, get data to send to service
challenge_path = pathlib.Path('/usr/share/nginx/html/'
'.well-known/acme-challenge/')
if challenge_path.is_dir():
rmdir(challenge_path)
challenge_path.mkdir()
challenge_path.chmod(0o755)
if len(args) != 1:
sys.exit(f'Give a service to renew: {", ".join(services)} ')
service = args[0]
if service not in services:
sys.exit(f'Give a service to renew: {", ".join(services)} ')
fqdn = f'{service}.drheck.dev'
cmd = ['/usr/bin/certbot', 'certonly', '--manual', '-d', fqdn]
p = pexpect.spawnu(' '.join(cmd))
# p.logfile = sys.stderr
res = p.expect(['Create a file containing just this data:\r\n\r\n([^\r]+)\r',
pexpect.TIMEOUT, pexpect.EOF], timeout=20)
if res > 0:
sys.exit('Timed out')
data = p.match.group(1)
long_match = ('And make it available on your web server at this URL:'
'\r\n\r\nhttp://%s/.well-known/acme-challenge/([^\r]+)\r')
res = p.expect([long_match % (fqdn,), pexpect.TIMEOUT, pexpect.EOF])
if res > 0:
sys.exit('Timed out')
filename = p.match.group(1)
res = p.expect(['Press Enter to Continue', pexpect.EOF], timeout=0)
# Certbot is paused. Got the data
# put data in acme-challenge file
data_file = challenge_path / pathlib.Path(filename)
try:
with open(data_file, 'w') as f:
f.write(data)
except:
sys.exit(f'Failed to write {data_file}')
data_file.chmod(0o644)
# put symlink in nginx enabled sites
symlink_name = pathlib.Path(f'{service}-le')
nx_conf = pathlib.Path('/etc/nginx')
avail_path = nx_conf / pathlib.Path('sites-available')
enabled_path = nx_conf / pathlib.Path('sites-enabled')
service_available_file = avail_path / symlink_name
service_enabled_symlink = enabled_path / symlink_name
if not service_enabled_symlink.is_symlink():
service_enabled_symlink.symlink_to(service_available_file)
# open port 80 to ${service}.drheck.dev
os.environ['state'] = 'HTTP_DOWN'
os.environ['ssl_service'] = service
cmd = ['/usr/bin/ssh', '-i', '/root/.ssh/id_autofirewall', '-o',
'SendEnv=state', '-o', 'SendEnv=ssl_service', '-l', 'luke131',
'danknasty', 'doas', '-n', '/usr/local/bin/ssl-update.sh']
print(f'cmd: {cmd}')
p = pexpect.spawnu(' '.join(cmd))
p.logfile = sys.stderr
res = p.expect(["""Enter passphrase for key '/root/.ssh/id_autofirewall':""", pexpect.TIMEOUT, pexpect.EOF])
if res > 0:
sys.exit('Couldnt send decryption key to ssh.')
p.sendline(decrypt_pp)
res = p.expect(['success', pexpect.TIMEOUT, pexpect.EOF])
if res > 0:
sys.exit(f'Failed. error: {p.before}')
print(f'Turned up HTTP for {service}')
# restart nginx
# continue with certbot
# close port 80 to git.drheck.dev
# remove symlink in nginx enabled sites
# restart nginx
# chmod
# /etc/letsencrypt/live/git.drheck.dev
# /etc/letsencrypt/archive/git.drheck.dev
# restart gitea
# p.expect(r'up\s+(.*?),\s+([0-9]+) users?,\s+load averages?: ([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9]),?\s+([0-9]+\.[0-9][0-9])')
# duration, users, av1, av5, av15 = p.match.groups()
if __name__ == '__main__':
main(sys.argv[1:])