Skip to content

Commit 366e624

Browse files
Only allow x-trusted-proxy header to be valid for so long (ansible#574)
1 parent 06f2879 commit 366e624

File tree

2 files changed

+44
-3
lines changed

2 files changed

+44
-3
lines changed

ansible_base/jwt_consumer/common/util.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
11
import logging
22
import time
3+
from base64 import b64encode
34

45
from cryptography.exceptions import InvalidSignature
56
from cryptography.hazmat.primitives import hashes, serialization
67
from cryptography.hazmat.primitives.asymmetric import padding
78

89
from ansible_base.jwt_consumer.common.cert import JWTCert, JWTCertException
10+
from ansible_base.lib.utils.settings import get_setting
911

1012
logger = logging.getLogger('ansible_base.jwt_consumer.common.util')
1113

@@ -42,6 +44,15 @@ def validate_x_trusted_proxy_header(header_value: str, ignore_cache=False) -> bo
4244
logger.warning("Failed to validate x-trusted-proxy-header, malformed, expected value to contain a -")
4345
return False
4446

47+
# Validate that the header has been cut within the last 300ms (by default)
48+
try:
49+
if time.time_ns() - int(timestamp) > get_setting('trusted_header_timeout_in_ns', 300000000):
50+
logger.warning(f"Timestamp {timestamp} was too old to be valid alter trusted_header_timeout_in_ns if needed")
51+
return False
52+
except ValueError:
53+
logger.warning(f"Unable to convert timestamp (base64) {b64encode(timestamp.encode('UTF-8'))} into an integer")
54+
return False
55+
4556
try:
4657
signature_bytes = bytes.fromhex(signature)
4758
except ValueError:

test_app/tests/jwt_consumer/common/test_util.py

Lines changed: 33 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import time
12
from unittest import mock
23

34
from django.test.utils import override_settings
@@ -30,14 +31,43 @@ def test_validate_trusted_proxy_header_bad_public_key(self, random_public_key):
3031
with override_settings(ANSIBLE_BASE_JWT_KEY=random_public_key):
3132
assert not validate_x_trusted_proxy_header("0-12345123451234512345")
3233

33-
def test_validate_x_trusted_proxy_header_invalid_signature(self, random_public_key, expected_log):
34+
def test_header_timeout(self, expected_log, rsa_keypair):
35+
header = generate_x_trusted_proxy_header(rsa_keypair.private)
36+
with override_settings(ANSIBLE_BASE_JWT_KEY=rsa_keypair.public):
37+
# Assert this header is valid if used right away
38+
assert validate_x_trusted_proxy_header(header) is True
39+
40+
# By default the header is only valid for 300ms so a 1/2 second sleep will expire it
41+
time.sleep(0.5)
42+
with expected_log(
43+
'ansible_base.jwt_consumer.common.util.logger',
44+
'warning',
45+
'was too old to be valid alter trusted_header_timeout_in_ns if needed',
46+
):
47+
assert validate_x_trusted_proxy_header(header) is False
48+
49+
def test_invalid_header_timestamp(self, expected_log, rsa_keypair):
50+
header = generate_x_trusted_proxy_header(rsa_keypair.private)
51+
_, signed_part = header.split('-')
52+
header = f'asdf-{signed_part}'
53+
with override_settings(ANSIBLE_BASE_JWT_KEY=rsa_keypair.public):
54+
with expected_log(
55+
'ansible_base.jwt_consumer.common.util.logger',
56+
'warning',
57+
'Unable to convert timestamp (base64)',
58+
):
59+
assert validate_x_trusted_proxy_header(header) is False
60+
61+
def test_validate_x_trusted_proxy_header_invalid_signature(self, random_public_key, expected_log, rsa_keypair):
3462
with override_settings(ANSIBLE_BASE_JWT_KEY=random_public_key):
35-
# Idealy we would mock match bytes.fromhex but I couldn't get that to work
63+
# Ideally we would mock match bytes.fromhex but I couldn't get that to work
3664
# with mock.patch('ansible_base.jwt_consumer.common.util.validate_x_trusted_proxy_header.bytes.fromhex', side_effect=ValueError()):
65+
header = generate_x_trusted_proxy_header(rsa_keypair.private)
3766
with expected_log(
3867
'ansible_base.jwt_consumer.common.util.logger',
3968
'warning',
4069
'Failed to validate x-trusted-proxy-header, malformed, expected signature to well-formed base64',
4170
):
4271
# 0 is invalid bytes
43-
assert validate_x_trusted_proxy_header("0-0") is False
72+
timestamp, junk = header.split('-')
73+
assert validate_x_trusted_proxy_header(f"{timestamp}-0") is False

0 commit comments

Comments
 (0)