mirror of
https://github.com/step-security/harden-runner.git
synced 2026-06-05 19:53:33 +00:00
Merge pull request #186 from step-security/cache-solve
Add cache endpoint to allowed list automatically
This commit is contained in:
commit
2e205a28d0
13 changed files with 66025 additions and 3857 deletions
12
dist/index.js
vendored
12
dist/index.js
vendored
|
|
@ -484,8 +484,8 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|||
};
|
||||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
exports.OidcClient = void 0;
|
||||
const http_client_1 = __nccwpck_require__(925);
|
||||
const auth_1 = __nccwpck_require__(702);
|
||||
const http_client_1 = __nccwpck_require__(59);
|
||||
const auth_1 = __nccwpck_require__(402);
|
||||
const core_1 = __nccwpck_require__(186);
|
||||
class OidcClient {
|
||||
static createHttpClient(allowRetry = true, maxRetry = 10) {
|
||||
|
|
@ -599,7 +599,7 @@ exports.toCommandProperties = toCommandProperties;
|
|||
|
||||
/***/ }),
|
||||
|
||||
/***/ 702:
|
||||
/***/ 402:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
|
@ -665,7 +665,7 @@ exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHand
|
|||
|
||||
/***/ }),
|
||||
|
||||
/***/ 925:
|
||||
/***/ 59:
|
||||
/***/ ((__unused_webpack_module, exports, __nccwpck_require__) => {
|
||||
|
||||
"use strict";
|
||||
|
|
@ -673,7 +673,7 @@ exports.PersonalAccessTokenCredentialHandler = PersonalAccessTokenCredentialHand
|
|||
Object.defineProperty(exports, "__esModule", ({ value: true }));
|
||||
const http = __nccwpck_require__(605);
|
||||
const https = __nccwpck_require__(211);
|
||||
const pm = __nccwpck_require__(443);
|
||||
const pm = __nccwpck_require__(437);
|
||||
let tunnel;
|
||||
var HttpCodes;
|
||||
(function (HttpCodes) {
|
||||
|
|
@ -1210,7 +1210,7 @@ exports.HttpClient = HttpClient;
|
|||
|
||||
/***/ }),
|
||||
|
||||
/***/ 443:
|
||||
/***/ 437:
|
||||
/***/ ((__unused_webpack_module, exports) => {
|
||||
|
||||
"use strict";
|
||||
|
|
|
|||
2
dist/index.js.map
vendored
2
dist/index.js.map
vendored
File diff suppressed because one or more lines are too long
1
dist/post/cache.txt
vendored
Normal file
1
dist/post/cache.txt
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
# This is sample cache file
|
||||
59822
dist/post/index.js
vendored
59822
dist/post/index.js
vendored
File diff suppressed because one or more lines are too long
2
dist/post/index.js.map
vendored
2
dist/post/index.js.map
vendored
File diff suppressed because one or more lines are too long
2015
dist/pre/index.js
vendored
2015
dist/pre/index.js
vendored
File diff suppressed because it is too large
Load diff
2
dist/pre/index.js.map
vendored
2
dist/pre/index.js.map
vendored
File diff suppressed because one or more lines are too long
927
package-lock.json
generated
927
package-lock.json
generated
File diff suppressed because it is too large
Load diff
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "step-security-harden-runner",
|
||||
"version": "1.4.3",
|
||||
"version": "1.5.0",
|
||||
"description": "Security agent for GitHub-hosted runner to monitor the build process",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
|
@ -22,10 +22,11 @@
|
|||
},
|
||||
"homepage": "https://github.com/step-security/harden-runner#readme",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.0.4",
|
||||
"@actions/core": "^1.5.0",
|
||||
"@actions/exec": "^1.1.0",
|
||||
"@actions/github": "^5.0.0",
|
||||
"@actions/http-client": "^1.0.11",
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@actions/tool-cache": "^1.7.1",
|
||||
"ansi-regex": ">=5.0.1",
|
||||
"is-docker": "^3.0.0",
|
||||
|
|
|
|||
121
src/cache.ts
Normal file
121
src/cache.ts
Normal file
|
|
@ -0,0 +1,121 @@
|
|||
import * as core from "@actions/core";
|
||||
import { HttpClient } from "@actions/http-client";
|
||||
import { RequestOptions } from "@actions/http-client/lib/interfaces";
|
||||
import { BearerCredentialHandler } from "@actions/http-client/lib/auth";
|
||||
import * as crypto from "crypto";
|
||||
|
||||
const versionSalt = "1.0";
|
||||
export const cacheKey = "harden-runner-cacheKey";
|
||||
export const cacheFile = "/home/agent/cache.txt";
|
||||
|
||||
function getCacheApiUrl(resource: string): string {
|
||||
const baseUrl: string = process.env["ACTIONS_CACHE_URL"] || "";
|
||||
if (!baseUrl) {
|
||||
throw new Error("Cache Service Url not found, unable to restore cache.");
|
||||
}
|
||||
|
||||
const url = `${baseUrl}_apis/artifactcache/${resource}`;
|
||||
core.debug(`Resource Url: ${url}`);
|
||||
return url;
|
||||
}
|
||||
|
||||
function createAcceptHeader(type: string, apiVersion: string): string {
|
||||
return `${type};api-version=${apiVersion}`;
|
||||
}
|
||||
|
||||
function getRequestOptions(): RequestOptions {
|
||||
const token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
|
||||
|
||||
const requestOptions: RequestOptions = {
|
||||
headers: {
|
||||
Accept: createAcceptHeader("application/json", "6.0-preview.1"),
|
||||
Authorization: `Bearer ${token}`,
|
||||
},
|
||||
};
|
||||
|
||||
return requestOptions;
|
||||
}
|
||||
|
||||
function createHttpClient(): HttpClient {
|
||||
const token = process.env["ACTIONS_RUNTIME_TOKEN"] || "";
|
||||
const bhandler = new BearerCredentialHandler(token);
|
||||
return new HttpClient("actions/cache", [bhandler], getRequestOptions());
|
||||
}
|
||||
|
||||
export function getCacheVersion(
|
||||
paths: string[],
|
||||
compressionMethod?: CompressionMethod
|
||||
): string {
|
||||
const components = paths.concat(
|
||||
!compressionMethod || compressionMethod === CompressionMethod.Gzip
|
||||
? []
|
||||
: [compressionMethod]
|
||||
);
|
||||
|
||||
// Add salt to cache version to support breaking changes in cache entry
|
||||
components.push(versionSalt);
|
||||
|
||||
return crypto.createHash("sha256").update(components.join("|")).digest("hex");
|
||||
}
|
||||
|
||||
export async function getCacheEntry(
|
||||
keys: string[],
|
||||
paths: string[],
|
||||
options?: InternalCacheOptions
|
||||
): Promise<ArtifactCacheEntry | null> {
|
||||
const httpClient = createHttpClient();
|
||||
const version = getCacheVersion(paths, options?.compressionMethod);
|
||||
const resource = `cache?keys=${encodeURIComponent(
|
||||
keys.join(",")
|
||||
)}&version=${version}`;
|
||||
|
||||
const response = await httpClient.getJson<ArtifactCacheEntry>(
|
||||
getCacheApiUrl(resource)
|
||||
);
|
||||
if (response.statusCode === 204) {
|
||||
throw new Error("Request returned 204 status");
|
||||
}
|
||||
if (!isSuccessStatusCode(response.statusCode)) {
|
||||
throw new Error(`Cache service responded with ${response.statusCode}`);
|
||||
}
|
||||
|
||||
const cacheResult = response.result;
|
||||
const cacheDownloadUrl = cacheResult?.archiveLocation;
|
||||
if (!cacheDownloadUrl) {
|
||||
throw new Error("Cache still be done, but not found.");
|
||||
}
|
||||
|
||||
return cacheResult;
|
||||
}
|
||||
|
||||
export interface InternalCacheOptions {
|
||||
compressionMethod?: CompressionMethod;
|
||||
cacheSize?: number;
|
||||
}
|
||||
|
||||
export interface ArtifactCacheEntry {
|
||||
cacheKey?: string;
|
||||
scope?: string;
|
||||
creationTime?: string;
|
||||
archiveLocation?: string;
|
||||
}
|
||||
|
||||
function isSuccessStatusCode(statusCode?: number): boolean {
|
||||
if (!statusCode) {
|
||||
return false;
|
||||
}
|
||||
return statusCode >= 200 && statusCode < 300;
|
||||
}
|
||||
|
||||
export enum CompressionMethod {
|
||||
Gzip = "gzip",
|
||||
// Long range mode was added to zstd in v1.3.2.
|
||||
// This enum is for earlier version of zstd that does not have --long support
|
||||
ZstdWithoutLong = "zstd-without-long",
|
||||
Zstd = "zstd",
|
||||
}
|
||||
// Refer: https://github.com/actions/cache/blob/12681847c623a9274356751fdf0a63576ff3f846/src/utils/actionUtils.ts#L53
|
||||
const RefKey = "GITHUB_REF";
|
||||
export function isValidEvent(): boolean {
|
||||
return RefKey in process.env && Boolean(process.env[RefKey]);
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ export function verifyChecksum(downloadPath: string) {
|
|||
.digest("hex"); // checksum of downloaded file
|
||||
|
||||
const expectedChecksum: string =
|
||||
"2a65aa1423e36c53b0ccea732e280de72cd2f1584d876e385402abecac3c6807"; // checksum for v0.10.3
|
||||
"7027c15a988395f3dde5e77d9a58889669adbda52fbd527ae8216e6d81dd8b1a"; // checksum for v0.11.0
|
||||
|
||||
if (checksum !== expectedChecksum) {
|
||||
core.setFailed(
|
||||
|
|
|
|||
|
|
@ -3,6 +3,9 @@ import * as cp from "child_process";
|
|||
import * as core from "@actions/core";
|
||||
import * as common from "./common";
|
||||
import isDocker from "is-docker";
|
||||
import * as cache from "@actions/cache";
|
||||
import { cacheFile, cacheKey, isValidEvent } from "./cache";
|
||||
import path from "path";
|
||||
|
||||
(async () => {
|
||||
if (process.platform !== "linux") {
|
||||
|
|
@ -59,12 +62,23 @@ import isDocker from "is-docker";
|
|||
});
|
||||
}
|
||||
|
||||
if (!fs.existsSync(doneFile)) {
|
||||
var journalLog = cp.execSync("sudo journalctl -u agent.service", {
|
||||
encoding: "utf8",
|
||||
});
|
||||
console.log("Service log:");
|
||||
console.log(journalLog);
|
||||
// Always log the service log
|
||||
var journalLog = cp.execSync("sudo journalctl -u agent.service", {
|
||||
encoding: "utf8",
|
||||
});
|
||||
console.log("Service log:");
|
||||
console.log(journalLog);
|
||||
|
||||
if (isValidEvent()) {
|
||||
try {
|
||||
const cmd = "cp";
|
||||
const args = [path.join(__dirname, "cache.txt"), cacheFile];
|
||||
cp.execFileSync(cmd, args);
|
||||
const cacheResult = await cache.saveCache([cacheFile], cacheKey);
|
||||
console.log(cacheResult);
|
||||
} catch (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
}
|
||||
})();
|
||||
|
||||
|
|
|
|||
31
src/setup.ts
31
src/setup.ts
|
|
@ -8,6 +8,13 @@ import * as common from "./common";
|
|||
import * as tc from "@actions/tool-cache";
|
||||
import { verifyChecksum } from "./checksum";
|
||||
import isDocker from "is-docker";
|
||||
import {
|
||||
cacheFile,
|
||||
cacheKey,
|
||||
CompressionMethod,
|
||||
getCacheEntry,
|
||||
isValidEvent,
|
||||
} from "./cache";
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
|
|
@ -25,6 +32,8 @@ import isDocker from "is-docker";
|
|||
var api_url = `https://${env}.api.stepsecurity.io/v1`;
|
||||
var web_url = "https://app.stepsecurity.io";
|
||||
|
||||
console.log(`Step Security Job Correlation ID: ${correlation_id}`);
|
||||
|
||||
const confg = {
|
||||
repo: process.env["GITHUB_REPOSITORY"],
|
||||
run_id: process.env["GITHUB_RUN_ID"],
|
||||
|
|
@ -36,6 +45,24 @@ import isDocker from "is-docker";
|
|||
disable_telemetry: core.getBooleanInput("disable-telemetry"),
|
||||
};
|
||||
|
||||
if (isValidEvent()) {
|
||||
try {
|
||||
const cacheEntry = await getCacheEntry([cacheKey], [cacheFile], {
|
||||
compressionMethod: CompressionMethod.ZstdWithoutLong,
|
||||
});
|
||||
const url = new URL(cacheEntry.archiveLocation);
|
||||
core.info(`Adding cacheHost: ${url.hostname}:443 to allowed-endpoints`);
|
||||
confg.allowed_endpoints += ` ${url.hostname}:443`;
|
||||
} catch (exception) {
|
||||
// some exception has occurred.
|
||||
core.info("Unable to fetch cacheURL");
|
||||
if (confg.egress_policy === "block") {
|
||||
core.info("Switching egress-policy to audit mode");
|
||||
confg.egress_policy = "audit";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (confg.egress_policy !== "audit" && confg.egress_policy !== "block") {
|
||||
core.setFailed("egress-policy must be either audit or block");
|
||||
}
|
||||
|
|
@ -71,7 +98,7 @@ import isDocker from "is-docker";
|
|||
let auth = `token ${token}`;
|
||||
|
||||
const downloadPath: string = await tc.downloadTool(
|
||||
"https://github.com/step-security/agent/releases/download/v0.10.3/agent_0.10.3_linux_amd64.tar.gz",
|
||||
"https://github.com/step-security/agent/releases/download/v0.11.0/agent_0.11.0_linux_amd64.tar.gz",
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
|
|
@ -79,8 +106,6 @@ import isDocker from "is-docker";
|
|||
verifyChecksum(downloadPath); // NOTE: verifying agent's checksum, before extracting
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
|
||||
console.log(`Step Security Job Correlation ID: ${correlation_id}`);
|
||||
|
||||
if (!confg.disable_telemetry || confg.egress_policy === "audit") {
|
||||
common.printInfo(web_url);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue