Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

SigV4 Authentication support for http/protobuf exporter #324

Merged
merged 42 commits into from
Feb 13, 2025
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
b629da3
added sigv4 authentication to otlp exporter
liustve Feb 5, 2025
bd4e1d6
added unit tests
liustve Feb 6, 2025
7187839
removed logging
liustve Feb 6, 2025
6422607
more testing
liustve Feb 7, 2025
6462b66
Merge branch 'aws-observability:main' into sigv4_support
liustve Feb 10, 2025
a34e899
added extra test
liustve Feb 11, 2025
5fc6cfb
fixing sanitation issue
liustve Feb 11, 2025
db6d384
formatting
liustve Feb 11, 2025
579efb3
fix arbitrary url error
liustve Feb 11, 2025
f97fd24
linting imports
liustve Feb 11, 2025
451f194
linting fix
liustve Feb 11, 2025
6ce4d68
linting fix
liustve Feb 11, 2025
364f9de
linting fix
liustve Feb 11, 2025
f217ed1
lint fix
liustve Feb 11, 2025
5637278
linting fix
liustve Feb 11, 2025
8e1d0eb
lint fix
liustve Feb 11, 2025
bb591a2
linting fix
liustve Feb 11, 2025
ad4c0a0
linting fix
liustve Feb 11, 2025
f943b07
made botocore an optional dependency if not using otlp cw endpoint
liustve Feb 11, 2025
c796162
comments + linting fix
liustve Feb 11, 2025
0b65642
linting fix
liustve Feb 11, 2025
561fa01
linting fix
liustve Feb 11, 2025
4a46f99
Merge branch 'main' into sigv4_support
liustve Feb 12, 2025
bba778d
addressing comments
liustve Feb 12, 2025
1c8004c
linting fix
liustve Feb 12, 2025
de0e89f
linting fix
liustve Feb 12, 2025
c207167
tests + linting fix
liustve Feb 12, 2025
556b037
renaming
liustve Feb 12, 2025
b7749f8
lint
liustve Feb 12, 2025
e9bf1f1
linting + test fix
liustve Feb 12, 2025
56d7470
linting fix
liustve Feb 12, 2025
532ab25
linting fix
liustve Feb 12, 2025
6cb0f55
fixed test
liustve Feb 13, 2025
9a47ab3
lint fix + test fix
liustve Feb 13, 2025
407bcdc
linting fix
liustve Feb 13, 2025
46d7586
changed to broader exception
liustve Feb 13, 2025
01e74f8
linting fix
liustve Feb 13, 2025
667ac52
removed is xray otlp endpoint validation in the span exporter
liustve Feb 13, 2025
1b33012
linting fix
liustve Feb 13, 2025
c7d8410
removed unused import
liustve Feb 13, 2025
5aa970e
Merge branch 'main' into sigv4_support
liustve Feb 13, 2025
6cf44bd
removed validation for aws region
liustve Feb 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
# SPDX-License-Identifier: Apache-2.0
# Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
import os
import re
from logging import Logger, getLogger
from typing import ClassVar, Dict, List, Type, Union

Expand All @@ -19,6 +20,7 @@
AwsMetricAttributesSpanExporterBuilder,
)
from amazon.opentelemetry.distro.aws_span_metrics_processor_builder import AwsSpanMetricsProcessorBuilder
from amazon.opentelemetry.distro.otlp_sigv4_exporter import OTLPAwsSigV4Exporter
from amazon.opentelemetry.distro.otlp_udp_exporter import OTLPUdpSpanExporter
from amazon.opentelemetry.distro.sampler.aws_xray_remote_sampler import AwsXRayRemoteSampler
from amazon.opentelemetry.distro.scope_based_exporter import ScopeBasedPeriodicExportingMetricReader
Expand Down Expand Up @@ -315,6 +317,9 @@ def _customize_exporter(span_exporter: SpanExporter, resource: Resource) -> Span
traces_endpoint = os.environ.get(AWS_XRAY_DAEMON_ADDRESS_CONFIG, "127.0.0.1:2000")
span_exporter = OTLPUdpSpanExporter(endpoint=traces_endpoint)

if _is_otlp_endpoint_cloudwatch():
span_exporter = OTLPAwsSigV4Exporter(endpoint=os.getenv(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT))

