fix: add custom CA certificate support for login flows
What
Adds support for custom CA certificates in OAuth login flows to fix authentication failures behind corporate proxies with SSL/TLS inspection.
Why
The Codex CLI fails to authenticate when running behind corporate proxies that perform SSL/TLS inspection with
custom CA certificates. The OAuth token exchange fails because reqwest::Client::new() only trusts system
default certificate roots, causing it to reject connections intercepted by corporate proxies presenting
certificates signed by custom CAs.
This affects all Codex CLI users in enterprise environments with:
- SSL/TLS inspection proxies (Zscaler, Palo Alto Networks, etc.)
- Custom internal CA certificates
- Air-gapped environments with internal certificate authorities
Fixes #6849
How
Implementation:
- Added
build_login_http_client()function that creates an HTTP client with custom CA certificate support - Reads CA certificate path from environment variables (priority order):
CODEX_CA_CERTIFICATE(primary)SSL_CERT_FILE(fallback, standard across many tools)
- Updated all three authentication flows to use the custom client:
exchange_code_for_tokens()(OAuth code exchange)obtain_api_key()(API key exchange)run_device_code_login()(device code flow)
- Follows the same pattern as existing
otel_provider.rsimplementation
Testing:
- Added comprehensive test coverage with 3 new tests:
- Certificate loading via
CODEX_CA_CERTIFICATEenv var - Certificate loading via
SSL_CERT_FILEfallback - Invalid certificate rejection with proper error handling
- Certificate loading via
- All existing tests continue to pass
- Added
serial_testdependency to safely test env var manipulation
Usage:
export CODEX_CA_CERTIFICATE=/path/to/corporate-ca.pem
codex login
or using the standard env var:
export SSL_CERT_FILE=/path/to/corporate-ca.pem
codex login
Testing:
- ✅ All tests pass (cargo test)
- ✅ No clippy warnings (cargo clippy --tests -- -D warnings)
- ✅ Code formatted (cargo fmt --check)
- ✅ Tested with both environment variables
- ✅ Invalid certificate properly rejected with clear error message
All contributors have signed the CLA ✍️ ✅
Posted by the CLA Assistant Lite bot.
I have read the CLA Document and I hereby sign the CLA
@codex review
@codex review
@codex review
Codex Review: Didn't find any major issues. Breezy!
ℹ️ 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".
@3axap4eHko, thanks for the contribution! It looks like there are some minor code format issues. Please fix those when you have time.
@3axap4eHko, the PR is looking good. We'll need to document the new environment variables. Ideally, this should be part of the same PR. Do you want to take a shot at adding the documentation?
I'm assuming that your environment sets some sort of CA root authority that your general tools (browser etc.) properly check, so you don't tend to get errors in non-cli tooling. Is this something that we could generally load from the environment in the same way without having to manually configure this with environment variables? I.e. perhaps configuring the tooling to load from the OS Cert store where this is setup. There's probably a crate that sets this up automatically if that's the case. I'm not sure whether this is relevant to the use cases here, so please forgive the naive question.
I'm assuming that your environment sets some sort of CA root authority that your general tools (browser etc.) properly check, so you don't tend to get errors in non-cli tooling. Is this something that we could generally load from the environment in the same way without having to manually configure this with environment variables? I.e. perhaps configuring the tooling to load from the OS Cert store where this is setup. There's probably a crate that sets this up automatically if that's the case. I'm not sure whether this is relevant to the use cases here, so please forgive the naive question.
Totally fair question. Out of the box, reqwest already trusts the platform store (via either system OpenSSL/Schannel/Security‑Framework on the blocking TLS stack or rustls-native-certs when using the rustls backend). That’s the reason browsers and “normal” CLI calls don’t need extra configuration. The new code only kicks in when you explicitly point CODEX_CA_CERTIFICATE or SSL_CERT_FILE at a custom bundle, because corporate proxies often publish their own root chain that isn’t in the OS store.
So, for users whose proxy installs its cert into the regular Windows/macOS/Linux trust store, nothing special is required—the default client still works. The env knob is just for the cases where IT hands you a standalone PEM bundle instead of touching the OS store. In that scenario you still need manual configuration somewhere, either by importing the PEM into the OS trust store (which is painful to automate across platforms) or by telling the app exactly which file to add. The helper we added is that second option.
If we ever run into a platform where the OS store is available but reqwest/rustls can’t discover it, the fix would likely be as simple as enabling the rustls-native-certs crate (or the native-tls backend) for that target. But so far there hasn’t been a report that the default trust roots are being ignored; every bug we’ve seen came from proxy-issued certs that the OS store rightfully doesn’t trust.
@3axap4eHko, I wanted to give you an update on this PR. Since it involves security-sensitive code, we have pulled in some security experts outside of the codex team to provide a more thorough code review. This is a holiday week in the U.S. (Thanksgiving), so many OpenAI employees are out of the office. That means we probably won't make any further progress on this until next week.
After chatting about this with our internal security, we'd prefer to have the settings for this added to the config.toml file rather than as an environment variable. If you're concerned that you need single call setups (where you don't want to set this at the entire machine level) codex has a --config flag that can generally be used to pass config key/values like this. We don't need to add a specific command line flag for this. Let's call this setting login_ca_certificate.
Let's use rustls-pki-types to open / parse the pem files rather thank hand coded.
Happy to continue the implementation on this with codex, or let you complete it. The things I'd generally want to make sure are covered here:
- the rationale that covers why this setting is necessary is covered in the code as a doc comment on the code, and in the configuration docs (https://github.com/openai/codex/blob/main/docs/config.md#authentication-and-authorization)
- tests (the ones you have look good - modify to be config based instead of environment variable based)
After chatting about this with our internal security, we'd prefer to have the settings for this added to the config.toml file rather than as an environment variable. If you're concerned that you need single call setups (where you don't want to set this at the entire machine level) codex has a
--configflag that can generally be used to pass config key/values like this. We don't need to add a specific command line flag for this. Let's call this settinglogin_ca_certificate.Let's use rustls-pki-types to open / parse the pem files rather thank hand coded.
Happy to continue the implementation on this with codex, or let you complete it. The things I'd generally want to make sure are covered here:
- the rationale that covers why this setting is necessary is covered in the code as a doc comment on the code, and in the configuration docs (https://github.com/openai/codex/blob/main/docs/config.md#authentication-and-authorization)
- tests (the ones you have look good - modify to be config based instead of environment variable based)
@joshka-oai One thing I want to push back on in the "after chatting with internal security" part: environment-based CA configuration is a very common pattern on Unix. SSL_CERT_FILE is the standard OpenSSL convention and is used by tools like curl, git, Python requests, etc. It’s not inherently less secure than putting the same path in config.toml; both are just configuration channels, but it allows to avoid micromanage every cli tool.
If the Codex policy is "all TLS-related overrides must live in config.toml and we intentionally ignore SSL_CERT_FILE", that’s a valid product/design decision. But it’s different from saying that using SSL_CERT_FILE is a security concern. I’d prefer we frame this explicitly as "Codex chooses config-over-env for CA overrides" (and document that) rather than implying the standard env-based mechanism is problematic.
I'm in general agreement with you, but I'm not sure if there's things I don't know about this yet. I'll follow up with our security folk and will get back to you on this shortly.
I clarified with our Security team. We're fine with using environment variables on this. I misread a recommendation in a message.
The main part of getting this over the line is testing this correctly in a way that doesn't cause other usages of this code path to be impacted. Leaving the ball with you for now to take a look, but feel free to ask more questions or kick it back to us if you're not sure of the right direction on this.