Skip to content

[Bug] Internal components attempt TLS handshakes against plain-HTTP listeners in behind-proxy mode (Invalid HTTP method: '\x16\x03\x01...' log noise) #613

Description

@wwwlde

Environment

  • PegaProx: Beta 0.9.14, official Docker image ghcr.io/pegaprox/pegaprox:0.9.14
  • Deployment: Docker Compose; TLS chain: Traefik (TLS termination) → nginx (path routing, adapted from examples/nginx.conf) → PegaProx
  • Behind Reverse Proxy: enabled via Settings → Server (UI toggle); PEGAPROX_BEHIND_PROXY=true also set
  • Result: app binds to 127.0.0.1 with SSL disabled, as expected; nginx shares the container's network namespace (network_mode: "service:pegaprox") and reaches the backends on loopback
  • Web UI, SSE, noVNC, node shell and LXC terminal all work correctly — this issue is about log noise / a failing internal operation only

Symptom

With behind-proxy mode active (SSL disabled (handled by reverse proxy) in startup log), the log periodically fills with entries like:

<gevent._socket3.socket at 0x7f76057fdda0 object, fd=34, family=2, type=1, proto=0>: (from ('127.0.0.1', 34302)) Invalid HTTP method: '\x16\x03\x01\x05ð\x01\x00\x05ì\x03\x03...'
<gevent._socket3.socket at 0x7f76046f0f30 object, fd=34, family=2, type=1, proto=0>: (from ('127.0.0.1', 40540)) Expected GET method; Got command='\x16\x03\x01\x05ð...'

The payload is a TLS ClientHello (0x16 0x03 0x01). Decoding it shows:

  • SNI = localhost → the client connects by hostname, not IP
  • cipher list matches Python ssl defaults → the client is PegaProx's own Python code
  • source is always 127.0.0.1 → same container
  • both the main WSGI server (:5000, "Invalid HTTP method") and the VNC WebSocket server (:5001, "Expected GET method") receive these

In other words: an internal component performs https://localhost:<port> requests against listeners that are in plain-HTTP mode, fails the handshake every time, and both sides log binary noise. The very first entries appear ~1 s after startup, before any external request has been proxied, so at least the startup-time occurrences cannot be caused by request headers (X-Forwarded-Proto) — the https scheme must come from persisted config or a computed self-URL.

Suspected cause

The instance initially ran with built-in TLS enabled (default first boot), then was switched to behind-proxy mode. The internal self-URL used for VNC/SSH console WebSocket callbacks (cf. PEGAPROX_URL, default http://127.0.0.1:5000) appears to remain https://localhost:... — either persisted from the TLS-enabled era or derived from SSL config — and is not switched back to http:// when behind-proxy mode disables SSL.

A secondary candidate is the ACME auto-renewal thread (Started ACME auto-renewal thread appears in the log unconditionally), if it probes the local listener's certificate; however, the observed frequency is higher than a daily renewal check, so the self-URL callbacks fit the evidence better.

Reproduction

  1. Fresh Docker install, let it boot once with default built-in HTTPS
  2. Enable Behind Reverse Proxy (Settings → Server), restart
  3. Watch container logs — TLS ClientHello dumps from 127.0.0.1 appear against :5000 and :5001

Expected behavior

When behind-proxy mode disables SSL, internal self-requests (console WS callbacks, any self-probes) should switch to http://, or the components should read the effective scheme from a single source of truth. Failing that, a one-line warning ("skipping X: SSL disabled") would be far preferable to repeated binary dumps.

Workaround being verified

Explicitly setting PEGAPROX_URL=http://127.0.0.1:5000 in the container environment (will report back whether this silences the callbacks).

Related observation (Docker + behind-proxy UX)

PEGAPROX_BEHIND_PROXY=true alone switches the backend (bind 127.0.0.1, SSL off) but the frontend kept building console WebSocket URLs on page port + 1 (e.g. wss://host:444/...) until the UI toggle in Settings → Server was flipped — after which console WS correctly went through 443 per examples/nginx.conf. If intentional, a note on the Reverse Proxy docs page that the UI toggle (not only the env var) is required would save Docker users some debugging; PEGAPROX_URL is also worth mentioning on that page.

Happy to provide full logs, compose files, or run a patched build to verify a fix.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions