kubernetes-client / kubernetes-client/python
Broken websocket client on custom authorization header names
- Dominant language
- Python
- Stars
- 7.7k
- Forks
- 3.5k
- Avg merge
- 1d 14h
- Merged PRs (30d)
- 18
Description
**What happened (please include outputs or screenshots)**:
We use Kubernetes in our production, as per the security regulation, the API server requires a custom authorization header, say "MY-AUTH-TOKEN", instead of the default header name "Authorization". The token is more secure than the static service account token.
The `kubernetes.client.configuration.Configuration` class doesn't support the custom header name, but we can play a hack to workaround this:
```
from kubernetes import client
class MyConfiguration(client.Configuration):
def auth_settings(self):
auth = {}
if 'authorization' in self.api_key:
auth['BearerToken'] = {
'type': 'api_key',
'in': 'header',
'key': 'MY-AUTH-TOKEN', # set header name here
'value': self.get_api_key_with_prefix('authorization')
}
return auth
# usage
my_cfg = MyConfiguration(...)
v1 = client.CoreV1Api(client.ApiClient(my_cfg))
pod = v1.read_namespaced_pod('my-pod', 'my-ns')
```
However, this hack doesn't work on the `kubernetes.stream.stream()` call, the reason is the websocket client doesn't hornor the "auth_settings" in the configuration.
(code snippet in `kubernetes.stream.ws_client.py`)
```
def create_websocket(configuration, url, headers=None):
enableTrace(False)
# We just need to pass the Authorization, ignore all the other
# http headers we get from the generated code
header = []
if headers and 'authorization' in headers:
header.append("authorization: %s" % headers['authorization'])
...
```
Here the header name is hard coded as "authorization", any other custom headers are discarded.
As a result, the stream() call breaks, this breaks watching and exec.
**What you expected to happen**:
The websocket client should handle the auth headers in the same way as api client, see `ApiClient.update_params_for_auth()` impl.
The configuration object is passed in as the first parameter of `create_websocket` function, it has the ability to get the header name by calling `configuration.auth_settings()["key"]`
```
def create_websocket(configuration, url, headers=None):
enableTrace(False)
# We just need to pass the Authorization, ignore all the other
# http headers we get from the generated code
header = []
auth_settings = configuration.auth_settings().get('BearerToken')
if auth_settings:
if auth_settings['in'] == 'cookie':
header.append("Cookie: %s" % auth_settings['value'])
elif auth_settings['in'] == 'header':
header.append("%s: %s" % (auth_settings['key'], auth_settings['value']))
...
```
**How to reproduce it (as minimally and precisely as possible)**:
**Anything else we need to know?**:
**Environment**:
- Kubernetes version (`kubectl version`):
Any
- OS (e.g., MacOS 10.13.6):
Any
- Python version (`python --version`)
Any
- Python client version (`pip list | grep kubernetes`)
This issue exists on all client versions.
Contributor guide
Assessment
This issue has not been assessed yet.