Skip to content

Commit 98913a9

Browse files
alimx07MikeGoldsmithemdnetoaabmass
authored
fix(sdk-extension-aws): replace deprecated aws-auth ConfigMap check with JWT iss claim detection in AwsEksResourceDetector (#4414)
* fix(sdk-extension-aws): fix AwsEksResourceDetector on EKS Access Entries API clusters Replace aws-auth ConfigMap HTTP check with JWT iss claim decode. Clusters using the Access Entries API do not have aws-auth, causing HTTP 404 and silent empty resource. Pod service-account token iss always contains oidc.eks on EKS. Decoded locally, no network call required. * Add unit tests for JWT-based EKS detection in AwsEksResourceDetector * use regex-based JWT issuer validation instead of string matching * test(aws-sdk-extension): add non-EKS JWT raise path test * Apply suggestion from @MikeGoldsmith * style(aws-sdk-extension): minor formatting cleanup in EKS detector * add changelog 4414 file --------- Co-authored-by: Mike Goldsmith <goldsmith.mike@gmail.com> Co-authored-by: Emídio Neto <9735060+emdneto@users.noreply.github.com> Co-authored-by: Aaron Abbott <aaronabbott@google.com>
1 parent fab105f commit 98913a9

3 files changed

Lines changed: 127 additions & 5 deletions

File tree

  • sdk-extension/opentelemetry-sdk-extension-aws
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
`opentelemetry-sdk-extension-aws`: fix EKS detector failing on clusters using the Access Entries API

sdk-extension/opentelemetry-sdk-extension-aws/src/opentelemetry/sdk/extension/aws/resource/eks.py

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
# Copyright The OpenTelemetry Authors
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import base64
45
import json
56
import logging
67
import os
8+
import re
79
import ssl
810
from urllib.request import Request, urlopen
911

@@ -21,6 +23,10 @@
2123

2224
_TOKEN_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/token"
2325
_CERT_PATH = "/var/run/secrets/kubernetes.io/serviceaccount/ca.crt"
26+
_EKS_OIDC_RE = re.compile(
27+
r"^https://oidc\.eks\.[^.]+\.amazonaws\.com(?:\.cn)?/id/[A-F0-9]{32}$",
28+
re.ASCII,
29+
)
2430

2531

2632
def _aws_http_request(method, path, cred_value):
@@ -49,11 +55,18 @@ def _get_k8s_cred_value():
4955

5056

5157
def _is_eks(cred_value):
52-
return _aws_http_request(
53-
_GET_METHOD,
54-
"/api/v1/namespaces/kube-system/configmaps/aws-auth",
55-
cred_value,
56-
)
58+
parts = cred_value.removeprefix("Bearer ").split(".")
59+
if len(parts) != 3:
60+
return False
61+
try:
62+
seg = parts[1]
63+
payload = json.loads(
64+
base64.urlsafe_b64decode(seg + "=" * (-len(seg) % 4))
65+
)
66+
except ValueError as exception:
67+
logger.error("Failed to parse JWT for EKS detection: %s", exception)
68+
return False
69+
return bool(_EKS_OIDC_RE.match(payload.get("iss", "")))
5770

5871

5972
def _get_cluster_info(cred_value):

sdk-extension/opentelemetry-sdk-extension-aws/tests/resource/test_eks.py

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# Copyright The OpenTelemetry Authors
22
# SPDX-License-Identifier: Apache-2.0
33

4+
import base64
5+
import json
46
import unittest
57
from collections import OrderedDict
68
from unittest.mock import mock_open, patch
@@ -14,6 +16,17 @@
1416
ResourceAttributes,
1517
)
1618

