Skip to content

Commit

Permalink
Refresh exec-based API credentials when they expire (for Kubernetes c…
Browse files Browse the repository at this point in the history
…lient v11.0.0)

This is a partial fix for kubernetes-client/python#741, based on the version of this repo included in `kubernetes-client` v11.0.0 (https://github.com/kubernetes-client/python/tree/v11.0.0).

As described in kubernetes-client/python#741, some of the authentication schemes supported by Kubernetes require updating the client's credentials from time to time. The Kubernetes Python client currently does not support this, except for when using the `gcp` auth scheme. This is because the OpenAPI-generated code does not generally expect credentials to change after the client is configured.

However, in OpenAPITools/openapi-generator#3594, the OpenAPI-generated code added a (undocumented) hook on the `Configuration` object which provides a method for the client credentials to be refreshed as needed. Unfortunately, this version of the Kubernetes client is too old to have that hook, but this patch adds it with a subclass of `Configuration`. Then the `load_kube_config()` function, used by the Kubernetes API to set up the `Configuration` object from the client's local k8s config, just needs to be updated to take advantage of this hook.

This patch does this for `exec`-based authentication, which is a partial fix for kubernetes-client/python#741. The plan is to follow up to support this for all other authentication schemes which may require refreshing credentials. The follow-up patch will be based on the latest Kubernetes client and won't need the `Configuration` subclass.

As noted above, `load_kube_config()` already has a special-case monkeypatch to refresh GCP tokens. I presume this functionality was added before the OpenAPI generator added support for the refresh hook. A complete fix will probably include refactoring the GCP token refreshing to use the new hook.
  • Loading branch information
Eric Menendez committed Jul 23, 2021
1 parent d30f1e6 commit 76824d0
Showing 1 changed file with 17 additions and 1 deletion.
18 changes: 17 additions & 1 deletion config/kube_config.py
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,13 @@ def token(self):
expiry=parse_rfc3339(data['credential']['token_expiry']))


class ConfigurationWithRefreshHook(Configuration):
def get_api_key_with_prefix(self, identifier):
if self.refresh_api_key_hook is not None:
self.refresh_api_key_hook(self)
return super(ConfigurationWithRefreshHook, self).get_api_key_with_prefix(identifier)


class KubeConfigLoader(object):

def __init__(self, config_dict, active_context=None,
Expand Down Expand Up @@ -476,6 +483,8 @@ def _load_from_exec_plugin(self):
logging.error('exec: missing token field in plugin output')
return None
self.token = "Bearer %s" % status['token']
if 'expirationTimestamp' in status:
self.expiry = parse_rfc3339(status['expirationTimestamp'])
return True
except Exception as e:
logging.error(str(e))
Expand Down Expand Up @@ -540,6 +549,13 @@ def _gcp_get_api_key(*args):
# Note: this line runs for GCP auth tokens as well, but this entry
# will not be updated upon GCP token refresh.
client_configuration.api_key['authorization'] = self.token

def _refresh_api_key(client_configuration):
if ('expiry' in self.__dict__ and
self.expiry < datetime.datetime.now(tz=UTC)):
self._load_authentication()
self._set_config(client_configuration)
client_configuration.refresh_api_key_hook = _refresh_api_key
# copy these keys directly from self to configuration object
keys = ['host', 'ssl_ca_cert', 'cert_file', 'key_file', 'verify_ssl']
for key in keys:
Expand Down Expand Up @@ -739,7 +755,7 @@ def load_kube_config(config_file=None, context=None,
persist_config=persist_config)

if client_configuration is None:
config = type.__call__(Configuration)
config = type.__call__(ConfigurationWithRefreshHook)
loader.load_and_set(config)
Configuration.set_default(config)
else:
Expand Down

0 comments on commit 76824d0

Please sign in to comment.