"""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')