Skip to content

fix(adk): normalize ca_cert_path in GDCH SA JSON before token exchange#1901

Merged
EItanya merged 2 commits into
kagent-dev:mainfrom
jjamroga:fix/gdch-ca-cert-path-normalization
May 22, 2026
Merged

fix(adk): normalize ca_cert_path in GDCH SA JSON before token exchange#1901
EItanya merged 2 commits into
kagent-dev:mainfrom
jjamroga:fix/gdch-ca-cert-path-normalization

Conversation

@jjamroga
Copy link
Copy Markdown
Collaborator

Summary

  • GDCH service-account JSON generated by gdcloud iam service-accounts keys create includes a ca_cert_path pointing at the machine where the key was created — invalid inside the agent pod. google.oauth2.gdch_credentials honors this field and overrides requests.Session.verify, breaking token exchange.
  • Make the kagent ModelConfig tls.* fields authoritative for the STS call: rewrite the JSON's ca_cert_path to the kagent-mounted CA path when one is configured, strip it otherwise. The on-disk Secret is not modified.
  • Load credentials from the mutated dict via gdch_credentials.ServiceAccountCredentials.from_service_account_info rather than load_credentials_from_file.

Test plan

  • Unit tests added for all three normalization branches, on-disk JSON immutability, expiry handling, and token caching (tests/unittests/models/test_token_source.py, 6 cases).
  • uv run ruff check clean on changed files.
  • uv run pytest tests/unittests/models/ — no regressions beyond pre-existing test_tls_e2e.py fixture failures unrelated to this change.
  • Manual end-to-end against a GDC inference endpoint with: (a) no tls.caCertSecretRef and a system-trusted STS CA, (b) tls.caCertSecretRef set to a private CA secret, (c) tls.disableVerify=true against a self-signed STS.

🤖 Generated with Claude Code

Copilot AI review requested due to automatic review settings May 21, 2026 00:35
@github-actions github-actions Bot added the bug Something isn't working label May 21, 2026
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses GDCH service-account JSONs that embed a machine-local ca_cert_path, which google.oauth2.gdch_credentials can honor during STS token exchange and inadvertently override the agent’s intended TLS configuration inside the pod. The fix normalizes the in-memory SA JSON at refresh time so kagent’s ModelConfig TLS settings control the STS call behavior.

Changes:

  • Load GDCH credentials from an in-memory, normalized SA JSON dict (rewrite/remove ca_cert_path) before token exchange.
  • Ensure kagent TLS config takes precedence for STS verification behavior, including tls_disable_verify.
  • Add unit tests covering normalization branches, on-disk JSON immutability, expiry handling, and token caching.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
python/packages/kagent-adk/src/kagent/adk/models/_token_source.py Normalizes ca_cert_path in GDCH SA JSON prior to STS refresh and switches to from_service_account_info for credential construction.
python/packages/kagent-adk/tests/unittests/models/test_token_source.py Adds unit tests validating CA path normalization behavior, immutability of the SA file, expiry usage, and caching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from google.auth.transport import requests as google_requests
from google.oauth2 import gdch_credentials

with open(self._sa_path) as f:
Comment on lines 70 to 75
session = requests.Session()
if self._tls_disable_verify:
session.verify = False
elif self._ca_cert_path:
# Replaces the REQUESTS_CA_BUNDLE environment variable
session.verify = self._ca_cert_path
creds.refresh(google_requests.Request(session=session))
The ca_cert_path field embedded in GDCH service-account JSON by
`gdcloud iam service-accounts keys create` references a path on the
machine where the key was generated, not a path that exists inside the
agent pod. google.oauth2.gdch_credentials reads that field and uses it
for TLS verification of the STS call, overriding the requests.Session
verify setting — so token exchange fails before the request leaves the
process.

Make the kagent ModelConfig tls.* fields authoritative for the STS
call's TLS configuration:

- When tls.caCertSecretRef is set, rewrite the JSON's ca_cert_path to
  the mounted CA path (/etc/ssl/certs/custom/<key>).
- When tls.disableVerify is set, strip ca_cert_path and disable verify.
- Otherwise, strip ca_cert_path and fall back to system CAs.

Load credentials from the mutated dict via
gdch_credentials.ServiceAccountCredentials.from_service_account_info
instead of google.auth.load_credentials_from_file. The on-disk SA JSON
is not modified.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Jamroga <jjamroga@gmail.com>
@jjamroga jjamroga force-pushed the fix/gdch-ca-cert-path-normalization branch from 3ad6979 to e63a6be Compare May 21, 2026 01:13
- Pass `"r", encoding="utf-8"` to open() for the SA JSON, matching the
  rest of the package (adk/cli.py, adk/_token.py).
- Use requests.Session as a context manager so adapters/connections are
  closed after each STS refresh.

Test mock for the disable_verify case configures __enter__ to return the
same mock instance so verify assignments inside the `with` block remain
observable.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Signed-off-by: Jonathan Jamroga <jjamroga@gmail.com>
@EItanya EItanya merged commit d79ed09 into kagent-dev:main May 22, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants