Skip to content

Commit 943745a

Browse files
authored
[v2] Register feature ids for IMDS and HTTP credentials (aws#9745)
1 parent a3871e6 commit 943745a

File tree

3 files changed

+102
-3
lines changed

3 files changed

+102
-3
lines changed

awscli/botocore/credentials.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
UnknownCredentialError,
4242
)
4343
from botocore.tokens import SSOTokenProvider
44+
from botocore.useragent import register_feature_id
4445
from botocore.utils import (
4546
ArnParser,
4647
ContainerMetadataFetcher,
@@ -1130,6 +1131,7 @@ def load(self):
11301131
metadata = fetcher.retrieve_iam_role_credentials()
11311132
if not metadata:
11321133
return None
1134+
register_feature_id('CREDENTIALS_IMDS')
11331135
logger.debug(
11341136
'Found credentials from IAM Role: %s', metadata['role_name']
11351137
)
@@ -2056,6 +2058,7 @@ def fetch_creds():
20562058
response = self._fetcher.retrieve_full_uri(
20572059
full_uri, headers=headers
20582060
)
2061+
register_feature_id('CREDENTIALS_HTTP')
20592062
except MetadataRetrievalError as e:
20602063
logger.debug(
20612064
"Error retrieving container metadata: %s", e, exc_info=True

awscli/botocore/useragent.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@
7676
'FLEXIBLE_CHECKSUMS_REQ_WHEN_REQUIRED': 'a',
7777
'FLEXIBLE_CHECKSUMS_RES_WHEN_SUPPORTED': 'b',
7878
'FLEXIBLE_CHECKSUMS_RES_WHEN_REQUIRED': 'c',
79+
'CREDENTIALS_HTTP': 'z',
80+
'CREDENTIALS_IMDS': '0',
7981
'BEARER_SERVICE_ENV_VARS': '3',
8082
}
8183

tests/functional/botocore/test_credentials.py

Lines changed: 97 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@
1919
import threading
2020
import time
2121
import uuid
22-
from datetime import datetime, timedelta
22+
from datetime import datetime, timedelta, timezone
23+
from unittest.mock import patch
2324

2425
import pytest
2526
from botocore import UNSIGNED
@@ -51,6 +52,7 @@
5152

5253
from tests import (
5354
BaseEnvVar,
55+
ClientHTTPStubber,
5456
BaseSessionTest,
5557
IntegerRefresher,
5658
SessionHTTPStubber,
@@ -60,9 +62,13 @@
6062
temporary_file,
6163
unittest,
6264
)
65+
from tests.functional.test_useragent import (
66+
get_captured_ua_strings,
67+
parse_registered_feature_ids,
68+
)
6369

64-
TIME_IN_ONE_HOUR = datetime.utcnow() + timedelta(hours=1)
65-
TIME_IN_SIX_MONTHS = datetime.utcnow() + timedelta(hours=4320)
70+
TIME_IN_ONE_HOUR = datetime.now(tz=timezone.utc) + timedelta(hours=1)
71+
TIME_IN_SIX_MONTHS = datetime.now(tz=timezone.utc) + timedelta(hours=4320)
6672

6773

6874
class TestCredentialRefreshRaces(unittest.TestCase):
@@ -1085,3 +1091,91 @@ def add_credential_response(self, stubber):
10851091
}
10861092
}
10871093
stubber.add_response(body=json.dumps(response).encode('utf-8'))
1094+
1095+
1096+
class TestFeatureIdRegistered:
1097+
@patch(
1098+
"botocore.utils.InstanceMetadataFetcher.retrieve_iam_role_credentials"
1099+
)
1100+
@patch("botocore.credentials.ContainerProvider.load", return_value=None)
1101+
@patch("botocore.credentials.ConfigProvider.load", return_value=None)
1102+
@patch(
1103+
"botocore.credentials.SharedCredentialProvider.load", return_value=None
1104+
)
1105+
@patch("botocore.credentials.EnvProvider.load", return_value=None)
1106+
def test_user_agent_has_imds_credentials_feature_id(
1107+
self,
1108+
_unused_mock_env_load,
1109+
_unused_mock_shared_load,
1110+
_unused_mock_config_load,
1111+
_unused_mock_container_load,
1112+
mock_retrieve_iam_role_credentials,
1113+
patched_session,
1114+
):
1115+
fake_creds = {
1116+
"role_name": "FAKEROLE",
1117+
"access_key": "FAKEACCESSKEY",
1118+
"secret_key": "FAKESECRET",
1119+
"token": "FAKETOKEN",
1120+
"expiry_time": "2099-01-01T00:00:00Z",
1121+
}
1122+
mock_retrieve_iam_role_credentials.return_value = fake_creds
1123+
1124+
client = patched_session.create_client("s3", region_name="us-east-1")
1125+
with ClientHTTPStubber(client, strict=True) as http_stubber:
1126+
# We want to call this twice to assert that the feature id exists
1127+
# for multiple calls with the same credentials
1128+
http_stubber.add_response()
1129+
http_stubber.add_response()
1130+
client.list_buckets()
1131+
client.list_buckets()
1132+
1133+
ua_string = get_captured_ua_strings(http_stubber)
1134+
feature_list_one = parse_registered_feature_ids(ua_string[0])
1135+
feature_list_two = parse_registered_feature_ids(ua_string[1])
1136+
assert '0' in feature_list_one and '0' in feature_list_two
1137+
1138+
@patch("botocore.credentials.ContainerMetadataFetcher.retrieve_full_uri")
1139+
@patch("botocore.credentials.ConfigProvider.load", return_value=None)
1140+
@patch(
1141+
"botocore.credentials.SharedCredentialProvider.load", return_value=None
1142+
)
1143+
@patch("botocore.credentials.EnvProvider.load", return_value=None)
1144+
def test_user_agent_has_http_credentials_feature_id(
1145+
self,
1146+
_unused_mock_env_load,
1147+
_unused_mock_shared_load,
1148+
_unused_mock_config_load,
1149+
mock_load_http_credentials,
1150+
monkeypatch,
1151+
patched_session,
1152+
):
1153+
environ = {
1154+
'AWS_CONTAINER_CREDENTIALS_FULL_URI': 'http://localhost/foo',
1155+
'AWS_CONTAINER_AUTHORIZATION_TOKEN': 'Basic auth-token',
1156+
}
1157+
for var in environ:
1158+
monkeypatch.setenv(var, environ[var])
1159+
1160+
fake_creds = {
1161+
"AccessKeyId": "FAKEACCESSKEY",
1162+
"SecretAccessKey": "FAKESECRET",
1163+
"Token": "FAKETOKEN",
1164+
"Expiration": "2099-01-01T00:00:00Z",
1165+
"AccountId": "01234567890",
1166+
}
1167+
mock_load_http_credentials.return_value = fake_creds
1168+
1169+
client = patched_session.create_client("s3", region_name="us-east-1")
1170+
with ClientHTTPStubber(client, strict=True) as http_stubber:
1171+
# We want to call this twice to assert that the feature id exists
1172+
# for multiple calls with the same credentials
1173+
http_stubber.add_response()
1174+
http_stubber.add_response()
1175+
client.list_buckets()
1176+
client.list_buckets()
1177+
1178+
ua_string = get_captured_ua_strings(http_stubber)
1179+
feature_list_one = parse_registered_feature_ids(ua_string[0])
1180+
feature_list_two = parse_registered_feature_ids(ua_string[1])
1181+
assert 'z' in feature_list_one and 'z' in feature_list_two

0 commit comments

Comments
 (0)