diff --git a/run.py b/run.py index c3298d3..4beca5a 100644 --- a/run.py +++ b/run.py @@ -1,25 +1,65 @@ -"""Dev/Prod entry point. Runs the Flask app, over TLS when certs exist.""" +"""Entrypoint. Runs the Flask app over HTTPS (self-signed cert in dev). + +TLS is mandatory in this design: the passphrase and all plaintext stay +in the browser, and the API only makes sense over an encrypted channel +so browsers refuse to send crypto JS/code over an untrusted channel ( +mixed content / HSTS-chaining attacks). + +Two modes: + - certs/cert.pem + certs/key.pem present -> serve https://127.0.0.1:8443 + - otherwise -> generate a self-signed certificate (only for local dev) + and start on https://127.0.0.1:8443 + +If openssl is unavailable, falls back to plain HTTP on 5000 with a loud +warning — dev convenience only, NOT suitable for production. +""" import os import ssl +import subprocess +import sys from backend.app import app from backend import storage +CERT = os.path.join("certs", "cert.pem") +KEY = os.path.join("certs", "key.pem") + + +def _ensure_self_signed_cert(): + if os.path.exists(CERT) and os.path.exists(KEY): + return + os.makedirs("certs", exist_ok=True) + subprocess.run( + [ + "openssl", "req", "-x509", "-newkey", "rsa:2048", + "-keyout", KEY, "-out", CERT, + "-days", "365", "-nodes", + "-subj", "/CN=localhost", + "-addext", "subjectAltName=DNS:localhost,IP:127.0.0.1", + ], + check=True, + stdout=subprocess.DEVNULL, + stderr=subprocess.DEVNULL, + ) + print("Self-signed dev certificate created in certs/.") + def main(): storage.init_db() - cert = os.path.join("certs", "cert.pem") - key = os.path.join("certs", "key.pem") + port = int(os.environ.get("PORT", "8443")) + host = os.environ.get("HOST", "127.0.0.1") - if os.path.exists(cert) and os.path.exists(key): + try: + _ensure_self_signed_cert() context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) - context.load_cert_chain(cert, key) - app.run(host="127.0.0.1", port=8443, ssl_context=context) - else: - print("WARNING: no certs/ found, running WITHOUT TLS (dev only!).") - app.run(host="127.0.0.1", port=5000) + context.load_cert_chain(CERT, KEY) + print(f"Serving on https://{host}:{port} (self-signed dev TLS).") + app.run(host=host, port=port, ssl_context=context) + except (FileNotFoundError, subprocess.CalledProcessError) as e: + print(f"WARNING: no TLS available ({e}); falling back to HTTP.", file=sys.stderr) + app.run(host=host, port=5000) if __name__ == "__main__":