Aucun changement de comportement. Les chaines concernees ne contenaient aucune substitution. A noter pour plus tard : run_https.py conserve une banniere en caracteres semi-graphiques, du meme type que celle qui faisait planter security_scan.py sur une console Windows en cp1252. Le script n'etant lance qu'en developpement et de facon explicite, le point est signale sans etre corrige ici. Co-Authored-By: Claude Opus 5 <[email protected]>
130 lines
4.8 KiB
Python
130 lines
4.8 KiB
Python
"""Development HTTPS server for testing production settings locally.
|
|
|
|
Generates a self-signed certificate (if not present) and runs the
|
|
application via Waitress wrapped in a TLS socket. This simulates the
|
|
production environment where Nginx handles TLS termination.
|
|
|
|
Usage:
|
|
python run_https.py
|
|
|
|
The server will listen on https://localhost:8443
|
|
Accept the self-signed certificate warning in your browser to proceed.
|
|
"""
|
|
|
|
import os
|
|
import socket
|
|
import subprocess
|
|
import ssl
|
|
import sys
|
|
from waitress.server import create_server
|
|
from app.app import create_app
|
|
|
|
CERT_FILE = 'certs/localhost.pem'
|
|
KEY_FILE = 'certs/localhost-key.pem'
|
|
|
|
|
|
def generate_self_signed_cert():
|
|
"""Generate a self-signed certificate for local HTTPS testing.
|
|
|
|
Uses OpenSSL to create a key and certificate valid for 365 days.
|
|
Skips generation if certificate files already exist.
|
|
"""
|
|
if os.path.exists(CERT_FILE) and os.path.exists(KEY_FILE):
|
|
print('[OK] Self-signed certificate already exists.')
|
|
return
|
|
|
|
os.makedirs('certs', exist_ok=True)
|
|
|
|
print('[INFO] Generating self-signed certificate for localhost...')
|
|
try:
|
|
subprocess.run([
|
|
'openssl', 'req', '-x509', '-newkey', 'rsa:2048',
|
|
'-keyout', KEY_FILE,
|
|
'-out', CERT_FILE,
|
|
'-days', '365',
|
|
'-nodes',
|
|
'-subj', '/CN=localhost'
|
|
], check=True, capture_output=True)
|
|
print('[OK] Certificate generated: certs/localhost.pem')
|
|
except FileNotFoundError:
|
|
print('[ERROR] OpenSSL not found. Install OpenSSL or use:')
|
|
print(' winget install OpenSSL.OpenSSL')
|
|
print(' OR download from https://slproweb.com/products/Win32OpenSSL.html')
|
|
sys.exit(1)
|
|
except subprocess.CalledProcessError as e:
|
|
print(f'[ERROR] Certificate generation failed: {e}')
|
|
sys.exit(1)
|
|
|
|
|
|
def main():
|
|
"""Start the HTTPS development server."""
|
|
port = int(os.getenv('HTTPS_PORT', 8443))
|
|
host = os.getenv('HOST', '127.0.0.1')
|
|
|
|
# Ensure certificate exists
|
|
generate_self_signed_cert()
|
|
|
|
# Create the Flask application
|
|
app = create_app()
|
|
|
|
# Create SSL context
|
|
context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
context.load_cert_chain(CERT_FILE, KEY_FILE)
|
|
|
|
# Create a TCP socket, wrap it with TLS, then pass to Waitress
|
|
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM, 0)
|
|
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
|
sock.bind((host, port))
|
|
sock.listen(5)
|
|
|
|
# Wrap the socket with TLS
|
|
ssl_sock = context.wrap_socket(sock, server_side=True)
|
|
|
|
# =====================================================================
|
|
# WSGI Middleware: Tell Flask the connection is HTTPS
|
|
#
|
|
# Waitress doesn't know the underlying socket is TLS, so Flask sees
|
|
# wsgi.url_scheme = "http". Without this middleware, force_https()
|
|
# would cause an infinite redirect loop (ERR_TOO_MANY_REDIRECTS).
|
|
# =====================================================================
|
|
class ForceHTTPSMiddleware:
|
|
"""WSGI middleware that sets url_scheme to 'https'.
|
|
|
|
Since we're wrapping the TCP socket with SSL before passing it
|
|
to Waitress, Flask's request.is_secure returns False because
|
|
Waitress reports wsgi.url_scheme='http'. This middleware fixes
|
|
that so Flask correctly identifies the connection as HTTPS.
|
|
"""
|
|
|
|
def __init__(self, wsgi_app):
|
|
self.wsgi_app = wsgi_app
|
|
|
|
def __call__(self, environ, start_response):
|
|
environ['wsgi.url_scheme'] = 'https'
|
|
environ['HTTPS'] = 'on'
|
|
return self.wsgi_app(environ, start_response)
|
|
|
|
# Wrap the Flask app with the HTTPS middleware
|
|
app.wsgi_app = ForceHTTPSMiddleware(app.wsgi_app)
|
|
|
|
print('\n╔══════════════════════════════════════════════════════╗')
|
|
print('║ TEAM TRYOUTS - Development HTTPS Server ║')
|
|
print('╠══════════════════════════════════════════════════════╣')
|
|
print(f'║ URL: https://{host}:{port} ║')
|
|
print('║ Cert: self-signed (accept browser warning) ║')
|
|
print('║ Press Ctrl+C to stop ║')
|
|
print('╚══════════════════════════════════════════════════════╝\n')
|
|
|
|
# Create Waitress server with the SSL-wrapped socket
|
|
server = create_server(
|
|
app,
|
|
sockets=[ssl_sock],
|
|
threads=4,
|
|
channel_timeout=30,
|
|
)
|
|
server.run()
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|