feat(aicore): clear AICORE_CLIENT_SECRET after token acquisition (AFSDK-4291) - #257
feat(aicore): clear AICORE_CLIENT_SECRET after token acquisition (AFSDK-4291)#257tiagoek wants to merge 2 commits into
Conversation
439f8d8 to
ccadb06
Compare
… acquisition After the first successful litellm.completion() call, AICORE_CLIENT_SECRET is removed from os.environ. LiteLLM has captured the secret inside its token creator closure at that point and no longer reads from the environment. Removing it minimises the exposure window to child processes and container introspection APIs (AFSDK-4291 / HASI2026203 SEC-309). The flag is reset when credentials are reloaded (credential rotation flow) so the secret is cleared again after the retry succeeds. No-op in transparent TLS mode where the secret was never written. Relates-to: AFSDK-4291
3e9ef43 to
ed315de
Compare
PR #257 clears AICORE_CLIENT_SECRET from env after every successful completion() call. The TestReactive401UpdatesEnv test asserts the env value after the retry — patch _clear_client_secret as no-op so the assertion can still verify set_aicore_config() wrote the rotated secret.
SDK Module Review
0 finding(s): 0 posted as inline comment(s) on the affected lines, 0 not tied to a code line (listed above). Generated by sdk-review-skill · v1 |
Closing — approach not viable with current LiteLLM SAP providerThis PR assumes that after the first successful What actually happensLiteLLM's SAP provider calls Removing the env var after the first call causes every subsequent call to fail. Integration tests confirmed this — all three Why there is no clean fixAny "clear and restore" approach around each call introduces thread-safety issues and would be more fragile than the problem it tries to solve. Patching LiteLLM internals to read from a module-level store instead of What stays in placePR #256 (
The |
…ce mode Implements Option 3 from the AFSDK-4306 security alignment meeting: SDK absorbs all routing complexity so agent code is identical in all environments. The deployer controls routing by choosing which env vars to inject. Two new modes in set_aicore_config(): Proxy mode (AICORE_PROXY_URL set): - Routes all LiteLLM calls through an external LiteLLM proxy - Sets litellm.api_base / litellm.api_key globally - Rewrites sap/<model> → litellm_proxy/<model> transparently in completion() and acompletion() wrappers (including on auth-error retry) - No AI Core credentials written to the process environment - JWT never reaches the agent process (proxy handles OAuth) Destination mode (AICORE_DESTINATION_NAME set): - Loads AI Core credentials at startup from a named BTP Destination Service destination via the existing sap_cloud_sdk.destination client - Deployer only injects Destination Service binding — AI Core client_secret is never in the K8s Secret, only in BTP Destination Service - Combined with _clear_client_secret() (PR #257), the secret is removed from env after the first successful LiteLLM call Direct mode (neither set): existing behaviour unchanged, including transparent TLS (AICORE_TRANSPARENT_TLS). Adds 30 unit tests covering both new modes and all edge cases. AFSDK-4306
Description
Stacked on #256 — merge that PR first, then retarget this one to
main.After the first successful
litellm.completion()/litellm.acompletion()call,AICORE_CLIENT_SECRETis removed fromos.environ. At that point LiteLLM has already captured the secret inside its token creator closure and no longer reads it from the environment. Removing it minimises the window of exposure to child processes and container introspection APIs.Behaviour details
AuthenticationErrorreload_aicore_credentials()(from feat(aicore): transparent TLS mode and reactive credential reload #256) resets the internal flag before re-callingset_aicore_config(), so the secret is written back temporarily and cleared again after the retry succeeds_configure_destination_mode()and cleared after the first call; the deployer never needs to inject it into the K8s SecretAICORE_CLIENT_SECRETis removed fromos.environafter the first successful completion call.Code that reads
os.environ["AICORE_CLIENT_SECRET"]after callingcompletion()oracompletion()will receive aKeyError(or empty string via.get()).Affected pattern:
Known affected agents — migration required before this PR merges:
PMDRA/skills_agent.pyAICORE_CLIENT_SECRETfrom env post-completion()billing-anomaly/_credentials.pyAICORE_CLIENT_SECRETfrom env post-completion()Issues have been opened in the respective repos with the migration path below. This PR stays draft until all three confirm migration or provide a timeline.
Migration path:
Remove any code that reads
AICORE_CLIENT_SECRETdirectly fromos.environafterset_aicore_config()/completion(). The secret is an implementation detail of the LiteLLM integration — application code should not depend on it.If the secret is needed for a purpose outside LiteLLM (e.g. a separate HTTP call), read it directly from the mounted secret volume at
/etc/secrets/appfnd/aicore/<instance>/clientsecretbefore callingset_aicore_config().Related Issues
Type of Change
How to Test
python -m pytest tests/aicore/unit/ -v # Expected: 73 passedManual verification:
set_aicore_config()followed by a successfulcompletion()callos.environ.get("AICORE_CLIENT_SECRET")isNonecompletion()call — verify it succeeds (LiteLLM uses its cached token)AuthenticationErrorcompletion()recovers: reloads credentials, clears secret again after retryChecklist
Additional Notes
This is the second in a series of stacked PRs addressing credential exposure in the
aicoremodule:AuthenticationError+ proactive watcher (watch_aicore_config)CLIENT_SECRETfrom env after token acquisitionWhen #256 is merged to
main, this PR should be retargeted fromfeat/aicore-transparent-tlstomainbefore merging.