Skip to content

Commit 62b561c

Browse files
Dan RogersCircleCI
authored andcommitted
Add support for regular expression matching and sanitizing of headers in Falcon. (open-telemetry#1412)
1 parent d0a3fe5 commit 62b561c

File tree

4 files changed

+125
-31
lines changed

4 files changed

+125
-31
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
2929
([#1323](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1323))
3030
- `opentelemetry-instrumentation-wsgi` Add support for regular expression matching and sanitization of HTTP headers.
3131
([#1402](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1402))
32+
- `opentelemetry-instrumentation-falcon` Add support for regular expression matching and sanitization of HTTP headers.
33+
([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412))
3234
- `opentelemetry-instrumentation-flask` Add support for regular expression matching and sanitization of HTTP headers.
3335
([#1413](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1413))
3436
- `opentelemetry-instrumentation-pyramid` Add support for regular expression matching and sanitization of HTTP headers.
@@ -44,6 +46,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4446
([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333))
4547
- `opentelemetry-instrumentation-asgi` Make ASGIGetter.get() compare all keys in a case insensitive manner.
4648
([#1333](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1333))
49+
- Use resp.text instead of resp.body for Falcon 3 to avoid a deprecation warning.
50+
([#1412](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1412))
4751

4852
## [1.13.0-0.34b0](https://github.com/open-telemetry/opentelemetry-python/releases/tag/v1.13.0-0.34b0) - 2022-09-26
4953

instrumentation/opentelemetry-instrumentation-falcon/src/opentelemetry/instrumentation/falcon/__init__.py

Lines changed: 69 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,16 @@
1919
2020
* The Falcon resource and method name is used as the Span name.
2121
* The ``falcon.resource`` Span attribute is set so the matched resource.
22-
* Error from Falcon resources are properly caught and recorded.
22+
* Errors from Falcon resources are properly caught and recorded.
2323
2424
Configuration
2525
-------------
2626
2727
Exclude lists
2828
*************
29-
To exclude certain URLs from being tracked, set the environment variable ``OTEL_PYTHON_FALCON_EXCLUDED_URLS``
30-
(or ``OTEL_PYTHON_EXCLUDED_URLS`` as fallback) with comma delimited regexes representing which URLs to exclude.
29+
To exclude certain URLs from tracking, set the environment variable ``OTEL_PYTHON_FALCON_EXCLUDED_URLS``
30+
(or ``OTEL_PYTHON_EXCLUDED_URLS`` to cover all instrumentations) to a string of comma delimited regexes that match the
31+
URLs.
3132
3233
For example,
3334
@@ -39,16 +40,16 @@
3940
4041
Request attributes
4142
********************
42-
To extract certain attributes from Falcon's request object and use them as span attributes, set the environment variable ``OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS`` to a comma
43-
delimited list of request attribute names.
43+
To extract attributes from Falcon's request object and use them as span attributes, set the environment variable
44+
``OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS`` to a comma delimited list of request attribute names.
4445
4546
For example,
4647
4748
::
4849
4950
export OTEL_PYTHON_FALCON_TRACED_REQUEST_ATTRS='query_string,uri_template'
5051
51-
will extract query_string and uri_template attributes from every traced request and add them as span attritbues.
52+
will extract the ``query_string`` and ``uri_template`` attributes from every traced request and add them as span attributes.
5253
5354
Falcon Request object reference: https://falcon.readthedocs.io/en/stable/api/request_and_response.html#id1
5455
@@ -73,8 +74,9 @@ def on_get(self, req, resp):
7374
7475
Request and Response hooks
7576
***************************
76-
The instrumentation supports specifying request and response hooks. These are functions that get called back by the instrumentation right after a Span is created for a request
77-
and right before the span is finished while processing a response. The hooks can be configured as follows:
77+
This instrumentation supports request and response hooks. These are functions that get called
78+
right after a span is created for a request and right before the span is finished for the response.
79+
The hooks can be configured as follows:
7880
7981
::
8082
@@ -88,54 +90,93 @@ def response_hook(span, req, resp):
8890
8991
Capture HTTP request and response headers
9092
*****************************************
91-
You can configure the agent to capture predefined HTTP headers as span attributes, according to the `semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.
93+
You can configure the agent to capture specified HTTP headers as span attributes, according to the
94+
`semantic convention <https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/semantic_conventions/http.md#http-request-and-response-headers>`_.
9295
9396
Request headers
9497
***************
95-
To capture predefined HTTP request headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST``
96-
to a comma-separated list of HTTP header names.
98+
To capture HTTP request headers as span attributes, set the environment variable
99+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST`` to a comma delimited list of HTTP header names.
97100
98101
For example,
99-
100102
::
101103
102104
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="content-type,custom_request_header"
103105
104-
will extract ``content-type`` and ``custom_request_header`` from request headers and add them as span attributes.
106+
will extract ``content-type`` and ``custom_request_header`` from the request headers and add them as span attributes.
107+
108+
Request header names in Falcon are case-insensitive and ``-`` characters are replaced by ``_``. So, giving the header
109+
name as ``CUStom_Header`` in the environment variable will capture the header named ``custom-header``.
110+
111+
Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example:
112+
::
113+
114+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST="Accept.*,X-.*"
105115
106-
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
107-
Request header names in falcon are case insensitive and - characters are replaced by _. So, giving header name as ``CUStom_Header`` in environment variable will be able capture header with name ``custom-header``.
116+
Would match all request headers that start with ``Accept`` and ``X-``.
108117
109-
The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
110-
The value of the attribute will be single item list containing all the header values.
118+
To capture all request headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST`` to ``".*"``.
119+
::
111120
112-
Example of the added span attribute,
121+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST=".*"
122+
123+
The name of the added span attribute will follow the format ``http.request.header.<header_name>`` where ``<header_name>``
124+
is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a
125+
single item list containing all the header values.
126+
127+
For example:
113128
``http.request.header.custom_request_header = ["<value1>,<value2>"]``
114129
115130
Response headers
116131
****************
117-
To capture predefined HTTP response headers as span attributes, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE``
118-
to a comma-separated list of HTTP header names.
132+
To capture HTTP response headers as span attributes, set the environment variable
133+
``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE`` to a comma delimited list of HTTP header names.
119134
120135
For example,
121-
122136
::
123137
124138
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="content-type,custom_response_header"
125139
126-
will extract ``content-type`` and ``custom_response_header`` from response headers and add them as span attributes.
140+
will extract ``content-type`` and ``custom_response_header`` from the response headers and add them as span attributes.
141+
142+
Response header names in Falcon are case-insensitive. So, giving the header name as ``CUStom-Header`` in the environment
143+
variable will capture the header named ``custom-header``.
144+
145+
Regular expressions may also be used to match multiple headers that correspond to the given pattern. For example:
146+
::
147+
148+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE="Content.*,X-.*"
149+
150+
Would match all response headers that start with ``Content`` and ``X-``.
127151
128-
It is recommended that you should give the correct names of the headers to be captured in the environment variable.
129-
Response header names captured in falcon are case insensitive. So, giving header name as ``CUStomHeader`` in environment variable will be able capture header with name ``customheader``.
152+
To capture all response headers, set ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE`` to ``".*"``.
153+
::
154+
155+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE=".*"
130156
131-
The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>`` being the normalized HTTP header name (lowercase, with - characters replaced by _ ).
132-
The value of the attribute will be single item list containing all the header values.
157+
The name of the added span attribute will follow the format ``http.response.header.<header_name>`` where ``<header_name>``
158+
is the normalized HTTP header name (lowercase, with ``-`` replaced by ``_``). The value of the attribute will be a
159+
single item list containing all the header values.
133160
134-
Example of the added span attribute,
161+
For example:
135162
``http.response.header.custom_response_header = ["<value1>,<value2>"]``
136163
164+
Sanitizing headers
165+
******************
166+
In order to prevent storing sensitive data such as personally identifiable information (PII), session keys, passwords,
167+
etc, set the environment variable ``OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS``
168+
to a comma delimited list of HTTP header names to be sanitized. Regexes may be used, and all header names will be
169+
matched in a case-insensitive manner.
170+
171+
For example,
172+
::
173+
174+
export OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS=".*session.*,set-cookie"
175+
176+
will replace the value of headers such as ``session-id`` and ``set-cookie`` with ``[REDACTED]`` in the span.
177+
137178
Note:
138-
Environment variable names to capture http headers are still experimental, and thus are subject to change.
179+
The environment variable names used to capture HTTP headers are still experimental, and thus are subject to change.
139180
140181
API
141182
---

instrumentation/opentelemetry-instrumentation-falcon/tests/app.py

Lines changed: 16 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,13 @@ class HelloWorldResource:
88
def _handle_request(self, _, resp):
99
# pylint: disable=no-member
1010
resp.status = falcon.HTTP_201
11-
resp.body = "Hello World"
11+
12+
_parsed_falcon_version = package_version.parse(falcon.__version__)
13+
if _parsed_falcon_version < package_version.parse("3.0.0"):
14+
# Falcon 1 and Falcon 2
15+
resp.body = "Hello World"
16+
else:
17+
resp.text = "Hello World"
1218

1319
def on_get(self, req, resp):
1420
self._handle_request(req, resp)
@@ -44,6 +50,15 @@ def on_get(self, _, resp):
4450
"my-custom-header", "my-custom-value-1,my-custom-header-2"
4551
)
4652
resp.set_header("dont-capture-me", "test-value")
53+
resp.set_header(
54+
"my-custom-regex-header-1",
55+
"my-custom-regex-value-1,my-custom-regex-value-2",
56+
)
57+
resp.set_header(
58+
"My-Custom-Regex-Header-2",
59+
"my-custom-regex-value-3,my-custom-regex-value-4",
60+
)
61+
resp.set_header("my-secret-header", "my-secret-value")
4762

4863

4964
def make_app():

instrumentation/opentelemetry-instrumentation-falcon/tests/test_falcon.py

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
from opentelemetry.test.wsgitestutil import WsgiTestBase
4242
from opentelemetry.trace import StatusCode
4343
from opentelemetry.util.http import (
44+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS,
4445
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST,
4546
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE,
4647
)
@@ -421,8 +422,9 @@ def test_mark_span_internal_in_presence_of_span_from_other_framework(self):
421422
@patch.dict(
422423
"os.environ",
423424
{
424-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header",
425-
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header",
425+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SANITIZE_FIELDS: ".*my-secret.*",
426+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_REQUEST: "Custom-Test-Header-1,Custom-Test-Header-2,invalid-header,Regex-Test-Header-.*,Regex-Invalid-Test-Header-.*,.*my-secret.*",
427+
OTEL_INSTRUMENTATION_HTTP_CAPTURE_HEADERS_SERVER_RESPONSE: "content-type,content-length,my-custom-header,invalid-header,my-custom-regex-header-.*,invalid-regex-header-.*,.*my-secret.*",
426428
},
427429
)
428430
class TestCustomRequestResponseHeaders(TestFalconBase):
@@ -431,6 +433,9 @@ def test_custom_request_header_added_in_server_span(self):
431433
"Custom-Test-Header-1": "Test Value 1",
432434
"Custom-Test-Header-2": "TestValue2,TestValue3",
433435
"Custom-Test-Header-3": "TestValue4",
436+
"Regex-Test-Header-1": "Regex Test Value 1",
437+
"regex-test-header-2": "RegexTestValue2,RegexTestValue3",
438+
"My-Secret-Header": "My Secret Value",
434439
}
435440
self.client().simulate_request(
436441
method="GET", path="/hello", headers=headers
@@ -443,6 +448,11 @@ def test_custom_request_header_added_in_server_span(self):
443448
"http.request.header.custom_test_header_2": (
444449
"TestValue2,TestValue3",
445450
),
451+
"http.request.header.regex_test_header_1": ("Regex Test Value 1",),
452+
"http.request.header.regex_test_header_2": (
453+
"RegexTestValue2,RegexTestValue3",
454+
),
455+
"http.request.header.my_secret_header": ("[REDACTED]",),
446456
}
447457
not_expected = {
448458
"http.request.header.custom_test_header_3": ("TestValue4",),
@@ -459,6 +469,9 @@ def test_custom_request_header_not_added_in_internal_span(self):
459469
headers = {
460470
"Custom-Test-Header-1": "Test Value 1",
461471
"Custom-Test-Header-2": "TestValue2,TestValue3",
472+
"Regex-Test-Header-1": "Regex Test Value 1",
473+
"regex-test-header-2": "RegexTestValue2,RegexTestValue3",
474+
"My-Secret-Header": "My Secret Value",
462475
}
463476
self.client().simulate_request(
464477
method="GET", path="/hello", headers=headers
@@ -470,6 +483,13 @@ def test_custom_request_header_not_added_in_internal_span(self):
470483
"http.request.header.custom_test_header_2": (
471484
"TestValue2,TestValue3",
472485
),
486+
"http.request.header.regex_test_header_1": (
487+
"Regex Test Value 1",
488+
),
489+
"http.request.header.regex_test_header_2": (
490+
"RegexTestValue2,RegexTestValue3",
491+
),
492+
"http.request.header.my_secret_header": ("[REDACTED]",),
473493
}
474494
self.assertEqual(span.kind, trace.SpanKind.INTERNAL)
475495
for key, _ in not_expected.items():
@@ -494,6 +514,13 @@ def test_custom_response_header_added_in_server_span(self):
494514
"http.response.header.my_custom_header": (
495515
"my-custom-value-1,my-custom-header-2",
496516
),
517+
"http.response.header.my_custom_regex_header_1": (
518+
"my-custom-regex-value-1,my-custom-regex-value-2",
519+
),
520+
"http.response.header.my_custom_regex_header_2": (
521+
"my-custom-regex-value-3,my-custom-regex-value-4",
522+
),
523+
"http.response.header.my_secret_header": ("[REDACTED]",),
497524
}
498525
not_expected = {
499526
"http.response.header.dont_capture_me": ("test-value",)
@@ -524,6 +551,13 @@ def test_custom_response_header_not_added_in_internal_span(self):
524551
"http.response.header.my_custom_header": (
525552
"my-custom-value-1,my-custom-header-2",
526553
),
554+
"http.response.header.my_custom_regex_header_1": (
555+
"my-custom-regex-value-1,my-custom-regex-value-2",
556+
),
557+
"http.response.header.my_custom_regex_header_2": (
558+
"my-custom-regex-value-3,my-custom-regex-value-4",
559+
),
560+
"http.response.header.my_secret_header": ("[REDACTED]",),
527561
}
528562
self.assertEqual(span.kind, trace.SpanKind.INTERNAL)
529563
for key, _ in not_expected.items():

0 commit comments

Comments
 (0)