Skip to content

config: write api_key['BearerToken'] so v36+ SDK auth works - #2585

Merged
k8s-ci-robot merged 2 commits into
kubernetes-client:masterfrom
jmacek:fix-incluster-auth-v36-bearertoken-key
May 21, 2026
Merged

config: write api_key['BearerToken'] so v36+ SDK auth works#2585
k8s-ci-robot merged 2 commits into
kubernetes-client:masterfrom
jmacek:fix-incluster-auth-v36-bearertoken-key

Conversation

@jmacek

@jmacekjmacek commented May 21, 2026

Copy link
Copy Markdown
Contributor

What this PR does

Updates the in-cluster and kubeconfig loaders in kubernetes/base/config/, plus kubernetes_asyncio/config/incluster_config.py, to write the bearer token under api_key['BearerToken'] (the v36+ lookup key) instead of api_key['authorization'] (the v35 lookup key). Updates the corresponding test assertions to read the new key. Adds a regression test that exercises the end-to-end flow (Configuration → ApiClient → outgoing Authorization header) which the existing tests did not cover.

Why

v36 rewrote Configuration.auth_settings() to look up the bearer-token credential under api_key['BearerToken'], matching the OpenAPI security scheme name. This rename was declared a breaking change in the project CHANGELOG as part of the openapi-generator v6.6.0 upgrade. The hand-written loaders in kubernetes/base/config/ and kubernetes_asyncio/config/incluster_config.py were not updated to follow the rename - they still write api_key['authorization']. (Asyncio's kube_config.py already writes 'BearerToken', so it isn't affected.)

Net effect on v36: every call to load_incluster_config() (and load_kube_config() with a static token, including the async equivalents) produces a Configuration whose auth_settings() yields no bearer credential, so outgoing API requests are sent without an Authorization header and the apiserver treats them as system:anonymous. The failure mode is silent - no warning, no exception, just 401s from every API call.

See #2582 for the user-side report and a minimal repro.

Test plan

  • Added test_load_incluster_sets_request_authorization_header to kubernetes/base/config/incluster_config_test.py. It drives a real ApiClient.update_params_for_auth() against a freshly-loaded Configuration and asserts the resulting headers contain an Authorization entry - the end-to-end invariant that v36 broke.
  • Verified the new regression test fails on kubernetes==36.0.0 against the unfixed loader (AssertionError: 'authorization' not found in {}) and passes with the fix.
  • Verified all 103 sync tests in kubernetes/base/config/ and 72 async tests in kubernetes_asyncio/config/ pass on v36 with the change applied.

Fixes#2582

Fix `load_incluster_config()` and `load_kube_config()` (sync and async, with a static token) so requests carry an `Authorization` header on `kubernetes-client/python` v36+. Without this fix, in-cluster pods upgrading to v36 silently send unauthenticated requests and the apiserver rejects them as `system:anonymous`.

@k8s-ci-robotk8s-ci-robot added do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. labels May 21, 2026
@linux-foundation-easycla

linux-foundation-easyclaBot commented May 21, 2026

Copy link
Copy Markdown

CLA Signed
The committers listed above are authorized under a signed CLA.

  • ✅ login: Jmacek / name: Jmacek (322667b)

@k8s-ci-robotk8s-ci-robot added the needs-kind Indicates a PR lacks a `kind/foo` label and requires one. label May 21, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

Welcome @jmacek!

It looks like this is your first PR to kubernetes-client/python 🎉. Please refer to our pull request process documentation to help your PR have a smooth ride to approval.

You will be prompted by a bot to use commands during the review process. Do not be afraid to follow the prompts! It is okay to experiment. Here is the bot commands documentation.

You can also check if kubernetes-client/python has its own contribution guidelines.

You may want to refer to our testing guide if you run into trouble with your tests not passing.

If you are having difficulty getting your pull request seen, please follow the recommended escalation practices. Also, for tips and tricks in the contribution process you may want to read the Kubernetes contributor cheat sheet. We want to make sure your contribution gets all the attention it needs!

Thank you, and welcome to Kubernetes. 😃

@k8s-ci-robotk8s-ci-robot added size/M Denotes a PR that changes 30-99 lines, ignoring generated files. cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels May 21, 2026
@jmacek
jmacekforce-pushed the fix-incluster-auth-v36-bearertoken-key branch from f651938 to 322667bCompareMay 21, 2026 17:12
@k8s-ci-robotk8s-ci-robot added release-note Denotes a PR that will be considered when it comes time to generate release notes. cncf-cla: yes Indicates the PR's author has signed the CNCF CLA. and removed do-not-merge/invalid-commit-message Indicates that a PR should not merge because it has an invalid commit message. do-not-merge/release-note-label-needed Indicates that a PR should not merge because it's missing one of the release note labels. cncf-cla: no Indicates the PR's author has not signed the CNCF CLA. labels May 21, 2026
@jmacek

Copy link
Copy Markdown
ContributorAuthor

/kind bug

@k8s-ci-robotk8s-ci-robot added kind/bug Categorizes issue or PR as related to a bug. and removed needs-kind Indicates a PR lacks a `kind/foo` label and requires one. labels May 21, 2026
v36 rewrote Configuration.auth_settings() to look up the bearer-token
credential under api_key['BearerToken'], matching the OpenAPI security
scheme name. The in-cluster and kubeconfig loaders were not updated -
they still write api_key['authorization'], the v35 lookup key. The
same gap exists in kubernetes_asyncio/config/incluster_config.py.
As a result, on v36 every call to load_incluster_config() (and
load_kube_config() with a static token, including the async equivalents)
produces a Configuration whose auth_settings() yields no bearer
credential, so outgoing API requests are sent without an Authorization
header and the apiserver treats them as system:anonymous.
Write the token under both 'authorization' (v35) and 'BearerToken'
(v36+) in all three affected loaders so requests carry the expected
header. The old key is preserved as a backward-compatibility hedge for
any third-party code introspecting api_key['authorization'] directly.
Add a regression test in incluster_config_test that drives a real
ApiClient.update_params_for_auth() against a freshly-loaded
Configuration and asserts the resulting headers contain an Authorization
entry - the end-to-end invariant that v36 quietly broke.
@jmacek
jmacekforce-pushed the fix-incluster-auth-v36-bearertoken-key branch from 322667b to c3608a4CompareMay 21, 2026 18:29
client_configuration.ssl_ca_cert = self.ssl_ca_cert
if self.token is not None:
client_configuration.api_key['authorization'] = self.token
client_configuration.api_key['BearerToken'] = self.token

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Does it make sense to have 2 configurations with that same token?
Either one or the other would be fine. It has to be consistent everywhere.

Do we know why that BearerToken was introduced? What was the intention?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

I think this is related https://github.com/kubernetes-client/python/blob/master/CHANGELOG.md#breaking-change-from-upgrading-openapi-generator-to-v660. It was introduced in upstream client generator. Same question here. I think upstream did a replacement, rather than keeping both. cc @yliaog

Copy link
Copy Markdown
ContributorAuthor

Choose a reason for hiding this comment

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

Thanks both - the original intent of writing both keys was to hedge backward compatibility for any third-party code introspecting api_key['authorization'] directly. But since the rename was already documented as a breaking change in the v6.6.0 generator upgrade, aligning the loaders with that decision is the right call. Switching to a straight replacement.

Address review feedback: the openapi-generator v6.6.0 upgrade
declared this rename as a breaking change in the project CHANGELOG,
so the loaders should follow the rename rather than preserve the
v35 key for backward compatibility.
Removes the api_key['authorization'] write in all three affected
loaders (sync incluster, sync kube_config, async incluster) and
updates the corresponding test assertions to read 'BearerToken'.
@yliaog

Copy link
Copy Markdown
Contributor

thanks for the fix

/lgtm
/approve

@k8s-ci-robotk8s-ci-robot added the lgtm "Looks good to me", indicates that a PR is ready to be merged. label May 21, 2026
@k8s-ci-robot

Copy link
Copy Markdown
Contributor

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: Jmacek, yliaog

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@k8s-ci-robotk8s-ci-robot added the approved Indicates a PR has been approved by an approver from all required OWNERS files. label May 21, 2026
@k8s-ci-robot
k8s-ci-robot merged commit 46989af into kubernetes-client:masterMay 21, 2026
10 checks passed
@aojea

Copy link
Copy Markdown
Contributor

I thought that this was covered by current e2e tests, can we add one test case?

@yliaog

Copy link
Copy Markdown
Contributor

Yes, the current e2e tests are all using config outside cluster, it's better to add one using in cluster config.