19+
20+
def _bearer_jwt(payload: dict) -> str:
21+
header = base64.urlsafe_b64encode(b'{"alg":"RS256"}').rstrip(b"=").decode()
22+
body = (
23+
base64.urlsafe_b64encode(json.dumps(payload).encode())
24+
.rstrip(b"=")
25+
.decode()
26+
)
27+
return f"Bearer {header}.{body}.fakesig"
28+
29+
1730
MockEksResourceAttributes = {
1831
ResourceAttributes.CLOUD_PROVIDER: CloudProviderValues.AWS.value,
1932
ResourceAttributes.CLOUD_PLATFORM: CloudPlatformValues.AWS_EKS.value,
@@ -127,3 +140,98 @@ def test_if_no_eks_paths_should_not_raise(
127140
AwsEksResourceDetector(raise_on_error=True).detect()
128141
except RuntimeError:
129142
self.fail("Should not raise")
143+
144+
@patch(
145+
"opentelemetry.sdk.extension.aws.resource.eks._get_k8s_cred_value",
146+
return_value=_bearer_jwt(
147+
{
148+
"iss": "https://oidc.eks.eu-west-2.amazonaws.com/id/A1B2C3D4E5F6A1B2C3D4E5F6A1B2C3D4"
149+
}
150+
),
151+
)
152+
@patch(
153+
"opentelemetry.sdk.extension.aws.resource.eks._is_k8s",
154+
return_value=True,
155+
)
156+
@patch(
157+
"opentelemetry.sdk.extension.aws.resource.eks._get_cluster_info",
158+
return_value=f"""{{
159+
"data": {{
160+
"cluster.name": "{MockEksResourceAttributes[ResourceAttributes.K8S_CLUSTER_NAME]}"
161+
}}
162+
}}
163+
""",
164+
)
165+
@patch(
166+
"builtins.open",
167+
new_callable=mock_open,
168+
read_data=f"14:name=systemd:/docker/{MockEksResourceAttributes[ResourceAttributes.CONTAINER_ID]}\n",
169+
)
170+
def test_eks_oidc_jwt_detected(
171+
self,
172+
mock_open_function,
173+
mock_get_cluster_info,
174+
mock_is_k8s,
175+
mock_get_k8s_cred_value,
176+
):
177+
actual = AwsEksResourceDetector().detect()
178+
self.assertEqual(
179+
actual.attributes.get(ResourceAttributes.CLOUD_PLATFORM),
180+
CloudPlatformValues.AWS_EKS.value,
181+
)
182+
183+
@patch(
184+
"opentelemetry.sdk.extension.aws.resource.eks._get_k8s_cred_value",
185+
return_value=_bearer_jwt({"iss": "https://accounts.google.com"}),
186+
)
187+
@patch(
188+
"opentelemetry.sdk.extension.aws.resource.eks._is_k8s",
189+
return_value=True,
190+
)
191+
def test_non_eks_jwt_returns_empty(
192+
self, mock_is_k8s, mock_get_k8s_cred_value
193+
):
194+
actual = AwsEksResourceDetector().detect()
195+
self.assertEqual(actual.attributes, {})
196+
197+
@patch(
198+
"opentelemetry.sdk.extension.aws.resource.eks._get_k8s_cred_value",
199+
return_value=_bearer_jwt({"iss": "https://wrong.jwt.com"}),
200+
)
201+
@patch(
202+
"opentelemetry.sdk.extension.aws.resource.eks._is_k8s",
203+
return_value=True,
204+
)
205+
def test_non_eks_jwt_should_raise(
206+
self, mock_is_k8s, mock_get_k8s_cred_value
207+
):
208+
with self.assertRaises(RuntimeError):
209+
AwsEksResourceDetector(raise_on_error=True).detect()
210+
211+
@patch(
212+
"opentelemetry.sdk.extension.aws.resource.eks._get_k8s_cred_value",
213+
return_value="Bearer notajwt.otel",
214+
)
215+
@patch(
216+
"opentelemetry.sdk.extension.aws.resource.eks._is_k8s",
217+
return_value=True,
218+
)
219+
def test_is_eks_wrong_parts_count_should_raise(
220+
self, mock_is_k8s, mock_get_k8s_cred_value
221+
):
222+
with self.assertRaises(RuntimeError):
223+
AwsEksResourceDetector(raise_on_error=True).detect()
224+
225+
@patch(
226+
"opentelemetry.sdk.extension.aws.resource.eks._get_k8s_cred_value",
227+
return_value="Bearer header.eyJpc3MiOg.fakesig",
228+
)
229+
@patch(
230+
"opentelemetry.sdk.extension.aws.resource.eks._is_k8s",
231+
return_value=True,
232+
)
233+
def test_is_eks_invalid_json_payload_should_raise(
234+
self, mock_is_k8s, mock_get_k8s_cred_value
235+
):
236+
with self.assertRaises(RuntimeError):
237+
AwsEksResourceDetector(raise_on_error=True).detect()

0 commit comments

Comments
 (0)