Don't import separate path constants, import whole module in case the constants are not so constant
This commit is contained in:
parent
43f55e5917
commit
0ca993a9ed
@ -4,7 +4,7 @@ import json
|
|||||||
import os
|
import os
|
||||||
from spoc.flock import locked
|
from spoc.flock import locked
|
||||||
|
|
||||||
from .paths import CONF_FILE, CONF_LOCK
|
from . import paths
|
||||||
|
|
||||||
data = {}
|
data = {}
|
||||||
mtime = None
|
mtime = None
|
||||||
@ -12,35 +12,35 @@ mtime = None
|
|||||||
def load():
|
def load():
|
||||||
global data
|
global data
|
||||||
global mtime
|
global mtime
|
||||||
file_mtime = os.stat(CONF_FILE).st_mtime
|
file_mtime = os.stat(paths.CONF_FILE).st_mtime
|
||||||
if mtime != file_mtime:
|
if mtime != file_mtime:
|
||||||
with open(CONF_FILE, 'r') as f:
|
with open(paths.CONF_FILE, 'r') as f:
|
||||||
data = json.load(f)
|
data = json.load(f)
|
||||||
mtime = file_mtime
|
mtime = file_mtime
|
||||||
|
|
||||||
def save():
|
def save():
|
||||||
global mtime
|
global mtime
|
||||||
with open(CONF_FILE, 'w') as f:
|
with open(paths.CONF_FILE, 'w') as f:
|
||||||
json.dump(data, f, sort_keys=True, indent=4)
|
json.dump(data, f, sort_keys=True, indent=4)
|
||||||
mtime = os.stat(CONF_FILE).st_mtime
|
mtime = os.stat(paths.CONF_FILE).st_mtime
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def get_apps():
|
def get_apps():
|
||||||
load()
|
load()
|
||||||
return data['apps']
|
return data['apps']
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def get_host():
|
def get_host():
|
||||||
load()
|
load()
|
||||||
return data['host']
|
return data['host']
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def register_app(name, definition):
|
def register_app(name, definition):
|
||||||
load()
|
load()
|
||||||
data['apps'][name] = definition
|
data['apps'][name] = definition
|
||||||
save()
|
save()
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def unregister_app(name):
|
def unregister_app(name):
|
||||||
load()
|
load()
|
||||||
try:
|
try:
|
||||||
@ -49,14 +49,17 @@ def unregister_app(name):
|
|||||||
except KeyError:
|
except KeyError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def set_host(key, value):
|
def set_host(key, value):
|
||||||
load()
|
load()
|
||||||
data['host'][key] = value
|
data['host'][key] = value
|
||||||
save()
|
save()
|
||||||
|
|
||||||
@locked(CONF_LOCK)
|
@locked(paths.CONF_LOCK)
|
||||||
def set_app(name, key, value):
|
def set_app(name, key, value):
|
||||||
load()
|
load()
|
||||||
|
try:
|
||||||
data['apps'][name][key] = value
|
data['apps'][name][key] = value
|
||||||
save()
|
save()
|
||||||
|
except KeyError:
|
||||||
|
pass
|
||||||
|
@ -9,7 +9,7 @@ from cryptography.hazmat.primitives import hashes, serialization
|
|||||||
from cryptography.hazmat.primitives.asymmetric import ec
|
from cryptography.hazmat.primitives.asymmetric import ec
|
||||||
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
from cryptography.x509.oid import NameOID, ExtendedKeyUsageOID
|
||||||
|
|
||||||
from .paths import ACME_CRON, CERT_PUB_FILE, CERT_KEY_FILE
|
from . import paths
|
||||||
|
|
||||||
def create_selfsigned_cert(domain):
|
def create_selfsigned_cert(domain):
|
||||||
# Create selfsigned certificate with wildcard alternative subject name
|
# Create selfsigned certificate with wildcard alternative subject name
|
||||||
@ -31,25 +31,24 @@ def create_selfsigned_cert(domain):
|
|||||||
.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True) \
|
.add_extension(x509.KeyUsage(digital_signature=True, content_commitment=False, key_encipherment=False, data_encipherment=False, key_agreement=False, key_cert_sign=False, crl_sign=False, encipher_only=False, decipher_only=False), critical=True) \
|
||||||
.add_extension(x509.ExtendedKeyUsage((ExtendedKeyUsageOID.SERVER_AUTH, ExtendedKeyUsageOID.CLIENT_AUTH)), critical=False) \
|
.add_extension(x509.ExtendedKeyUsage((ExtendedKeyUsageOID.SERVER_AUTH, ExtendedKeyUsageOID.CLIENT_AUTH)), critical=False) \
|
||||||
.sign(private_key, hashes.SHA256(), default_backend())
|
.sign(private_key, hashes.SHA256(), default_backend())
|
||||||
with open(CERT_PUB_FILE, 'wb') as f:
|
with open(paths.CERT_PUB_FILE, 'wb') as f:
|
||||||
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
f.write(cert.public_bytes(serialization.Encoding.PEM))
|
||||||
with open(CERT_KEY_FILE, 'wb') as f:
|
with open(paths.CERT_KEY_FILE, 'wb') as f:
|
||||||
f.write(private_key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
|
f.write(private_key.private_bytes(serialization.Encoding.PEM, serialization.PrivateFormat.PKCS8, serialization.NoEncryption()))
|
||||||
os.chmod(CERT_KEY_FILE, 0o600)
|
os.chmod(paths.CERT_KEY_FILE, 0o600)
|
||||||
|
|
||||||
def get_cert_info():
|
def get_cert_info():
|
||||||
# Gather certificate data important for setup-host
|
# Gather certificate data important for setup-host
|
||||||
with open(CERT_PUB_FILE, 'rb') as f:
|
with open(paths.CERT_PUB_FILE, 'rb') as f:
|
||||||
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
|
cert = x509.load_pem_x509_certificate(f.read(), default_backend())
|
||||||
data = {'subject': cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
|
data = {'subject': cert.subject.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
|
||||||
'issuer': cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
|
'issuer': cert.issuer.get_attributes_for_oid(NameOID.COMMON_NAME)[0].value,
|
||||||
'expires': f'{cert.not_valid_after} UTC',
|
'expires': f'{cert.not_valid_after} UTC',
|
||||||
'method': 'manual'}
|
'method': 'manual'}
|
||||||
if os.access(ACME_CRON, os.X_OK):
|
if os.access(paths.ACME_CRON, os.X_OK):
|
||||||
data['method'] = 'automatic'
|
data['method'] = 'automatic'
|
||||||
# Naive method of inferring if the cert is selfsigned
|
# Naive method of inferring if the cert is selfsigned
|
||||||
# Good enough as reputable CAs will never have the same subject and issuer CN
|
# Good enough as reputable CAs will never have the same subject and issuer CN and the 'method' field is used just to populate a GUI element and not for any real cryptography
|
||||||
# and the 'method' field is used just to populate a GUI element and not for any real cryptography
|
|
||||||
elif data['subject'] == data['issuer']:
|
elif data['subject'] == data['issuer']:
|
||||||
data['method'] = 'selfsigned'
|
data['method'] = 'selfsigned'
|
||||||
return data
|
return data
|
||||||
|
@ -6,7 +6,7 @@ import requests
|
|||||||
import socket
|
import socket
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .paths import MYIP_URL, PING_URL
|
from . import paths
|
||||||
|
|
||||||
def compile_url(domain, port, proto='https'):
|
def compile_url(domain, port, proto='https'):
|
||||||
port = '' if (proto in (None, 'https') and port == '443') or (proto == 'http' and port == '80') else f':{port}'
|
port = '' if (proto in (None, 'https') and port == '443') or (proto == 'http' and port == '80') else f':{port}'
|
||||||
@ -30,7 +30,7 @@ def get_external_ip(version):
|
|||||||
allowed_gai_family = requests.packages.urllib3.util.connection.allowed_gai_family
|
allowed_gai_family = requests.packages.urllib3.util.connection.allowed_gai_family
|
||||||
try:
|
try:
|
||||||
requests.packages.urllib3.util.connection.allowed_gai_family = lambda: family
|
requests.packages.urllib3.util.connection.allowed_gai_family = lambda: family
|
||||||
return requests.get(MYIP_URL, timeout=5).text
|
return requests.get(paths.MYIP_URL, timeout=5).text
|
||||||
except:
|
except:
|
||||||
return None
|
return None
|
||||||
finally:
|
finally:
|
||||||
@ -52,7 +52,7 @@ def resolve_ip(domain, query_type):
|
|||||||
|
|
||||||
def ping_url(url):
|
def ping_url(url):
|
||||||
try:
|
try:
|
||||||
return requests.get(PING_URL, params={'url': url}, timeout=5).text == 'vm-pong'
|
return requests.get(paths.PING_URL, params={'url': url}, timeout=5).text == 'vm-pong'
|
||||||
except requests.exceptions.Timeout:
|
except requests.exceptions.Timeout:
|
||||||
raise
|
raise
|
||||||
except:
|
except:
|
||||||
|
@ -1,11 +1,11 @@
|
|||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
|
|
||||||
# Config
|
# Module config
|
||||||
CONF_FILE = '/etc/vmmgr/config.json'
|
CONF_FILE = '/etc/vmmgr/config.json'
|
||||||
CONF_LOCK = '/var/lock/vmmgr-config.lock'
|
CONF_LOCK = '/var/lock/vmmgr-config.lock'
|
||||||
|
|
||||||
# Crypto
|
# Crypto
|
||||||
ACME_CRON = '/etc/periodic/daily/acme-sh'
|
ACME_CRON = '/etc/periodic/daily/acme.sh'
|
||||||
ACME_DIR = '/etc/acme.sh.d'
|
ACME_DIR = '/etc/acme.sh.d'
|
||||||
CERT_KEY_FILE = '/etc/ssl/services.key'
|
CERT_KEY_FILE = '/etc/ssl/services.key'
|
||||||
CERT_PUB_FILE = '/etc/ssl/services.pem'
|
CERT_PUB_FILE = '/etc/ssl/services.pem'
|
||||||
@ -19,7 +19,7 @@ NGINX_DIR = '/etc/nginx/conf.d'
|
|||||||
AUTHORIZED_KEYS = '/root/.ssh/authorized_keys'
|
AUTHORIZED_KEYS = '/root/.ssh/authorized_keys'
|
||||||
INTERFACES_FILE = '/etc/network/interfaces'
|
INTERFACES_FILE = '/etc/network/interfaces'
|
||||||
WG_CONF_FILE = '/etc/wireguard/wg0.conf'
|
WG_CONF_FILE = '/etc/wireguard/wg0.conf'
|
||||||
WG_CONF_FILE_DISABLED = '/etc/wireguard/wg0.conf.disabled'
|
WG_CONF_DISABLED = '/etc/wireguard/wg0.conf.disabled'
|
||||||
|
|
||||||
# URLs
|
# URLs
|
||||||
MYIP_URL = 'https://repo.spotter.cz/tools/myip.php'
|
MYIP_URL = 'https://repo.spotter.cz/tools/myip.php'
|
||||||
|
@ -3,19 +3,19 @@
|
|||||||
import os
|
import os
|
||||||
import subprocess
|
import subprocess
|
||||||
|
|
||||||
from .paths import AUTHORIZED_KEYS, INTERFACES_FILE, WG_CONF_FILE, WG_CONF_FILE_DISABLED
|
from . import paths
|
||||||
|
|
||||||
def get_authorized_keys():
|
def get_authorized_keys():
|
||||||
# Fetches content of root's authorized_files
|
# Fetches content of root's authorized_files
|
||||||
try:
|
try:
|
||||||
with open(AUTHORIZED_KEYS) as f:
|
with open(paths.AUTHORIZED_KEYS) as f:
|
||||||
return f.read()
|
return f.read()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
return ''
|
return ''
|
||||||
|
|
||||||
def set_authorized_keys(keys):
|
def set_authorized_keys(keys):
|
||||||
# Saves content of root's authorized_files
|
# Saves content of root's authorized_files
|
||||||
with open(AUTHORIZED_KEYS, 'w') as f:
|
with open(paths.AUTHORIZED_KEYS, 'w') as f:
|
||||||
f.write(keys)
|
f.write(keys)
|
||||||
# Enable or disable SSH service
|
# Enable or disable SSH service
|
||||||
if keys.strip():
|
if keys.strip():
|
||||||
@ -27,18 +27,18 @@ def set_authorized_keys(keys):
|
|||||||
|
|
||||||
def is_wireguard_running():
|
def is_wireguard_running():
|
||||||
# Returns status of wg0 interface (inferred from existence of its config file)
|
# Returns status of wg0 interface (inferred from existence of its config file)
|
||||||
return os.path.exists(WG_CONF_FILE)
|
return os.path.exists(paths.WG_CONF_FILE)
|
||||||
|
|
||||||
def regenerate_wireguard_key():
|
def regenerate_wireguard_key():
|
||||||
# Regenerates WireGuard key pair for wg0 interface
|
# Regenerates WireGuard key pair for wg0 interface
|
||||||
was_running = is_wireguard_running()
|
was_running = is_wireguard_running()
|
||||||
if was_running:
|
if was_running:
|
||||||
stop_wireguard()
|
stop_wireguard()
|
||||||
privkey = subprocess.run(['wg', 'genkey'], stdout=subprocess.PIPE).stdout.decode().strip()
|
privkey = subprocess.run(['/usr/bin/wg', 'genkey'], stdout=subprocess.PIPE).stdout.decode().strip()
|
||||||
with open(WG_CONF_FILE_DISABLED) as f:
|
with open(paths.WG_CONF_DISABLED) as f:
|
||||||
conf_lines = f.readlines()
|
conf_lines = f.readlines()
|
||||||
conf_lines[2] = f'PrivateKey = {privkey}\n'
|
conf_lines[2] = f'PrivateKey = {privkey}\n'
|
||||||
with open(WG_CONF_FILE_DISABLED, 'w') as f:
|
with open(paths.WG_CONF_DISABLED, 'w') as f:
|
||||||
f.writelines(conf_lines)
|
f.writelines(conf_lines)
|
||||||
if was_running:
|
if was_running:
|
||||||
start_wireguard()
|
start_wireguard()
|
||||||
@ -48,13 +48,13 @@ def get_wireguard_conf():
|
|||||||
# Returns dictionary with WireGuard wg0 interface VPN IP, public key, listen port and peer info
|
# Returns dictionary with WireGuard wg0 interface VPN IP, public key, listen port and peer info
|
||||||
result = {'running': is_wireguard_running()}
|
result = {'running': is_wireguard_running()}
|
||||||
# IP
|
# IP
|
||||||
with open(INTERFACES_FILE) as f:
|
with open(paths.INTERFACES_FILE) as f:
|
||||||
for line in f.read().splitlines():
|
for line in f.read().splitlines():
|
||||||
if '172.17.255' in line:
|
if '172.17.255' in line:
|
||||||
result['ip'] = line.split('.')[-1]
|
result['ip'] = line.split('.')[-1]
|
||||||
break
|
break
|
||||||
# Listen port and peers
|
# Listen port and peers
|
||||||
with open(WG_CONF_FILE if result['running'] else WG_CONF_FILE_DISABLED) as f:
|
with open(paths.WG_CONF_FILE if result['running'] else paths.WG_CONF_DISABLED) as f:
|
||||||
conf_lines = f.read().splitlines()
|
conf_lines = f.read().splitlines()
|
||||||
result['port'] = conf_lines[1].split()[2]
|
result['port'] = conf_lines[1].split()[2]
|
||||||
result['peers'] = '\n'.join(conf_lines[4:])
|
result['peers'] = '\n'.join(conf_lines[4:])
|
||||||
@ -62,8 +62,7 @@ def get_wireguard_conf():
|
|||||||
privkey = conf_lines[2].split()[2]
|
privkey = conf_lines[2].split()[2]
|
||||||
if privkey == 'None':
|
if privkey == 'None':
|
||||||
privkey = regenerate_wireguard_key()
|
privkey = regenerate_wireguard_key()
|
||||||
p = subprocess.Popen(['wg', 'pubkey'], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
|
result['pubkey'] = subprocess.run(['/usr/bin/wg', 'pubkey'], input=privkey.encode(), stdout=subprocess.PIPE).stdout.decode().strip()
|
||||||
result['pubkey'] = p.communicate(privkey.encode())[0].decode().strip()
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
def set_wireguard_conf(ip, port, peers):
|
def set_wireguard_conf(ip, port, peers):
|
||||||
@ -73,18 +72,18 @@ def set_wireguard_conf(ip, port, peers):
|
|||||||
stop_wireguard()
|
stop_wireguard()
|
||||||
# IP
|
# IP
|
||||||
interface_lines = []
|
interface_lines = []
|
||||||
with open(INTERFACES_FILE) as f:
|
with open(paths.INTERFACES_FILE) as f:
|
||||||
for line in f.readlines():
|
for line in f.readlines():
|
||||||
if '172.17.255' in line:
|
if '172.17.255' in line:
|
||||||
line = f' address 172.17.255.{ip}\n'
|
line = f' address 172.17.255.{ip}\n'
|
||||||
interface_lines.append(line)
|
interface_lines.append(line)
|
||||||
with open(INTERFACES_FILE, 'w') as f:
|
with open(paths.INTERFACES_FILE, 'w') as f:
|
||||||
f.writelines(interface_lines)
|
f.writelines(interface_lines)
|
||||||
# Recreate config (listen port and peers)
|
# Recreate config (listen port and peers)
|
||||||
with open(WG_CONF_FILE_DISABLED) as f:
|
with open(paths.WG_CONF_DISABLED) as f:
|
||||||
conf_lines = f.readlines()[:4]
|
conf_lines = f.readlines()[:4]
|
||||||
conf_lines[1] = f'ListenPort = {port}\n'
|
conf_lines[1] = f'ListenPort = {port}\n'
|
||||||
with open(WG_CONF_FILE_DISABLED, 'w') as f:
|
with open(paths.WG_CONF_DISABLED, 'w') as f:
|
||||||
f.writelines(conf_lines)
|
f.writelines(conf_lines)
|
||||||
f.write(peers)
|
f.write(peers)
|
||||||
if was_running:
|
if was_running:
|
||||||
@ -93,15 +92,15 @@ def set_wireguard_conf(ip, port, peers):
|
|||||||
def start_wireguard():
|
def start_wireguard():
|
||||||
# Sets up WireGuard interface
|
# Sets up WireGuard interface
|
||||||
try:
|
try:
|
||||||
os.rename(WG_CONF_FILE_DISABLED, WG_CONF_FILE)
|
os.rename(paths.WG_CONF_DISABLED, paths.WG_CONF_FILE)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
subprocess.run(['ifdown', 'wg0'])
|
subprocess.run(['/sbin/ifdown', 'wg0'])
|
||||||
subprocess.run(['ifup', 'wg0'])
|
subprocess.run(['/sbin/ifup', 'wg0'])
|
||||||
|
|
||||||
def stop_wireguard():
|
def stop_wireguard():
|
||||||
# Tears down WireGuard interface
|
# Tears down WireGuard interface
|
||||||
subprocess.run(['ifdown', 'wg0'])
|
subprocess.run(['/sbin/ifdown', 'wg0'])
|
||||||
try:
|
try:
|
||||||
os.rename(WG_CONF_FILE, WG_CONF_FILE_DISABLED)
|
os.rename(paths.WG_CONF_FILE, paths.WG_CONF_DISABLED)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
@ -12,8 +12,7 @@ from spoc.container import Container, ContainerState
|
|||||||
from spoc.depsolver import DepSolver
|
from spoc.depsolver import DepSolver
|
||||||
from spoc.image import Image
|
from spoc.image import Image
|
||||||
|
|
||||||
from . import config, crypto, net, templates
|
from . import config, crypto, net, paths, templates
|
||||||
from .paths import ACME_CRON, ACME_DIR, ISSUE_FILE, MOTD_FILE, NGINX_DIR
|
|
||||||
|
|
||||||
def register_app(app, host, login, password):
|
def register_app(app, host, login, password):
|
||||||
# Register newly installed application, its subdomain and credentials (called at the end of package install.sh)
|
# Register newly installed application, its subdomain and credentials (called at the end of package install.sh)
|
||||||
@ -35,22 +34,23 @@ def register_proxy(app):
|
|||||||
host = config.get_host()
|
host = config.get_host()
|
||||||
# Assume that the main container has always the same name as the app
|
# Assume that the main container has always the same name as the app
|
||||||
app_ip = spoc_net.get_ip(app)
|
app_ip = spoc_net.get_ip(app)
|
||||||
with open(os.path.join(NGINX_DIR, f'{app}.conf'), 'w') as f:
|
with open(os.path.join(paths.NGINX_DIR, f'{app}.conf'), 'w') as f:
|
||||||
f.write(templates.NGINX.format(app=app, host=app_host, ip=app_ip, domain=host['domain'], port=host['port']))
|
f.write(templates.NGINX.format(app=app, host=app_host, ip=app_ip, domain=host['domain'], port=host['port']))
|
||||||
reload_nginx()
|
reload_nginx()
|
||||||
|
|
||||||
def unregister_proxy(app):
|
def unregister_proxy(app):
|
||||||
# Remove proxy configuration and reload nginx
|
# Remove proxy configuration and reload nginx
|
||||||
try:
|
try:
|
||||||
os.unlink(os.path.join(NGINX_DIR, f'{app}.conf'))
|
os.unlink(os.path.join(paths.NGINX_DIR, f'{app}.conf'))
|
||||||
reload_nginx()
|
reload_nginx()
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
pass
|
pass
|
||||||
|
|
||||||
def update_host(domain, port):
|
def update_host(domain, port):
|
||||||
config.set_host(domain, port)
|
config.set_host('domain', domain)
|
||||||
|
config.set_host('port', port)
|
||||||
# Rebuild nginx config for the portal app. Web interface calls restart_nginx() in WSGI close handler
|
# Rebuild nginx config for the portal app. Web interface calls restart_nginx() in WSGI close handler
|
||||||
with open(os.path.join(NGINX_DIR, 'default.conf'), 'w') as f:
|
with open(os.path.join(paths.NGINX_DIR, 'default.conf'), 'w') as f:
|
||||||
f.write(templates.NGINX_DEFAULT.format(port=port, domain_esc=domain.replace('.', '\\.')))
|
f.write(templates.NGINX_DEFAULT.format(port=port, domain_esc=domain.replace('.', '\\.')))
|
||||||
|
|
||||||
def reload_nginx():
|
def reload_nginx():
|
||||||
@ -65,9 +65,9 @@ def rebuild_issue():
|
|||||||
url_host = net.compile_url(host['domain'], host['port'])
|
url_host = net.compile_url(host['domain'], host['port'])
|
||||||
url_ip = net.compile_url(net.get_local_ip(), host['port'])
|
url_ip = net.compile_url(net.get_local_ip(), host['port'])
|
||||||
issue = templates.ISSUE.format(url_host=url_host, url_ip=url_ip)
|
issue = templates.ISSUE.format(url_host=url_host, url_ip=url_ip)
|
||||||
with open(ISSUE_FILE, 'w') as f:
|
with open(paths.ISSUE_FILE, 'w') as f:
|
||||||
f.write(issue)
|
f.write(issue)
|
||||||
with open(MOTD_FILE, 'w') as f:
|
with open(paths.MOTD_FILE, 'w') as f:
|
||||||
f.write(issue)
|
f.write(issue)
|
||||||
|
|
||||||
def update_password(oldpassword, newpassword):
|
def update_password(oldpassword, newpassword):
|
||||||
@ -82,7 +82,7 @@ def update_password(oldpassword, newpassword):
|
|||||||
|
|
||||||
def create_selfsigned_cert():
|
def create_selfsigned_cert():
|
||||||
# Disable acme.sh cronjob
|
# Disable acme.sh cronjob
|
||||||
os.chmod(ACME_CRON, 0o640)
|
os.chmod(paths.ACME_CRON, 0o640)
|
||||||
# Create selfsigned certificate with wildcard alternative subject name
|
# Create selfsigned certificate with wildcard alternative subject name
|
||||||
domain = config.get_host()['domain']
|
domain = config.get_host()['domain']
|
||||||
crypto.create_selfsigned_cert(domain)
|
crypto.create_selfsigned_cert(domain)
|
||||||
@ -92,34 +92,34 @@ def create_selfsigned_cert():
|
|||||||
def request_acme_cert():
|
def request_acme_cert():
|
||||||
# Remove all possible conflicting certificates requested in the past
|
# Remove all possible conflicting certificates requested in the past
|
||||||
domain = config.get_host()['domain']
|
domain = config.get_host()['domain']
|
||||||
certs = [i for i in os.listdir(ACME_DIR) if i not in ('account.conf', 'ca', 'http.header')]
|
certs = [i for i in os.listdir(paths.ACME_DIR) if i not in ('account.conf', 'ca', 'http.header')]
|
||||||
for cert in certs:
|
for cert in certs:
|
||||||
if cert != domain:
|
if cert != domain:
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', '/etc/acme.sh.d', '--remove', '-d', cert])
|
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--remove', '-d', cert])
|
||||||
# Compile an acme.sh command for certificate requisition only if the certificate hasn't been requested before
|
# Compile an acme.sh command for certificate requisition only if the certificate hasn't been requested before
|
||||||
if not os.path.exists(os.path.join(ACME_DIR, domain)):
|
if not os.path.exists(os.path.join(paths.ACME_DIR, domain)):
|
||||||
cmd = ['/usr/bin/acme.sh', '--issue', '-d', domain]
|
cmd = ['/usr/bin/acme.sh', '--issue', '-d', domain]
|
||||||
for app,definition in config.get_apps():
|
for app,definition in config.get_apps():
|
||||||
cmd += ['-d', f'{definition["host"]}.{domain}']
|
cmd += ['-d', f'{definition["host"]}.{domain}']
|
||||||
cmd += ['-w', ACME_DIR]
|
cmd += ['-w', paths.ACME_DIR]
|
||||||
# Request the certificate
|
# Request the certificate
|
||||||
subprocess.run(cmd, check=True)
|
subprocess.run(cmd, check=True)
|
||||||
# Otherwise just try to renew
|
# Otherwise just try to renew
|
||||||
else:
|
else:
|
||||||
# Acme.sh returns code 2 on skipped renew
|
# Acme.sh returns code 2 on skipped renew
|
||||||
try:
|
try:
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', '/etc/acme.sh.d', '--renew', '-d', domain], check=True)
|
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--renew', '-d', domain], check=True)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
if e.returncode != 2:
|
if e.returncode != 2:
|
||||||
raise
|
raise
|
||||||
# Install the issued certificate
|
# Install the issued certificate
|
||||||
subprocess.run(['/usr/bin/acme.sh', '--home', '/etc/acme.sh.d', '--install-cert', '-d', domain, '--key-file', crypto.CERT_KEY_FILE, '--fullchain-file', crypto.CERT_PUB_FILE, '--reloadcmd', '/sbin/service nginx reload'], check=True)
|
subprocess.run(['/usr/bin/acme.sh', '--home', paths.ACME_DIR, '--install-cert', '-d', domain, '--key-file', crypto.CERT_KEY_FILE, '--fullchain-file', crypto.CERT_PUB_FILE, '--reloadcmd', '/sbin/service nginx reload'], check=True)
|
||||||
# Enable acme.sh cronjob
|
# Enable acme.sh cronjob
|
||||||
os.chmod(ACME_CRON, 0o750)
|
os.chmod(paths.ACME_CRON, 0o750)
|
||||||
|
|
||||||
def install_manual_cert(public_file, private_file):
|
def install_manual_cert(public_file, private_file):
|
||||||
# Disable acme.sh cronjob
|
# Disable acme.sh cronjob
|
||||||
os.chmod(ACME_CRON, 0o640)
|
os.chmod(paths.ACME_CRON, 0o640)
|
||||||
# Copy certificate files
|
# Copy certificate files
|
||||||
shutil.copyfile(public_file, crypto.CERT_PUB_FILE)
|
shutil.copyfile(public_file, crypto.CERT_PUB_FILE)
|
||||||
shutil.copyfile(private_file, crypto.CERT_KEY_FILE)
|
shutil.copyfile(private_file, crypto.CERT_KEY_FILE)
|
||||||
|
Loading…
Reference in New Issue
Block a user