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

Commit 3f6e65b

Browse files
authored
Merge pull request #147 from roycaihw/automated-cherry-pick-of-#79-upstream-release-8.0-base64-patch
Automated cherry pick of #79
2 parents 5c242ea + 4017a9d commit 3f6e65b

File tree

5 files changed

+116
-13
lines changed

5 files changed

+116
-13
lines changed

.travis.yml

-4
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,6 @@ matrix:
2525
env: TOXENV=py36
2626
- python: 3.6
2727
env: TOXENV=py36-functional
28-
- python: 3.7
29-
env: TOXENV=py37
30-
- python: 3.7
31-
env: TOXENV=py37-functional
3228

3329
install:
3430
- pip install tox

config/kube_config.py

+17-4
Original file line numberDiff line numberDiff line change
@@ -259,18 +259,31 @@ def _load_oid_token(self, provider):
259259
if 'config' not in provider:
260260
return
261261

262-
parts = provider['config']['id-token'].split('.')
262+
reserved_characters = frozenset(["=", "+", "/"])
263+
token = provider['config']['id-token']
263264

265+
if any(char in token for char in reserved_characters):
266+
# Invalid jwt, as it contains url-unsafe chars
267+
return
268+
269+
parts = token.split('.')
264270
if len(parts) != 3: # Not a valid JWT
265-
return None
271+
return
272+
273+
padding = (4 - len(parts[1]) % 4) * '='
274+
if len(padding) == 3:
275+
# According to spec, 3 padding characters cannot occur
276+
# in a valid jwt
277+
# https://tools.ietf.org/html/rfc7515#appendix-C
278+
return
266279

267280
if PY3:
268281
jwt_attributes = json.loads(
269-
base64.b64decode(parts[1]).decode('utf-8')
282+
base64.b64decode(parts[1] + padding).decode('utf-8')
270283
)
271284
else:
272285
jwt_attributes = json.loads(
273-
base64.b64decode(parts[1] + "==")
286+
base64.b64decode(parts[1] + padding)
274287
)
275288

276289
expire = jwt_attributes.get('exp')

config/kube_config_test.py

+97-4
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,10 @@ def _base64(string):
4747
return base64.standard_b64encode(string.encode()).decode()
4848

4949

50+
def _urlsafe_unpadded_b64encode(string):
51+
return base64.urlsafe_b64encode(string.encode()).decode().rstrip('=')
52+
53+
5054
def _format_expiry_datetime(dt):
5155
return dt.strftime(EXPIRY_DATETIME_FORMAT)
5256

@@ -94,12 +98,33 @@ def _raise_exception(st):
9498

9599
TEST_OIDC_TOKEN = "test-oidc-token"
96100
TEST_OIDC_INFO = "{\"name\": \"test\"}"
97-
TEST_OIDC_BASE = _base64(TEST_OIDC_TOKEN) + "." + _base64(TEST_OIDC_INFO)
98-
TEST_OIDC_LOGIN = TEST_OIDC_BASE + "." + TEST_CLIENT_CERT_BASE64
101+
TEST_OIDC_BASE = ".".join([
102+
_urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
103+
_urlsafe_unpadded_b64encode(TEST_OIDC_INFO)
104+
])
105+
TEST_OIDC_LOGIN = ".".join([
106+
TEST_OIDC_BASE,
107+
_urlsafe_unpadded_b64encode(TEST_CLIENT_CERT_BASE64)
108+
])
99109
TEST_OIDC_TOKEN = "Bearer %s" % TEST_OIDC_LOGIN
100110
TEST_OIDC_EXP = "{\"name\": \"test\",\"exp\": 536457600}"
101-
TEST_OIDC_EXP_BASE = _base64(TEST_OIDC_TOKEN) + "." + _base64(TEST_OIDC_EXP)
102-
TEST_OIDC_EXPIRED_LOGIN = TEST_OIDC_EXP_BASE + "." + TEST_CLIENT_CERT_BASE64
111+
TEST_OIDC_EXP_BASE = _urlsafe_unpadded_b64encode(
112+
TEST_OIDC_TOKEN) + "." + _urlsafe_unpadded_b64encode(TEST_OIDC_EXP)
113+
TEST_OIDC_EXPIRED_LOGIN = ".".join([
114+
TEST_OIDC_EXP_BASE,
115+
_urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
116+
])
117+
TEST_OIDC_CONTAINS_RESERVED_CHARACTERS = ".".join([
118+
_urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
119+
_urlsafe_unpadded_b64encode(TEST_OIDC_INFO).replace("a", "+"),
120+
_urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
121+
])
122+
TEST_OIDC_INVALID_PADDING_LENGTH = ".".join([
123+
_urlsafe_unpadded_b64encode(TEST_OIDC_TOKEN),
124+
"aaaaa",
125+
_urlsafe_unpadded_b64encode(TEST_CLIENT_CERT)
126+
])
127+
103128
TEST_OIDC_CA = _base64(TEST_CERTIFICATE_AUTH)
104129

