#!/usr/bin/python3
#
# Copyright (C) 2026 Red Hat, Inc.
# SPDX-License-Identifier: LGPL-2.1-or-later
#
# TLS termination proxy for Anaconda Web UI remote installation.
#
# Terminates TLS on port 443 and forwards plaintext TCP to cockpit-ws
# on 127.0.0.1:80. Uses the certificate generated by
# cockpit-certificate-ensure.

import asyncio
import glob
import ssl
import sys

LISTEN_ADDRESS = "0.0.0.0"
LISTEN_PORT = 443
BACKEND_ADDRESS = "127.0.0.1"
BACKEND_PORT = 80
CERT_DIR = "/etc/cockpit/ws-certs.d"


async def pipe(reader, writer):
    """Forward data from reader to writer until EOF or connection error."""
    try:
        while data := await reader.read(8192):
            writer.write(data)
            await writer.drain()
    except (ConnectionResetError, BrokenPipeError, OSError):
        pass
    finally:
        writer.close()


async def handle(client_reader, client_writer):
    """Handle a TLS client by opening a plaintext connection to the backend and piping both directions."""
    try:
        backend_reader, backend_writer = await asyncio.open_connection(
            BACKEND_ADDRESS, BACKEND_PORT
        )
        await asyncio.gather(
            pipe(client_reader, backend_writer),
            pipe(backend_reader, client_writer),
        )
    except (ConnectionRefusedError, OSError) as e:
        print(f"Backend connection failed: {e}", file=sys.stderr)
        client_writer.close()


def create_ssl_context():
    """Load the certificate generated by cockpit-certificate-ensure."""
    ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
    certs = glob.glob(f"{CERT_DIR}/*.cert")
    if not certs:
        print(f"No certificates found in {CERT_DIR}", file=sys.stderr)
        sys.exit(1)
    cert_file = certs[0]
    key_file = cert_file.rsplit(".", 1)[0] + ".key"
    ctx.load_cert_chain(certfile=cert_file, keyfile=key_file)
    return ctx


async def main():
    ssl_ctx = create_ssl_context()
    server = await asyncio.start_server(
        handle, LISTEN_ADDRESS, LISTEN_PORT, ssl=ssl_ctx
    )
    print(
        f"TLS proxy listening on {LISTEN_ADDRESS}:{LISTEN_PORT} -> "
        f"{BACKEND_ADDRESS}:{BACKEND_PORT}",
        file=sys.stderr,
    )
    async with server:
        await server.serve_forever()


asyncio.run(main())
