Skip to content

Stop trusting compromised eMudhra roots in the exploration-tests image - #13026

Open
eliottness wants to merge 1 commit into
masterfrom
eliottness/emudhra-ca-roots
Open

eliottness wants to merge 1 commit into
masterfrom
eliottness/emudhra-ca-roots

Conversation

@eliottness

@eliottness eliottness commented Oct 23, 2026 •

Copy link
Copy Markdown

What Does This Do

Stops the debugger exploration-tests image from trusting emSign ECC Root CA - G3 and AAA Certificate Services. dd-java-agent/agent-debugger/exploration-tests/Dockerfile.exploration-tests (base debian:bookworm-slim):

  • upgrades ca-certificates and re-runs update-ca-certificates -f;
  • deletes both roots from every SDKMAN and Gradle-provisioned JDK cacerts;
  • deletes Gradle's generated gradle-api-*.jar files, which embed an old CA snapshot. The ./gradlew dependencies warm-up step is unchanged; the CA cleanup runs after it.

The APPSEC-71273 ledger lists 17 impacted image references in this repository: 0 fixed here, 1 fixed at their in-repo image source (clean once that image is republished), 16 follow-ups.

Motivation

Incident IR-62343: the compromised eMudhra intermediates chain to emSign ECC Root CA - G3 and AAA Certificate Services, and every CI image whose CA stores still trust them keeps the exposure.

