Repository navigation
fix(ENGHLP-1703): keep TLS when DIODE_SKIP_TLS_VERIFY is set - #114
Conversation
Co-authored-by: Cursor <cursoragent@cursor.com>
Coverage Report
|
|||||||||||||||||||||||||||||||||||||||||||||
|
AI Code Review — risk tier: Advisory only. A human owns the merge decision. Summary — Net positive. No new correctness or security issues at this head. The previously open loopback-listener access-control gap is now genuinely resolved: the unauthenticated TCP fallback is off by default and requires an explicit opt-in (with a one-off warning), falling closed otherwise. Resolved since last review
Nits
🤖 AI Code Review · run · prompts: Previous review · 2026-10-06 · 3170683AI Code Review — risk tier: Advisory only. A human owns the merge decision. Summary — Net positive: the shutdown race flagged in the last round is genuinely fixed (connect and proxy Resolved since last review
Previously raised — still open
Nits
🤖 AI Code Review · run · prompts: Previous review · 2026-10-06 · 90d6ae4AI Code Review — risk tier: Advisory only. A human owns the merge decision. Summary — Net positive: the Findings
Resolved since last review
Previously raised — still open
🤖 AI Code Review · run · prompts: Previous review · 2026-10-06 · 58e0ff2AI Code Review — risk tier: Advisory only. A human owns the merge decision. Summary — Net positive: replacing the probe-and-pin approach with a local plaintext-to-TLS tunnel is a much sounder design, and it resolves every previously open finding tied to the old pinning code (that code is gone entirely). Two new issues surface in the delta, both edge cases of the new tunnel rather than problems with the core approach. Findings
Resolved since last review
Pre-existing / out-of-scope
🤖 AI Code Review · run · prompts: |
Probe up to three times and pin all distinct peer leaf certs for gRPC skip-verify, wrap connect/TLS failures in DiodeConfigError, derive SNI override from getpeercert(), and require TLS 1.2+ on the probe handshake. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (pushed
|
Inline PROTOCOL_TLS_CLIENT setup at wrap_socket so CodeQL sees TLS 1.2+ on the peer-cert probe handshake (addresses PR review discussion r4046196340). Co-authored-by: Cursor <cursoragent@cursor.com>
|
/ai-review |
Widen _fetch_peer_leaf_certificate handshake handling to catch OSError alongside ssl.SSLError so transient network failures during skip-verify probes surface as DiodeConfigError instead of raw exceptions. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (open finding: unwrapped TLS probe errors)Pushed fix for the remaining Medium item on Verification: /ai-review |
Parse bracketed IPv6 authorities for the peer-cert probe and wrap HTTPS_PROXY CONNECT in TLS before sending the tunnel request. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (low findings on
|
Use PROTOCOL_TLS_CLIENT with minimum_version TLSv1_2 when wrapping the skip-verify probe's HTTPS_PROXY socket so CodeQL matches the peer handshake path. Co-authored-by: Cursor <cursoragent@cursor.com>
The peer-cert probe issues CONNECT over a plain TCP socket to the proxy, matching grpc.http_proxy, so the target TLS handshake is not stacked on an existing proxy SSLSocket. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (nested TLS on
|
|
@codex review |
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 197c0b75a5
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
P1: I don't think this fully matches Go's In context.check_hostname = False
context.verify_mode = ssl.CERT_NONE
tls_sock = context.wrap_socket(raw_sock, server_hostname=host)
der_cert = tls_sock.getpeercert(binary_form=True)
server_name = _tls_server_name_from_peercert(
tls_sock.getpeercert(),
host,
)The problem is that Python returns an empty decoded dict from We then pin the peer leaf cert as a trusted root and set: ("grpc.ssl_target_name_override", host)which means gRPC can still reject the connection if the certificate SAN/CN doesn't match the target hostname/IP. For example: Go's Can we add an integration test with a real self-signed TLS server where the target hostname deliberately does not match the cert SAN, and make sure |
|
P1: skip_tls_verify still rejects expired/not-yet-valid certificates The DER decoding fix removes certificate validation from the probe path, but the actual gRPC channel still builds normal TLS credentials from the pinned leaf: credentials = grpc.ssl_channel_credentials( That means gRPC/BoringSSL still performs certificate validity checks during the real channel handshake. So an expired or not-yet-valid self-signed certificate can still fail with CERTIFICATE_VERIFY_FAILED even when DIODE_SKIP_TLS_VERIFY=true. This does not match Go's InsecureSkipVerify: true, which skips both chain/hostname validation and certificate validity checks. Could we add an integration test using an expired certificate and verify that skip_tls_verify=True can actually establish the gRPC channel? The current implementation appears to fix the probe-side validation but still reapplies verification when the real gRPC channel is created. |
The branch was behind develop and had dropped a CODEOWNERS entry. The .gitignore additions were local tooling and are not part of this change. Co-authored-by: Cursor <cursoragent@cursor.com>
grpcio has no InsecureSkipVerify, and pinning the server leaf with a name override cannot match Go: it rejects expired certificates and multi-label wildcards, rewrites :authority, and breaks on SNI-selected certificates, load-balanced leaves and rotation. SkipVerifyTunnel is a plaintext local listener (a Unix socket in a 0700 directory, or loopback) that forwards each connection to the server over TLS with CERT_NONE. The server sees the real host as SNI. HTTP CONNECT proxies, including Basic auth, are handled here; https:// proxy URLs are rejected as gRPC does not support them either. Tests use real TLS servers: expired and wrong-name certificates, SNI, ALPN, rotation, a 1024-bit key, proxy auth and rejection, and socket cleanup. Co-authored-by: Cursor <cursoragent@cursor.com>
Replace the pin-and-rename skip-verify path with SkipVerifyTunnel. A secure target with verification off now gets a plaintext gRPC channel to the local tunnel, with grpc.default_authority set to the real authority and gRPC's own proxy handling disabled because the tunnel does the CONNECT. Previously the channel was downgraded to plaintext h2c against the real server. - add skip_tls_verify to DiodeClient and DiodeOTLPClient (it was documented but not implemented) - pick HTTPS_PROXY for secure targets whether or not verification is on - share channel creation between both clients in _open_grpc_channel - close the tunnel with the client, and on garbage collection - drop the cryptography runtime dependency the pinning code needed - replace the test that asserted the plaintext downgrade with real-server tests for skip_tls_verify, the env var, the OTLP client and close() Co-authored-by: Cursor <cursoragent@cursor.com>
Co-authored-by: Cursor <cursoragent@cursor.com>
|
/ai-review |
…stener - Send a plain CONNECT whatever the proxy URL scheme says, as grpc-go does, instead of raising from the client constructor when skip-verify is on. Default the proxy port to 443 for https:// URLs. - Retry the Unix socket in /tmp when TMPDIR makes the socket path too long, so the unauthenticated loopback listener is only used where Unix sockets are unavailable. Log a warning once when it is, and document the reduced isolation. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (pushed
|
|
/ai-review |
…abandon them The upstream connect and proxy CONNECT ran on an executor thread, which task cancellation cannot interrupt. close() waited five seconds, then stopped the loop with the connection still in flight, leaving a pending task and relying on garbage collection to clean up. Connect and CONNECT now use loop.sock_connect, sock_sendall and sock_recv, so every step is cancellable and _shutdown() finishes promptly. The shutdown wait on the default executor, which getaddrinfo still uses, is bounded. The regression test holds a CONNECT open on a proxy that never answers, and fails against the previous implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (pushed
|
|
/ai-review |
The loopback fallback is not access controlled: any local user can connect to it and reach the server through the client's proxy settings. Raise a DiodeConfigError unless DIODE_SKIP_TLS_VERIFY_ALLOW_LOOPBACK is set, in which case the fallback works and logs a warning once. Linux and macOS use a 0700 Unix socket and are unaffected. Co-authored-by: Cursor <cursoragent@cursor.com>
AI review follow-up (pushed
|
|
/ai-review |
|
🔁 AI Code Review updated for |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 36441ce183
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…iagnosable Codex review: - Do not attempt a Unix socket on win32 and treat NotImplementedError and AttributeError from asyncio.start_unix_server as 'unavailable', so the opt-in loopback fallback is reachable on the Windows Proactor loop. Independent review: - close() from the loop thread (a finalizer run by the GC there) no longer hangs and raises; teardown moves into the loop thread's own exit. - close() in a forked child no longer removes the parent's socket directory. - close() now closes upstream TLS sockets: it flushes the close_notify briefly, then aborts. Without this each client cycle leaked two descriptors. - Record why the tunnel could not reach the server and add it to DiodeClientError and OTLPClientError for UNAVAILABLE, instead of a bare 'Socket closed'. - Finalize on the channel rather than the client, so client.channel keeps working after the client is collected. - Validate the proxy URL host and port in the constructor. - Percent-escape the unix: target so a % in TMPDIR works. - Decode proxy credentials as bytes, as Go sends them. - Bound getaddrinfo and redact proxy credentials from log lines. Tests: replace the rotation test that built a second tunnel with one that swaps the certificate under a single tunnel, and assert the default secure path's channel arguments. The new descriptor-leak and lifecycle tests fail against the previous implementation. Co-authored-by: Cursor <cursoragent@cursor.com>
|
@codex review |
…hreads The test asserted that no tunnel thread existed anywhere in the process. The client.channel test leaves its tunnel to a finalizer, so under some orderings (seen on Python 3.10 in CI) a thread from that test was still alive. Compare the thread set before and after instead, and have the channel test run its finalizer before it returns. Co-authored-by: Cursor <cursoragent@cursor.com>
|
Codex Review: Didn't find any major issues. Bravo. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
|
🎉 This PR is included in version 1.14.2 🎉 The release is available on GitHub release Your semantic-release bot 📦🚀 |
Summary
DIODE_SKIP_TLS_VERIFY/skip_tls_verifybehave like Go'sInsecureSkipVerify: the connection stays on TLS and only certificate validation is skipped.h2c) and surfaced as 404 or protocol errors.grpc.ssl_target_name_overridecould not match Go (see below).What changed
netboxlabs/diode/sdk/_skip_verify_tunnel.py. For a secure target with verification off, gRPC speaks plaintext to a private local listener (a Unix socket in a0700directory, or loopback where Unix sockets are unavailable). The tunnel forwards each connection to the server over TLS withCERT_NONE, minimum TLS 1.2 and ALPNh2. The server sees the real host as SNI and:authority, and nothing is pinned.CONNECTproxies are handled by the tunnel, including Basic auth from the proxy URL.https://proxy URLs raiseDiodeConfigErrorin this mode, as gRPC does not support them either.skip_tls_verifyis added toDiodeClientandDiodeOTLPClient(it was documented but not implemented).HTTPS_PROXYis now chosen for secure targets whether or not verification is on._open_grpc_channel. The tunnel closes withclose(), on leaving awithblock, and on garbage collection.cryptographyruntime dependency is removed. It is now a test-only extra.Why not pinning
A lab that runs the Go and Python SDKs against a Diode ingester behind nginx found six ways the pinning approach differed from Go: expired and not-yet-valid certificates, multi-label wildcards, Host-based routing (the name override rewrites
:authority), SNI-selected certificates, load-balanced backends with different leaves, and certificate rotation under a live client. Each earlier review round fixed one symptom of that design.How tested
pytest testson Python 3.10, 3.13 and 3.14: 235 passed. The new tests use real TLS servers (expired certificate with the wrong name, SNI and ALPN, rotation, a 1024-bit key, proxy auth and a 407, a real gRPC server for both clients, socket cleanup) instead of mocking the TLS layer. The old test that asserted the plaintext downgrade is removed.ruff check netboxlabs tests: clean.close().Linear