added 'read' service and bug fixes
* added read service (kavita) * renamed script to match repo bugs: * chown keys after custom site scripts in case of pfx or other generation (this was not getting chowned before and breaking the site)
This commit is contained in:
parent
f0cd9433de
commit
a4e16f4cb1
11
README.md
11
README.md
@ -5,21 +5,20 @@ automation for cert renewal with local hooks
|
|||||||
given a service:
|
given a service:
|
||||||
|
|
||||||
* start letsencrypt's certbot "manually", getting ownership proof data
|
* start letsencrypt's certbot "manually", getting ownership proof data
|
||||||
* write proof into nginx's serving path
|
* turn up a custom nginx site for the proof
|
||||||
* log into the firewall, allow http for the given service
|
* log into the firewall, allow http to the given service
|
||||||
* enable http for the given service
|
* enable http for the given service in nginx
|
||||||
* instruct let's encrypt to check the proof
|
* instruct let's encrypt to check the proof
|
||||||
* get new keys
|
* new expiration date on certs
|
||||||
* disable http for the service
|
* disable http for the service
|
||||||
* log into firewall, block http for the given service
|
* log into firewall, block http for the given service
|
||||||
* set permissions and ownership on new keys
|
|
||||||
* perform service specific hooks
|
* perform service specific hooks
|
||||||
* jellyfin/plex: generate a pkcs12 key and
|
* jellyfin/plex: generate a pkcs12 key and
|
||||||
put it in the right place
|
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
|
All secrets are GPG encrypted and one password prompt allows for script access
|
||||||
to all secrets necessary.
|
to all secrets necessary.
|
||||||
|
|
||||||
State:
|
State:
|
||||||
|
|
||||||
* running for all services, no known bugs at this time
|
* running for all services, no known bugs at this time
|
||||||
|
@ -18,7 +18,8 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
import time
|
import time
|
||||||
|
|
||||||
supported_services = ['git', 'plex', 'jellyfin', 'photoprism', 'nextcloud']
|
supported_services = [
|
||||||
|
'git', 'plex', 'jellyfin', 'photoprism', 'nextcloud', 'read']
|
||||||
|
|
||||||
restart_delay = {
|
restart_delay = {
|
||||||
'plex': 10
|
'plex': 10
|
||||||
@ -29,22 +30,22 @@ pfx_key_path = {
|
|||||||
'jellyfin': '/etc/letsencrypt/live/jellyfin.drheck.dev/jellyfin.pfx',
|
'jellyfin': '/etc/letsencrypt/live/jellyfin.drheck.dev/jellyfin.pfx',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# Cert owning user if different than the name of the service
|
||||||
users = {
|
users = {
|
||||||
'jellyfin': 'jellyfin',
|
|
||||||
'git': 'gitea',
|
'git': 'gitea',
|
||||||
'plex': 'plex',
|
'read': 'http',
|
||||||
'photoprism': 'photoprism',
|
|
||||||
'nextcloud': 'nextcloud',
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# systemd service names that don't match the service name
|
||||||
|
# service : systemd_service
|
||||||
systemd_services = {
|
systemd_services = {
|
||||||
'git': 'gitea',
|
'git': 'gitea',
|
||||||
'plex': 'plexmediaserver',
|
'plex': 'plexmediaserver',
|
||||||
|
'read': 'kavita',
|
||||||
}
|
}
|
||||||
|
|
||||||
cert_files = ['privkey1.pem', 'fullchain1.pem', 'chain1.pem', 'cert1.pem']
|
cert_files = ['privkey1.pem', 'fullchain1.pem', 'chain1.pem', 'cert1.pem']
|
||||||
|
|
||||||
|
|
||||||
router = 'danknasty'
|
router = 'danknasty'
|
||||||
router_user = 'luke131'
|
router_user = 'luke131'
|
||||||
router_key = '/root/.ssh/id_autofirewall'
|
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'env for fw: ssl_service: {service}')
|
||||||
log.info(f'cmd to connect to firewall: "{" ".join(cmd)}"')
|
log.info(f'cmd to connect to firewall: "{" ".join(cmd)}"')
|
||||||
p = pexpect.spawnu(' '.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])
|
pexpect.TIMEOUT, pexpect.EOF])
|
||||||
if res > 0:
|
if res > 0:
|
||||||
sys.exit('Couldnt send decryption key to ssh.')
|
sys.exit('Couldnt send decryption key to ssh.')
|
||||||
@ -149,8 +152,8 @@ def restart(service):
|
|||||||
cmd = ['/usr/bin/systemctl', 'status', systemd_service]
|
cmd = ['/usr/bin/systemctl', 'status', systemd_service]
|
||||||
log.info(f'cmd to show status of service: "{" ".join(cmd)}"')
|
log.info(f'cmd to show status of service: "{" ".join(cmd)}"')
|
||||||
p = subprocess.run(cmd, capture_output=True)
|
p = subprocess.run(cmd, capture_output=True)
|
||||||
stderr = p.stdout.decode('UTF-8')
|
stderr = p.stderr.decode('UTF-8')
|
||||||
sys.stderr.write(p.stdout)
|
sys.stderr.write(stderr)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
log.info(f'{service} has restarted OK')
|
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']
|
'-certfile', f'/etc/letsencrypt/live/{service}.drheck.dev/chain.pem']
|
||||||
log.info(f'cmd to encrypt private key: "{" ".join(cmd)}"')
|
log.info(f'cmd to encrypt private key: "{" ".join(cmd)}"')
|
||||||
p = pexpect.spawnu(' '.join(cmd))
|
p = pexpect.spawnu(' '.join(cmd))
|
||||||
|
p.logfile = sys.stderr
|
||||||
res = p.expect(['Enter Export Password:', pexpect.EOF, pexpect.TIMEOUT])
|
res = p.expect(['Enter Export Password:', pexpect.EOF, pexpect.TIMEOUT])
|
||||||
if res > 0:
|
if res > 0:
|
||||||
sys.exit('Failed to run openssl to generate '
|
sys.exit('Failed to run openssl to generate '
|
||||||
@ -196,7 +200,8 @@ def pfx_gen(service):
|
|||||||
|
|
||||||
|
|
||||||
def main(args):
|
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}')
|
log.info(f'program start: {sys.argv}')
|
||||||
if len(args) != 1:
|
if len(args) != 1:
|
||||||
@ -232,9 +237,9 @@ def main(args):
|
|||||||
fqdn = f'{service}.drheck.dev'
|
fqdn = f'{service}.drheck.dev'
|
||||||
log.info(f'fqdn: {fqdn}')
|
log.info(f'fqdn: {fqdn}')
|
||||||
# Dry run:
|
# Dry run:
|
||||||
cmd = ['/usr/bin/certbot', '--dry-run', 'certonly', '--manual', '-d', fqdn]
|
# cmd = ['/usr/bin/certbot', '--dry-run', 'certonly', '--manual', '-d', fqdn]
|
||||||
# Real run:
|
# 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)}"')
|
log.info(f'certbot cmd: "{" ".join(cmd)}"')
|
||||||
cb = pexpect.spawnu(' '.join(cmd))
|
cb = pexpect.spawnu(' '.join(cmd))
|
||||||
res = cb.expect(
|
res = cb.expect(
|
||||||
@ -319,12 +324,21 @@ def main(args):
|
|||||||
log.info(f'live keypath: {live}')
|
log.info(f'live keypath: {live}')
|
||||||
log.info(f'archive keypath: {archive}')
|
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}')
|
log.info(f'service {service} has user {user}')
|
||||||
uid = pwd.getpwnam(user).pw_uid
|
uid = pwd.getpwnam(user).pw_uid
|
||||||
gid = pwd.getpwnam(user).pw_gid
|
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)
|
os.chown(live, uid, gid)
|
||||||
log.info(f'live keypath chmodded')
|
log.info(f'live keypath chmodded')
|
||||||
os.chown(archive, uid, gid)
|
os.chown(archive, uid, gid)
|
||||||
@ -334,11 +348,6 @@ def main(args):
|
|||||||
log.info(f'{cert_file} chowned to service user')
|
log.info(f'{cert_file} chowned to service user')
|
||||||
log.info(f'chmodded new keys from certbot')
|
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)
|
restart(service)
|
||||||
|
|
||||||
|
|
Loading…
x
Reference in New Issue
Block a user