Skip to content

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

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

vandonr wants to merge 12 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".

@datadog-prod-us1-3

This comment has been minimized.

@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

@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 14.03 s 14.02 s [-1.0%; +1.1%] (no difference)
startup:insecure-bank:tracing:Agent 13.01 s 13.01 s [-0.9%; +0.9%] (no difference)
startup:petclinic:appsec:Agent 16.47 s 16.34 s [-5.3%; +6.9%] (unstable)
startup:petclinic:iast:Agent 16.94 s 16.89 s [-0.6%; +1.3%] (no difference)
startup:petclinic:profiling:Agent 16.08 s 16.80 s [-8.5%; -0.1%] (maybe better)
startup:petclinic:sca:Agent 16.88 s 16.67 s [+0.3%; +2.1%] (maybe worse)
startup:petclinic:tracing:Agent 16.06 s 15.71 s [-2.0%; +6.5%] (no difference)

Commit: c093f2a5 · 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

+ some other fixes

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
@pedramsafaei

Copy link
Copy Markdown

@vandonr I will address @dougqh's five comments as above (ClassValue, fatal-error rethrow, SDK partition metadata and no synthesized ARN, compatible peer.service pending your preference, decoder removed). I can then push to my branch for you to pull again

@pedramsafaei

Copy link
Copy Markdown

@dougqh Thanks, all five taken. Pushed to my branch, head 9e8bb14 (pedramsafaei@9e8bb14fd3)

CC @vandonr

@vandonr

vandonr commented Sep 30, 2026

Copy link
Copy Markdown
Contributor Author

Pushed to my branch, head 9e8bb14 (pedramsafaei@9e8bb14fd3)

Thanks, I'll take a look. On a side note, since I'm now duplicating fixes in this branch, it'd help if you pushed new changes as separate commits rather than force-pushing a mono-commit (which makes it harder to see what are the new changes && to cherry-pick them here)

vandonr and others added 6 commits September 30, 2026 14:41
Use GenericClassValue for the optional accountId method handle so credentials classes and their application class loaders can be unloaded. Retain compatibility with the SDK 2.2.0 floor and cover present, absent, malformed, and failing providers.

Adapted from Pedram Safaei's changes in PR #12602 (9e8bb14).
Review: #12665 (comment)

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
Keep the credentials-derived aws_account for bare table names, but only emit aws.table.arn from request ARNs. Remove the region-to-partition table and ARN builder, and preserve service-gating regression coverage.

Adapted from Pedram Safaei's changes in PR #12602 (9e8bb14).
Review: #12665 (comment)

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
Remove the undocumented access-key decoder and its opt-in configuration, including configuration metadata and decoder tests. Older SDKs without accountId now leave the credentials account absent.

Adapted from Pedram Safaei's changes in PR #12602 (9e8bb14).
Review: #12665 (comment)

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
Rethrow VirtualMachineError and ThreadDeath while treating other provider and lookup failures, including LinkageError, as an unavailable account. Include method-handle lookup inside the error boundary and cover fatal and recoverable provider errors.

Adapted from Pedram Safaei's changes in PR #12602 (9e8bb14).
Review: #12665 (comment)

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
Keep aws.table.name, tablename, and peer.service unchanged for ARN TableName values on both SDK versions. Retain validation before adding aws.table.arn, reject other services and empty table names, and cover the behavior with unit and integration tests.

Adapted from Pedram Safaei's changes in PR #12602 (9e8bb14).
Review: #12665 (comment)

Co-authored-by: Pedram Safaei <pedramsafaei@users.noreply.github.com>
Adopt automatic AWS SDK helper discovery and claim AwsAccountIdentity and
AwsArn from aws-java-common through isHelperClass so both remain injectable.
@pedramsafaei

Copy link
Copy Markdown

Pushed to my branch, head 9e8bb14 (pedramsafaei@9e8bb14fd3)

Thanks, I'll take a look. On a side note, since I'm now duplicating fixes in this branch, it'd help if you pushed new changes as separate commits rather than force-pushing a mono-commit (which makes it harder to see what are the new changes && to cherry-pick them here)

Absolutely I apologize I'm too used to squashing commits as I've worked with a one commit policy for years :D will remember not to do that here let me know if anything else is needed. Thank you

@pedramsafaei

Copy link
Copy Markdown

Hi @vandonr
Following up any other feedback or can we merge this?

@vandonr

vandonr commented Oct 6, 2026

Copy link
Copy Markdown
Contributor Author

Hi @vandonr Following up any other feedback or can we merge this?

as far as I'm concerned, yes, but ween need review/approval from @PerfectSlayer and @ValentinZakharov.

@pedramsafaei

Copy link
Copy Markdown

Hi @vandonr Following up any other feedback or can we merge this?

as far as I'm concerned, yes, but ween need review/approval from @PerfectSlayer and @ValentinZakharov.

@PerfectSlayer and @ValentinZakharov quick bump here. Thank you

@ValentinZakharov ValentinZakharov left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Hi @pedramsafaei, @vandonr
I have feeling that the PR description seems to diverge slightly from the code
For example, in description there is mentioned dd.trace.aws.account.from.access.key.enabled but I can’t see it in code
synthesized aws.table.arn built from the client Region, account and table name - also absent in code

@pedramsafaei

Copy link
Copy Markdown

Hi @pedramsafaei, @vandonr I have feeling that the PR description seems to diverge slightly from the code For example, in description there is mentioned dd.trace.aws.account.from.access.key.enabled but I can’t see it in code synthesized aws.table.arn built from the client Region, account and table name - also absent in code

@ValentinZakharov You’re right, the description still reflects an earlier version of the implementation. The access-key decoding and configuration flag were removed, and bare DynamoDB table names no longer produce a synthesized ARN. The current implementation adds aws_account from the credentials when available, preserves the original table-name tags, and applies ExpectedBucketOwner only after a successful S3 response.

I don't have access to edit, @vandonr could you update the mirror description to match the current implementation? No code change should be needed. Thank you.

span.setTag(InstrumentationTags.AWS_TABLE_ARN, arn.raw());
}
} else {
account = callerAccount(attributes);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Heads-up on aws_account semantics: python tracer always derives it from the resource identifier (SNS TopicArn / SQS QueueUrl), i.e. the resource owner.

Here the bare-name path takes it from the signing credentials (caller's account). It's correct for DynamoDB bare names (caller == owner), but it sets a precedent that aws_account can come from the caller - which would diverge from python if reused for cross-account services.

Can we document that aws_account = owner of the addressed resource, and credentials are only used where caller is guaranteed to be the owner?

@pedramsafaei pedramsafaei Oct 12, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@ValentinZakharov Agreed it should be documented and I'll rename the private helper so it cannot be mistaken for a general caller-account source. Pushed as pedramsafaei@f4f8f85.

@vandonr could you cherry-pick this commit into the mirror? Thank you.

@pedramsafaei

Copy link
Copy Markdown

Hi team bumping this. Thank you
CC @vandonr

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.

5 participants