initial commit

This commit is contained in:
LuKe Tidd 2022-07-02 14:24:53 -04:00
parent 4981a04b9d
commit 95fb9386a8
4 changed files with 190 additions and 1 deletions

View File

@ -1,2 +1,3 @@
# ssl-update
# mycertbot
my process for updating certs

1
remote/authorized_keys Normal file
View File

@ -0,0 +1 @@
command="/usr/local/bin/ssl-update.sh",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIORHtLf6y6rF7EQ9UdPhILDUOLybxffwjyHzxsmOk735 autofirewall

55
remote/ssl-update.sh Normal file
View File

@ -0,0 +1,55 @@
#!/bin/ksh
# install to /usr/local/bin on firewall
# OpenBSD pdksh
hostname_file='/etc/myname'
if [ "$(id -u)" -ne 0 ]; then
printf 'Must be run as root.\n' >&2
exit 1
fi
if [ ! -f "$hostname_file" ]; then
printf 'No hostname file. Is this the right server?\n' >&2
exit 1
fi
if [ "$(<"$hostname_file")" != 'danknasty' ]; then
printf 'Only designed to be run on danknasty.\n' >&2
exit 1
fi
fw_config="/etc/pf_${ssl_service}.conf"
if [ ! -f "$fw_config" ]; then
printf 'No firewall config found for %s at %s.\n' "$ssl_service" "$fw_config" >&2
exit 1
fi
printf 'Setting "%s" for "%s".\n' "$state" "$ssl_service"
printf 'Firewall config: "%s"\n' "$fw_config"
if [ "$state" == 'HTTP_UP' ]; then
printf 'Removing comment\n'
sed -i 's/^# pass/pass/' "$fw_config"
if [ $? != 0 ]; then
printf 'Failed to configure %s http port up.\n' "$ssl_service" >&2
exit 1
fi
elif [ "$state" == 'HTTP_DOWN' ]; then
printf 'Adding comment\n'
sed -i 's/^pass/# pass/' "$fw_config"
if [ $? != 0 ]; then
printf 'Failed to configure %s http port down.\n' "$ssl_service" >&2
exit 1
fi
else
printf 'Invalid state: %s.\n' "$state" >&2
exit 1
fi
/usr/local/bin/pfhup
if [ $? != 0 ]; then
printf 'Failed to restart firewall. Check config immediately.\n' >&2
exit 1
fi
printf 'success\n'

132
update_cert.py Executable file
View File

@ -0,0 +1,132 @@
#!/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:])