Additional Notes

  • The exploration-tests jobs run on the prebuilt registry.ddbuild.io/ci/dd-trace-java:exploration-tests image, so this change takes effect only after someone runs the manual build-exploration-tests-image job once it is merged. That tag has no clean published version yet.
  • An earlier run of exploration-tests-method-jsoup failed on this PR. The failure cannot come from this Dockerfile, because the job used the unchanged prebuilt image; touching dd-java-agent/agent-debugger/** only made the job run. Other PRs touching the debugger passed the same job that day (Require explicit locales and charsets in tracer code #13020 and Close tracers built by debugger tests (quick fix) #13040), so this PR was rebased onto master to re-run the pipeline.
  • Every new image reference was pulled and every changed Dockerfile was built, then scanned with the eMudhra scanner (it checks every file in the image for the two roots' public keys); all returned empty hits. Images that CI never builds or pulls are left alone.

Follow-ups (not in this PR):

  • Needs a rebuild at its source (Datadog-built or mirrored image) (13): registry.ddbuild.io/images/mirror/amazon/aws-cli:2.4.29, 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/benchmarking-platform:java-dsm-kafka, 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/benchmarking-platform:java-debugger, registry.ddbuild.io/images/mirror/dd-apm-test-agent/ddapm-test-agent:v1.64.1, registry.ddbuild.io/images/mirror/datadog/agent:7.40.1, registry.ddbuild.io/images/mirror/sqlserver:latest, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-base, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-7, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-8, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-11, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-17, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-21, registry.ddbuild.io/images/mirror/dd-trace-java-docker-build:ci-25.
  • No clean upstream version yet (2): gcr.io/google.com/cloudsdktool/google-cloud-cli:emulators, mcr.microsoft.com/mssql/server:latest.
  • Unscannable from outside the registry (pull access denied) (1): 486234852809.dkr.ecr.us-east-1.amazonaws.com/ci/agent-key-management-tools/gpg:1.

Contributor Checklist

Jira ticket: APPSEC-71277 (parent APPSEC-71273)

@datadog-datadog-prod-us1

datadog-datadog-prod-us1 Bot commented Oct 23, 2026 •

Copy link
Copy Markdown
Contributor

🎯 Code Coverage (details)
• Patch Coverage: 100.00%
• Overall Coverage: 74.15% (+14.16%)

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

@dd-octo-sts

dd-octo-sts Bot commented Oct 23, 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.97 s 13.88 s [-0.1%; +1.4%] (no difference)
startup:insecure-bank:tracing:Agent 12.94 s 12.91 s [-0.7%; +1.1%] (no difference)
startup:petclinic:appsec:Agent 17.08 s 16.29 s [+0.5%; +9.1%] (maybe worse)
startup:petclinic:iast:Agent 16.84 s 16.96 s [-1.4%; +0.1%] (no difference)
startup:petclinic:profiling:Agent 16.47 s 16.58 s [-1.7%; +0.4%] (no difference)
startup:petclinic:sca:Agent 16.99 s 16.94 s [-0.6%; +1.2%] (no difference)
startup:petclinic:tracing:Agent 16.09 s 16.18 s [-1.4%; +0.3%] (no difference)

Commit: e2531257 · 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.

Upgrade ca-certificates in the debian:bookworm-slim base and delete emSign ECC Root CA - G3 / AAA Certificate Services
from every SDKMAN and Gradle-provisioned JDK cacerts in the exploration-tests image (IR-62343). The image is published
by the manual build-exploration-tests-image job; re-run it after merge.

Jira: APPSEC-71277
@eliottness
eliottness force-pushed the eliottness/emudhra-ca-roots branch from 626140e to e253125 Compare October 26, 2026 12:41
@eliottness eliottness added tag: no release notes Changes to exclude from release notes comp: testing Testing labels Oct 26, 2026
@pr-commenter

pr-commenter Bot commented Oct 26, 2026

Copy link
Copy Markdown

Debugger benchmarks

Parameters

Baseline Candidate
baseline_or_candidate baseline candidate
ci_job_date 1793019153 1793019469
end_time 2026-10-26T12:54:02 2026-10-26T12:59:17
git_branch master eliottness/emudhra-ca-roots
git_commit_sha 90f57b5 e253125
start_time 2026-10-26T12:52:34 2026-10-26T12:57:50
See matching parameters
Baseline Candidate
ci_job_id 2175278310 2175278310
ci_pipeline_id 147457924 147457924
cpu_model Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz Intel(R) Xeon(R) Platinum 8259CL CPU @ 2.50GHz
git_commit_date 1793018459 1793018459

Summary

Found 0 performance improvements and 0 performance regressions! Performance is the same for 10 metrics, 5 unstable metrics.

See unchanged results
scenario Δ mean agg_http_req_duration_min Δ mean agg_http_req_duration_p50 Δ mean agg_http_req_duration_p75 Δ mean agg_http_req_duration_p99 Δ mean throughput
scenario:noprobe unstable
[-21807.604ns; +23343.079ns] or [-6.871%; +7.355%]
unstable
[-36.755µs; +32.554µs] or [-10.391%; +9.203%]
unstable
[-46.814µs; +42.684µs] or [-12.710%; +11.588%]
unstable
[-129.786µs; +76.874µs] or [-10.469%; +6.201%]
same
scenario:basic same same same unstable
[-275.635µs; -34.929µs] or [-24.674%; -3.127%]
same
scenario:loop same same same same same
Request duration reports for reports
gantt
    title reports - request duration [CI 0.99] : candidate=None, baseline=None
    dateFormat X
    axisFormat %s
section baseline
noprobe (353.737 µs) : 326, 381
.   : milestone, 354,
basic (325.55 µs) : 318, 333
.   : milestone, 326,
loop (7.582 ms) : 7575, 7588
.   : milestone, 7582,
section candidate
noprobe (351.636 µs) : 314, 389
.   : milestone, 352,
basic (324.815 µs) : 319, 331
.   : milestone, 325,
loop (7.604 ms) : 7537, 7671
.   : milestone, 7604,
Loading
  • baseline results
Scenario Request median duration [CI 0.99]
noprobe 353.737 µs [326.009 µs, 381.465 µs]
basic 325.55 µs [317.855 µs, 333.244 µs]
loop 7.582 ms [7.575 ms, 7.588 ms]
  • candidate results
Scenario Request median duration [CI 0.99]
noprobe 351.636 µs [314.233 µs, 389.04 µs]
basic 324.815 µs [319.018 µs, 330.611 µs]
loop 7.604 ms [7.537 ms, 7.671 ms]

@eliottness
eliottness marked this pull request as ready for review October 26, 2026 13:33
@eliottness
eliottness requested a review from a team as a code owner October 26, 2026 13:33
@eliottness
eliottness requested review from ojung and removed request for a team October 26, 2026 13:33
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 26, 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-10-26T13:40:49.968800Z e253125 Draft marked ready
🔒 Security Review ✅ Completed 2026-10-26T13:37:01.440873Z e253125 Draft marked ready
ℹ️ 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.

@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: e25312578b

ℹ️ 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".

RUN set -eu; \
mkdir -p "$HOME/.gradle/jdks"; \
keytool="$HOME/.sdkman/candidates/java/17.0.10-tem/bin/keytool"; \
find "$HOME/.sdkman/candidates/java" "$HOME/.gradle/jdks" -type f -path '*/lib/security/cacerts' | \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Scrub the JDK provisioned when tests start

In the OkHttp job, .gitlab/exploration-tests.yml:148-150 requests a Java 11 test launcher, while this image installs only SDKMAN Java 8 and 17. Gradle provisions missing toolchains on demand, so the Java 11 store is created under $HOME/.gradle/jdks only when jvmTest runs, after this build-time find has completed; that test JVM consequently retains both affected roots. Pre-provision Java 11 before this cleanup or scrub newly provisioned stores at job startup.

Useful? React with 👍 / 👎.

find "$HOME/.sdkman/candidates/java" "$HOME/.gradle/jdks" -type f -path '*/lib/security/cacerts' | \
while IFS= read -r store; do \
"$keytool" -list -v -keystore "$store" -storepass changeit | \
awk '/^Alias name: / { alias = substr($0, 13) } /^Owner: .*([eE]mSign ECC Root CA - G3|AAA Certificate Services)/ { print alias }' | \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Remove all four roots required by the JDK mitigation

For the pinned Java 17.0.10 installation, this predicate leaves emSign Root CA - G1 and emSign Root CA - G2 in cacerts; that JDK release explicitly added all three eMudhra roots, and Oracle's incident mitigation requires removing G1, G2, ECC G3, and AAA. Consequently the image's existing Java 17 runtime remains only partially mitigated even when this loop succeeds. Match the G1 and G2 owner subjects as well, or replace the pinned runtimes with fixed JDK builds.

Useful? React with 👍 / 👎.

Comment on lines +60 to +61
# Remove compromised CA entries from every SDKMAN and Gradle-provisioned JDK.
RUN set -eu; \

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Distrust the roots before downloading build inputs

This cleanup runs only after the Maven dependency resolutions at lines 31, 39, and 44 and the Gradle resolution at line 58, so those downloads use the newly installed standalone JDK stores before the required root removals. In the compromised-CA attack scenario this change addresses, a forged repository certificate can therefore inject artifacts into the image's warmed caches, and deleting the trust anchors afterward does not remove those artifacts. Scrub the SDKMAN JDKs immediately after installation, then separately repeat the cleanup after any later toolchain provisioning.

Useful? React with 👍 / 👎.

@datadog-datadog-prod-us1 datadog-datadog-prod-us1 Bot 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.

Bits Code Review: FAIL

Deleting Gradle's generated API jar leaves AAA Certificate Services in its source google.p12 truststore. Gradle can continue trusting that root independently of JDK cacerts and regenerate the removed snapshot.

Open Bits AI session

🤖 Bits Code Review · Commit e253125


# Do not publish Gradle's generated API jar with its old embedded CA snapshot.
RUN if [ -d "$HOME/.gradle/caches" ]; then \
find "$HOME/.gradle/caches" -type f -path '*/generated-gradle-jars/gradle-api-*.jar' -delete; \

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.

P1 Remove the root from Gradle's source truststore too

After the okhttp warm-up, Gradle's cached distribution retains google-api-client-*.jar, whose embedded google.p12 truststore contains AAA Certificate Services. Gradle's GCS transport uses this store independently of JDK cacerts. Deleting the generated API jar therefore leaves the root trusted and permits regeneration of the same CA snapshot. Sanitize the source truststore as well as removing generated copies.

React: 👍 useful · 👎 wrong · 😕 unclear · 👀 right, but not acting on it

What each reaction means
  • 👍 Useful: correct, and you're fixing it or will.
  • 👎 Wrong: the finding is incorrect, for example the bug can't happen or the code was misread.
  • 😕 Unclear: you can't tell what it means or why it matters.
  • 👀 Right, not acting: correct, but intended, out of scope for this PR, a known risk, or too minor.

🤖 Bits Code Review · Open Bits AI session

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

comp: testing Testing tag: no release notes Changes to exclude from release notes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant