vdi-cloud/tools/prepare-live-config.py

44 lines
3.4 KiB
Python

"""Generate private local configuration and a CA for the device HTTPS channel."""
import argparse
import datetime
import ipaddress
import json
import pathlib
import secrets
import os
from cryptography import x509
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import rsa
from cryptography.x509.oid import NameOID
p = argparse.ArgumentParser()
p.add_argument('--ip', required=True)
p.add_argument('--pve-hostname', required=True, help='DNS name present in the PVE certificate')
p.add_argument('--pve-ip', required=True)
args = p.parse_args()
ip = ipaddress.ip_address(args.ip)
root = pathlib.Path(__file__).resolve().parent.parent
target = root / '.local/live'
target.mkdir(parents=True, exist_ok=True)
if (target / '.env').exists():
raise RuntimeError('Existing configuration retained; edit it deliberately instead of regenerating secrets')
access = json.loads((target / 'pve-access.json').read_text())
access['PVE_URL']=f'https://{args.pve_hostname}:8006'
now = datetime.datetime.now(datetime.timezone.utc)
key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
name = x509.Name([x509.NameAttribute(NameOID.COMMON_NAME, 'VDI local device CA')])
ca = x509.CertificateBuilder().subject_name(name).issuer_name(name).public_key(key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(minutes=5)).not_valid_after(now+datetime.timedelta(days=3650)).add_extension(x509.BasicConstraints(ca=True,path_length=0),critical=True).add_extension(x509.KeyUsage(True,False,False,False,False,True,True,False,False),critical=True).sign(key,hashes.SHA256())
server_key = rsa.generate_private_key(public_exponent=65537, key_size=3072)
server = x509.CertificateBuilder().subject_name(x509.Name([x509.NameAttribute(NameOID.COMMON_NAME,str(ip))])).issuer_name(name).public_key(server_key.public_key()).serial_number(x509.random_serial_number()).not_valid_before(now-datetime.timedelta(minutes=5)).not_valid_after(now+datetime.timedelta(days=365)).add_extension(x509.BasicConstraints(ca=False,path_length=None),critical=True).add_extension(x509.SubjectAlternativeName([x509.IPAddress(ip),x509.DNSName('localhost')]),critical=False).add_extension(x509.ExtendedKeyUsage([x509.oid.ExtendedKeyUsageOID.SERVER_AUTH]),critical=False).sign(key,hashes.SHA256())
tls = target / 'tls'; tls.mkdir(exist_ok=True)
def save_private(path, data):
path.write_bytes(data); os.chmod(path, 0o600)
for name, obj in [('ca',key),('server',server_key)]:
save_private(tls/f'{name}-key.pem', obj.private_bytes(serialization.Encoding.PEM,serialization.PrivateFormat.PKCS8,serialization.NoEncryption()))
(tls/'ca.pem').write_bytes(ca.public_bytes(serialization.Encoding.PEM))
(tls/'server.pem').write_bytes(server.public_bytes(serialization.Encoding.PEM)+ca.public_bytes(serialization.Encoding.PEM))
env = dict(VDI_DB_PASSWORD=secrets.token_hex(24),VDI_JWT_SECRET=secrets.token_hex(48),VDI_ADMIN_USERNAME='admin',VDI_ADMIN_PASSWORD=secrets.token_urlsafe(24),VDI_LIVE_BIND_IP=str(ip),PVE_HOSTNAME=args.pve_hostname,PVE_HOST_IP=str(ipaddress.ip_address(args.pve_ip)),**access)
save_private(target/'.env', ''.join(f'{k}={v}\n' for k,v in env.items()).encode())
save_private(target/'admin-login.txt', f'Console: http://127.0.0.1:5182\nUsername: {env["VDI_ADMIN_USERNAME"]}\nPassword: {env["VDI_ADMIN_PASSWORD"]}\nDevice HTTPS: https://{ip}:8443\n'.encode())
print('Private environment, admin login, CA and HTTPS certificate generated in .local/live')