octo-sts-8Bot pushed a commit to wolfi-dev/os that referenced this pull request May 26, 2026
…test
v36.0.0 rewrote Configuration.auth_settings() to look up the bearer
credential under api_key['BearerToken'], but left the hand-written
loaders in kubernetes/base/config/ and kubernetes_asyncio/config/
writing api_key['authorization']. Net effect: every load_incluster_config()
on v36 produces a Configuration with no bearer credential, requests go
out unauthenticated, and the apiserver rejects them as system:anonymous.
Cherry-pick the two upstream commits from PR #2585 to write 'BearerToken'
in all three loaders, and bump epoch to 1. Drop the cherry-picks once a
v36.0.1 upstream release ships with the fix applied.
Also add a subpackage test that drives InClusterConfigLoader._set_config
and asserts api_key['BearerToken'] is populated, so the fix can't be
silently dropped on a future bump.
Refs:
- Upstream PR: kubernetes-client/python#2585
- Upstream issue: kubernetes-client/python#2591
Signed-off-by: Francesco Bartolini <francesco.bartolini@chainguard.dev>
Export: 6f0eda5c49c32b1ea5cb945c2c36e44ec0c20d35
@jmacek
jmacek deleted the fix-incluster-auth-v36-bearertoken-key branch May 26, 2026 15:56
morgan-wowk added a commit to TangleML/tangle that referenced this pull request May 28, 2026
### Context
kubernetes v36 introduced two regressions that break local and staging operation:
1. `ApiClient.__deserialize_model()` now accesses `self.configuration` which is unset when constructed via `ApiClient.__new__()` (bypassing `__init__`).
2. `_set_config()` stores exec credentials under `api_key['authorization']` but `auth_settings()` was changed to check `api_key['BearerToken']`, leaving all requests unauthenticated (`system:anonymous`).
### Solution
Pin to `<v36`
### References
- [kubernetes-client/python v36.0.0 release](https://github.com/kubernetes-client/python/releases/tag/v36.0.0)
- [CHANGELOG](https://github.com/kubernetes-client/python/blob/release-36.0/CHANGELOG.md)
- ![image.png](https://app.graphite.com/user-attachments/assets/091de1b7-34d2-45a1-82c2-733e26ed144f.png)
- [Issue #2584 — BearerToken auth regression](kubernetes-client/python#2584)
- [Issue #2582 — ApiClient constructor regression](kubernetes-client/python#2582)
- [PR #2585 — Upstream fix for BearerToken (merged 2026-05-21)](kubernetes-client/python#2585)
renovateBot added a commit to sdwilsh/ansible-playbooks that referenced this pull request Jun 1, 2026
##### [\`36.0.2\`](https://github.com/kubernetes-client/python/releases/tag/v36.0.2)
Getting started:
```bash
pip install --pre --upgrade kubernetes
```
Or from source, download attached zip file, then
```bash
unzip client-python-v36.0.2.zip
cd client-python-v36.0.2
python setup-release.py install
```
Then follow examples in <https://github.com/kubernetes-client/python/tree/release-36.0/examples>
Changelog: <https://github.com/kubernetes-client/python/blob/release-36.0/CHANGELOG.md>
---
##### [\`36.0.1\`](https://github.com/kubernetes-client/python/blob/HEAD/CHANGELOG.md#v3601)
Kubernetes API Version: v1.36.1
##### Bug or Regression
- Fix `load_incluster_config()` and `load_kube_config()` (sync and async, with a static token) so requests carry an `Authorization` header on `kubernetes-client/python` v36+. Without this fix, in-cluster pods upgrading to v36 silently send unauthenticated requests and the apiserver rejects them as `system:anonymous`. ([#2585](kubernetes-client/python#2585), [@jmacek](https://github.com/Jmacek))
##### Deprecation
- Support new exec v5 websocket subprotocol ([#2486](kubernetes-client/python#2486), [@aojea](https://github.com/aojea))
sdwilsh pushed a commit to sdwilsh/ansible-playbooks that referenced this pull request Jun 6, 2026
##### [\`36.0.2\`](https://github.com/kubernetes-client/python/releases/tag/v36.0.2)
Getting started:
```bash
pip install --pre --upgrade kubernetes
```
Or from source, download attached zip file, then
```bash
unzip client-python-v36.0.2.zip
cd client-python-v36.0.2
python setup-release.py install
```
Then follow examples in <https://github.com/kubernetes-client/python/tree/release-36.0/examples>
Changelog: <https://github.com/kubernetes-client/python/blob/release-36.0/CHANGELOG.md>
---
##### [\`36.0.1\`](https://github.com/kubernetes-client/python/blob/HEAD/CHANGELOG.md#v3601)
Kubernetes API Version: v1.36.1
##### Bug or Regression
- Fix `load_incluster_config()` and `load_kube_config()` (sync and async, with a static token) so requests carry an `Authorization` header on `kubernetes-client/python` v36+. Without this fix, in-cluster pods upgrading to v36 silently send unauthenticated requests and the apiserver rejects them as `system:anonymous`. ([#2585](kubernetes-client/python#2585), [@jmacek](https://github.com/Jmacek))
##### Deprecation
- Support new exec v5 websocket subprotocol ([#2486](kubernetes-client/python#2486), [@aojea](https://github.com/aojea))
aaaaahaaaaa added a commit to digitl-cloud/interloper that referenced this pull request Jul 7, 2026
kubernetes-client/python 36.0.0 regenerated Configuration.auth_settings()
to read the token from api_key['BearerToken'], while load_incluster_config()
still writes api_key['authorization']. auth_settings() therefore returns {}
and every API request is sent without an Authorization header, which the
apiserver rejects as system:anonymous (401/403 depending on cluster config).
Fixed upstream in 36.0.1 (kubernetes-client/python#2585).
This broke every scheduled run launch in prod since 0.30.1 shipped the
downgrade from 36.0.2 to 36.0.0 (re-lock side effect of 5c38108).
36.0.1+ is currently unreachable: it requires aiohttp>=3.13.5, which the
google-adk 2.3.0 stack caps below. Excluding 36.0.0 resolves to 35.0.0
with no other lock changes; drop the exclusion note once google-adk allows
newer aiohttp and 36.0.2+ can be picked up.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
yonromai added a commit to marin-community/marin that referenced this pull request Aug 18, 2026
## Summary
Allow Iris controller and test environments to use kubernetes-client v36
while excluding broken v36.0.0. Lock v36.0.3, which contains
kubernetes-client/python#2585's bearer-token fix. The v36 lock also adds
aiohttp to the existing dependency closure.
## Testing
- A temporary in-cluster config loaded v36.0.3 from the lock and
produced its bearer authorization header without network access.
- 50 focused Kubernetes service tests passed.
- 1,502 affected safe tests passed.
- The Dockerfile's exact frozen controller sync installed v36.0.3.
- CoreWeave live smoke passed on this head: the controller image built,
deployed, exercised Kubernetes API operations, and completed the
integration pipeline.
- Iris unit, lint, integration, CodeQL, docs, Rust, and all ordinary
unit shards passed.
Opening the draft triggered the first live CoreWeave smoke before that
mutation was intended; the current-head rerun was deliberate.
## Non-green CI
- Levanter TPU reproduced the same pre-existing FP8 gradient assertion
as #8360 (1,369 passed, 1 failed); aggregate `unit-tests` reflects it.
Tracked in #8403.
- GCP brought up a healthy controller, but both TPU zones reported no
capacity. No workers became healthy, so smoke tests did not run. Cleanup
passed, deleted the controller VM, and issued deletion requests for the
remaining labeled TPUs.
- Iris E2E hit its 10-minute cap while apt fetched Playwright
dependencies, before tests. Levanter Torch hit its 15-minute cap after
FFmpeg setup consumed most of the budget; tests reached 33% with no
reported failure before cancellation. Tracked in #8404.
Fixes#5924
Co-authored-by: yoblin <268258002+yoblin@users.noreply.github.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by an approver from all required OWNERS files.cncf-cla: yesIndicates the PR's author has signed the CNCF CLA.kind/bugCategorizes issue or PR as related to a bug.lgtm"Looks good to me", indicates that a PR is ready to be merged.release-noteDenotes a PR that will be considered when it comes time to generate release notes.size/MDenotes a PR that changes 30-99 lines, ignoring generated files.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Configuration.auth_settings() returns empty dict in v36.0.0 - all BearerToken-authed requests go anonymous

6 participants

@jmacek@k8s-ci-robot@yliaog@aojea@faust64@roycaihw