feat: HTTPS via self-signed dev certificate (auto-generated)

run.py now ensures a self-signed TLS certificate exists (openssl,
SAN localhost/127.0.0.1, 365 days) and serves on https://127.0.0.1:8443.
Falls back to plain HTTP with a loud warning only when openssl is
unavailable. Verified end-to-end with curl over TLS.

🤖 Generated with Codebuff
Co-Authored-By: Codebuff <noreply@codebuff.com>
This commit is contained in:
2026-10-11 00:03:45 +02:00
parent 9bab17e5dd
commit d6f508f848
+49 -9
View File
@@ -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 os
import ssl import ssl
import subprocess
import sys
from backend.app import app from backend.app import app
from backend import storage 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(): def main():
storage.init_db() storage.init_db()
cert = os.path.join("certs", "cert.pem") port = int(os.environ.get("PORT", "8443"))
key = os.path.join("certs", "key.pem") 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 = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
context.load_cert_chain(cert, key) context.load_cert_chain(CERT, KEY)
app.run(host="127.0.0.1", port=8443, ssl_context=context) print(f"Serving on https://{host}:{port} (self-signed dev TLS).")
else: app.run(host=host, port=port, ssl_context=context)
print("WARNING: no certs/ found, running WITHOUT TLS (dev only!).") except (FileNotFoundError, subprocess.CalledProcessError) as e:
app.run(host="127.0.0.1", port=5000) print(f"WARNING: no TLS available ({e}); falling back to HTTP.", file=sys.stderr)
app.run(host=host, port=5000)
if __name__ == "__main__": if __name__ == "__main__":