Skip to content

Tag DynamoDB and S3 client spans with the owning AWS account - #12665

Open
vandonr wants to merge 5 commits into
masterfrom
contrib/pr-12602
Open

vandonr wants to merge 5 commits into
masterfrom
contrib/pr-12602

Conversation

@vandonr

@vandonr vandonr commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Duplicate of #12602 by @pedramsafaei


DynamoDB and S3 client spans named the table or bucket but never the account that owns it, so two accounts each holding an "orders" table produced indistinguishable spans. The owning account is available to the tracer without any extra call in three situations, and this change uses all of them:

DynamoDB, TableName given as an ARN. The ARN carries partition, Region and account. Spans now get aws.table.arn and aws_account, and aws.table.name / tablename / peer.service carry the bare table name instead of the raw ARN.

DynamoDB, bare TableName. DynamoDB documents that a bare name is resolved in the requestor's own account ("If you only provide the table name parameter instead of a complete ARN, the API operation will be performed on the table in the account to which the requestor belongs"), so the account owning the signing credentials is the table owner. On SDK v2 the account is read from the credentials object (AwsCredentialsIdentity.accountId(), SDK 2.26+, populated by the STS, SSO, profile, process and container credential providers) through a reflective, per-class cached getter so the instrumentation still loads against 2.2.0. Spans get aws_account and a synthesized aws.table.arn built from the client Region, account and table name. SDK v1 does not expose the signing credentials to request handlers, so v1 only handles the ARN form.

S3, ExpectedBucketOwner. When the caller sets it, S3 rejects the request with 403 on a mismatch, so it is authoritative for the owner. Spans get aws_account on both SDKs.

For older v2 SDKs whose credentials carry no account, the account can optionally be decoded from the access key ID, which encodes it in its trailing base32 characters. That encoding is not part of the documented AWS API surface, so it is behind
dd.trace.aws.account.from.access.key.enabled
(DD_TRACE_AWS_ACCOUNT_FROM_ACCESS_KEY_ENABLED), default false.

aws_account is the tag name dd-trace-py already uses for the SNS and SQS account it derives from TopicArn and QueueUrl, and the dimension tag on the aws.dynamodb.* integration metrics, so the span joins to the metrics for the same table without translation. It is also on the allow list of the Agent's credit card obfuscator; a 12-digit account under any other key is redacted to "?" by default, which is why no second account-bearing key is introduced.

ARNs are parsed once into a new AwsArn value type (the SDK's own software.amazon.awssdk.arns.Arn is not available at the 2.2.0 floor nor to SDK v1), which also owns the table-name extraction shared by both decorators. Partition mapping, table ARN assembly and access key decoding live in AwsAccountIdentity. Both sit in the aws-java-common module and are registered as injected helper classes by both SDK instrumentations.

What Does This Do

Motivation

Additional Notes

Contributor Checklist

Jira ticket: [PROJ-IDENT]

DynamoDB and S3 client spans named the table or bucket but never the
account that owns it, so two accounts each holding an "orders" table
produced indistinguishable spans. The owning account is available to
the tracer without any extra call in three situations, and this change
uses all of them:

DynamoDB, TableName given as an ARN. The ARN carries partition, Region
and account. Spans now get aws.table.arn and aws_account, and
aws.table.name / tablename / peer.service carry the bare table name
instead of the raw ARN.

DynamoDB, bare TableName. DynamoDB documents that a bare name is
resolved in the requestor's own account ("If you only provide the table
name parameter instead of a complete ARN, the API operation will be
performed on the table in the account to which the requestor belongs"),
so the account owning the signing credentials is the table owner. On
SDK v2 the account is read from the credentials object
(AwsCredentialsIdentity.accountId(), SDK 2.26+, populated by the STS,
SSO, profile, process and container credential providers) through a
reflective, per-class cached getter so the instrumentation still loads
against 2.2.0. Spans get aws_account and a synthesized aws.table.arn
built from the client Region, account and table name. SDK v1 does not
expose the signing credentials to request handlers, so v1 only handles
the ARN form.

S3, ExpectedBucketOwner. When the caller sets it, S3 rejects the
request with 403 on a mismatch, so it is authoritative for the owner.
Spans get aws_account on both SDKs.

For older v2 SDKs whose credentials carry no account, the account can
optionally be decoded from the access key ID, which encodes it in its
trailing base32 characters. That encoding is not part of the documented
AWS API surface, so it is behind
dd.trace.aws.account.from.access.key.enabled
(DD_TRACE_AWS_ACCOUNT_FROM_ACCESS_KEY_ENABLED), default false.

aws_account is the tag name dd-trace-py already uses for the SNS and
SQS account it derives from TopicArn and QueueUrl, and the dimension
tag on the aws.dynamodb.* integration metrics, so the span joins to the
metrics for the same table without translation. It is also on the
allow list of the Agent's credit card obfuscator; a 12-digit account
under any other key is redacted to "?" by default, which is why no
second account-bearing key is introduced.

ARNs are parsed once into a new AwsArn value type (the SDK's own
software.amazon.awssdk.arns.Arn is not available at the 2.2.0 floor nor
to SDK v1), which also owns the table-name extraction shared by both
decorators. Partition mapping, table ARN assembly and access key decoding
live in AwsAccountIdentity. Both sit in the aws-java-common module and
are registered as injected helper classes by both SDK instrumentations.
@vandonr
vandonr requested review from a team as code owners September 28, 2026 12:38
@vandonr
vandonr requested review from PerfectSlayer, ValentinZakharov and mhlidd and removed request for a team September 28, 2026 12:38
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-28T12:45:07.026293Z a9e1606 PR opened
🔒 Security Review ✅ Completed 2026-09-28T12:42:58.551048Z a9e1606 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@dd-octo-sts

dd-octo-sts Bot commented Sep 28, 2026

Copy link
Copy Markdown
Contributor

Hi! 👋 Thanks for your pull request! 🎉

To help us review it, please make sure to:

  • Add at least one type, and one component or instrumentation label to the pull request

If you need help, please check our contributing guidelines.

@vandonr vandonr added inst: aws s3 AWS S3 instrumentation inst: aws dynamodb AWS DynamoDB Instrumentation type: feature Enhancements and improvements labels Sep 28, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: a9e16065da

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +150 to +153
request
.getValueForField("ExpectedBucketOwner", String.class)
.filter(AwsAccountIdentity::isAccountId)
.ifPresent(owner -> setBucketOwner(span, owner));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Tag ExpectedBucketOwner only after successful S3 requests

When a caller supplies a well-formed but incorrect 12-digit ExpectedBucketOwner, S3 returns 403 because the value does not identify the actual owner, yet this request-time code still records it as aws_account. Failed ownership checks therefore emit a confidently incorrect owning-account tag; defer or remove this tag unless the S3 request succeeds. The v1 decorator repeats the same eager assignment.

Useful? React with 👍 / 👎.

@datadog-prod-us1-3

datadog-prod-us1-3 Bot commented Sep 28, 2026 •

Copy link
Copy Markdown

🎯 Code Coverage (details)
• Patch Coverage: 0.00%
• Overall Coverage: 57.61% (-1.48%)

This comment will be updated automatically if new data arrives.
🔗 Commit SHA: b97f18c | Docs | Give us feedback!

@datadog-prod-us1-3 datadog-prod-us1-3 Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bits Code Review: FAIL

The change can fabricate DynamoDB ownership for unrelated AWS requests, decode legacy access keys into false account IDs, and retain an asserted S3 owner after S3 rejects it.

Open Bits AI session

🤖 Bits Code Review · Commit a9e1606 · @DataDog review to ask questions

request
.getValueForField("ExpectedBucketOwner", String.class)
.filter(AwsAccountIdentity::isAccountId)
.ifPresent(owner -> setBucketOwner(span, owner));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Tag expected bucket owners only after success

A well-formed but incorrect ExpectedBucketOwner causes S3 to return 403, yet its asserted value remains tagged as the bucket's actual account. Failed spans therefore report ownership that S3 explicitly rejected. Both SDK paths need to apply the tag only after a successful response.

Was this helpful? React 👍 or 👎
🤖 Bits Code Review · @DataDog review to ask questions · Open Bits AI session

@dd-octo-sts

dd-octo-sts Bot commented Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

🟢 Java Benchmark SLOs — All performance SLOs passed

Suite Status
Startup 🟢 pass

SLO thresholds are defined here based on automatically generated metrics. A warning is raised when results are within 5% of the threshold.

PR vs. master results
Scenario Candidate master Δ (95% CI of mean)
startup:insecure-bank:iast:Agent 13.94 s 13.96 s [-1.0%; +0.7%] (no difference)
startup:insecure-bank:tracing:Agent 12.90 s 13.03 s [-2.0%; +0.0%] (no difference)
startup:petclinic:appsec:Agent 16.57 s 16.84 s [-6.0%; +2.8%] (no difference)
startup:petclinic:iast:Agent 16.78 s 17.00 s [-1.9%; -0.6%] (maybe better)
startup:petclinic:profiling:Agent 16.52 s 16.94 s [-3.7%; -1.2%] (significantly better)
startup:petclinic:sca:Agent 17.03 s 16.89 s [-0.1%; +1.8%] (no difference)
startup:petclinic:tracing:Agent 15.68 s 16.03 s [-6.4%; +2.0%] (no difference)

Commit: b97f18c5 · CI Pipeline · Benchmarking Platform UI


Load and DaCapo benchmarks can be triggered manually in the GitLab pipeline. Results will appear in the Benchmarking Platform UI after completion.

@vandonr

vandonr commented Sep 28, 2026

Copy link
Copy Markdown
Contributor Author

@pedramsafaei what do you think about the AI comment about tagging only after a successful request ?
Do you think it's useful or misleading to have a "wrong" account ID tagged as AWS_ACCOUNT in the case of a 403 for instance ?

@pedramsafaei

pedramsafaei commented Sep 28, 2026 •

Copy link
Copy Markdown

@vandonr On the 403 question it is misleading, I think. aws_account reads as "the account that owns this resource" and gets joined to the AWS integration metrics on that basis, and a 403 is exactly S3 telling us the asserted owner is wrong. Nothing useful is lost by withholding it and the error span still has the bucket and the 403. So it is now tagged only from the response hooks (v2 via an ExecutionAttribute on a 2xx, v1 in afterResponse), with a test for the 403 case. The ARN and credentials-derived accounts are unaffected.

Pushed to my branch, head 2b35c67: pedramsafaei@2b35c67107. Could you reset contrib/pr-12602 to it? It covers the three Codex/Bits findings (DynamoDB enrichment gated on the service, the ExpectedBucketOwner change above, legacy pre-2019 access keys rejected via the format-marker bit) and the check_inst 4/4 failure, which was a SpotBugs ICAST in the base32 decoder.

Two things I can't do is validate_supported_configurations_v2_local_file needs DD_TRACE_AWS_ACCOUNT_FROM_ACCESS_KEY_ENABLED registered in the Feature Parity Dashboard, and I could not reproduce the test_inst 8/8 / test_inst_latest 2/6 failures locally (all aws-java suites pass, including against ddapm-test-agent). If they still fail on the new head, could you paste the failing test names from the GitLab log? Thank you

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

inst: aws dynamodb AWS DynamoDB Instrumentation inst: aws s3 AWS S3 instrumentation type: feature Enhancements and improvements

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants