Skip to content

Broken websocket client on custom authorization header names #2510

@jiankyu

Description

@jiankyu

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    kind/bugCategorizes issue or PR as related to a bug.

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions