feat(aicore): transparent TLS mode and reactive credential reload - #256
feat(aicore): transparent TLS mode and reactive credential reload#256tiagoek wants to merge 6 commits into
Conversation
…ature Address reviewer feedback on PR #256: 1. Remove reload_aicore_credentials() wrapper — inline set_aicore_config() directly in the except AuthenticationError blocks. The wrapper added a named function for a single call; inlining is simpler and clearer. 2. Remove transparent TLS feature (AICORE_TRANSPARENT_TLS env var, _is_transparent_tls(), conditional client_secret handling in set_aicore_config()). This feature is blocked on an upstream LiteLLM PR and is not needed for the credential rotation fix. Nicole flagged that it belongs in a future secrets-resolver refactor. Behavior unchanged: AuthenticationError still triggers set_aicore_config() + retry, completely transparent to callers.
Production validation — proactive credential reload (
|
| Pod | Image | Threads in PID 1 |
|---|---|---|
Pre-fix (6ad5b0854983) |
0.3.3-20260825... (no watcher call) |
14 |
Post-fix (1858f9060d95) |
0.3.3-20260825200836_fd446f2+1 |
15 |
The extra thread is aicore-secret-watcher (daemon, polls /etc/secrets/appfnd/aicore/aicore-instance/ mtime every 30 s). It will call set_aicore_config() on the next kubelet symlink-swap rotation without requiring a pod restart.
autonomous-documentation-org unit tests: 286 passed, 1 skipped — coverage 90.51%
E2E validation — reactive credential reload (live pod test)Environment: Kyma managed runtime, namespace Test script
Execution outputWhat this proves
|
…ature Address reviewer feedback on PR #256: 1. Remove reload_aicore_credentials() wrapper — inline set_aicore_config() directly in the except AuthenticationError blocks. The wrapper added a named function for a single call; inlining is simpler and clearer. 2. Remove transparent TLS feature (AICORE_TRANSPARENT_TLS env var, _is_transparent_tls(), conditional client_secret handling in set_aicore_config()). This feature is blocked on an upstream LiteLLM PR and is not needed for the credential rotation fix. Nicole flagged that it belongs in a future secrets-resolver refactor. Behavior unchanged: AuthenticationError still triggers set_aicore_config() + retry, completely transparent to callers.
439f8d8 to
ccadb06
Compare
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
…ature Address reviewer feedback on PR #256: 1. Remove reload_aicore_credentials() wrapper — inline set_aicore_config() directly in the except AuthenticationError blocks. The wrapper added a named function for a single call; inlining is simpler and clearer. 2. Remove transparent TLS feature (AICORE_TRANSPARENT_TLS env var, _is_transparent_tls(), conditional client_secret handling in set_aicore_config()). This feature is blocked on an upstream LiteLLM PR and is not needed for the credential rotation fix. Nicole flagged that it belongs in a future secrets-resolver refactor. Behavior unchanged: AuthenticationError still triggers set_aicore_config() + retry, completely transparent to callers.
- Add watch_aicore_config() daemon thread that polls secret directory mtime every 30s; on change calls set_aicore_config() proactively before LiteLLM's cached OAuth token expires (avoids 401 entirely) - Add _get_secret_dir_mtime() helper — returns 0.0 on OSError so missing dirs are handled safely - Fix ruff format: add blank line after local imports inside except blocks in completion.py (sync and async paths) - Add test_aicore_watcher.py (10 cases) and test_credential_rotation_flow.py (7 cases) covering watcher unit behavior and the LiteLLM env-update contract
- Remove __wrapped__ introspection in test_credential_rotation_flow that caused ty call-non-callable error; watcher call is already verified via reloaded.wait() - Fix trailing blank lines in test_aicore.py (end-of-file-fixer) - Bump version 0.38.0 → 0.41.0 (new public API: watch_aicore_config)
ccadb06 to
10bfdc8
Compare

Description
This PR addresses two security concerns with how the
aicoremodule handles credentials at runtime.1. Reactive credential reload on
AuthenticationError✅ completecompletion()andacompletion()now interceptlitellm.AuthenticationError, re-read credentials from the mounted secret volume viareload_aicore_credentials(), and retry the call once. This covers credential rotation scenarios (client secret rotation by the platform) without requiring a pod restart. If the retry also fails, the error propagates normally — no retry loop.reload_aicore_credentials()is exported as a public function for callers that need to trigger a manual reload.This feature is independently complete and works today with no additional changes.
2. Transparent TLS mode (⚠️ SDK complete — pending LiteLLM upstream
AICORE_TRANSPARENT_TLS)Adds opt-in support for infrastructure-managed mTLS authentication. The mechanism:
AICORE_TRANSPARENT_TLS=truein the pod environmentset_aicore_config()skips writingAICORE_CLIENT_SECRETtoos.environand actively removes any stale value already presentLiteLLM upstream dependency: The current public litellm
validate_credentials()requires exactly one credential mode (client_secret,cert_str+key_str, orcert_file_path+key_file_path). A 4th mode (transparent_tls=True) is needed to allow a no-credential token request where the sidecar provides the cert. Until that upstream change lands andlitellmminimum version is bumped inpyproject.toml,AICORE_TRANSPARENT_TLS=truewill result in aValueErrorfrom LiteLLM on the first completion call.Alternative today: Use proxy mode or destination mode from PR #271, which do not require the LiteLLM upstream change and address CVE 9.9 for the majority of agent deployments.
Any stale
AICORE_CLIENT_SECRETalready present in the environment is explicitly removed when transparent TLS mode is active, preventing accidental reuse.Related Issues
Type of Change
How to Test
Reactive credential reload (works today):
set_aicore_config()followed bycompletion()successfullyAuthenticationError(e.g. revoke the current token or wait for expiry)completion()call succeeds without a pod restartTransparent TLS mode (SDK side only — LiteLLM upstream pending):
AICORE_TRANSPARENT_TLS=truein the environment before callingset_aicore_config()AICORE_CLIENT_SECRETis not present inos.environafter the callAICORE_CLIENT_ID,AICORE_AUTH_URL,AICORE_BASE_URLare still set normallylitellm.completion()will still raiseValueErrorin transparent TLS modeUnit tests:
python -m pytest tests/aicore/unit/ -v # Expected: 65 passedChecklist
Breaking Changes
None. All changes are additive or opt-in:
AICORE_TRANSPARENT_TLS— requires explicit opt-in; default behavior is unchangedreload_aicore_credentials()— new public function, no existing callers affectedAuthenticationError— same exception type propagates if retry also fails; callers that catchAuthenticationErrormay observe a slight delay before receiving it (one additional attempt), but the contract is unchangedAdditional Notes
Dependency on LiteLLM upstream:
validate_credentials()inlitellm/llms/sap/credentials.pyneeds a 4th bypass mode for transparent TLS. The code change is minimal — add an optionaltransparent_tls: bool = Falseparameter that skips the credential requirement check when the sidecar handles authentication. A separate PR toBerriAI/litellmwill be submitted for this.Stacked PRs:
feat/aicore-clear-client-secret) — clearsAICORE_CLIENT_SECRETfrom env after first token acquisitionfeat/aicore-proxy-routing) — Option 3: proxy routing + BTP Destination Service mode (does not require the LiteLLM upstream change)