Skip to content

fix: use system CAs during OIDC enrollment#1014

Closed
seantechco wants to merge 1 commit intoopenziti:mainfrom
FocusApiary:fix/enrollment-ca-trust
Closed

fix: use system CAs during OIDC enrollment#1014
seantechco wants to merge 1 commit intoopenziti:mainfrom
FocusApiary:fix/enrollment-ca-trust

Conversation

@seantechco
Copy link
Copy Markdown

Summary

When enrolling via URL (ziti-edge-tunnel enroll -u), the SDK downloads the Ziti internal CA from /.well-known/est/cacerts, then creates a TLS context trusting only that CA at ziti_enroll.c:403. This breaks reconnection to the enrollment endpoint when it uses a publicly-trusted certificate (e.g., Let's Encrypt behind a reverse proxy).

Root Cause

In well_known_certs_cb() at line 403:

er->tls = default_tls_context(er->cfg.id.ca, strlen(er->cfg.id.ca));

This replaces the TLS context with one that trusts only the Ziti CA. The subsequent ziti_ctrl_init() fires a /version request to the enrollment controller URL. If that endpoint presents a publicly-trusted cert (Let's Encrypt), the TLS handshake fails because the Ziti CA doesn't trust Let's Encrypt.

Fix

Use the system CA bundle (NULL, 0) during enrollment so the SDK can verify both:

  • Publicly-trusted enrollment endpoints (Let's Encrypt)
  • Ziti internal CA endpoints (discovered via /version)

The Ziti CA is still saved in er->cfg.id.ca for the identity config file, ensuring post-enrollment runtime connections use the correct trust chain.

Reproduction

  1. Deploy a Ziti controller behind a reverse proxy (e.g., Envoy Gateway)
  2. Configure a separate enrollment endpoint (enroll.example.com) with a Let's Encrypt cert via TLS passthrough
  3. The main controller endpoint (ziti.example.com) presents the Ziti internal CA cert
  4. Both URLs appear in /version apiBaseUrls
  5. Run ziti-edge-tunnel enroll -u https://enroll.example.com -i identity.json
  6. Enrollment fails with "operation canceled" after downloading the CA

Test Plan

  • Verify enrollment succeeds with split-endpoint deployment (LE + Ziti CA)
  • Verify enrollment still works with single-endpoint deployment
  • Verify the identity config file contains the correct Ziti CA after enrollment
  • Verify post-enrollment tunnel connection uses the Ziti CA from the identity file

🤖 Generated with Claude Code

When enrolling via URL (-u), the SDK downloads the Ziti internal CA
from /.well-known/est/cacerts, then creates a TLS context trusting
ONLY that CA. This breaks reconnection to the enrollment endpoint
when it uses a publicly-trusted certificate (e.g., Let's Encrypt
behind a reverse proxy).

The fix: use the system CA bundle (NULL) during enrollment so the SDK
can verify both publicly-trusted enrollment endpoints and Ziti
internal CA endpoints discovered via /version. The Ziti CA is still
saved in the identity config for post-enrollment runtime use.

Fixes enrollment failures in split-endpoint deployments where:
- enroll.example.com presents a Let's Encrypt cert
- ziti.example.com presents a Ziti internal CA cert
- Both are discovered via /version apiBaseUrls

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@seantechco seantechco requested a review from a team as a code owner March 20, 2026 19:34
@ekoby
Copy link
Copy Markdown
Member

ekoby commented Mar 21, 2026

this sounds like you're trying to address controller misconfiguration.
edge-api endpoints (including enrollment) should use internal CA

this change will (most likely) break standard installations and (definitely) enrollment on controllers that do not have LE certs

@ekoby ekoby closed this Mar 25, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants