apache / apache/infrastructure-actions
Harden `actions/setup-java` action
- Dominant language
- Python
- Stars
- 30
- Forks
- 83
- Avg merge
- 2d 18h
- Merged PRs (30d)
- 79
Description
Although admittedly this is not a priority item, we should open an issue and possibly contribute some supply-chain verification steps. The following paragraphs were generated by Claude, so they might contain errors, but they agree with what I have been able to observe.
## Current situation
Currently the downloads of **all** JDK distributions configured by `actions/setup-java` rely entirely on the TLS connection to the apposite CDN servers. Neither **signatures** nor **checksums** are verified by the action itself.
A direct inspection of every per-vendor installer in [`src/distributions/`](https://github.com/actions/setup-java/tree/main/src/distributions) confirms this: not one of them computes a hash or invokes any signature verification primitive. The pattern is uniformly `tc.downloadTool(url)` followed by `extractJdkFile(...)` followed by `tc.cacheDir(...)`. Several installers even *parse* a `checksum` field from the upstream metadata API (Adopt, Temurin, Corretto, Dragonwell, SAPMachine, Semeru) and then never use it.
This means a compromise of any vendor CDN (or of any of the certificate authorities that vendor's CDN trusts) silently propagates into every CI run, every cached tool entry on every runner image, and every downstream artifact built against that JDK. It also means there is no defense in depth against the more pedestrian failure mode of an archive being corrupted in transit.
## Possible improvements
### Distributions that publish a detached signature on the archive
These are the highest-value targets: a signature ties the artifact to a publisher-controlled key, so verification defends against CDN compromise even when the attacker can also rewrite a published checksum on the same origin. For all five entries below, the signing key is long-lived, publicly documented, and the signature file is available at a predictable URL alongside the archive that `setup-java` already downloads.
| Distribution | Signature artifact | Scheme | Publisher key | Key location |
|---|---|---|---|---|
| Temurin (Adoptium) | `.sig` per asset, URL exposed as `signature_link` field in the existing `api.adoptium.net` JSON | GPG (RSA 4096) | `3B04D753C9050D9A5D343F39843C48A565F8F04B` "Adoptium GPG Key (DEB/RPM Signing Key)" | `keyserver.ubuntu.com`, plus `adoptium.net/installation/gpg-verification` |
| Adopt (legacy AdoptOpenJDK) | `.sig` per asset (same Adoptium pipeline) | GPG (RSA 4096) | Same key as Temurin | Same as Temurin |
| Corretto (Amazon) | `.tar.gz.sig` next to each archive on `corretto.aws` | GPG | `6DC3636DAE534049C8B94623A122542AB04F24E3` "Amazon Services LLC (Amazon Corretto release)" | `apt.corretto.aws/corretto.key`. Has rolled at least once (Dec 2024); verification logic must accommodate rotation. |
| Microsoft Build of OpenJDK | `.sig` per archive on `aka.ms/download-jdk/...` | GPG | "Microsoft public key" published on the download page | `learn.microsoft.com/en-us/java/openjdk/download` |
| Semeru (IBM) | `.sig` per archive | **OpenSSL / X.509**, not GPG. | `ibm-semeru-public-.pem` plus DigiCert code-signing certificate | `ibm.com/support/pages/semeru-runtimes-verification`. Requires a different code path from the GPG-based vendors. |
### Distributions without archive signatures: checksum sources
For the remaining seven, no detached cryptographic signature is published on the standalone archive, so publisher-key authentication is not possible without changes upstream. The next-best defense is verifying the SHA-256 / SHA-512 from a source distinct from the CDN serving the archive. The "Independent of archive CDN?" column captures whether the checksum is fetched from a different origin from the binary; where the answer is no, an attacker who controls the download origin can also rewrite the checksum, so the verification only buys defense against in-transit corruption.
| Distribution | Archive download origin | Checksum source | Independent of archive CDN? | Algorithm |
|---|---|---|---|---|
| Zulu (Azul) | `cdn.azul.com` | Azul Metadata API at `api.azul.com/metadata/v1/zulu/packages/...` returns `sha256_hash` field | Yes (`api.azul.com` vs `cdn.azul.com`) | SHA-256 |
| Liberica (BellSoft) | `download.bell-sw.com` | BellSoft API at `api.bell-sw.com/v1/liberica/releases` returns `sha1` field per asset | Yes (`api.bell-sw.com` vs `download.bell-sw.com`) | SHA-1 (weak; SHA-256 also published on the human-facing `bell-sw.com/pages/downloads/` page) |
| SAPMachine | `github.com/SAP/SapMachine/releases/download/...` | `sap.github.io/SapMachine/assets/data/sapmachine_releases.json` returns checksum per asset | Partially (both ultimately served by GitHub Pages / GitHub Releases, but distinct origins and TLS chains) | SHA-256 |
| Dragonwell (Alibaba) | `github.com/dragonwell-project/.../releases/download/...` | `dragonwell-jdk.io/map_with_checksum.json` | Yes (`dragonwell-jdk.io` vs `github.com`) | SHA-256 |
| Oracle JDK / Oracle GraalVM | `download.oracle.com` | SHA-256 published on the human-facing `oracle.com/java/technologies/downloads/` and `oracle.com/java/technologies/javase/graalvm-jdk*-readme.html` pages | Yes-ish (same parent domain `oracle.com`, different subdomains; both Oracle-controlled, so a full Oracle compromise defeats both) | SHA-256 |
| GraalVM Community | `github.com/graalvm/graalvm-ce-builds/releases/download/...` | SHA-256 in the GitHub Release notes (rendered from the same repo's release metadata) | No (both served by GitHub from the same release) | SHA-256 |
| JetBrains Runtime | `cache-redirector.jetbrains.com` and `github.com/JetBrains/JetBrainsRuntime/releases/download/...` | SHA-512 in the GitHub Release notes | No for the GitHub-hosted archives; yes for `cache-redirector.jetbrains.com` where the release notes on GitHub provide an independent reference | SHA-512 |
A note on the "independent" column: for entries marked "no" or "partially", checksum verification still meaningfully defends against transient corruption, mirror substitution, and any compromise that affects only the binary CDN. It does not defend against a compromise of the vendor's overall infrastructure. Honest framing of this in any documentation we write would help avoid false security claims.
Contributor guide
Assessment
This issue has not been assessed yet.