if not _is_application_signals_enabled():
return span_exporter

Expand All @@ -328,6 +333,10 @@ def _customize_span_processors(provider: TracerProvider, resource: Resource) ->
# Construct and set local and remote attributes span processor
provider.add_span_processor(AttributePropagatingSpanProcessorBuilder().build())

# Do not export metrics if it's CloudWatch OTLP endpoint
if _is_otlp_endpoint_cloudwatch():
return

# Export 100% spans and not export Application-Signals metrics if on Lambda.
if _is_lambda_environment():
_export_unsampled_span_for_lambda(provider, resource)
Expand Down Expand Up @@ -437,6 +446,16 @@ def _is_lambda_environment():
return AWS_LAMBDA_FUNCTION_NAME_CONFIG in os.environ


def _is_otlp_endpoint_cloudwatch():
# Detects if it's the OTLP endpoint in CloudWatchs
otlp_endpoint = os.environ.get(OTEL_EXPORTER_OTLP_TRACES_ENDPOINT)
if not otlp_endpoint:
return False
pattern = r"xray\.([a-z0-9-]+)\.amazonaws\.com"

return bool(re.match(pattern, otlp_endpoint.lower()))


def _get_metric_export_interval():
export_interval_millis = float(os.environ.get(METRIC_EXPORT_INTERVAL_CONFIG, DEFAULT_METRIC_EXPORT_INTERVAL))
_logger.debug("Span Metrics export interval: %s", export_interval_millis)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
# Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
# SPDX-License-Identifier: Apache-2.0
import logging
import re
from typing import Dict, Optional

import requests
from botocore import session
from botocore.auth import NoCredentialsError, SigV4Auth
from botocore.awsrequest import AWSRequest
from grpc import Compression

from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter

AWS_SERVICE = "xray"

_logger = logging.getLogger(__name__)


class OTLPAwsSigV4Exporter(OTLPSpanExporter):

def __init__(
self,
endpoint: Optional[str] = None,
certificate_file: Optional[str] = None,
client_key_file: Optional[str] = None,
client_certificate_file: Optional[str] = None,
headers: Optional[Dict[str, str]] = None,
timeout: Optional[int] = None,
compression: Optional[Compression] = None,
rsession: Optional[requests.Session] = None,
):

self._aws_region = self._validate_exporter_endpoint(endpoint)
super().__init__(
endpoint=endpoint,
certificate_file=certificate_file,
client_key_file=client_key_file,
client_certificate_file=client_certificate_file,
headers=headers,
timeout=timeout,
compression=compression,
session=rsession,
)

def _export(self, serialized_data: bytes):
if self._aws_region:
request = AWSRequest(
method="POST",
url=self._endpoint,
data=serialized_data,
headers={"Content-Type": "application/x-protobuf"},
)

botocore_session = session.Session()
credentials = botocore_session.get_credentials()
if credentials is not None:
signer = SigV4Auth(credentials, AWS_SERVICE, self._aws_region)

try:
signer.add_auth(request)
self._session.headers.update(dict(request.headers))

except NoCredentialsError as signing_error:
_logger.error("Failed to sign request: %s", signing_error)

else:
_logger.error("Failed to get credentials to export span to OTLP CloudWatch endpoint")

return super()._export(serialized_data)

@staticmethod
def _validate_exporter_endpoint(endpoint: str) -> Optional[str]:
if not endpoint:
return None

match = re.search(rf"{AWS_SERVICE}\.([a-z0-9-]+)\.amazonaws\.com", endpoint)

if match:
region = match.group(1)
xray_regions = session.Session().get_available_regions(AWS_SERVICE)
if region in xray_regions:
return region

_logger.error(
"Invalid AWS region: %s. Valid regions are %s. Resolving to default endpoint.", region, xray_regions
)
return None

_logger.error(
"Invalid XRay traces endpoint: %s. Resolving to default endpoint. "
"The traces endpoint follows the pattern https://xray.[AWSRegion].amazonaws.com/v1/traces. "
"For example, for the US West (Oregon) (us-west-2) Region, the endpoint will be "
"https://xray.us-west-2.amazonaws.com/v1/traces.",
endpoint,
)

return None
Loading
Loading