105130

@@ -406,6 +431,22 @@ class TestKubeConfigLoader(BaseTestCase):
406431
"user": "expired_oidc_nocert"
407432
}
408433
},
434+
{
435+
"name": "oidc_contains_reserved_character",
436+
"context": {
437+
"cluster": "default",
438+
"user": "oidc_contains_reserved_character"
439+
440+
}
441+
},
442+
{
443+
"name": "oidc_invalid_padding_length",
444+
"context": {
445+
"cluster": "default",
446+
"user": "oidc_invalid_padding_length"
447+
448+
}
449+
},
409450
{
410451
"name": "user_pass",
411452
"context": {
@@ -592,6 +633,38 @@ class TestKubeConfigLoader(BaseTestCase):
592633
}
593634
}
594635
},
636+
{
637+
"name": "oidc_contains_reserved_character",
638+
"user": {
639+
"auth-provider": {
640+
"name": "oidc",
641+
"config": {
642+
"client-id": "tectonic-kubectl",
643+
"client-secret": "FAKE_SECRET",
644+
"id-token": TEST_OIDC_CONTAINS_RESERVED_CHARACTERS,
645+
"idp-issuer-url": "https://example.org/identity",
646+
"refresh-token":
647+
"lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
648+
}
649+
}
650+
}
651+
},
652+
{
653+
"name": "oidc_invalid_padding_length",
654+
"user": {
655+
"auth-provider": {
656+
"name": "oidc",
657+
"config": {
658+
"client-id": "tectonic-kubectl",
659+
"client-secret": "FAKE_SECRET",
660+
"id-token": TEST_OIDC_INVALID_PADDING_LENGTH,
661+
"idp-issuer-url": "https://example.org/identity",
662+
"refresh-token":
663+
"lucWJjEhlxZW01cXI3YmVlcYnpxNGhzk"
664+
}
665+
}
666+
}
667+
},
595668
{
596669
"name": "user_pass",
597670
"user": {
@@ -790,6 +863,26 @@ def test_oidc_with_refresh_nocert(
790863
self.assertTrue(loader._load_auth_provider_token())
791864
self.assertEqual("Bearer abc123", loader.token)
792865

866+
def test_oidc_fails_if_contains_reserved_chars(self):
867+
loader = KubeConfigLoader(
868+
config_dict=self.TEST_KUBE_CONFIG,
869+
active_context="oidc_contains_reserved_character",
870+
)
871+
self.assertEqual(
872+
loader._load_oid_token("oidc_contains_reserved_character"),
873+
None,
874+
)
875+
876+
def test_oidc_fails_if_invalid_padding_length(self):
877+
loader = KubeConfigLoader(
878+
config_dict=self.TEST_KUBE_CONFIG,
879+
active_context="oidc_invalid_padding_length",
880+
)
881+
self.assertEqual(
882+
loader._load_oid_token("oidc_invalid_padding_length"),
883+
None,
884+
)
885+
793886
def test_user_pass(self):
794887
expected = FakeConfig(host=TEST_HOST, token=TEST_BASIC_TOKEN)
795888
actual = FakeConfig()

run_tox.sh

+1
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ popd > /dev/null
3636
cd "${TMP_DIR}"
3737
git clone https://github.com/kubernetes-client/python.git
3838
cd python
39+
git checkout release-8.0
3940
git config user.email "[email protected]"
4041
git config user.name "kubenetes client"
4142
git rm -rf kubernetes/base

tox.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[tox]
22
skipsdist = True
3-
envlist = py27, py34, py35, py36, py37
3+
envlist = py27, py34, py35, py36
44

55
[testenv]
66
passenv = TOXENV CI TRAVIS TRAVIS_*

0 commit comments

Comments
 (0)