Skip to content

Commit 6ffb216

Browse files
Merge pull request #24 from aws/mirelap-IMDSv2-PR
2 parents 798e71a + 03119b0 commit 6ffb216

File tree

3 files changed

+64
-18
lines changed

3 files changed

+64
-18
lines changed

codeguru_profiler_agent/agent_metadata/aws_ec2_instance.py

Lines changed: 39 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,19 @@
99
EC2_HOST_NAME_URI = DEFAULT_EC2_METADATA_URI + "local-hostname"
1010
EC2_HOST_INSTANCE_TYPE_URI = DEFAULT_EC2_METADATA_URI + "instance-type"
1111

12+
# Used for IMDSv2 to retrieve API token that will be used to call the EC2 METADATA service.
13+
# https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/instancedata-data-retrieval.html
14+
# Bandit marks the following line as risky because it contains the word "token",
15+
# thought it doesn't contain any secret; ignoring with # nosec
16+
# https://bandit.readthedocs.io/en/latest/plugins/b105_hardcoded_password_string.html
17+
EC2_API_TOKEN_URI = "http://169.254.169.254/latest/api/token" # nosec
18+
EC2_METADATA_TOKEN_HEADER_KEY = 'X-aws-ec2-metadata-token' # nosec
19+
EC2_METADATA_TOKEN_TTL_HEADER_KEY = 'X-aws-ec2-metadata-token-ttl-seconds' # nosec
20+
EC2_METADATA_TOKEN_TTL_HEADER_VALUE = '21600' # nosec
21+
1222
logger = logging.getLogger(__name__)
1323

24+
1425
class AWSEC2Instance(FleetInfo):
1526
"""
1627
This class will get and parse the EC2 metadata if available.
@@ -26,29 +37,47 @@ def get_fleet_instance_id(self):
2637
return self.host_name
2738

2839
@classmethod
29-
def __look_up_host_name(cls):
30-
# The id of the fleet element. Eg. host name in ec2.
31-
return http_get(url=EC2_HOST_NAME_URI).read().decode()
40+
def __look_up_host_name(cls, token):
41+
"""
42+
The id of the fleet element. Eg. host name in ec2.
43+
"""
44+
return cls.__look_up_with_IMDSv2(EC2_HOST_NAME_URI, token)
45+
46+
@classmethod
47+
def __look_up_instance_type(cls, token):
48+
"""
49+
The type of the instance. Eg. m5.2xlarge
50+
"""
51+
return cls.__look_up_with_IMDSv2(EC2_HOST_INSTANCE_TYPE_URI, token)
3252

3353
@classmethod
34-
def __look_up_instance_type(cls):
35-
return http_get(url=EC2_HOST_INSTANCE_TYPE_URI).read().decode()
54+
def __look_up_with_IMDSv2(cls, url, token):
55+
return http_get(url=url,
56+
headers={EC2_METADATA_TOKEN_HEADER_KEY: token}) \
57+
.read().decode()
58+
59+
@classmethod
60+
def __look_up_ec2_api_token(cls):
61+
return http_get(url=EC2_API_TOKEN_URI,
62+
headers={EC2_METADATA_TOKEN_TTL_HEADER_KEY: EC2_METADATA_TOKEN_TTL_HEADER_VALUE}) \
63+
.read().decode()
3664

3765
@classmethod
3866
def look_up_metadata(cls):
3967
try:
68+
token = cls.__look_up_ec2_api_token()
4069
return cls(
41-
host_name=cls.__look_up_host_name(),
42-
host_type=cls.__look_up_instance_type()
70+
host_name=cls.__look_up_host_name(token),
71+
host_type=cls.__look_up_instance_type(token)
4372
)
4473
except Exception:
4574
log_exception(logger, "Unable to get Ec2 instance metadata, this is normal when running in a different "
4675
"environment (e.g. Fargate), profiler will still work")
4776
return None
48-
77+
4978
def serialize_to_map(self):
5079
return {
51-
"computeType": "aws_ec2_instance",
52-
"hostName": self.host_name,
80+
"computeType": "aws_ec2_instance",
81+
"hostName": self.host_name,
5382
"hostType": self.host_type
5483
}

codeguru_profiler_agent/agent_metadata/fleet_info.py

Lines changed: 20 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,29 @@
1-
from abc import ABCMeta, abstractmethod
1+
import logging
2+
import os
23
import uuid
4+
5+
from abc import ABCMeta, abstractmethod
36
from urllib import request
4-
import os
7+
from urllib.request import Request
58

69
METADATA_URI_TIMEOUT_SECONDS = 3
710

8-
def http_get(url):
11+
logger = logging.getLogger(__name__)
12+
13+
14+
def http_get(url, headers={}):
915
# request.urlopen has been flagged as risky because it can be used to open local files if url starts with
1016
# file://, to protect us from that we add a check in the passed url.
1117
# With this check we can tell bandit (static analysis tool) to ignore this error with #nosec
18+
# https://bandit.readthedocs.io/en/latest/blacklists/blacklist_calls.html#b310-urllib-urlopen
19+
20+
logger.debug("Making a request to {} with headers set for these keys: {}".format(url, headers.keys()))
1221
if not url.startswith("http"):
1322
raise ValueError("url for metadata is not a valid http address. We will not try to get metadata")
14-
return request.urlopen(url, timeout=METADATA_URI_TIMEOUT_SECONDS) # nosec
23+
req = Request(url)
24+
for key in headers:
25+
req.add_header(key, headers[key])
26+
return request.urlopen(req, timeout=METADATA_URI_TIMEOUT_SECONDS) # nosec
1527

1628

1729
class FleetInfo(metaclass=ABCMeta): # pragma: no cover
@@ -23,6 +35,7 @@ def __init__(self):
2335
This id can be the hostname for an EC2 or the task ARN for Fargate.
2436
@return the id in string.
2537
"""
38+
2639
@abstractmethod
2740
def get_fleet_instance_id(self):
2841
pass
@@ -42,7 +55,7 @@ class DefaultFleetInfo(FleetInfo):
4255

4356
def __init__(self):
4457
self.fleet_instance_id = str(uuid.uuid4())
45-
try:
58+
try:
4659
# sched_getaffinity gives the number of logical cpus that the process can use on Unix systems.
4760
# If not available, we default to the cpu_count().
4861
self.vCPUs = len(os.sched_getaffinity(0))
@@ -54,7 +67,7 @@ def get_fleet_instance_id(self):
5467

5568
def serialize_to_map(self):
5669
return {
57-
"id": self.fleet_instance_id,
58-
"type": "UNKNOWN",
70+
"id": self.fleet_instance_id,
71+
"type": "UNKNOWN",
5972
"vCPUs": self.vCPUs
6073
}

test/unit/agent_metadata/test_aws_ec2_instance.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
import httpretty
33
import sys
44
from codeguru_profiler_agent.agent_metadata.aws_ec2_instance import EC2_HOST_NAME_URI, \
5-
EC2_HOST_INSTANCE_TYPE_URI
5+
EC2_HOST_INSTANCE_TYPE_URI, EC2_API_TOKEN_URI
66
from codeguru_profiler_agent.agent_metadata.aws_ec2_instance import AWSEC2Instance
77

88

@@ -41,6 +41,10 @@ def around(self):
4141
httpretty.GET,
4242
EC2_HOST_INSTANCE_TYPE_URI,
4343
body="testHostType")
44+
httpretty.register_uri(
45+
httpretty.GET,
46+
EC2_API_TOKEN_URI,
47+
body="fakeAndDummy")
4448
yield
4549
httpretty.disable()
4650
httpretty.reset()

0 commit comments

Comments
 (0)