From 4a0ab9d184033f319aa67754d3ce57ab2025adc7 Mon Sep 17 00:00:00 2001 From: beatz174-bit Date: Mon, 13 Apr 2026 14:05:26 +1000 Subject: [PATCH] Fix upstream TLS verification configuration for mtls-bridge --- monitoring/mtls-bridge/app.py | 86 +++++++++++++++++++++-- monitoring/mtls-bridge/docker-compose.yml | 3 +- 2 files changed, 82 insertions(+), 7 deletions(-) diff --git a/monitoring/mtls-bridge/app.py b/monitoring/mtls-bridge/app.py index d195262..66d72ed 100644 --- a/monitoring/mtls-bridge/app.py +++ b/monitoring/mtls-bridge/app.py @@ -1,8 +1,9 @@ import logging import os +import time import requests -from flask import Flask, Response, request +from flask import Flask, Response, g, request app = Flask(__name__) @@ -11,45 +12,118 @@ logging.basicConfig( format="%(asctime)s %(levelname)s %(message)s", ) logger = logging.getLogger("mtls-bridge") +logging.getLogger("werkzeug").setLevel(logging.WARNING) # Config via env TARGET_URL = os.environ.get("TARGET_URL") CLIENT_CERT = os.environ.get("CLIENT_CERT", "/certs/client.crt") CLIENT_KEY = os.environ.get("CLIENT_KEY", "/certs/client.key") -CA_CERT = os.environ.get("CA_CERT", "/certs/ca.crt") +UPSTREAM_CA_CERT = os.environ.get("UPSTREAM_CA_CERT", os.environ.get("CA_CERT", "")).strip() TIMEOUT = int(os.environ.get("TIMEOUT", "5")) +def get_verify_setting(): + if not UPSTREAM_CA_CERT: + return True + + lowered = UPSTREAM_CA_CERT.lower() + if lowered in {"false", "0", "no"}: + logger.warning("TLS verification for upstream is disabled via UPSTREAM_CA_CERT=%s", UPSTREAM_CA_CERT) + return False + + if not os.path.exists(UPSTREAM_CA_CERT): + logger.warning( + "Configured UPSTREAM_CA_CERT path does not exist: %s (falling back to system CA bundle)", + UPSTREAM_CA_CERT, + ) + return True + + return UPSTREAM_CA_CERT + + +VERIFY_SETTING = get_verify_setting() + +logger.info( + "mtls-bridge starting target_url=%s timeout=%ss cert=%s key=%s verify=%s log_level=%s", + TARGET_URL, + TIMEOUT, + CLIENT_CERT, + CLIENT_KEY, + VERIFY_SETTING, + os.environ.get("LOG_LEVEL", "INFO"), +) + + @app.route("/health", methods=["GET"]) def health(): + logger.debug("healthcheck request from %s", request.remote_addr) return "OK", 200 +@app.before_request +def before_request(): + g.request_start = time.time() + + +@app.after_request +def after_request(response): + elapsed_ms = int((time.time() - g.request_start) * 1000) + if request.path != "/health": + logger.info( + "request complete method=%s path=%s status=%s elapsed_ms=%s", + request.method, + request.path, + response.status_code, + elapsed_ms, + ) + return response + + @app.route("/", defaults={"path": ""}, methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) @app.route("/", methods=["GET", "POST", "PUT", "DELETE", "PATCH"]) def proxy(path): - logger.info("request method=%s path=/%s", request.method, path) + request_path = f"/{path}" if path else "/" + request_size = len(request.get_data(cache=True)) + logger.info( + "incoming request method=%s path=%s query=%s remote=%s bytes=%s", + request.method, + request_path, + request.query_string.decode("utf-8", "ignore"), + request.remote_addr, + request_size, + ) if not TARGET_URL: + logger.error("TARGET_URL is not set; cannot proxy request") return Response("TARGET_URL is not set", status=500) try: url = f"{TARGET_URL.rstrip('/')}/{path}".rstrip("/") + start_time = time.time() headers = {k: v for k, v in request.headers if k.lower() != "host"} headers["X-Forwarded-By"] = "mtls-bridge" + logger.debug("forwarding request to upstream url=%s headers=%s", url, headers) + resp = requests.request( method=request.method, url=url, headers=headers, - data=request.get_data(), + data=request.get_data(cache=True), cookies=request.cookies, cert=(CLIENT_CERT, CLIENT_KEY), - verify=CA_CERT, + verify=VERIFY_SETTING, timeout=TIMEOUT, ) - logger.info("upstream status=%s url=%s", resp.status_code, url) + elapsed_ms = int((time.time() - start_time) * 1000) + logger.info( + "upstream response status=%s url=%s elapsed_ms=%s response_bytes=%s", + resp.status_code, + url, + elapsed_ms, + len(resp.content), + ) excluded_headers = ["content-encoding", "content-length", "transfer-encoding", "connection"] response_headers = [ diff --git a/monitoring/mtls-bridge/docker-compose.yml b/monitoring/mtls-bridge/docker-compose.yml index 3b3acd2..2034e32 100644 --- a/monitoring/mtls-bridge/docker-compose.yml +++ b/monitoring/mtls-bridge/docker-compose.yml @@ -9,8 +9,9 @@ services: - TARGET_URL=https://node-red.lan.ddnsgeek.com/docker-update-lockouts/clear - CLIENT_CERT=/certs/clients/office-pc/office-pc.crt - CLIENT_KEY=/certs/clients/office-pc/office-pc.key - - CA_CERT=/certs/ca/clents-ca.crt - TIMEOUT=5 + - LOG_LEVEL=${MTLS_BRIDGE_LOG_LEVEL:-INFO} + - UPSTREAM_CA_CERT=${MTLS_BRIDGE_UPSTREAM_CA_CERT:-} volumes: - ${PROJECT_ROOT}/core/traefik/certs:/certs:ro healthcheck: