Skip to content

Commit

Permalink
fix: prevent tracing internal requests.
Browse files Browse the repository at this point in the history
The urllib3, library which we instrument, is used by the requests
package, and this last one is used by Instana Python Tracer to connect
with an Instana Agent.

When the INSTANA_ALLOW_EXIT_AS_ROOT environment variable is setup, the
internal requests are traced as EXIT_ROOT spans.

This commit prevents the internal requests to be traced by our lib.

Signed-off-by: Paulo Vital <paulo.vital@ibm.com>
  • Loading branch information
pvital committed Jul 24, 2024
1 parent 84674cb commit 9eeffcf
Show file tree
Hide file tree
Showing 3 changed files with 45 additions and 11 deletions.
37 changes: 28 additions & 9 deletions src/instana/agent/host.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ class HostAgent(BaseAgent):
"""
AGENT_DISCOVERY_PATH = "com.instana.plugin.python.discovery"
AGENT_DATA_PATH = "com.instana.plugin.python.%d"
AGENT_HEADERS = {
"User-Agent": f"instana-python-tracer/{VERSION}",
}

def __init__(self):
super(HostAgent, self).__init__()
Expand Down Expand Up @@ -154,7 +157,8 @@ def is_agent_listening(self, host, port):
result = False
try:
url = "http://%s:%s/" % (host, port)
response = self.client.get(url, timeout=5)
headers = self.AGENT_HEADERS
response = self.client.get(url, timeout=5, headers=headers)

if 200 <= response.status_code < 300:
logger.debug("Instana host agent found on %s:%d", host, port)
Expand All @@ -174,9 +178,11 @@ def announce(self, discovery):
"""
try:
url = self.__discovery_url()
headers = {"Content-Type": "application/json"}
headers.update(self.AGENT_HEADERS)
response = self.client.put(url,
data=to_json(discovery),
headers={"Content-Type": "application/json"},
headers=headers,
timeout=0.8)
except Exception as exc:
logger.debug("announce: connection error (%s)", type(exc))
Expand Down Expand Up @@ -224,10 +230,14 @@ def log_message_to_host_agent(self, message):
payload["m"] = message

url = self.__agent_logger_url()
headers = {
"Content-Type": "application/json",
"X-Log-Level": "INFO",
}
headers.update(self.AGENT_HEADERS)
response = self.client.post(url,
data=to_json(payload),
headers={"Content-Type": "application/json",
"X-Log-Level": "INFO"},
headers=headers,
timeout=0.8)

if 200 <= response.status_code <= 204:
Expand All @@ -241,7 +251,8 @@ def is_agent_ready(self):
"""
ready = False
try:
response = self.client.head(self.__data_url(), timeout=0.8)
headers = self.AGENT_HEADERS
response = self.client.head(self.__data_url(), timeout=0.8, headers=headers)

if response.status_code == 200:
ready = True
Expand All @@ -259,9 +270,11 @@ def report_data_payload(self, payload):
span_count = len(payload['spans'])
if span_count > 0:
logger.debug("Reporting %d spans", span_count)
headers = {"Content-Type": "application/json"}
headers.update(self.AGENT_HEADERS)
response = self.client.post(self.__traces_url(),
data=to_json(payload['spans']),
headers={"Content-Type": "application/json"},
headers=headers,
timeout=0.8)

if response is not None and 200 <= response.status_code <= 204:
Expand All @@ -271,19 +284,23 @@ def report_data_payload(self, payload):
profile_count = len(payload['profiles'])
if profile_count > 0:
logger.debug("Reporting %d profiles", profile_count)
headers = {"Content-Type": "application/json"}
headers.update(self.AGENT_HEADERS)
response = self.client.post(self.__profiles_url(),
data=to_json(payload['profiles']),
headers={"Content-Type": "application/json"},
headers=headers,
timeout=0.8)

if response is not None and 200 <= response.status_code <= 204:
self.last_seen = datetime.now()

# Report metrics
metric_bundle = payload["metrics"]["plugins"][0]["data"]
headers = {"Content-Type": "application/json"}
headers.update(self.AGENT_HEADERS)
response = self.client.post(self.__data_url(),
data=to_json(metric_bundle),
headers={"Content-Type": "application/json"},
headers=headers,
timeout=0.8)

if response is not None and 200 <= response.status_code <= 204:
Expand Down Expand Up @@ -375,9 +392,11 @@ def __task_response(self, message_id, data):

logger.debug("Task response is %s: %s", self.__response_url(message_id), payload)

headers = {"Content-Type": "application/json"}
headers.update(self.AGENT_HEADERS)
response = self.client.post(self.__response_url(message_id),
data=payload,
headers={"Content-Type": "application/json"},
headers=headers,
timeout=0.8)
except Exception as exc:
logger.debug("__task_response: Instana host agent connection error (%s)", type(exc))
Expand Down
4 changes: 2 additions & 2 deletions src/instana/instrumentation/urllib3.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

from ..log import logger
from ..singletons import agent
from ..util.traceutils import get_tracer_tuple, tracing_is_off
from ..util.traceutils import get_tracer_tuple, is_internal_request, tracing_is_off
from ..util.secrets import strip_secrets_from_query

try:
Expand Down Expand Up @@ -78,7 +78,7 @@ def collect_response(scope, response):
def urlopen_with_instana(wrapped, instance, args, kwargs):
tracer, parent_span, operation_name = get_tracer_tuple()
# If we're not tracing, just return; boto3 has it's own visibility
if (tracing_is_off() or (operation_name == 'boto3')):
if tracing_is_off() or (operation_name == 'boto3') or is_internal_request(kwargs.get("headers", {})):
return wrapped(*args, **kwargs)

with tracer.start_active_span("urllib3", child_of=parent_span) as scope:
Expand Down
15 changes: 15 additions & 0 deletions src/instana/util/traceutils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# (c) Copyright IBM Corp. 2021
# (c) Copyright Instana Inc. 2021

from typing import Any, Dict
from ..singletons import agent, tracer, async_tracer, tornado_tracer
from ..log import logger

Expand Down Expand Up @@ -43,3 +44,17 @@ def get_tracer_tuple():

def tracing_is_off():
return not (bool(get_active_tracer()) or agent.options.allow_exit_as_root)


def is_internal_request(headers: Dict[str, Any]) -> bool:
"""
Check for the "instana-python-tracer/{VERSION}" string in the "User-Agent"
header for identify internal requests.
"""
# make all keys from the dict lowercase.
headers = dict(map(lambda kv: (kv[0].lower(), kv[1]), headers.items()))

if "user-agent" in headers.keys():
if "instana-python-tracer" in headers["user-agent"]:
return True
return False

0 comments on commit 9eeffcf

Please sign in to comment.