mirror of
https://github.com/actions/setup-java.git
synced 2026-09-10 20:54:22 +02:00
Fix alpine failures by switching default back to only warn on verification failures. To prevent build failures due to missing GPG or rotated vendor keys. (#1262)
Also allow multiple GPG keys to be provided. Co-authored-by: John <1615532+johnoliver@users.noreply.github.com>
This commit is contained in:
@@ -12,6 +12,9 @@ export const INPUT_SET_DEFAULT = 'set-default';
|
||||
export const INPUT_PROBLEM_MATCHER = 'problem-matcher';
|
||||
export const INPUT_VERIFY_SIGNATURE = 'verify-signature';
|
||||
export const INPUT_VERIFY_SIGNATURE_PUBLIC_KEY = 'verify-signature-public-key';
|
||||
export const SIGNATURE_VERIFICATION_DOCUMENTATION_URL =
|
||||
'https://github.com/actions/setup-java#download-integrity-and-signatures';
|
||||
export const SIGNATURE_VERIFICATION_FAILURE_HELP = `If this is a legitimate vendor signing-key rotation, see ${SIGNATURE_VERIFICATION_DOCUMENTATION_URL} for instructions to configure the updated public key or temporarily disable signature verification.`;
|
||||
export const INPUT_MVN_SERVER_CREDENTIALS = 'mvn-server-credentials';
|
||||
export const INPUT_MVN_REPOSITORIES = 'mvn-repositories';
|
||||
export const INPUT_MVN_REPOSITORIES_INCLUDE_CENTRAL =
|
||||
|
||||
@@ -9,12 +9,13 @@ import {
|
||||
getToolcachePath,
|
||||
isVersionSatisfies
|
||||
} from '../util.js';
|
||||
import {
|
||||
import type {
|
||||
ChecksumAlgorithm,
|
||||
ChecksumMetadata,
|
||||
JavaDownloadRelease,
|
||||
JavaInstallerOptions,
|
||||
JavaInstallerResults
|
||||
JavaInstallerResults,
|
||||
SignatureVerificationKey
|
||||
} from './base-models.js';
|
||||
import {MACOS_JAVA_CONTENT_POSTFIX} from '../constants.js';
|
||||
import {RetryingHttpClient} from '../retrying-http-client.js';
|
||||
@@ -44,7 +45,8 @@ export abstract class JavaBase {
|
||||
private floatingVersionVerified = false;
|
||||
protected setDefault: boolean;
|
||||
protected verifySignature: boolean;
|
||||
protected verifySignaturePublicKey: string | undefined;
|
||||
protected verifySignatureExplicitlyRequested: boolean;
|
||||
protected verifySignaturePublicKey: SignatureVerificationKey | undefined;
|
||||
|
||||
constructor(
|
||||
protected distribution: string,
|
||||
@@ -70,6 +72,8 @@ export abstract class JavaBase {
|
||||
: true;
|
||||
this.verifySignature =
|
||||
installerOptions.verifySignature ?? this.supportsSignatureVerification();
|
||||
this.verifySignatureExplicitlyRequested =
|
||||
installerOptions.verifySignature === true;
|
||||
this.verifySignaturePublicKey = installerOptions.verifySignaturePublicKey;
|
||||
}
|
||||
|
||||
@@ -369,6 +373,7 @@ export abstract class JavaBase {
|
||||
source: this.getJdkReleaseIdentity(javaRelease),
|
||||
verification: getJdkVerificationIdentity(
|
||||
this.verifySignature,
|
||||
this.verifySignatureExplicitlyRequested,
|
||||
this.verifySignaturePublicKey
|
||||
),
|
||||
path: this.getJdkCachePath(javaRelease.version)
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
export type SignatureVerificationKey = string | readonly string[];
|
||||
|
||||
export interface JavaInstallerOptions {
|
||||
version: string;
|
||||
architecture: string;
|
||||
@@ -7,7 +9,7 @@ export interface JavaInstallerOptions {
|
||||
cacheJdk?: boolean;
|
||||
setDefault?: boolean;
|
||||
verifySignature?: boolean;
|
||||
verifySignaturePublicKey?: string;
|
||||
verifySignaturePublicKey?: SignatureVerificationKey;
|
||||
}
|
||||
|
||||
export interface JavaInstallerResults {
|
||||
|
||||
@@ -65,7 +65,7 @@ export class LocalDistribution extends JavaBase {
|
||||
architecture: this.architecture,
|
||||
version: this.version,
|
||||
source,
|
||||
verification: getJdkVerificationIdentity(false),
|
||||
verification: getJdkVerificationIdentity(false, false),
|
||||
path: this.getJdkCachePath(this.version)
|
||||
};
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from '../../util.js';
|
||||
import * as gpg from '../../gpg.js';
|
||||
import {MICROSOFT_PUBLIC_KEY} from './microsoft-key.js';
|
||||
import {SIGNATURE_VERIFICATION_FAILURE_HELP} from '../../constants.js';
|
||||
import * as core from '@actions/core';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import fs from 'fs';
|
||||
@@ -34,22 +35,31 @@ export class MicrosoftDistributions extends JavaBase {
|
||||
let javaArchivePath = await this.downloadAndVerify(javaRelease);
|
||||
|
||||
if (this.verifySignature) {
|
||||
if (!javaRelease.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
javaArchivePath,
|
||||
javaRelease.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? MICROSOFT_PUBLIC_KEY
|
||||
);
|
||||
if (!javaRelease.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Microsoft Build of OpenJDK version ${javaRelease.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
javaArchivePath,
|
||||
javaRelease.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? MICROSOFT_PUBLIC_KEY
|
||||
);
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version}. Signature URL: ${javaRelease.signatureUrl}. Error: ${(error as Error).message} ${SIGNATURE_VERIFICATION_FAILURE_HELP}`,
|
||||
{cause: error}
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to verify signature for Microsoft Build of OpenJDK version ${javaRelease.version}. Signature URL: ${javaRelease.signatureUrl}. Error: ${(error as Error).message}`,
|
||||
{cause: error}
|
||||
if (this.verifySignatureExplicitlyRequested) {
|
||||
throw error;
|
||||
}
|
||||
core.warning(
|
||||
error instanceof Error ? error.message : `Unknown error: ${error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,10 @@ import * as gpg from '../../gpg.js';
|
||||
import {ADOPTIUM_PUBLIC_KEY} from './adoptium-key.js';
|
||||
import {JavaBase} from '../base-installer.js';
|
||||
import {ITemurinAvailableVersions} from './models.js';
|
||||
import {MACOS_JAVA_CONTENT_POSTFIX} from '../../constants.js';
|
||||
import {
|
||||
MACOS_JAVA_CONTENT_POSTFIX,
|
||||
SIGNATURE_VERIFICATION_FAILURE_HELP
|
||||
} from '../../constants.js';
|
||||
import {
|
||||
JavaDownloadRelease,
|
||||
JavaInstallerOptions,
|
||||
@@ -141,24 +144,41 @@ export class TemurinDistribution extends JavaBase {
|
||||
const archivePath = await this.downloadAndVerify(release);
|
||||
|
||||
if (this.verifySignature) {
|
||||
if (!release.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${release.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
archivePath,
|
||||
release.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? ADOPTIUM_PUBLIC_KEY
|
||||
);
|
||||
if (!(await gpg.isGpgAvailable())) {
|
||||
throw new Error(
|
||||
"Input 'verify-signature' is enabled, but gpg is not available."
|
||||
);
|
||||
}
|
||||
if (!release.signatureUrl) {
|
||||
throw new Error(
|
||||
`Input 'verify-signature' is enabled, but no signature URL was found for Temurin version ${release.version}.`
|
||||
);
|
||||
}
|
||||
core.info(`Verifying Java package signature...`);
|
||||
try {
|
||||
await gpg.verifyPackageSignature(
|
||||
archivePath,
|
||||
release.signatureUrl,
|
||||
this.verifySignaturePublicKey ?? ADOPTIUM_PUBLIC_KEY
|
||||
);
|
||||
} catch (error) {
|
||||
const verificationError = new Error(
|
||||
`Failed to verify signature for Temurin version ${release.version} from ${release.signatureUrl}: ${(error as Error).message} ${SIGNATURE_VERIFICATION_FAILURE_HELP}`,
|
||||
{cause: error}
|
||||
);
|
||||
if (this.verifySignatureExplicitlyRequested) {
|
||||
throw verificationError;
|
||||
} else {
|
||||
core.warning(verificationError.message);
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
throw new Error(
|
||||
`Failed to verify signature for Temurin version ${release.version} from ${release.signatureUrl}: ${
|
||||
(error as Error).message
|
||||
}`,
|
||||
{cause: error}
|
||||
if (this.verifySignatureExplicitlyRequested) {
|
||||
throw error;
|
||||
}
|
||||
core.warning(
|
||||
error instanceof Error ? error.message : `Unknown error: ${error}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
+16
-5
@@ -5,11 +5,16 @@ import * as io from '@actions/io';
|
||||
import * as exec from '@actions/exec';
|
||||
import * as tc from '@actions/tool-cache';
|
||||
import * as util from './util.js';
|
||||
import {ExecOptions} from '@actions/exec';
|
||||
import type {ExecOptions} from '@actions/exec';
|
||||
import type {SignatureVerificationKey} from './distributions/base-models.js';
|
||||
|
||||
export const GPG_HOME_PREFIX = 'setup-java-gpg-';
|
||||
const VERIFY_GPG_HOME_PREFIX = 'verify-signature-gpg-home-';
|
||||
|
||||
export async function isGpgAvailable(): Promise<boolean> {
|
||||
return Boolean(await io.which('gpg', false));
|
||||
}
|
||||
|
||||
// Convert a Windows path (D:\a\_temp\...) to a POSIX path (/d/a/_temp/...).
|
||||
// The Git-bundled GPG on Windows (MSYS2-based) uses POSIX path conventions
|
||||
// internally. Passing Windows paths with backslashes can cause fatal GPG errors
|
||||
@@ -97,7 +102,7 @@ export async function removeGpgHome(gpgHome: string): Promise<void> {
|
||||
export async function verifyPackageSignature(
|
||||
archivePath: string,
|
||||
signatureUrl: string,
|
||||
publicKeyContent: string
|
||||
publicKeyContent: SignatureVerificationKey
|
||||
) {
|
||||
const signaturePath = await tc.downloadTool(signatureUrl);
|
||||
let gpgHome: string;
|
||||
@@ -117,8 +122,14 @@ export async function verifyPackageSignature(
|
||||
);
|
||||
}
|
||||
try {
|
||||
const publicKeyFile = path.join(gpgHome, 'public-key.asc');
|
||||
fs.writeFileSync(publicKeyFile, publicKeyContent, {encoding: 'utf-8'});
|
||||
const publicKeys = Array.isArray(publicKeyContent)
|
||||
? publicKeyContent
|
||||
: [publicKeyContent];
|
||||
const publicKeyFiles = publicKeys.map((publicKey, index) => {
|
||||
const publicKeyFile = path.join(gpgHome, `public-key-${index}.asc`);
|
||||
fs.writeFileSync(publicKeyFile, publicKey, {encoding: 'utf-8'});
|
||||
return toGpgPath(publicKeyFile);
|
||||
});
|
||||
const options: ExecOptions = {silent: true};
|
||||
await exec.exec(
|
||||
'gpg',
|
||||
@@ -127,7 +138,7 @@ export async function verifyPackageSignature(
|
||||
toGpgPath(gpgHome),
|
||||
'--batch',
|
||||
'--import',
|
||||
toGpgPath(publicKeyFile)
|
||||
...publicKeyFiles
|
||||
],
|
||||
options
|
||||
);
|
||||
|
||||
+19
-6
@@ -4,6 +4,7 @@ import path from 'path';
|
||||
import * as cache from '@actions/cache';
|
||||
import * as core from '@actions/core';
|
||||
import {isCacheFeatureAvailable} from './cache-feature.js';
|
||||
import type {SignatureVerificationKey} from './distributions/base-models.js';
|
||||
|
||||
const STATE_JDK_CACHES = 'jdk-caches';
|
||||
const JDK_CACHE_KEY_VERSION = 1;
|
||||
@@ -117,18 +118,30 @@ function getInstallationIdentity(
|
||||
|
||||
export function getJdkVerificationIdentity(
|
||||
verifySignature: boolean,
|
||||
publicKey?: string
|
||||
enforceSignatureVerification: boolean,
|
||||
publicKey?: SignatureVerificationKey
|
||||
): string {
|
||||
if (!verifySignature) {
|
||||
return 'unverified';
|
||||
return 'disabled';
|
||||
}
|
||||
const verificationPolicy = enforceSignatureVerification
|
||||
? 'enforced'
|
||||
: 'check-and-warn';
|
||||
if (!publicKey) {
|
||||
return 'verified:bundled';
|
||||
return `${verificationPolicy}:bundled`;
|
||||
}
|
||||
|
||||
const normalizedKey = publicKey.replace(/\r\n?/g, '\n').trim();
|
||||
const fingerprint = createHash('sha256').update(normalizedKey).digest('hex');
|
||||
return `verified:custom:sha256:${fingerprint}`;
|
||||
const publicKeys = Array.isArray(publicKey) ? publicKey : [publicKey];
|
||||
const normalizedKeys = publicKeys.map(key =>
|
||||
key.replace(/\r\n?/g, '\n').trim()
|
||||
);
|
||||
const fingerprintSource = Array.isArray(publicKey)
|
||||
? normalizedKeys.map(key => `${Buffer.byteLength(key)}:${key}`).join('')
|
||||
: normalizedKeys[0];
|
||||
const fingerprint = createHash('sha256')
|
||||
.update(fingerprintSource)
|
||||
.digest('hex');
|
||||
return `${verificationPolicy}:custom:sha256:${fingerprint}`;
|
||||
}
|
||||
|
||||
export async function saveJdkCaches(): Promise<void> {
|
||||
|
||||
Reference in New Issue
Block a user