1
0
Fork 0
mirror of synced 2026-06-05 17:28:19 +00:00

add third-party runner support via bravo agent

Detect Depot/Namespace/Warp/Blacksmith runners and install the bravo
agent variant. Bravo install mirrors installAgentForSelfHosted: TLS
gate via isTLSEnabled, hand-picked config literal with random api_key,
correlation_id set to RUNNER_NAME so it matches server-side correlation
derived from job logs (is_github_hosted=true keeps the agent from
overriding correlation_id to customer-hostname).

Bumps agent-ebpf to v1.8.1 and macOS installer to v0.0.5.
This commit is contained in:
Varun Sharma 2026-04-19 06:29:19 -07:00
commit 20c37511ec
No known key found for this signature in database
11 changed files with 352 additions and 25 deletions

13
dist/index.js vendored
View file

@ -31913,6 +31913,19 @@ function isAgentInstalled(platform) {
function shouldDeployAgentOnSelfHosted(deployOnSelfHostedVm, isContainer, agentAlreadyInstalled) {
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
}
function detectThirdPartyRunnerProvider() {
var _a;
if (process.env["DEPOT_RUNNER"] === "1")
return "depot";
if (process.env["NAMESPACE_GITHUB_RUNTIME"])
return "namespace";
const runnerName = (_a = process.env["RUNNER_NAME"]) !== null && _a !== void 0 ? _a : "";
if (runnerName.startsWith("warp-"))
return "warp";
if (runnerName.startsWith("blacksmith-"))
return "blacksmith";
return null;
}
function utils_getAnnotationLogs(platform) {
switch (platform) {
case "linux":

2
dist/index.js.map vendored

File diff suppressed because one or more lines are too long

52
dist/post/index.js vendored
View file

@ -31919,6 +31919,19 @@ function isAgentInstalled(platform) {
function shouldDeployAgentOnSelfHosted(deployOnSelfHostedVm, isContainer, agentAlreadyInstalled) {
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
}
function detectThirdPartyRunnerProvider() {
var _a;
if (process.env["DEPOT_RUNNER"] === "1")
return "depot";
if (process.env["NAMESPACE_GITHUB_RUNTIME"])
return "namespace";
const runnerName = (_a = process.env["RUNNER_NAME"]) !== null && _a !== void 0 ? _a : "";
if (runnerName.startsWith("warp-"))
return "warp";
if (runnerName.startsWith("blacksmith-"))
return "blacksmith";
return null;
}
function getAnnotationLogs(platform) {
switch (platform) {
case "linux":
@ -32205,6 +32218,7 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
console.log(`[!] ${ARC_RUNNER_MESSAGE}`);
return;
}
const thirdPartyProvider = detectThirdPartyRunnerProvider();
if (process.env.STATE_selfHosted === "true") {
return;
}
@ -32218,7 +32232,12 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
}
switch (process.platform) {
case "linux":
yield handleLinuxCleanup();
if (thirdPartyProvider) {
yield handleAgentBravoCleanup();
}
else {
yield handleLinuxCleanup();
}
break;
case "win32":
yield handleWindowsCleanup();
@ -32234,6 +32253,37 @@ var cleanup_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _
console.log(exception);
}
}))();
function handleAgentBravoCleanup() {
return cleanup_awaiter(this, void 0, void 0, function* () {
external_child_process_.execFileSync("/usr/bin/echo", ["step_policy_jobend"]);
const doneFile = "/home/agent/done.json";
let counter = 0;
while (true) {
if (!external_fs_.existsSync(doneFile)) {
counter++;
if (counter > 10) {
console.log("timed out");
break;
}
yield sleep(1000);
}
else {
console.log(external_fs_.readFileSync(doneFile, "utf-8"));
break;
}
}
const log = "/home/agent/agent.log";
if (external_fs_.existsSync(log)) {
console.log("log:");
console.log(external_fs_.readFileSync(log, "utf-8"));
}
const status = "/home/agent/agent.status";
if (external_fs_.existsSync(status)) {
console.log("status:");
console.log(external_fs_.readFileSync(status, "utf-8"));
}
});
}
function handleLinuxCleanup() {
return cleanup_awaiter(this, void 0, void 0, function* () {
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {

File diff suppressed because one or more lines are too long

126
dist/pre/index.js vendored
View file

@ -84976,6 +84976,7 @@ __nccwpck_require__.r(__webpack_exports__);
// EXPORTS
__nccwpck_require__.d(__webpack_exports__, {
installAgentForBravo: () => (/* binding */ installAgentForBravo),
installAgentForSelfHosted: () => (/* binding */ installAgentForSelfHosted),
sleep: () => (/* binding */ setup_sleep)
});
@ -85037,6 +85038,19 @@ function isAgentInstalled(platform) {
function shouldDeployAgentOnSelfHosted(deployOnSelfHostedVm, isContainer, agentAlreadyInstalled) {
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
}
function detectThirdPartyRunnerProvider() {
var _a;
if (process.env["DEPOT_RUNNER"] === "1")
return "depot";
if (process.env["NAMESPACE_GITHUB_RUNTIME"])
return "namespace";
const runnerName = (_a = process.env["RUNNER_NAME"]) !== null && _a !== void 0 ? _a : "";
if (runnerName.startsWith("warp-"))
return "warp";
if (runnerName.startsWith("blacksmith-"))
return "blacksmith";
return null;
}
function utils_getAnnotationLogs(platform) {
switch (platform) {
case "linux":
@ -85441,19 +85455,23 @@ var external_crypto_ = __nccwpck_require__(6982);
const CHECKSUMS = {
tls: {
amd64: "86d042adcdc03eb1ea50d35d265da47622a6d0aedef9657f84ce1eb7f04d6057",
arm64: "ea1074a2358d50db9a9fe18ae3971b87305cda63f262c494a5f43b25f4e524ce",
amd64: "6105000c6c61f4a3ca27ed3a2796baa206bdb1eb83f0463adb0ec7e565af6e1c",
arm64: "0992da262be06580335725263ba6ee5c009dfd0448a948b7768ec077fdb9d3d8",
},
non_tls: {
amd64: "4aaaeebbe10e619d8ce13e8cc4a1acbafc8f891e8cdd319984480b9ec08407b8", // v0.15.0
},
darwin: "797399a3a3f6f9c4c000a02e0d8c7b16499129c9bdc2ad9cf2a10072c10654fb",
bravo: {
amd64: "2eeaa1b3cfb05adea0a4e2a36e342ccaf95b41aeb82a6a6e217d2971c15f5553",
arm64: "8d7035ffbda165ad86de8bd00bf861c038e4a9e6d501adadc53a265945882533",
},
darwin: "fe26a1f6af4afe9f1a854d8633832f5d18ab542827003cae445b3a64021d612c",
windows: {
amd64: "e98f8b9cf9ecf6566f1e16a470fbe4aef01610a644fd8203a1bab3ff142186c8", // v1.0.0
},
};
// verifyChecksum returns true if checksum is valid
function verifyChecksum(downloadPath, isTLS, variant, platform) {
function verifyChecksum(downloadPath, isTLS, variant, platform, agentType = "default") {
const fileBuffer = external_fs_.readFileSync(downloadPath);
const checksum = external_crypto_.createHash("sha256")
.update(fileBuffer)
@ -85461,9 +85479,14 @@ function verifyChecksum(downloadPath, isTLS, variant, platform) {
let expectedChecksum = "";
switch (platform) {
case "linux":
expectedChecksum = isTLS
? CHECKSUMS["tls"][variant]
: CHECKSUMS["non_tls"][variant];
if (agentType === "bravo") {
expectedChecksum = CHECKSUMS["bravo"][variant];
}
else {
expectedChecksum = isTLS
? CHECKSUMS["tls"][variant]
: CHECKSUMS["non_tls"][variant];
}
break;
case "darwin":
expectedChecksum = CHECKSUMS["darwin"];
@ -85513,7 +85536,7 @@ function installAgent(isTLS, configStr) {
encoding: "utf8",
});
if (isTLS) {
downloadPath = yield tool_cache.downloadTool(`https://github.com/step-security/agent-ebpf/releases/download/v1.8.0/harden-runner_1.8.0_linux_${variant}.tar.gz`, undefined, auth);
downloadPath = yield tool_cache.downloadTool(`https://github.com/step-security/agent-ebpf/releases/download/v1.8.1/harden-runner_1.8.1_linux_${variant}.tar.gz`, undefined, auth);
}
else {
if (variant === "arm64") {
@ -85542,6 +85565,51 @@ function installAgent(isTLS, configStr) {
return true;
});
}
function installAgentBravo(configStr) {
return install_agent_awaiter(this, void 0, void 0, function* () {
// Note: to avoid github rate limiting
const token = lib_core.getInput("token", { required: true });
const auth = `token ${token}`;
const variant = process.arch === "x64" ? "amd64" : "arm64";
const downloadPath = yield tool_cache.downloadTool(`https://github.com/step-security/agent-ebpf/releases/download/v1.8.1/harden-runner-bravo_1.8.1_linux_${variant}.tar.gz`, undefined, auth);
if (!verifyChecksum(downloadPath, true, variant, "linux", "bravo")) {
return false;
}
const extractPath = yield tool_cache.extractTar(downloadPath);
external_child_process_.execFileSync("cp", [external_path_.join(extractPath, "agent"), "/home/agent/agent"]);
external_child_process_.execSync("chmod +x /home/agent/agent");
external_fs_.writeFileSync("/home/agent/agent.json", configStr);
const logStream = external_fs_.openSync("/home/agent/agent.stdout", "a");
const agentProcess = external_child_process_.spawn("sudo", ["/home/agent/agent"], {
cwd: "/home/agent",
detached: true,
stdio: ["ignore", logStream, logStream],
});
agentProcess.unref();
const agentStatus = "/home/agent/agent.status";
const deadline = Date.now() + 10000;
while (true) {
if (!external_fs_.existsSync(agentStatus)) {
if (Date.now() >= deadline) {
console.log("timed out waiting for bravo agent");
if (external_fs_.existsSync("/home/agent/agent.stdout")) {
console.log(external_fs_.readFileSync("/home/agent/agent.stdout", "utf-8"));
}
if (external_fs_.existsSync("/home/agent/agent.log")) {
console.log(external_fs_.readFileSync("/home/agent/agent.log", "utf-8"));
}
break;
}
yield new Promise((resolve) => setTimeout(resolve, 300));
}
else {
console.log(external_fs_.readFileSync(agentStatus, "utf-8"));
break;
}
}
return true;
});
}
function installMacosAgent(configStr) {
return install_agent_awaiter(this, void 0, void 0, function* () {
const token = lib_core.getInput("token", { required: true });
@ -85557,7 +85625,7 @@ function installMacosAgent(configStr) {
external_fs_.writeFileSync("/opt/step-security/agent.json", configStr);
lib_core.info("✓ Successfully created agent.json at /opt/step-security/agent.json");
// Download installer package
const downloadUrl = "https://github.com/step-security/agent-releases/releases/download/v0.0.4-mac/macos-installer-0.0.4.tar.gz";
const downloadUrl = "https://github.com/step-security/agent-releases/releases/download/v0.0.5-mac/macos-installer-0.0.5.tar.gz";
lib_core.info(`Downloading macOS installer.. : ${downloadUrl}`);
const downloadPath = yield tool_cache.downloadTool(downloadUrl, undefined, auth);
lib_core.info(`✓ Successfully downloaded installer to: ${downloadPath}`);
@ -85896,6 +85964,12 @@ var __rest = (undefined && undefined.__rest) || function (s, e) {
const runnerName = process.env.RUNNER_NAME || "";
lib_core.info(`RUNNER_NAME: ${runnerName}`);
if (!isGithubHosted()) {
const thirdPartyProvider = detectThirdPartyRunnerProvider();
if (thirdPartyProvider) {
lib_core.info(`Detected ${thirdPartyProvider} runner environment. Installing agent-bravo.`);
yield installAgentForBravo(github.context.repo.owner, confg);
return;
}
external_fs_.appendFileSync(process.env.GITHUB_STATE, `selfHosted=true${external_os_.EOL}`, {
encoding: "utf8",
});
@ -86097,6 +86171,40 @@ function installAgentForSelfHosted(owner, confg) {
}
});
}
function installAgentForBravo(owner, confg) {
var _a;
return setup_awaiter(this, void 0, void 0, function* () {
try {
console.log("Installing Harden Runner bravo agent for third-party runner");
let isTLS = yield isTLSEnabled(owner);
if (!isTLS) {
console.log("TLS is not enabled for this organization. Bravo agent installation skipped.");
return;
}
const bravoConfig = {
customer: owner,
correlation_id: (_a = process.env["RUNNER_NAME"]) !== null && _a !== void 0 ? _a : v4(),
working_directory: confg.working_directory,
api_url: confg.api_url,
api_key: v4(),
allowed_endpoints: confg.allowed_endpoints,
egress_policy: confg.egress_policy,
disable_telemetry: confg.disable_telemetry,
disable_sudo: confg.disable_sudo,
disable_sudo_and_containers: confg.disable_sudo_and_containers,
disable_file_monitoring: confg.disable_file_monitoring,
is_github_hosted: true,
};
const bravoConfigStr = JSON.stringify(bravoConfig);
external_child_process_.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
yield installAgentBravo(bravoConfigStr);
}
catch (error) {
console.log(`Failed to install bravo agent: ${error.message}`);
}
});
}
})();

File diff suppressed because one or more lines are too long

View file

@ -4,13 +4,17 @@ import * as fs from "fs";
const CHECKSUMS = {
tls: {
amd64: "86d042adcdc03eb1ea50d35d265da47622a6d0aedef9657f84ce1eb7f04d6057", // v1.8.0
arm64: "ea1074a2358d50db9a9fe18ae3971b87305cda63f262c494a5f43b25f4e524ce",
amd64: "6105000c6c61f4a3ca27ed3a2796baa206bdb1eb83f0463adb0ec7e565af6e1c", // v1.8.1
arm64: "0992da262be06580335725263ba6ee5c009dfd0448a948b7768ec077fdb9d3d8",
},
non_tls: {
amd64: "4aaaeebbe10e619d8ce13e8cc4a1acbafc8f891e8cdd319984480b9ec08407b8", // v0.15.0
},
darwin: "797399a3a3f6f9c4c000a02e0d8c7b16499129c9bdc2ad9cf2a10072c10654fb", // v0.0.4
bravo: {
amd64: "2eeaa1b3cfb05adea0a4e2a36e342ccaf95b41aeb82a6a6e217d2971c15f5553", // v1.8.1
arm64: "8d7035ffbda165ad86de8bd00bf861c038e4a9e6d501adadc53a265945882533",
},
darwin: "fe26a1f6af4afe9f1a854d8633832f5d18ab542827003cae445b3a64021d612c", // v0.0.5
windows: {
amd64: "e98f8b9cf9ecf6566f1e16a470fbe4aef01610a644fd8203a1bab3ff142186c8", // v1.0.0
},
@ -21,7 +25,8 @@ export function verifyChecksum(
downloadPath: string,
isTLS: boolean,
variant: string,
platform: string
platform: string,
agentType: "default" | "bravo" = "default"
) {
const fileBuffer: Buffer = fs.readFileSync(downloadPath);
const checksum: string = crypto
@ -33,9 +38,13 @@ export function verifyChecksum(
switch (platform) {
case "linux":
expectedChecksum = isTLS
? CHECKSUMS["tls"][variant]
: CHECKSUMS["non_tls"][variant];
if (agentType === "bravo") {
expectedChecksum = CHECKSUMS["bravo"][variant];
} else {
expectedChecksum = isTLS
? CHECKSUMS["tls"][variant]
: CHECKSUMS["non_tls"][variant];
}
break;
case "darwin":
expectedChecksum = CHECKSUMS["darwin"];

View file

@ -6,7 +6,7 @@ import isDocker from "is-docker";
import { isARCRunner } from "./arc-runner";
import { isGithubHosted } from "./tls-inspect";
import { context } from "@actions/github";
import { isPlatformSupported, isAgentInstalled } from "./utils";
import { isPlatformSupported, isAgentInstalled, detectThirdPartyRunnerProvider } from "./utils";
(async () => {
console.log("[harden-runner] post-step");
@ -31,6 +31,8 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
return;
}
const thirdPartyProvider = detectThirdPartyRunnerProvider();
if (process.env.STATE_selfHosted === "true") {
return;
}
@ -49,7 +51,11 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
switch (process.platform) {
case "linux":
await handleLinuxCleanup();
if (thirdPartyProvider) {
await handleAgentBravoCleanup();
} else {
await handleLinuxCleanup();
}
break;
case "win32":
await handleWindowsCleanup();
@ -66,6 +72,38 @@ import { isPlatformSupported, isAgentInstalled } from "./utils";
}
})();
async function handleAgentBravoCleanup() {
cp.execFileSync("/usr/bin/echo", ["step_policy_jobend"]);
const doneFile = "/home/agent/done.json";
let counter = 0;
while (true) {
if (!fs.existsSync(doneFile)) {
counter++;
if (counter > 10) {
console.log("timed out");
break;
}
await sleep(1000);
} else {
console.log(fs.readFileSync(doneFile, "utf-8"));
break;
}
}
const log = "/home/agent/agent.log";
if (fs.existsSync(log)) {
console.log("log:");
console.log(fs.readFileSync(log, "utf-8"));
}
const status = "/home/agent/agent.status";
if (fs.existsSync(status)) {
console.log("status:");
console.log(fs.readFileSync(status, "utf-8"));
}
}
async function handleLinuxCleanup() {
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
return;

View file

@ -26,7 +26,7 @@ export async function installAgent(
if (isTLS) {
downloadPath = await tc.downloadTool(
`https://github.com/step-security/agent-ebpf/releases/download/v1.8.0/harden-runner_1.8.0_linux_${variant}.tar.gz`,
`https://github.com/step-security/agent-ebpf/releases/download/v1.8.1/harden-runner_1.8.1_linux_${variant}.tar.gz`,
undefined,
auth
);
@ -69,6 +69,60 @@ export async function installAgent(
return true;
}
export async function installAgentBravo(configStr: string): Promise<boolean> {
// Note: to avoid github rate limiting
const token = core.getInput("token", { required: true });
const auth = `token ${token}`;
const variant = process.arch === "x64" ? "amd64" : "arm64";
const downloadPath = await tc.downloadTool(
`https://github.com/step-security/agent-ebpf/releases/download/v1.8.1/harden-runner-bravo_1.8.1_linux_${variant}.tar.gz`,
undefined,
auth
);
if (!verifyChecksum(downloadPath, true, variant, "linux", "bravo")) {
return false;
}
const extractPath = await tc.extractTar(downloadPath);
cp.execFileSync("cp", [path.join(extractPath, "agent"), "/home/agent/agent"]);
cp.execSync("chmod +x /home/agent/agent");
fs.writeFileSync("/home/agent/agent.json", configStr);
const logStream = fs.openSync("/home/agent/agent.stdout", "a");
const agentProcess = cp.spawn("sudo", ["/home/agent/agent"], {
cwd: "/home/agent",
detached: true,
stdio: ["ignore", logStream, logStream],
});
agentProcess.unref();
const agentStatus = "/home/agent/agent.status";
const deadline = Date.now() + 10000;
while (true) {
if (!fs.existsSync(agentStatus)) {
if (Date.now() >= deadline) {
console.log("timed out waiting for bravo agent");
if (fs.existsSync("/home/agent/agent.stdout")) {
console.log(fs.readFileSync("/home/agent/agent.stdout", "utf-8"));
}
if (fs.existsSync("/home/agent/agent.log")) {
console.log(fs.readFileSync("/home/agent/agent.log", "utf-8"));
}
break;
}
await new Promise((resolve) => setTimeout(resolve, 300));
} else {
console.log(fs.readFileSync(agentStatus, "utf-8"));
break;
}
}
return true;
}
export async function installMacosAgent(configStr: string): Promise<boolean> {
const token = core.getInput("token", { required: true });
const auth = `token ${token}`;
@ -89,7 +143,7 @@ export async function installMacosAgent(configStr: string): Promise<boolean> {
// Download installer package
const downloadUrl =
"https://github.com/step-security/agent-releases/releases/download/v0.0.4-mac/macos-installer-0.0.4.tar.gz";
"https://github.com/step-security/agent-releases/releases/download/v0.0.5-mac/macos-installer-0.0.5.tar.gz";
core.info(`Downloading macOS installer.. : ${downloadUrl}`);
const downloadPath = await tc.downloadTool(downloadUrl, undefined, auth);
core.info(`✓ Successfully downloaded installer to: ${downloadPath}`);

View file

@ -33,11 +33,12 @@ import {
import { isGithubHosted, isTLSEnabled } from "./tls-inspect";
import {
installAgent,
installAgentBravo,
installMacosAgent,
installWindowsAgent,
} from "./install-agent";
import { chownForFolder, isAgentInstalled, isPlatformSupported, shouldDeployAgentOnSelfHosted } from "./utils";
import { chownForFolder, detectThirdPartyRunnerProvider, isAgentInstalled, isPlatformSupported, shouldDeployAgentOnSelfHosted } from "./utils";
interface MonitorResponse {
runner_ip_address?: string;
@ -289,6 +290,13 @@ interface MonitorResponse {
const runnerName = process.env.RUNNER_NAME || "";
core.info(`RUNNER_NAME: ${runnerName}`);
if (!isGithubHosted()) {
const thirdPartyProvider = detectThirdPartyRunnerProvider();
if (thirdPartyProvider) {
core.info(`Detected ${thirdPartyProvider} runner environment. Installing agent-bravo.`);
await installAgentForBravo(context.repo.owner, confg);
return;
}
fs.appendFileSync(process.env.GITHUB_STATE, `selfHosted=true${EOL}`, {
encoding: "utf8",
});
@ -528,3 +536,39 @@ export async function installAgentForSelfHosted(owner: string, confg: Configurat
console.log(`Failed to install agent for self-hosted runner: ${error.message}`);
}
}
export async function installAgentForBravo(owner: string, confg: Configuration) {
try {
console.log("Installing Harden Runner bravo agent for third-party runner");
let isTLS = await isTLSEnabled(owner);
if (!isTLS) {
console.log("TLS is not enabled for this organization. Bravo agent installation skipped.");
return;
}
const bravoConfig = {
customer: owner,
correlation_id: process.env["RUNNER_NAME"] ?? uuidv4(),
working_directory: confg.working_directory,
api_url: confg.api_url,
api_key: uuidv4(),
allowed_endpoints: confg.allowed_endpoints,
egress_policy: confg.egress_policy,
disable_telemetry: confg.disable_telemetry,
disable_sudo: confg.disable_sudo,
disable_sudo_and_containers: confg.disable_sudo_and_containers,
disable_file_monitoring: confg.disable_file_monitoring,
is_github_hosted: true,
};
const bravoConfigStr = JSON.stringify(bravoConfig);
cp.execSync("sudo mkdir -p /home/agent");
chownForFolder(process.env.USER, "/home/agent");
await installAgentBravo(bravoConfigStr);
} catch (error) {
console.log(`Failed to install bravo agent: ${error.message}`);
}
}

View file

@ -40,6 +40,17 @@ export function shouldDeployAgentOnSelfHosted(
return deployOnSelfHostedVm && !isContainer && !agentAlreadyInstalled;
}
export type ThirdPartyRunnerProvider = "depot" | "namespace" | "warp" | "blacksmith";
export function detectThirdPartyRunnerProvider(): ThirdPartyRunnerProvider | null {
if (process.env["DEPOT_RUNNER"] === "1") return "depot";
if (process.env["NAMESPACE_GITHUB_RUNTIME"]) return "namespace";
const runnerName = process.env["RUNNER_NAME"] ?? "";
if (runnerName.startsWith("warp-")) return "warp";
if (runnerName.startsWith("blacksmith-")) return "blacksmith";
return null;
}
export function getAnnotationLogs(platform: NodeJS.Platform) {
switch (platform) {
case "linux":