cosign(install): verify binary signature with keyless verification bundle

Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax
2025-12-16 11:46:22 +01:00
parent 5e6dd63795
commit 44e7279490
4 changed files with 52 additions and 10 deletions

View File

@@ -34,6 +34,13 @@ import {DownloadVersion} from '../types/cosign/cosign';
import {GitHubRelease} from '../types/github';
import {dockerfileContent} from './dockerfile';
export interface DownloadOpts {
version: string;
ghaNoCache?: boolean;
skipState?: boolean;
verifySignature?: boolean;
}
export interface InstallOpts {
githubToken?: string;
buildx?: Buildx;
@@ -48,8 +55,8 @@ export class Install {
this.buildx = opts?.buildx || new Buildx();
}
public async download(v: string, ghaNoCache?: boolean, skipState?: boolean): Promise<string> {
const version: DownloadVersion = await Install.getDownloadVersion(v);
public async download(opts: DownloadOpts): Promise<string> {
const version: DownloadVersion = await Install.getDownloadVersion(opts.version);
core.debug(`Install.download version: ${version.version}`);
const release: GitHubRelease = await Install.getRelease(version, this.githubToken);
@@ -68,7 +75,7 @@ export class Install {
htcVersion: vspec,
baseCacheDir: path.join(os.homedir(), '.bin'),
cacheFile: os.platform() == 'win32' ? 'cosign.exe' : 'cosign',
ghaNoCache: ghaNoCache
ghaNoCache: opts.ghaNoCache
});
const cacheFoundPath = await installCache.find();
@@ -83,7 +90,11 @@ export class Install {
const htcDownloadPath = await tc.downloadTool(downloadURL, undefined, this.githubToken);
core.debug(`Install.download htcDownloadPath: ${htcDownloadPath}`);
const cacheSavePath = await installCache.save(htcDownloadPath, skipState);
if (opts.verifySignature && semver.satisfies(vspec, '>=3.0.1')) {
await this.verifySignature(htcDownloadPath, downloadURL);
}
const cacheSavePath = await installCache.save(htcDownloadPath, opts.skipState);
core.info(`Cached to ${cacheSavePath}`);
return cacheSavePath;
}
@@ -176,6 +187,27 @@ export class Install {
return await new Buildx({standalone: buildStandalone}).getCommand(args);
}
private async verifySignature(cosignBinPath: string, downloadURL: string): Promise<void> {
const cosignBootstrapPath = path.join(Context.tmpDir(), `cosign-bootstrap${os.platform() == 'win32' ? '.exe' : ''}`);
fs.copyFileSync(cosignBinPath, cosignBootstrapPath);
fs.chmodSync(cosignBootstrapPath, '0755');
const bundleURL = `${downloadURL}.sigstore.json`;
core.info(`Downloading keyless verification bundle at ${bundleURL}`);
const bundlePath = await tc.downloadTool(bundleURL, undefined, this.githubToken);
core.debug(`Install.verifySignature bundlePath: ${bundlePath}`);
core.info(`Verifying cosign binary signature with keyless verification bundle`);
// prettier-ignore
await Exec.exec(cosignBootstrapPath, [
'verify-blob',
'--certificate-identity', 'keyless@projectsigstore.iam.gserviceaccount.com',
'--certificate-oidc-issuer', 'https://accounts.google.com',
'--bundle', bundlePath,
cosignBinPath
]);
}
private filename(): string {
let arch: string;
switch (os.arch()) {