Skip to content

feat(aicore): transparent TLS mode and reactive credential reload - #256

Draft
tiagoek wants to merge 1 commit into
mainfrom
feat/aicore-transparent-tls
Draft

feat(aicore): transparent TLS mode and reactive credential reload#256
tiagoek wants to merge 1 commit into
mainfrom
feat/aicore-transparent-tls

Conversation

@tiagoek

@tiagoek tiagoek commented Aug 4, 2026

Copy link
Copy Markdown
Contributor

Disclaimer: Do not include SAP-internal or customer-specific information in this PR (e.g. internal system URLs, customer names, tenant IDs, or confidential configurations). This is a public repository.

Description

This PR addresses two security concerns with how the aicore module handles credentials at runtime:

1. Transparent TLS mode (AICORE_TRANSPARENT_TLS)

Adds opt-in support for infrastructure-managed mTLS authentication. When AICORE_TRANSPARENT_TLS=true is set, set_aicore_config() skips writing AICORE_CLIENT_SECRET to os.environ entirely. Instead, the infrastructure sidecar proxy intercepts the OAuth2 token request and adds the mTLS client certificate transparently — the agent process never holds a shared secret.

This is the same authentication pattern already used by the agentgateway module (introduced in #220). It requires a companion change to the LiteLLM SAP provider (adding a 4th credential mode that allows plain HTTPS token requests with no client_secret or certificate material) — that upstream PR is tracked separately.

Any stale AICORE_CLIENT_SECRET already present in the environment is explicitly removed when transparent TLS mode is active, preventing accidental reuse.

2. Reactive credential reload on AuthenticationError

completion() and acompletion() now intercept litellm.AuthenticationError, re-read credentials from the mounted secret volume via reload_aicore_credentials(), and retry the call once. This covers credential rotation scenarios (client secret rotation or mTLS certificate rotation by cert-manager) without requiring a pod restart. If the retry also fails, the error propagates normally — no retry loop.

reload_aicore_credentials() is also exported as a public function for callers that need to trigger a manual reload.

Related Issue

Type of Change

  • New feature (non-breaking change that adds functionality)
  • Bug fix (non-breaking change that fixes an issue)
  • Breaking change (fix or feature that would cause existing functionality to change)
  • Documentation update
  • Code refactoring
  • Dependency update

How to Test

Transparent TLS mode:

  1. Set AICORE_TRANSPARENT_TLS=true in the environment before calling set_aicore_config()
  2. Verify AICORE_CLIENT_SECRET is not present in os.environ after the call
  3. Verify AICORE_CLIENT_ID, AICORE_AUTH_URL, AICORE_BASE_URL are still set normally
  4. Full end-to-end test requires the LiteLLM upstream change (plain HTTPS token request without client credentials) — until that PR lands, litellm.completion() will still raise ValueError in transparent TLS mode

Reactive credential reload:

  1. Call set_aicore_config() followed by completion() successfully
  2. Simulate credential rotation: update the mounted secret file with new credentials
  3. Invalidate the current token (or wait for expiry)
  4. Verify the next completion() call succeeds without a pod restart

Unit tests:

python -m pytest tests/aicore/unit/ -v
# Expected: 65 passed

Checklist

  • I have read the Contributing Guidelines
  • I have verified that my changes solve the issue
  • I have added/updated automated tests to cover my changes
  • All tests pass locally
  • I have verified that my code follows the Code Guidelines
  • I have updated documentation (if applicable)
  • I have added type hints for all public APIs
  • My code does not contain sensitive information (credentials, tokens, etc.)
  • I have followed Conventional Commits for commit messages

Breaking Changes

None. All changes are additive or opt-in:

  • AICORE_TRANSPARENT_TLS — requires explicit opt-in; default behavior is unchanged
  • reload_aicore_credentials() — new public function, no existing callers affected
  • Retry on AuthenticationError — same exception type propagates if retry also fails; callers that catch AuthenticationError may observe a slight delay before receiving it (one additional attempt), but the contract is unchanged

Additional Notes

Dependency on LiteLLM upstream: The transparent TLS feature requires a companion change to litellm/llms/sap/credentials.py that adds a 4th authentication mode (transparent_tls=True) bypassing the current validate_credentials() requirement for at least one of client_secret, cert_str+key_str, or cert_file_path+key_file_path. Until that upstream PR is merged and the litellm minimum version in pyproject.toml is bumped, setting AICORE_TRANSPARENT_TLS=true will result in a ValueError from LiteLLM on the first completion call.

Stacked PR: A follow-up PR (feat/aicore-clear-client-secret) based on this branch addresses AFSDK-4291 — clearing AICORE_CLIENT_SECRET from os.environ after the first successful token acquisition. It is kept separate to allow independent review and to allow time for assessing impact on downstream consumers that read the secret from os.environ directly.

Introduces two security improvements for AI Core credential handling:

1. Transparent TLS mode (AICORE_TRANSPARENT_TLS=true): when active,
   set_aicore_config() skips writing AICORE_CLIENT_SECRET to os.environ
   and removes any stale value. The infrastructure sidecar proxy adds
   the mTLS certificate transparently on the SDK's behalf — no secret
   material needed in the agent process. Addresses HASI2026203 /
   SEC-309 (credentials exposed as env vars with excessive scope).

2. Reactive credential reload on AuthenticationError: completion() and
   acompletion() now intercept litellm.AuthenticationError, re-read
   credentials from the mounted secret volume, and retry once. Covers
   client_secret rotation and mTLS certificate rotation (cert-manager
   updates the volume file; the next failed token refresh triggers the
   reload) without requiring a pod restart.

Relates-to: AFSDK-4306
logger = logging.getLogger(__name__)


def reload_aicore_credentials() -> None:

@NicoleMGomes NicoleMGomes Aug 4, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Why are we creating a new method that only calls other?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

reload_aicore_credentials() serves two purposes: it's called automatically by completion()/acompletion() on AuthenticationError (reactive reload on credential rotation), and it's also exposed as a public API for callers that need to trigger a manual reload. Keeping it as a named function makes the automatic behavior explicit and gives callers a stable surface without coupling them to set_aicore_config() internals.

# When set, the infrastructure sidecar adds the mTLS certificate transparently.
# The SDK calls the XSUAA token endpoint over plain HTTPS with only client_id.
# No client_secret or certificate material is required in the service binding.
TRANSPARENT_TLS_ENV_VAR = "AICORE_TRANSPARENT_TLS"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Can we understand if we can have a single variable to set transparent proxy usage and not specific by module?

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