Skip to content
This repository was archived by the owner on Mar 13, 2022. It is now read-only.

Commit 54d188f

Browse files
authored
Merge pull request #205 from greed42/plugin-certificates
Accept client certificates from an authn/authz plugin
2 parents ab61ca3 + b85aff2 commit 54d188f

File tree

2 files changed

+70
-11
lines changed

2 files changed

+70
-11
lines changed

config/kube_config.py

+35-11
Original file line numberDiff line numberDiff line change
@@ -474,11 +474,31 @@ def _load_from_exec_plugin(self):
474474
return
475475
try:
476476
status = ExecProvider(self._user['exec']).run()
477-
if 'token' not in status:
478-
logging.error('exec: missing token field in plugin output')
479-
return None
480-
self.token = "Bearer %s" % status['token']
481-
return True
477+
if 'token' in status:
478+
self.token = "Bearer %s" % status['token']
479+
return True
480+
if 'clientCertificateData' in status:
481+
# https://kubernetes.io/docs/reference/access-authn-authz/authentication/#input-and-output-formats
482+
# Plugin has provided certificates instead of a token.
483+
if 'clientKeyData' not in status:
484+
logging.error('exec: missing clientKeyData field in '
485+
'plugin output')
486+
return None
487+
base_path = self._get_base_path(self._cluster.path)
488+
self.cert_file = FileOrData(
489+
status, None,
490+
data_key_name='clientCertificateData',
491+
file_base_path=base_path,
492+
base64_file_content=False).as_file()
493+
self.key_file = FileOrData(
494+
status, None,
495+
data_key_name='clientKeyData',
496+
file_base_path=base_path,
497+
base64_file_content=False).as_file()
498+
return True
499+
logging.error('exec: missing token or clientCertificateData field '
500+
'in plugin output')
501+
return None
482502
except Exception as e:
483503
logging.error(str(e))
484504

@@ -514,12 +534,16 @@ def _load_cluster_info(self):
514534
self.ssl_ca_cert = FileOrData(
515535
self._cluster, 'certificate-authority',
516536
file_base_path=base_path).as_file()
517-
self.cert_file = FileOrData(
518-
self._user, 'client-certificate',
519-
file_base_path=base_path).as_file()
520-
self.key_file = FileOrData(
521-
self._user, 'client-key',
522-
file_base_path=base_path).as_file()
537+
if 'cert_file' not in self.__dict__:
538+
# cert_file could have been provided by
539+
# _load_from_exec_plugin; only load from the _user
540+
# section if we need it.
541+
self.cert_file = FileOrData(
542+
self._user, 'client-certificate',
543+
file_base_path=base_path).as_file()
544+
self.key_file = FileOrData(
545+
self._user, 'client-key',
546+
file_base_path=base_path).as_file()
523547
if 'insecure-skip-tls-verify' in self._cluster:
524548
self.verify_ssl = not self._cluster['insecure-skip-tls-verify']
525549

config/kube_config_test.py

+35
Original file line numberDiff line numberDiff line change
@@ -551,6 +551,13 @@ class TestKubeConfigLoader(BaseTestCase):
551551
"user": "exec_cred_user"
552552
}
553553
},
554+
{
555+
"name": "exec_cred_user_certificate",
556+
"context": {
557+
"cluster": "ssl",
558+
"user": "exec_cred_user_certificate"
559+
}
560+
},
554561
{
555562
"name": "contexttestcmdpath",
556563
"context": {
@@ -875,6 +882,16 @@ class TestKubeConfigLoader(BaseTestCase):
875882
}
876883
}
877884
},
885+
{
886+
"name": "exec_cred_user_certificate",
887+
"user": {
888+
"exec": {
889+
"apiVersion": "client.authentication.k8s.io/v1beta1",
890+
"command": "custom-certificate-authenticator",
891+
"args": []
892+
}
893+
}
894+
},
878895
{
879896
"name": "usertestcmdpath",
880897
"user": {
@@ -1305,6 +1322,24 @@ def test_user_exec_auth(self, mock):
13051322
active_context="exec_cred_user").load_and_set(actual)
13061323
self.assertEqual(expected, actual)
13071324

1325+
@mock.patch('kubernetes.config.kube_config.ExecProvider.run')
1326+
def test_user_exec_auth_certificates(self, mock):
1327+
mock.return_value = {
1328+
"clientCertificateData": TEST_CLIENT_CERT,
1329+
"clientKeyData": TEST_CLIENT_KEY,
1330+
}
1331+
expected = FakeConfig(
1332+
host=TEST_SSL_HOST,
1333+
cert_file=self._create_temp_file(TEST_CLIENT_CERT),
1334+
key_file=self._create_temp_file(TEST_CLIENT_KEY),
1335+
ssl_ca_cert=self._create_temp_file(TEST_CERTIFICATE_AUTH),
1336+
verify_ssl=True)
1337+
actual = FakeConfig()
1338+
KubeConfigLoader(
1339+
config_dict=self.TEST_KUBE_CONFIG,
1340+
active_context="exec_cred_user_certificate").load_and_set(actual)
1341+
self.assertEqual(expected, actual)
1342+
13081343
def test_user_cmd_path(self):
13091344
A = namedtuple('A', ['token', 'expiry'])
13101345
token = "dummy"

0 commit comments

Comments
 (0)