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

Implement Client Key and Certificate File Support for All OTLP Exporters #4116

Merged
Show file tree
Hide file tree
Changes from 11 commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
39bc279
Implement client key file and client certificate file for all otlp ex…
chittalpatel Dec 14, 2023
6bef4e6
Update CHANGELOG.md
chittalpatel Dec 15, 2023
975b64c
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
srikanthccv Dec 31, 2023
5c7e9a3
Merge remote-tracking branch 'origin/main' into 2991-client-key-and-c…
chittalpatel Jan 5, 2024
df4c502
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
xrmx Apr 8, 2024
5425755
Merge branch '2991-client-key-and-certificate-for-otlp-exporters' of …
sandy2008 Aug 11, 2024
e0b9ac7
Update changelog and add logging to _load_credentials.
sandy2008 Aug 11, 2024
10ba43d
Change CHANGELOG.md PR to #4116
sandy2008 Aug 11, 2024
deba1ab
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
sandy2008 Aug 12, 2024
e59453d
refactor(otlp exporter): refactor client certificate and key handling
sandy2008 Aug 14, 2024
d254a53
refactor(otlp exporter): refactor client certificate and key handling
sandy2008 Aug 14, 2024
b7b5a2f
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
sandy2008 Aug 14, 2024
c87f715
test: Add coverage for OTLP exporters without client certificates
sandy2008 Aug 15, 2024
4f4942f
test: Add coverage for OTLP exporters without client certificates
sandy2008 Aug 15, 2024
de377b5
Merge branch '2991-client-key-and-certificate-for-otlp-exporters' of …
sandy2008 Aug 15, 2024
b16967a
test: Add coverage for OTLP exporters without client certificates
sandy2008 Aug 15, 2024
bfcea4e
fix: fix linting for test_otlp_trace_exporter
sandy2008 Aug 15, 2024
3b62fd5
fix: fix linting for test_otlp_trace_exporter
sandy2008 Aug 15, 2024
13c8963
test: add test case for OTLP log exporter with only certificate set
sandy2008 Aug 16, 2024
2feed0d
refactor: handle FileNotFoundError within _read_file function
sandy2008 Aug 16, 2024
b0901a5
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
sandy2008 Aug 16, 2024
9e1e80d
refactor: handle FileNotFoundError within _read_file function
sandy2008 Aug 16, 2024
cc0166a
Merge branch '2991-client-key-and-certificate-for-otlp-exporters' of …
sandy2008 Aug 16, 2024
5eeb62a
refactor: handle FileNotFoundError within _read_file function
sandy2008 Aug 16, 2024
d0f48d4
fix: fix linting for test_otlp_trace_exporter
sandy2008 Aug 16, 2024
89376ec
fix: fix linting for test_otlp_trace_exporter
sandy2008 Aug 16, 2024
c48d4ee
fix: fix linting for test_otlp_trace_exporter
sandy2008 Aug 18, 2024
e426963
Merge branch 'main' into 2991-client-key-and-certificate-for-otlp-exp…
lzchen Aug 19, 2024
5204b06
refactor: update `_read_file` function to make `file_path` mandatory
sandy2008 Aug 19, 2024
7d8decb
Merge branch '2991-client-key-and-certificate-for-otlp-exporters' of …
sandy2008 Aug 19, 2024
651cc7d
refactor: standardize handling of client certificate and key file ass…
sandy2008 Aug 20, 2024
19f2836
refactor: standardize handling of client certificate and key file ass…
sandy2008 Aug 20, 2024
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#4103](https://github.com/open-telemetry/opentelemetry-python/pull/4103))
- Update semantic conventions to version 1.27.0
([#4104](https://github.com/open-telemetry/opentelemetry-python/pull/4104))
- Implement Client Key and Certificate File Support for All OTLP Exporters
([#4116](https://github.com/open-telemetry/opentelemetry-python/pull/4116))

## Version 1.26.0/0.47b0 (2024-07-25)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@

from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
Expand Down Expand Up @@ -71,7 +73,10 @@ def __init__(
and environ.get(OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE) is not None
):
credentials = _get_credentials(
credentials, OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE
credentials,
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
)

environ_timeout = environ.get(OTEL_EXPORTER_OTLP_LOGS_TIMEOUT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
)
from opentelemetry.proto.resource.v1.resource_pb2 import Resource # noqa: F401
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_ENDPOINT,
Expand Down Expand Up @@ -118,22 +120,48 @@ def get_resource_data(
return _get_resource_data(sdk_resource_scope_data, resource_class, name)


def _load_credential_from_file(filepath) -> ChannelCredentials:
def _get_file_content(file_path: str) -> bytes:
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
with open(file_path, "rb") as file:
return file.read()
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved


def _load_credentials(
certificate_file: str,
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
client_key_file: str,
client_certificate_file: str,
) -> Optional[ChannelCredentials]:
try:
with open(filepath, "rb") as creds_file:
credential = creds_file.read()
return ssl_channel_credentials(credential)
except FileNotFoundError:
logger.exception("Failed to read credential file")
root_certificates = _get_file_content(certificate_file)
private_key = _get_file_content(client_key_file)
certificate_chain = _get_file_content(client_certificate_file)
return ssl_channel_credentials(
root_certificates=root_certificates,
private_key=private_key,
certificate_chain=certificate_chain,
)
except FileNotFoundError as e:
logger.exception(
f"Failed to read credential file: {e.filename}. Please check if the file exists and is accessible."
)
return None
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved


def _get_credentials(creds, environ_key):
def _get_credentials(
creds: Optional[ChannelCredentials],
certificate_file_env_key: str,
client_key_file_env_key: str,
client_certificate_file_env_key: str,
) -> ChannelCredentials:
if creds is not None:
return creds
creds_env = environ.get(environ_key)
if creds_env:
return _load_credential_from_file(creds_env)

certificate_file = environ.get(certificate_file_env_key)
if certificate_file:
client_key_file = environ.get(client_key_file_env_key)
client_certificate_file = environ.get(client_certificate_file_env_key)
return _load_credentials(
certificate_file, client_key_file, client_certificate_file
)
return ssl_channel_credentials()


Expand Down Expand Up @@ -214,7 +242,10 @@ def __init__(
)
else:
credentials = _get_credentials(
credentials, OTEL_EXPORTER_OTLP_CERTIFICATE
credentials,
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
)
self._client = self._stub(
secure_channel(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,8 @@
from opentelemetry.proto.metrics.v1 import metrics_pb2 as pb2 # noqa: F401
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
Expand Down Expand Up @@ -113,7 +115,10 @@ def __init__(
and environ.get(OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE) is not None
):
credentials = _get_credentials(
credentials, OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE
credentials,
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
)

environ_timeout = environ.get(OTEL_EXPORTER_OTLP_METRICS_TIMEOUT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
)
from opentelemetry.proto.trace.v1.trace_pb2 import Status # noqa: F401
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY,
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
Expand Down Expand Up @@ -105,7 +107,10 @@ def __init__(
and environ.get(OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE) is not None
):
credentials = _get_credentials(
credentials, OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE
credentials,
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE,
)

environ_timeout = environ.get(OTEL_EXPORTER_OTLP_TRACES_TIMEOUT)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,8 @@
from opentelemetry.sdk._logs.export import LogExportResult
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
Expand Down Expand Up @@ -212,6 +214,10 @@ def test_exporting(self):
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT: "logs:4317",
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE: THIS_DIR
+ "/../fixtures/test.cert",
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE: THIS_DIR
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
+ "/../fixtures/test-client-cert.pem",
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY: THIS_DIR
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
+ "/../fixtures/test-client-key.pem",
OTEL_EXPORTER_OTLP_LOGS_HEADERS: " key1=value1,KEY2 = VALUE=2",
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT: "10",
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION: "gzip",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
OTEL_EXPORTER_OTLP_METRICS_DEFAULT_HISTOGRAM_AGGREGATION,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
Expand Down Expand Up @@ -223,6 +225,10 @@ def test_preferred_temporality(self):
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT: "collector:4317",
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE: THIS_DIR
+ "/fixtures/test.cert",
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE: THIS_DIR
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
+ "/fixtures/test-client-cert.pem",
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY: THIS_DIR
+ "/fixtures/test-client-key.pem",
OTEL_EXPORTER_OTLP_METRICS_HEADERS: " key1=value1,KEY2 = value=2",
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT: "10",
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION: "gzip",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,8 @@
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY,
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION,
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT,
OTEL_EXPORTER_OTLP_TRACES_HEADERS,
Expand Down Expand Up @@ -232,6 +234,10 @@ def test_exporting(self):
OTEL_EXPORTER_OTLP_TRACES_ENDPOINT: "collector:4317",
OTEL_EXPORTER_OTLP_TRACES_CERTIFICATE: THIS_DIR
+ "/fixtures/test.cert",
OTEL_EXPORTER_OTLP_TRACES_CLIENT_CERTIFICATE: THIS_DIR
+ "/fixtures/test-client-cert.pem",
OTEL_EXPORTER_OTLP_TRACES_CLIENT_KEY: THIS_DIR
+ "/fixtures/test-client-key.pem",
OTEL_EXPORTER_OTLP_TRACES_HEADERS: " key1=value1,KEY2 = value=2",
OTEL_EXPORTER_OTLP_TRACES_TIMEOUT: "10",
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION: "gzip",
Expand Down Expand Up @@ -868,20 +874,6 @@ def test_translate_key_values(self):
self.assertTrue(isinstance(arr_value.values[1], AnyValue))
self.assertEqual(arr_value.values[1].string_value, "123")

# Tracing specs currently does not support Mapping type attributes
# map_value = _translate_key_values(
# "map_type", {"asd": "123", "def": "456"}
# )
# self.assertTrue(isinstance(map_value, KeyValue))
# self.assertEqual(map_value.key, "map_type")
# self.assertTrue(isinstance(map_value.value, AnyValue))
# self.assertTrue(isinstance(map_value.value.kvlist_value, KeyValueList))

# kvlist_value = map_value.value.kvlist_value
# self.assertTrue(isinstance(kvlist_value.values[0], KeyValue))
# self.assertEqual(kvlist_value.values[0].key, "asd")
# self.assertEqual(kvlist_value.values[0].value.string_value, "123")

def test_dropped_values(self):
span = get_span_with_dropped_attributes_events_links()
# pylint:disable=protected-access
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,16 @@
from opentelemetry.exporter.otlp.proto.common._log_encoder import encode_logs
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_ENDPOINT,
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_TIMEOUT,
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT,
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
OTEL_EXPORTER_OTLP_LOGS_TIMEOUT,
OTEL_EXPORTER_OTLP_LOGS_COMPRESSION,
Expand Down Expand Up @@ -67,6 +71,8 @@ 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,
Expand All @@ -78,10 +84,25 @@ def __init__(
environ.get(OTEL_EXPORTER_OTLP_ENDPOINT, DEFAULT_ENDPOINT)
),
)
# Keeping these as instance variables because they are used in tests
self._certificate_file = certificate_file or environ.get(
OTEL_EXPORTER_OTLP_LOGS_CERTIFICATE,
environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, True),
)
self._client_key_file = client_key_file or environ.get(
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
OTEL_EXPORTER_OTLP_LOGS_CLIENT_KEY,
environ.get(OTEL_EXPORTER_OTLP_CLIENT_KEY, None),
)
self._client_certificate_file = client_certificate_file or environ.get(
OTEL_EXPORTER_OTLP_LOGS_CLIENT_CERTIFICATE,
environ.get(OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, None),
)
self._client_cert = self._client_certificate_file
sandy2008 marked this conversation as resolved.
Show resolved Hide resolved
if self._client_certificate_file and self._client_key_file:
self._client_cert = (
self._client_certificate_file,
self._client_key_file,
)
headers_string = environ.get(
OTEL_EXPORTER_OTLP_LOGS_HEADERS,
environ.get(OTEL_EXPORTER_OTLP_HEADERS, ""),
Expand Down Expand Up @@ -120,6 +141,7 @@ def _export(self, serialized_data: bytes):
data=data,
verify=self._certificate_file,
timeout=self._timeout,
cert=self._client_cert,
)

@staticmethod
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,11 +51,15 @@
from opentelemetry.sdk.environment_variables import (
OTEL_EXPORTER_OTLP_ENDPOINT,
OTEL_EXPORTER_OTLP_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_CLIENT_KEY,
OTEL_EXPORTER_OTLP_HEADERS,
OTEL_EXPORTER_OTLP_TIMEOUT,
OTEL_EXPORTER_OTLP_COMPRESSION,
OTEL_EXPORTER_OTLP_METRICS_ENDPOINT,
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
OTEL_EXPORTER_OTLP_METRICS_TIMEOUT,
OTEL_EXPORTER_OTLP_METRICS_COMPRESSION,
Expand Down Expand Up @@ -96,6 +100,8 @@ 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,
Expand All @@ -113,6 +119,20 @@ def __init__(
OTEL_EXPORTER_OTLP_METRICS_CERTIFICATE,
environ.get(OTEL_EXPORTER_OTLP_CERTIFICATE, True),
)
self._client_key_file = client_key_file or environ.get(
OTEL_EXPORTER_OTLP_METRICS_CLIENT_KEY,
environ.get(OTEL_EXPORTER_OTLP_CLIENT_KEY, None),
)
self._client_certificate_file = client_certificate_file or environ.get(
OTEL_EXPORTER_OTLP_METRICS_CLIENT_CERTIFICATE,
environ.get(OTEL_EXPORTER_OTLP_CLIENT_CERTIFICATE, None),
)
self._client_cert = self._client_certificate_file
if self._client_certificate_file and self._client_key_file:
self._client_cert = (
self._client_certificate_file,
self._client_key_file,
)
headers_string = environ.get(
OTEL_EXPORTER_OTLP_METRICS_HEADERS,
environ.get(OTEL_EXPORTER_OTLP_HEADERS, ""),
Expand Down Expand Up @@ -156,6 +176,7 @@ def _export(self, serialized_data: bytes):
data=data,
verify=self._certificate_file,
timeout=self._timeout,
cert=self._client_cert,
)

@staticmethod
Expand Down
Loading