Skip to content

TLS for host-owned listeners

Unreleased. A webhook-listen.http listener or trace-expect.otlp receiver can use TLS or mutual TLS. Existing suites without a top-level listeners: entry continue to expose plaintext HTTP endpoints.

listeners:
  callbacks:
    kind: webhook-listener
    security:
      profile: mtls
      serverCert: certs/listener-chain.pem
      serverKey: certs/listener-key.pem
      clientCaCert: certs/client-ca.pem
  traces:
    kind: otlp-receiver
    security:
      profile: tls
      serverCert: certs/listener-chain.pem
      serverKey: certs/listener-key.pem
steps:
  - id: callback
    type: webhook-listen.http
    listener: callbacks
    match:
      path: /events
      peerCertificateSha256: 4c6b15a5d32f4cdb772348bda08428e16ec7f6ed9f7e92b32dc3fa657a31a56e
  - id: trace
    type: trace-expect.otlp
    receiver: traces
    match:
      traceId: 0123456789abcdef0123456789abcdef

Each map key names a host resource contributed by a step. Its kind must match that step: webhook-listener for webhook-listen.http, otlp-receiver for trace-expect.otlp. Repeated steps naming one resource share its endpoint and capture buffer. An entry alone does not start a listener. The map can coexist with either inline environment or environment.source.

profile: tls needs serverCert and serverKey and does not accept clientCaCert. profile: mtls also requires clientCaCert; the listener rejects requests without a client certificate chaining to that CA during the TLS handshake. An invalid request creates no webhook or span capture. Client certificate revocation is not checked. There is no HTTPS-to-HTTP fallback.

The certificate and key are PEM files. The certificate file contains a leaf followed by optional intermediates; the matching key may be plain or encrypted PKCS#8. For an encrypted key, serverKeyPassword must be one whole ${secret:source/path} reference. Do not set it for an unencrypted key. The server leaf must be currently valid and advertise the IP SAN 127.0.0.1 and DNS SAN host.docker.internal, so both the local and container-facing URLs can be verified. Certificate paths are relative to the suite file's directory and checked using the existing lexical trusted-workspace path policy. vouchfx validate checks their location, availability and public certificate names; run loads the private key at first use, after topology startup and before publishing a listener URL or executing a step.

The staged svc::<name>, <name> and <name>_container values use https:// for configured listeners. The container form uses host.docker.internal; the webhook URL retains its random path token, while the OTLP value remains a base URL for the SUT's SDK to append /v1/traces. The SUT must trust the server certificate and, for mTLS, present a suitable client identity. Container routing to the host is required separately.

On mTLS, an accepted capture records the validated client's leaf DER SHA-256 fingerprint as lowercase 64-character hex. Optional match.peerCertificateSha256 compares it case-insensitively on either Core step; this criterion requires a named mTLS listener. It is absent on plaintext and server-only TLS captures. A timeout means no matching capture arrived; it does not distinguish a silent SUT, a network/trust failure, or a rejected handshake.

Inbound declarations join the existing security-assurance census. A declared listener whose identity was never started and confirmed makes the default run exit nonzero, including early and mixed-suite refusals. Outbound target security and its existing confirmation behavior remain unchanged.