- testwebserver/: eigenstaendiger Python-Webserver (python:3.12-alpine, kein Build), Port/TLS/Text in .env, zeigt Client-IP und loggt jeden Zugriff - start.sh: erkennt Docker/Podman (inkl. rootless Port-Grenze), prueft Port-Konflikte, erzeugt bei TLS=yes ein selbstsigniertes Zertifikat, Befehle up/down/restart/logs/ps/test/firewall - firewall-check.sh: erkennt auch HTTP-Dienste ohne TLS als erreichbar - .gitignore: testwebserver/.env versionieren, testwebserver/certs/ ausschliessen - README: Abschnitt Testwebserver Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
73 lines
2.8 KiB
Python
73 lines
2.8 KiB
Python
"""Minimaler Testwebserver: zeigt Hostname, Zeit und Client-Adresse und loggt jeden Zugriff.
|
|
|
|
GET / HTML-Seite
|
|
GET /health "ok" (fuer Skripte)
|
|
TLS=yes HTTPS mit /certs/server.crt + /certs/server.key
|
|
"""
|
|
|
|
import html
|
|
import os
|
|
import socket
|
|
import ssl
|
|
import sys
|
|
from datetime import datetime
|
|
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
|
|
|
|
TLS = os.environ.get("TLS", "no").lower() in ("yes", "true", "1")
|
|
MESSAGE = os.environ.get("MESSAGE", "Testwebserver laeuft")
|
|
PUBLIC_PORT = os.environ.get("PUBLIC_PORT", "?")
|
|
HOST = socket.gethostname()
|
|
|
|
|
|
class Handler(BaseHTTPRequestHandler):
|
|
server_version = "testwebserver/1.0"
|
|
|
|
def _send(self, code, body, ctype):
|
|
data = body.encode()
|
|
self.send_response(code)
|
|
self.send_header("Content-Type", f"{ctype}; charset=utf-8")
|
|
self.send_header("Content-Length", str(len(data)))
|
|
self.end_headers()
|
|
if self.command != "HEAD":
|
|
self.wfile.write(data)
|
|
|
|
def do_GET(self):
|
|
if self.path == "/health":
|
|
return self._send(200, "ok\n", "text/plain")
|
|
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
rows = "".join(f"<tr><th>{html.escape(k)}</th><td>{html.escape(v)}</td></tr>"
|
|
for k, v in self.headers.items())
|
|
page = f"""<!doctype html><html lang="de"><head><meta charset="utf-8">
|
|
<title>Testwebserver</title>
|
|
<style>body{{font-family:sans-serif;max-width:46rem;margin:2rem auto;padding:0 1rem}}
|
|
th{{text-align:left;padding-right:1rem;vertical-align:top}}h1{{color:#2a7d2a}}</style></head>
|
|
<body><h1>{html.escape(MESSAGE)}</h1>
|
|
<p>Container <b>{html.escape(HOST)}</b> · Port {html.escape(PUBLIC_PORT)}
|
|
({'HTTPS' if TLS else 'HTTP'}) · {now}</p>
|
|
<p>Deine Adresse (wie der Container sie sieht): <b>{html.escape(self.client_address[0])}</b></p>
|
|
<h2>Request-Header</h2><table>{rows}</table></body></html>"""
|
|
self._send(200, page, "text/html")
|
|
|
|
do_HEAD = do_GET
|
|
|
|
def log_message(self, fmt, *args):
|
|
print(f"{datetime.now():%Y-%m-%d %H:%M:%S} {self.client_address[0]} {fmt % args}", flush=True)
|
|
|
|
|
|
def main():
|
|
srv = ThreadingHTTPServer(("0.0.0.0", 8000), Handler)
|
|
if TLS:
|
|
ctx = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER)
|
|
try:
|
|
ctx.load_cert_chain("/certs/server.crt", "/certs/server.key")
|
|
except OSError as exc:
|
|
sys.exit(f"TLS=yes, aber Zertifikat fehlt ({exc}) - ./start.sh erzeugt es")
|
|
# Handshake erst im Request-Thread: ein haengender Client blockiert sonst alle anderen
|
|
srv.socket = ctx.wrap_socket(srv.socket, server_side=True, do_handshake_on_connect=False)
|
|
print(f"Testwebserver lauscht ({'HTTPS' if TLS else 'HTTP'}), von aussen Port {PUBLIC_PORT}", flush=True)
|
|
srv.serve_forever()
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|