mirror of
https://github.com/step-security/harden-runner
synced 2026-08-09 13:11:02 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9462746bdb | ||
|
|
20cf305ff2 | ||
|
|
c51e8eeb6c | ||
|
|
e152b90204 | ||
|
|
ee1faec052 | ||
|
|
1dc7c17646 | ||
|
|
df199fb7be | ||
|
|
03d096a772 | ||
|
|
40901073af | ||
|
|
95d9a5deda | ||
|
|
87e429d3fb | ||
|
|
ef891c3a30 | ||
|
|
1fa8c8a8b1 | ||
|
|
92c522aaa6 | ||
|
|
4719ad5578 | ||
|
|
4fde639ab4 | ||
|
|
f682f2f2d0 | ||
|
|
f4a75cfd61 | ||
|
|
95503d076c | ||
|
|
4b250a0739 | ||
|
|
5b0ab6abcf | ||
|
|
d11f2c1d65 | ||
|
|
b3fc98e4df | ||
|
|
92fc5d4bf7 | ||
|
|
b61b0a4938 | ||
|
|
e3d3f2baea | ||
|
|
646ac01e72 | ||
|
|
7bc18df383 | ||
|
|
17d38b322b | ||
|
|
ec9f2d5744 | ||
|
|
04bcbc31cf | ||
|
|
7c7a56fcaa | ||
|
|
6c439dc8bd | ||
|
|
bf5688696d | ||
|
|
5436dac7b5 | ||
|
|
88d305a353 | ||
|
|
b976878278 | ||
|
|
875cc92db2 | ||
|
|
002fdce3c6 | ||
|
|
2489e3fcb3 | ||
|
|
75dd441a81 | ||
|
|
4381ace9c4 | ||
|
|
a9da90b635 | ||
|
|
a60ef21c0c | ||
|
|
4ad512f165 | ||
|
|
6b41a39235 | ||
|
|
fa70c45ca9 | ||
|
|
eb47845632 | ||
|
|
1705d777e5 | ||
|
|
62893838e7 | ||
|
|
42a3378d67 | ||
|
|
f0cb479b0f | ||
|
|
9abfa72990 | ||
|
|
4379a81ea6 | ||
|
|
5a5cdce402 | ||
|
|
1106c3d7dd | ||
|
|
0634a2670c | ||
|
|
2e3c511341 | ||
|
|
40873e6a41 | ||
|
|
484c2799ec | ||
|
|
4c8582f455 | ||
|
|
e8d595cd66 | ||
|
|
5d277fc873 | ||
|
|
ff2ab228bd | ||
|
|
b81d650d0e | ||
|
|
769df4ef5d | ||
|
|
a7a8a29fbc | ||
|
|
75fd6ee0fe | ||
|
|
86338660c4 | ||
|
|
230ee49196 | ||
|
|
617de1d11d | ||
|
|
d930a8a3b2 | ||
|
|
c6295a65d1 | ||
|
|
3e118b145b | ||
|
|
b38e918ba8 | ||
|
|
0664d30cda | ||
|
|
b131ca5ebf | ||
|
|
2dc9579753 | ||
|
|
f054d811b5 | ||
|
|
8a09271fed | ||
|
|
6ec6af7d62 | ||
|
|
539365ba33 | ||
|
|
e34e1ce869 | ||
|
|
4cfb9f26cc | ||
|
|
8b7e2a634c | ||
|
|
18fe9c66a7 | ||
|
|
bb31c5e3c0 | ||
|
|
bf5f99ab75 | ||
|
|
db60402296 | ||
|
|
41d3351872 |
@@ -1,23 +0,0 @@
|
||||
name: Code Review
|
||||
on:
|
||||
pull_request:
|
||||
permissions:
|
||||
contents: read
|
||||
jobs:
|
||||
code-review:
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
allowed-endpoints: >
|
||||
api.github.com:443
|
||||
int.api.stepsecurity.io:443
|
||||
|
||||
- name: Code Review
|
||||
uses: step-security/ai-codewise@int
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
|
||||
# Initializes the CodeQL tools for scanning.
|
||||
- name: Initialize CodeQL
|
||||
uses: github/codeql-action/init@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/init@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
with:
|
||||
languages: ${{ matrix.language }}
|
||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||
@@ -61,7 +61,7 @@ jobs:
|
||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||
# If this step fails, then you should remove it and run the build manually (see below)
|
||||
- name: Autobuild
|
||||
uses: github/codeql-action/autobuild@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/autobuild@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
|
||||
# ℹ️ Command-line programs to run using the OS shell.
|
||||
# 📚 https://git.io/JvXDl
|
||||
@@ -75,4 +75,4 @@ jobs:
|
||||
# make release
|
||||
|
||||
- name: Perform CodeQL Analysis
|
||||
uses: github/codeql-action/analyze@2d790406f505036ef40ecba973cc774a50395aac
|
||||
uses: github/codeql-action/analyze@5d4e8d1aca955e8d8589aabd499c5cae939e33c7
|
||||
|
||||
@@ -3,6 +3,9 @@ name: RunsOn Tests
|
||||
on:
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
test-host-outbound:
|
||||
runs-on:
|
||||
|
||||
@@ -54,7 +54,7 @@ jobs:
|
||||
# Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF
|
||||
# format to the repository Actions tab.
|
||||
- name: "Upload artifact"
|
||||
uses: actions/upload-artifact@0b2256b8c012f0828dc542b3febcab082c67f72b # tag=v3.0.0
|
||||
uses: actions/upload-artifact@4cec3d8aa04e39d1a68397de0c4cd6fb9dce8ec1 # tag=v4.6.1
|
||||
with:
|
||||
name: SARIF file
|
||||
path: results.sarif
|
||||
@@ -62,6 +62,6 @@ jobs:
|
||||
|
||||
# Upload the results to GitHub's code scanning dashboard.
|
||||
- name: "Upload to code-scanning"
|
||||
uses: github/codeql-action/upload-sarif@2d790406f505036ef40ecba973cc774a50395aac # tag=v1.0.26
|
||||
uses: github/codeql-action/upload-sarif@5d4e8d1aca955e8d8589aabd499c5cae939e33c7 # tag=v1.0.26
|
||||
with:
|
||||
sarif_file: results.sarif
|
||||
|
||||
@@ -39,8 +39,37 @@ jobs:
|
||||
run: npm test -- --coverage
|
||||
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
|
||||
- name: Publish Test Results
|
||||
uses: step-security/publish-unit-test-result-action@cc82caac074385ae176d39d2d143ad05e1130b2d # v2.18.0
|
||||
uses: step-security/publish-unit-test-result-action@e88bfc6c0dffc68a1067d63526c80c81f248da11 # v2.20.0
|
||||
if: always()
|
||||
with:
|
||||
files: |
|
||||
reports/*.xml
|
||||
|
||||
build-check:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@5c7944e73c4c2a096b17a9cb74d65b6c2bbafbde # v2.9.1
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: audit
|
||||
allowed-endpoints: >
|
||||
github.com:443
|
||||
registry.npmjs.org:443
|
||||
|
||||
- name: Checkout
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: Install Dependencies
|
||||
run: npm ci
|
||||
- name: Run build
|
||||
run: npm run build
|
||||
- name: Check for changes in dist
|
||||
run: |
|
||||
if [[ `git status --porcelain dist` ]]; then
|
||||
echo "Changes detected in dist directory after build:"
|
||||
git status --porcelain dist
|
||||
git diff dist
|
||||
exit 1
|
||||
else
|
||||
echo "No changes in dist directory - build is clean"
|
||||
fi
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<p align="center">
|
||||
<picture>
|
||||
<source media="(prefers-color-scheme: light)" srcset="images/banner.png" width="400">
|
||||
<img alt="Dark Banner" src="images/banner-dark.png" width="400">
|
||||
<source media="(prefers-color-scheme: light)" srcset="images/harden-runner-new.png" width="400">
|
||||
<img alt="Dark Banner" src="images/harden-runner-new.png" width="400">
|
||||
</picture>
|
||||
</p>
|
||||
|
||||
@@ -15,31 +15,50 @@
|
||||
|
||||
# Harden-Runner
|
||||
|
||||
Harden-Runner secures CI/CD workflows by controlling network access and monitoring activities on GitHub-hosted and self-hosted runners. It blocks unauthorized network traffic and detects unusual activity to protect against potential threats. The name "Harden-Runner" comes from its purpose: strengthening the security of the runners used in GitHub Actions workflows.
|
||||
Corporate laptops and production servers typically have robust security monitoring in place to reduce risk and meet compliance requirements. However, CI/CD runners, which handle sensitive information like secrets for cloud environments and create production builds, often lack such security measures. This oversight has led to significant supply chain attacks, including the SolarWinds and Codecov breaches.
|
||||
|
||||
Traditional security monitoring and EDR solutions are ineffective for CI/CD runners due to their ephemeral nature. These tools also lack the necessary context to correlate events with specific workflow runs in a CI/CD environment.
|
||||
|
||||
StepSecurity Harden-Runner addresses this gap by providing security monitoring tailored for CI/CD runners. This approach brings CI/CD runners under the same level of security scrutiny as other critical systems, addressing a significant gap in the software supply chain.
|
||||
### Harden-Runner: Security Incidents Detected
|
||||
|
||||
- [Harden-Runner Detected the tj-actions/changed-files compromise](https://www.stepsecurity.io/blog/harden-runner-detection-tj-actions-changed-files-action-is-compromised) ([CVE-2025-30066](https://github.com/advisories/GHSA-mrrh-fwg8-r2c3))
|
||||
- [Harden-Runner Detected a CI/CD Supply Chain Attack in Google’s Open-Source Project Flank](https://www.stepsecurity.io/case-studies/flank)
|
||||
- [Harden-Runner Detected the NX Build System compromise](https://www.stepsecurity.io/blog/supply-chain-security-alert-popular-nx-build-system-package-compromised-with-data-stealing-malware)
|
||||
- [Harden-Runner Detected a CI/CD Supply Chain Attack in Microsoft’s Open-Source Project Azure Karpenter Provider in Real-Time](https://www.stepsecurity.io/case-studies/azure-karpenter-provider)
|
||||
- [Harden-Runner Detected Anomalous Traffic to api.ipify.org Across Multiple Customers](https://www.stepsecurity.io/blog/harden-runner-detects-anomalous-traffic-to-api-ipify-org-across-multiple-customers)
|
||||
- [Harden-Runner Detected an Unexpected Microsoft Defender Installation on GitHub-Hosted Ubuntu Runners](https://www.stepsecurity.io/blog/how-stepsecurity-harden-runner-detected-unexpected-microsoft-defender-installation-on-github-hosted-ubuntu-runners)
|
||||
- [Harden-Runner Flagged an Anomalous Outbound Call, Leading to a Docker Documentation Update](https://www.stepsecurity.io/blog/harden-runner-flags-anomalous-outbound-call-leading-to-docker-documentation-update)
|
||||
|
||||
### See It in Action
|
||||
Harden-Runner secures over **8 million CI/CD workflow runs every week**, protecting thousands of pipelines, including those from popular open-source projects by **Microsoft, Google, and CISA**. See how top projects are using Harden-Runner and explore the insights:
|
||||
➡️ [Who's using Harden-Runner?](https://docs.stepsecurity.io/whos-using-harden-runner)
|
||||
|
||||
## Quick Links
|
||||
- [Getting Started Guide](#getting-started)
|
||||
- [Why Choose Harden-Runner](#why-choose-harden-runner)
|
||||
- [Features and Capabilities](#features)
|
||||
- [Case Studies and Trusted Projects](#trusted-by-and-case-studies)
|
||||
- [Environment Compatibility Matrix](#environment-compatibility-matrix)
|
||||
- [How It Works](docs/how-it-works.md)
|
||||
- [Known Limitations](docs/limitations.md)
|
||||
- [Join the Discussions](#discussions)
|
||||
- [Official Documentation](https://docs.stepsecurity.io/harden-runner)
|
||||
|
||||
---
|
||||
|
||||
## Introduction
|
||||
|
||||
Learn how Harden-Runner works through the video below, which shows how it detected a supply chain attack on a **Google** open-source project.
|
||||
Learn how Harden-Runner works through the video below, which shows how it detected the tj-actions/changed-files compromise. [View the interactive demo here](https://app.storylane.io/share/679y2zgzljov).
|
||||
|
||||
<a href="https://youtu.be/Yz72qAOrN9s" target="_blank">
|
||||
<img src="images/case-study-thumbnail1.png" alt="Harden-Runner detected supply chain attack in a Google open-source project" title="This case study video shows how StepSecurity Harden-Runner detected a CI/CD supply chain attack in real-time in Google’s open-source project Flank">
|
||||
<a href="https://www.youtube.com/watch?v=oTAJ7Hy7nPU" target="_blank">
|
||||
<img src="images/case-study.png" alt="Harden-Runner detected the tj-actions/changed-files Breach" title="This case study video shows how StepSecurity Harden-Runner detected the tj-actions/changed-files Breach">
|
||||
</a>
|
||||
|
||||
---
|
||||
## Getting Started
|
||||
|
||||
This guide walks you through the steps to set up and use Harden-Runner in your CI/CD workflows.
|
||||
This guide walks you through the steps to set up and use Harden-Runner in your CI/CD workflows. For more details, refer to the [official documentation](https://docs.stepsecurity.io/harden-runner).
|
||||
|
||||
### **Step 1: Add Harden-Runner to Your Workflow**
|
||||
|
||||
@@ -49,11 +68,12 @@ To integrate Harden-Runner, follow these steps:
|
||||
- Add the following code as the first step in each job:
|
||||
```yaml
|
||||
steps:
|
||||
- uses: step-security/harden-runner@446798f8213ac2e75931c1b0769676d927801858 # v2.10.3
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@c6295a65d1254861815972266d5933fd6e532bdf # v2.11.1
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
**Tip**: Automate this step by pasting your workflow into the [StepSecurity online tool](https://app.stepsecurity.io/secureworkflow)
|
||||
**Tip**: Automate this step by pasting your workflow into the [StepSecurity online tool](https://app.stepsecurity.io/secure-workflow)
|
||||
<details>
|
||||
<summary>Click to view the full Getting Started Guide</summary>
|
||||
|
||||
@@ -65,7 +85,7 @@ Run your workflow. Once completed:
|
||||
<p align="left">
|
||||
<img src="images/buildlog1.png" alt="Link in workflow log" >
|
||||
</p>
|
||||
- Click on the provided link (e.g., [example link](https://appv2.stepsecurity.io/github/step-security/github-actions-goat/actions/runs/7704454287?jobid=20996777560&tab=network-events)) to access the **Process Monitor View**, which displays:
|
||||
- Click on the provided link (e.g., [example link](https://app.stepsecurity.io/github/step-security/github-actions-goat/actions/runs/7704454287?jobid=20996777560&tab=network-events)) to access the **Process Monitor View**, which displays:
|
||||
- **Network events**: Outbound network calls correlated with each step.
|
||||
- **File events**: File writes tracked during the job.
|
||||
<p align="left">
|
||||
@@ -91,11 +111,11 @@ Harden-Runner offers a comprehensive suite of features to enhance the security o
|
||||
|
||||
### Community (Free)
|
||||
|
||||
- **Block Network Egress Traffic with Domain Allowlist:** Control outbound network traffic by specifying allowed domains, preventing unauthorized data exfiltration.
|
||||
- **Detect Compromised Packages, Dependencies & Build Tools:** Identify and mitigate risks from malicious or vulnerable components in your build process.
|
||||
- **CI/CD-Aware Event Correlation:** Each outbound network connection, file operation, and process execution is mapped to the exact step, job, and workflow where it occurs.
|
||||
- **Automated Baseline Creation:** Harden-Runner builds a baseline for each job based on past outbound network connections.
|
||||
- **Anomaly Detection:** Once the baseline is created, any future outbound calls not in the baseline trigger a detection.
|
||||
- **Block Network Egress Traffic with Domain Allowlist:** Optionally use the automatically created baseline to control outbound network traffic by specifying allowed domains, preventing unauthorized data exfiltration.
|
||||
- **Detect Modification of Source Code:** Monitor and alert on unauthorized changes to your source code during the CI/CD pipeline.
|
||||
- **Disable Sudo Access:** Restrict the use of superuser privileges in your workflows to minimize security risks.
|
||||
- **Insights Page for CI/CD Runs:** Access detailed reports and analytics for each CI/CD run to monitor security events and compliance.
|
||||
|
||||
### Enterprise (Paid)
|
||||
|
||||
@@ -103,11 +123,11 @@ Includes all features in the **Community** tier, plus:
|
||||
|
||||
- **Support for Private Repositories:** Extend Harden-Runner's security capabilities to your private GitHub repositories.
|
||||
- **Support for Self-Hosted Runners:** Apply security controls and monitoring to self-hosted GitHub Actions runners.
|
||||
- **GitHub Checks Integration:** Enable GitHub Checks for Harden-Runner—if the baseline remains unchanged, the check passes; if it changes, the check fails, showing new outbound connections.
|
||||
- **View Outbound GitHub API calls at the Job Level:** Monitor HTTPS requests to GitHub APIs
|
||||
- **Determine Minimum GITHUB_TOKEN Permissions:** Monitor outbound HTTPS requests to GitHub APIs to recommend the least-privilege permissions needed for your workflows, enhancing security by reducing unnecessary access.
|
||||
- **View the Name and Path of Every File Written During the Build Process:** Gain visibility into every file written to the build environment, including the ability to correlate file writes with processes, ensuring complete transparency.
|
||||
- **View Process Names and Arguments:** Monitor every process executed during the build process, along with its arguments, and navigate the process tree to detect suspicious activities.
|
||||
- **Github Checks:** Display Harden Runner insights in the GitHub Checks UI, giving quick feedback on unusual network activity in pull requests.
|
||||
|
||||
For a detailed comparison and more information, please visit our [Pricing Page](https://www.stepsecurity.io/pricing).
|
||||
|
||||
@@ -117,26 +137,37 @@ Explore the full feature set in the [Features Documentation](https://docs.stepse
|
||||
|
||||
## Trusted By and Case Studies
|
||||
|
||||
Harden-Runner is trusted by over 5000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
|
||||
Harden-Runner is trusted by over 8000 leading open-source projects and enterprises, including Microsoft, Google, Kubernetes, and more.
|
||||
|
||||
### Trusted by
|
||||
|
||||
|
||||
| [](https://appv2.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/9947319332?jobid=27479776091&tab=network-events) | [](https://appv2.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/7587031851) | [](https://appv2.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/7576989995) | [](https://appv2.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/7446169664) | [](https://appv2.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/7590975903) | [](https://appv2.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/7591172950) | [](https://appv2.stepsecurity.io/github/nodejs/node/actions/runs/7591405720) | [](https://appv2.stepsecurity.io/github/aws/aperf/actions/runs/7631366761) |
|
||||
| [](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/9947319332?jobid=27479776091&tab=network-events) | [](https://app.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/7587031851) | [](https://app.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/7576989995) | [](https://app.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/7446169664) | [](https://app.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/7590975903) | [](https://app.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/7591172950) | [](https://app.stepsecurity.io/github/nodejs/node/actions/runs/7591405720) | [](https://app.stepsecurity.io/github/aws/aperf/actions/runs/7631366761) |
|
||||
| --------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| **CISA**<br>[Explore](https://appv2.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/9947319332?jobid=27479776091&tab=network-events) | **Microsoft**<br>[Explore](https://appv2.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/7587031851) | **Google**<br>[Explore](https://appv2.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/7576989995) | **DataDog**<br>[Explore](https://appv2.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/7446169664) | **Intel**<br>[Explore](https://appv2.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/7590975903) | **Kubernetes**<br>[Explore](https://appv2.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/7591172950) | **Node.js**<br>[Explore](https://appv2.stepsecurity.io/github/nodejs/node/actions/runs/7591405720) | **AWS**<br>[Explore](https://appv2.stepsecurity.io/github/aws/aperf/actions/runs/7631366761) |
|
||||
| **CISA**<br>[Explore](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/9947319332?jobid=27479776091&tab=network-events) | **Microsoft**<br>[Explore](https://app.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/7587031851) | **Google**<br>[Explore](https://app.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/7576989995) | **DataDog**<br>[Explore](https://app.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/7446169664) | **Intel**<br>[Explore](https://app.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/7590975903) | **Kubernetes**<br>[Explore](https://app.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/7591172950) | **Node.js**<br>[Explore](https://app.stepsecurity.io/github/nodejs/node/actions/runs/7591405720) | **AWS**<br>[Explore](https://app.stepsecurity.io/github/aws/aperf/actions/runs/7631366761) |
|
||||
|
||||
### Case Studies
|
||||
### Enterprise Case Studies
|
||||
|
||||
- [Harden-Runner Detects CI/CD Supply Chain Attack in Google’s Open-Source Project Flank](https://www.stepsecurity.io/case-studies/flank)
|
||||
- [StepSecurity Detects CI/CD Supply Chain Attack in Microsoft’s Open-Source Project Azure Karpenter Provider in Real-Time](https://www.stepsecurity.io/case-studies/azure-karpenter-provider)
|
||||
- [How Coveo Strengthened GitHub Actions Security with StepSecurity](https://www.stepsecurity.io/case-studies/coveo)
|
||||
- [Hashgraph Achieves Comprehensive CI/CD Security Without Compromising Development Speed](https://www.stepsecurity.io/case-studies/hashgraph)
|
||||
- [Chainguard Secures GitHub Actions with StepSecurity](https://www.stepsecurity.io/case-studies/chainguard)
|
||||
- [Kapiche secures their GitHub Actions software supply chain with Harden-Runner](https://www.stepsecurity.io/case-studies/kapiche)
|
||||
- [Arcjet Enhances CI/CD Security with Harden-Runner](https://www.stepsecurity.io/case-studies/arcjet)
|
||||
|
||||
---
|
||||
|
||||
## Environment Compatibility Matrix
|
||||
|
||||
Harden-Runner is designed to work seamlessly across a variety of runner environments, providing consistent security insights and protections regardless of where your workflows execute. For self-hosted runners, audit mode is deployed directly to the runner infrastructure without requiring any changes to your existing workflows. For more details, refer to the [official documentation](https://docs.stepsecurity.io/harden-runner).
|
||||
|
||||
| Environment Type | Compatibility | Audit Mode Deployment | Workflow Changes for Audit Mode |
|
||||
|------------------|---------------|--------------------------|-------------------|
|
||||
| GitHub-hosted runners | ✅ Full support | Add Harden-Runner Action to workflow | Yes |
|
||||
| Self-hosted VM runners | ✅ Full support | Include agent in runner image | No |
|
||||
| Self-hosted bare-metal runners | ✅ Full support | Install agent as a service | No |
|
||||
| Actions Runner Controller (ARC) | ✅ Full support | Deploy as DaemonSet | No |
|
||||
| RunsOn Runners | ✅ Full support | Pre-integrated | No |
|
||||
|
||||
## How It Works
|
||||
|
||||
Want to know the technical details? Dive into the architecture of Harden-Runner and its integrations for GitHub-hosted and self-hosted runners in our [How Harden-Runner Works Documentation](docs/how-it-works.md).
|
||||
|
||||
+5
-1
@@ -17,7 +17,11 @@ inputs:
|
||||
required: false
|
||||
default: "false"
|
||||
disable-sudo:
|
||||
description: "Disable sudo access for the runner account"
|
||||
description: "Disable sudo access for the runner account. Note: This parameter will be deprecated in the future. Please use disable-sudo-and-containers instead."
|
||||
required: false
|
||||
default: "false"
|
||||
disable-sudo-and-containers:
|
||||
description: "Disable sudo and container access for the runner account"
|
||||
required: false
|
||||
default: "false"
|
||||
disable-file-monitoring:
|
||||
|
||||
Vendored
+29799
-782
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+29824
-789
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+62924
-48976
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
Vendored
+1
-1
File diff suppressed because one or more lines are too long
+3
-3
@@ -3,12 +3,12 @@
|
||||
### GitHub-Hosted Runners
|
||||
|
||||
* Only Ubuntu VM is supported. Windows and MacOS GitHub-hosted runners are not supported. There is a discussion about that [here](https://github.com/step-security/harden-runner/discussions/121).
|
||||
* Harden-Runner is not supported when [job is run in a container](https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container) as it needs sudo access on the Ubuntu VM to run. It can be used to monitor jobs that use containers to run steps. The limitation is if the entire job is run in a container. That is not common for GitHub Actions workflows, as most of them run directly on `ubuntu-latest`. Note: This is not a limitation for Self-Hosted runners.
|
||||
* Harden-Runner is not supported when [job is run in a container](https://docs.github.com/en/actions/using-jobs/running-jobs-in-a-container) with built-in labels such as `ubuntu-latest`, as it needs sudo access on the Ubuntu VM to run. The limitation is if the entire job is run in a container. However, such jobs can be monitored when using custom VM images with GitHub-hosted runners. This is also not a limitation for Self-Hosted runners.
|
||||
|
||||
### Self-Hosted Actions Runner Controller (ARC) Runners
|
||||
|
||||
* Since ARC Harden Runner uses eBPF, only Linux jobs are supported. Windows and MacOS jobs are not supported.
|
||||
|
||||
### Self-Hosted VM Runners (e.g. on EC2)
|
||||
### Self-Hosted VM (e.g. on EC2) and Bare-metal Runners
|
||||
|
||||
* Only Ubuntu VM is supported. Windows and MacOS jobs are not supported.
|
||||
* Only Linux jobs are supported. Windows and MacOS jobs are not supported.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 407 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 90 KiB |
@@ -0,0 +1,11 @@
|
||||
[[IgnoredVulns]]
|
||||
id = "GHSA-h5c3-5r3r-rr8q"
|
||||
reason = "Untrusted headers are not processed"
|
||||
|
||||
[[IgnoredVulns]]
|
||||
id = "GHSA-rmvr-2pp2-xj38"
|
||||
reason = "Untrusted headers are not processed"
|
||||
|
||||
[[IgnoredVulns]]
|
||||
id = "GHSA-xx4v-prfh-6cgc"
|
||||
reason = "Untrusted headers are not processed"
|
||||
Generated
+1285
-931
File diff suppressed because it is too large
Load Diff
+4
-3
@@ -23,12 +23,13 @@
|
||||
},
|
||||
"homepage": "https://github.com/step-security/harden-runner#readme",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.1.4",
|
||||
"@actions/cache": "^4.0.0",
|
||||
"@actions/core": "^1.5.0",
|
||||
"@actions/exec": "^1.1.0",
|
||||
"@actions/github": "^5.0.0",
|
||||
"@actions/github": "^6.0.1",
|
||||
"@actions/http-client": "^2.0.1",
|
||||
"@actions/tool-cache": "^1.7.1",
|
||||
"@babel/helpers": "^7.26.10",
|
||||
"ansi-regex": ">=5.0.1",
|
||||
"is-docker": "^3.0.0",
|
||||
"node-fetch": ">=3.2.0",
|
||||
@@ -39,7 +40,7 @@
|
||||
"@types/node": "^16.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "^6.1.0",
|
||||
"@typescript-eslint/parser": "^6.1.0",
|
||||
"@vercel/ncc": "^0.30.0",
|
||||
"@vercel/ncc": "^0.38.3",
|
||||
"eslint": "^7.32.0",
|
||||
"eslint-config-google": "^0.14.0",
|
||||
"jest": "^29.3.1",
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
import { isARCRunner } from "./arc-runner";
|
||||
|
||||
it("should correctly recognize arc based runner", async () => {
|
||||
process.env["GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT"] =
|
||||
"actions-runner-controller/2.0.1";
|
||||
|
||||
let isArc: boolean = await isArcRunner();
|
||||
let isArc: boolean = await isARCRunner();
|
||||
expect(isArc).toBe(true);
|
||||
});
|
||||
|
||||
+16
-6
@@ -2,7 +2,7 @@ import * as cp from "child_process";
|
||||
import * as fs from "fs";
|
||||
import path from "path";
|
||||
|
||||
export function isArcRunner(): boolean {
|
||||
export function isARCRunner(): boolean {
|
||||
const runnerUserAgent = process.env["GITHUB_ACTIONS_RUNNER_EXTRA_USER_AGENT"];
|
||||
|
||||
let isARC = false;
|
||||
@@ -18,23 +18,33 @@ export function isArcRunner(): boolean {
|
||||
|
||||
function isSecondaryPod(): boolean {
|
||||
const workDir = "/__w";
|
||||
return fs.existsSync(workDir);
|
||||
let hasKubeEnv = process.env["KUBERNETES_PORT"] !== undefined;
|
||||
return fs.existsSync(workDir) && hasKubeEnv;
|
||||
}
|
||||
|
||||
export function sendAllowedEndpoints(endpoints: string): void {
|
||||
const startTime = Date.now();
|
||||
const allowedEndpoints = endpoints.split(" "); // endpoints are space separated
|
||||
|
||||
for (const endpoint of allowedEndpoints) {
|
||||
if (endpoint) {
|
||||
let sent = 0;
|
||||
for (let endpoint of allowedEndpoints) {
|
||||
endpoint = endpoint.trim();
|
||||
if (endpoint.length > 0) {
|
||||
let encodedEndpoint = Buffer.from(endpoint).toString("base64");
|
||||
let endpointPolicyStr = `step_policy_endpoint_${encodedEndpoint}`;
|
||||
echo(endpointPolicyStr);
|
||||
sent++;
|
||||
}
|
||||
}
|
||||
|
||||
if (allowedEndpoints.length > 0) {
|
||||
applyPolicy(allowedEndpoints.length);
|
||||
if (sent > 0) {
|
||||
applyPolicy(sent);
|
||||
}
|
||||
|
||||
const duration = Date.now() - startTime;
|
||||
console.log(
|
||||
`[harden-runner] sendAllowedEndpoints completed in ${duration}ms (sent ${sent} endpoints)`
|
||||
);
|
||||
}
|
||||
|
||||
function applyPolicy(count: number): void {
|
||||
|
||||
+3
-3
@@ -4,11 +4,11 @@ import * as fs from "fs";
|
||||
|
||||
const CHECKSUMS = {
|
||||
tls: {
|
||||
amd64: "38e7ed97ced6fe0c1cf0fb5ee3b3d521dfe28d5ddf1cdca72d130c8d1b4a314e", // v1.4.2
|
||||
arm64: "f67c80cc578c996d4f882c14fcdb63df57927d907cd22f1ec65f9fa940c08cf3",
|
||||
amd64: "2a5be06ab620340f6957ddd180450caab414671c8b7da1996d8e2755c1cd49fa", // v1.7.9
|
||||
arm64: "e9d0ccf3e4e62ba15ff208ef6e0afa0ad5e323c3dfc50527342f436a85e65a55",
|
||||
},
|
||||
non_tls: {
|
||||
amd64: "a9f1842e3d7f3d38c143dbe8ffe1948e6c8173cd04da072d9f9d128bb400844a", // v0.13.7
|
||||
amd64: "336093af8ebe969567b66fd035af3bd4f7e1c723ce680d6b4b5b2a1f79bc329e", // v0.14.2
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
+28
-7
@@ -2,11 +2,18 @@ import * as fs from "fs";
|
||||
import * as cp from "child_process";
|
||||
import * as common from "./common";
|
||||
import isDocker from "is-docker";
|
||||
import { isArcRunner } from "./arc-runner";
|
||||
import { isARCRunner } from "./arc-runner";
|
||||
import { isGithubHosted } from "./tls-inspect";
|
||||
import { context } from "@actions/github";
|
||||
(async () => {
|
||||
console.log("[harden-runner] post-step");
|
||||
|
||||
const customProperties = context?.payload?.repository?.custom_properties || {};
|
||||
if (customProperties["skip-harden-runner"] === "true") {
|
||||
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
@@ -16,7 +23,7 @@ import { isGithubHosted } from "./tls-inspect";
|
||||
return;
|
||||
}
|
||||
|
||||
if (isArcRunner()) {
|
||||
if (isARCRunner()) {
|
||||
console.log(`[!] ${common.ARC_RUNNER_MESSAGE}`);
|
||||
return;
|
||||
}
|
||||
@@ -25,6 +32,10 @@ import { isGithubHosted } from "./tls-inspect";
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.STATE_customVMImage === "true") {
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.env.STATE_isTLS === "false" && process.arch === "arm64") {
|
||||
return;
|
||||
}
|
||||
@@ -37,6 +48,11 @@ import { isGithubHosted } from "./tls-inspect";
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGithubHosted() && fs.existsSync("/home/agent/post_event.json")) {
|
||||
console.log("Post step already executed, skipping");
|
||||
return;
|
||||
}
|
||||
|
||||
fs.writeFileSync(
|
||||
"/home/agent/post_event.json",
|
||||
JSON.stringify({ event: "post" })
|
||||
@@ -81,12 +97,17 @@ import { isGithubHosted } from "./tls-inspect";
|
||||
}
|
||||
|
||||
var disable_sudo = process.env.STATE_disableSudo;
|
||||
if (disable_sudo !== "true") {
|
||||
var disable_sudo_and_containers = process.env.STATE_disableSudoAndContainers;
|
||||
|
||||
if (disable_sudo !== "true" && disable_sudo_and_containers !== "true") {
|
||||
try {
|
||||
var journalLog = cp.execSync("sudo journalctl -u agent.service --lines=1000", {
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10 // 10MB buffer
|
||||
});
|
||||
var journalLog = cp.execSync(
|
||||
"sudo journalctl -u agent.service --lines=1000",
|
||||
{
|
||||
encoding: "utf8",
|
||||
maxBuffer: 1024 * 1024 * 10, // 10MB buffer
|
||||
}
|
||||
);
|
||||
console.log("agent.service log:");
|
||||
console.log(journalLog);
|
||||
} catch (error) {
|
||||
|
||||
+30
-72
@@ -1,6 +1,6 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as fs from "fs";
|
||||
import { STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { STEPSECURITY_API_URL, STEPSECURITY_WEB_URL } from "./configs";
|
||||
|
||||
export function printInfo(web_url) {
|
||||
console.log(
|
||||
@@ -59,14 +59,12 @@ export async function addSummary() {
|
||||
return;
|
||||
}
|
||||
|
||||
const web_url = STEPSECURITY_WEB_URL;
|
||||
const insights_url = `${web_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}`;
|
||||
|
||||
const log = "/home/agent/agent.log";
|
||||
if (!fs.existsSync(log)) {
|
||||
const correlation_id = process.env.STATE_correlation_id;
|
||||
if (!correlation_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
let needsSubscription = false;
|
||||
try {
|
||||
let data = fs.readFileSync("/home/agent/annotation.log", "utf8");
|
||||
@@ -96,85 +94,45 @@ export async function addSummary() {
|
||||
return;
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(log, "utf-8");
|
||||
const lines = content.split("\n");
|
||||
|
||||
let tableEntries = [];
|
||||
|
||||
for (const line of lines) {
|
||||
processLogLine(line, tableEntries);
|
||||
}
|
||||
|
||||
if (tableEntries.length === 0) {
|
||||
// Extract owner and repo from GITHUB_REPOSITORY (format: owner/repo)
|
||||
const [owner, repo] = process.env["GITHUB_REPOSITORY"]?.split("/") || [];
|
||||
const run_id = process.env["GITHUB_RUN_ID"];
|
||||
|
||||
if (!owner || !repo || !run_id || !correlation_id) {
|
||||
return;
|
||||
}
|
||||
|
||||
const insightsRow = `<p><b><a href="${insights_url}">📄 View Full Report</a></b></p>`;
|
||||
|
||||
await core.summary.addSeparator().addRaw(`<h2>🛡 StepSecurity Report</h2>`);
|
||||
|
||||
tableEntries.sort((a, b) => {
|
||||
if (a.status === "❌ Blocked" && b.status !== "❌ Blocked") {
|
||||
return -1;
|
||||
} else if (a.status !== "❌ Blocked" && b.status === "❌ Blocked") {
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
// Fetch job summary from API
|
||||
const apiUrl = `${STEPSECURITY_API_URL}/github/${owner}/${repo}/actions/runs/${run_id}/correlation/${correlation_id}/job-markdown-summary`;
|
||||
|
||||
try {
|
||||
const response = await fetch(apiUrl);
|
||||
if (!response.ok) {
|
||||
console.error(`Failed to fetch job summary: ${response.status} ${response.statusText}`);
|
||||
return;
|
||||
}
|
||||
});
|
||||
|
||||
tableEntries = tableEntries.slice(0, 3);
|
||||
|
||||
await core.summary.addRaw(`
|
||||
<blockquote>
|
||||
<p>Preview of the outbound network calls during this workflow run.</p></blockquote>
|
||||
<h3>Network Calls</h3>
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Process</th>
|
||||
<th>Destination</th>
|
||||
<th>Status</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
${tableEntries
|
||||
.map(
|
||||
(entry) => `<tr>
|
||||
<td><code>${entry.process}</code></td>
|
||||
<td>${entry.domain.replace(/\.$/, "")}</td>
|
||||
<td>${entry.status}</td>
|
||||
</tr>`
|
||||
)
|
||||
.join("")}
|
||||
<tr>
|
||||
<td><code>...</code></td>
|
||||
<td><code>...</code></td>
|
||||
<td><code>...</code></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
${insightsRow}
|
||||
`);
|
||||
|
||||
await core.summary
|
||||
.addRaw(
|
||||
`<p><i>Markdown generated by the <a href="https://github.com/step-security/harden-runner">Harden-Runner GitHub Action</a>.</i></p>`
|
||||
)
|
||||
.addSeparator()
|
||||
.write();
|
||||
|
||||
const markdownSummary = await response.text();
|
||||
|
||||
// Render the markdown summary using core.summary.addRaw
|
||||
await core.summary.addRaw(markdownSummary).write();
|
||||
return;
|
||||
} catch (error) {
|
||||
console.error(`Error fetching job summary: ${error}`);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
export const STATUS_HARDEN_RUNNER_UNAVAILABLE = "409";
|
||||
|
||||
export const CONTAINER_MESSAGE =
|
||||
"This job is running in a container. Harden Runner does not run in a container as it needs sudo access to run. This job will not be monitored.";
|
||||
"This job is running in a container. Such jobs can be monitored by installing Harden Runner in a custom VM image for GitHub-hosted runners.";
|
||||
|
||||
export const UBUNTU_MESSAGE =
|
||||
"This job is not running in a GitHub Actions Hosted Runner Ubuntu VM. Harden Runner is only supported on Ubuntu VM. This job will not be monitored.";
|
||||
|
||||
export const SELF_HOSTED_NO_AGENT_MESSAGE =
|
||||
"This job is running on a self-hosted runner, but the runner does not have Harden-Runner installed. This job will not be monitored.";
|
||||
export const SELF_HOSTED_RUNNER_MESSAGE =
|
||||
"This job is running on a self-hosted runner.";
|
||||
|
||||
export const HARDEN_RUNNER_UNAVAILABLE_MESSAGE =
|
||||
"Sorry, we are currently experiencing issues with the Harden Runner installation process. It is currently unavailable.";
|
||||
|
||||
@@ -3,9 +3,16 @@ import * as core from "@actions/core";
|
||||
import isDocker from "is-docker";
|
||||
import { STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { isGithubHosted } from "./tls-inspect";
|
||||
import { context } from "@actions/github";
|
||||
(async () => {
|
||||
console.log("[harden-runner] main-step");
|
||||
|
||||
const customProperties = context?.payload?.repository?.custom_properties || {};
|
||||
if (customProperties["skip-harden-runner"] === "true") {
|
||||
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
|
||||
@@ -25,7 +25,9 @@ export async function installAgent(
|
||||
|
||||
if (isTLS) {
|
||||
downloadPath = await tc.downloadTool(
|
||||
`https://packages.stepsecurity.io/github-hosted/harden-runner_1.4.2_linux_${variant}.tar.gz`
|
||||
`https://github.com/step-security/agent-ebpf/releases/download/v1.7.9/harden-runner_1.7.9_linux_${variant}.tar.gz`,
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
} else {
|
||||
if (variant === "arm64") {
|
||||
@@ -33,7 +35,7 @@ export async function installAgent(
|
||||
return false;
|
||||
}
|
||||
downloadPath = await tc.downloadTool(
|
||||
"https://github.com/step-security/agent/releases/download/v0.13.7/agent_0.13.7_linux_amd64.tar.gz",
|
||||
"https://github.com/step-security/agent/releases/download/v0.14.2/agent_0.14.2_linux_amd64.tar.gz",
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
|
||||
@@ -8,6 +8,7 @@ export interface Configuration {
|
||||
egress_policy: string;
|
||||
disable_telemetry: boolean;
|
||||
disable_sudo: boolean;
|
||||
disable_sudo_and_containers: boolean;
|
||||
disable_file_monitoring: boolean;
|
||||
is_github_hosted: boolean;
|
||||
private: string;
|
||||
@@ -20,6 +21,7 @@ export interface PolicyResponse {
|
||||
policyName?: string;
|
||||
allowed_endpoints?: string[];
|
||||
disable_sudo?: boolean;
|
||||
disable_sudo_and_containers?: boolean;
|
||||
disable_file_monitoring?: boolean;
|
||||
disable_telemetry?: boolean;
|
||||
egress_policy?: string;
|
||||
|
||||
@@ -36,6 +36,7 @@ test("merge configs", async () => {
|
||||
egress_policy: "audit",
|
||||
disable_telemetry: false,
|
||||
disable_sudo: false,
|
||||
disable_sudo_and_containers: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
@@ -62,6 +63,7 @@ test("merge configs", async () => {
|
||||
egress_policy: "audit",
|
||||
disable_telemetry: false,
|
||||
disable_sudo: false,
|
||||
disable_sudo_and_containers: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
|
||||
+11
-2
@@ -39,7 +39,12 @@ export async function fetchPolicy(
|
||||
}
|
||||
|
||||
if (response === undefined && err !== undefined) {
|
||||
throw new Error(`[Policy Fetch] ${err}`);
|
||||
// Preserve the original error's statusCode if it exists
|
||||
const error = new Error(`[Policy Fetch] ${err}`);
|
||||
if (err.statusCode !== undefined) {
|
||||
(error as any).statusCode = err.statusCode;
|
||||
}
|
||||
throw error;
|
||||
} else {
|
||||
return response.result;
|
||||
}
|
||||
@@ -56,6 +61,10 @@ export function mergeConfigs(
|
||||
localConfig.disable_sudo = remoteConfig.disable_sudo;
|
||||
}
|
||||
|
||||
if (remoteConfig.disable_sudo_and_containers !== undefined) {
|
||||
localConfig.disable_sudo_and_containers = remoteConfig.disable_sudo_and_containers;
|
||||
}
|
||||
|
||||
if (remoteConfig.disable_file_monitoring !== undefined) {
|
||||
localConfig.disable_file_monitoring = remoteConfig.disable_file_monitoring;
|
||||
}
|
||||
@@ -66,7 +75,7 @@ export function mergeConfigs(
|
||||
return localConfig;
|
||||
}
|
||||
|
||||
function sleep(ms) {
|
||||
function sleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
+138
-41
@@ -19,8 +19,12 @@ import { Configuration, PolicyResponse } from "./interfaces";
|
||||
import { fetchPolicy, mergeConfigs } from "./policy-utils";
|
||||
import * as cache from "@actions/cache";
|
||||
import { getCacheEntry } from "@actions/cache/lib/internal/cacheHttpClient";
|
||||
import * as cacheTwirpClient from "@actions/cache/lib/internal/shared/cacheTwirpClient";
|
||||
import { GetCacheEntryDownloadURLRequest } from "@actions/cache/lib/generated/results/api/v1/cache";
|
||||
import { getCacheServiceVersion } from "@actions/cache/lib/internal/config";
|
||||
|
||||
import * as utils from "@actions/cache/lib/internal/cacheUtils";
|
||||
import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
import { isARCRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
import { STEPSECURITY_API_URL, STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { isGithubHosted, isTLSEnabled } from "./tls-inspect";
|
||||
import { installAgent } from "./install-agent";
|
||||
@@ -35,6 +39,12 @@ interface MonitorResponse {
|
||||
try {
|
||||
console.log("[harden-runner] pre-step");
|
||||
|
||||
const customProperties = context?.payload?.repository?.custom_properties || {};
|
||||
if (customProperties["skip-harden-runner"] === "true") {
|
||||
console.log("Skipping harden-runner: custom property 'skip-harden-runner' is set to 'true'");
|
||||
return;
|
||||
}
|
||||
|
||||
if (process.platform !== "linux") {
|
||||
console.log(common.UBUNTU_MESSAGE);
|
||||
return;
|
||||
@@ -58,6 +68,9 @@ interface MonitorResponse {
|
||||
egress_policy: core.getInput("egress-policy"),
|
||||
disable_telemetry: core.getBooleanInput("disable-telemetry"),
|
||||
disable_sudo: core.getBooleanInput("disable-sudo"),
|
||||
disable_sudo_and_containers: core.getBooleanInput(
|
||||
"disable-sudo-and-containers"
|
||||
),
|
||||
disable_file_monitoring: core.getBooleanInput("disable-file-monitoring"),
|
||||
private: context?.payload?.repository?.private || false,
|
||||
is_github_hosted: isGithubHosted(),
|
||||
@@ -78,7 +91,18 @@ interface MonitorResponse {
|
||||
confg = mergeConfigs(confg, result);
|
||||
} catch (err) {
|
||||
core.info(`[!] ${err}`);
|
||||
core.setFailed(err);
|
||||
// Only fail the job if ID token is not available
|
||||
if (err.message && err.message.includes('Unable to get ACTIONS_ID_TOKEN_REQUEST')) {
|
||||
core.setFailed('Policy store requires id-token write permission as it uses OIDC to fetch the policy from StepSecurity API. Please add "id-token: write" to your job permissions.');
|
||||
} else {
|
||||
// Handle different HTTP status codes
|
||||
if (err.statusCode >= 400 && err.statusCode < 500) {
|
||||
core.error('Policy not found');
|
||||
} else {
|
||||
core.error(`Unexpected error occurred: ${err}. Falling back to egress policy audit`);
|
||||
confg.egress_policy = 'audit';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
fs.appendFileSync(
|
||||
@@ -88,6 +112,13 @@ interface MonitorResponse {
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_STATE,
|
||||
`disableSudoAndContainers=${confg.disable_sudo_and_containers}${EOL}`,
|
||||
{
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
core.info(`[!] Current Configuration: \n${JSON.stringify(confg)}\n`);
|
||||
|
||||
if (confg.egress_policy !== "audit" && confg.egress_policy !== "block") {
|
||||
@@ -114,28 +145,78 @@ interface MonitorResponse {
|
||||
} catch (exception) {
|
||||
console.log(exception);
|
||||
}
|
||||
try {
|
||||
const compressionMethod: CompressionMethod =
|
||||
await utils.getCompressionMethod();
|
||||
const cacheFilePath = path.join(__dirname, "cache.txt");
|
||||
core.info(`cacheFilePath ${cacheFilePath}`);
|
||||
const cacheEntry: ArtifactCacheEntry = await getCacheEntry(
|
||||
[cacheKey],
|
||||
[cacheFilePath],
|
||||
{
|
||||
compressionMethod: compressionMethod,
|
||||
|
||||
const cacheServiceVersion: string = getCacheServiceVersion();
|
||||
|
||||
switch (cacheServiceVersion) {
|
||||
case "v2":
|
||||
core.info(`cache version: v2`);
|
||||
try {
|
||||
const cacheFilePath = path.join(__dirname, "cache.txt");
|
||||
core.info(`cacheFilePath ${cacheFilePath}`);
|
||||
|
||||
const twirpClient = cacheTwirpClient.internalCacheTwirpClient();
|
||||
const compressionMethod = await utils.getCompressionMethod();
|
||||
|
||||
const request: GetCacheEntryDownloadURLRequest = {
|
||||
key: cacheKey,
|
||||
restoreKeys: [],
|
||||
version: utils.getCacheVersion(
|
||||
[cacheFilePath],
|
||||
compressionMethod,
|
||||
false
|
||||
),
|
||||
};
|
||||
|
||||
const response = await twirpClient.GetCacheEntryDownloadURL(
|
||||
request
|
||||
);
|
||||
|
||||
const url = new URL(response.signedDownloadUrl);
|
||||
core.info(
|
||||
`Adding cacheHost: ${url.hostname}:443 to allowed-endpoints`
|
||||
);
|
||||
|
||||
confg.allowed_endpoints += ` ${url.hostname}:443`;
|
||||
} catch (e) {
|
||||
core.info(`Unable to fetch cacheURL ${e}`);
|
||||
if (confg.egress_policy === "block") {
|
||||
core.info("Switching egress-policy to audit mode");
|
||||
confg.egress_policy = "audit";
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case "v1":
|
||||
core.info(`cache version: v1`);
|
||||
|
||||
try {
|
||||
const compressionMethod: CompressionMethod =
|
||||
await utils.getCompressionMethod();
|
||||
const cacheFilePath = path.join(__dirname, "cache.txt");
|
||||
core.info(`cacheFilePath ${cacheFilePath}`);
|
||||
|
||||
const cacheEntry: ArtifactCacheEntry = await getCacheEntry(
|
||||
[cacheKey],
|
||||
[cacheFilePath],
|
||||
{
|
||||
compressionMethod: compressionMethod,
|
||||
}
|
||||
);
|
||||
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 ${exception}`);
|
||||
if (confg.egress_policy === "block") {
|
||||
core.info("Switching egress-policy to audit mode");
|
||||
confg.egress_policy = "audit";
|
||||
}
|
||||
}
|
||||
);
|
||||
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";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -143,7 +224,7 @@ interface MonitorResponse {
|
||||
common.printInfo(web_url);
|
||||
}
|
||||
|
||||
if (isArcRunner()) {
|
||||
if (isARCRunner()) {
|
||||
console.log(`[!] ${common.ARC_RUNNER_MESSAGE}`);
|
||||
if (confg.egress_policy === "block") {
|
||||
sendAllowedEndpoints(confg.allowed_endpoints);
|
||||
@@ -158,28 +239,37 @@ interface MonitorResponse {
|
||||
fs.appendFileSync(process.env.GITHUB_STATE, `selfHosted=true${EOL}`, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
if (!fs.existsSync("/home/agent/agent")) {
|
||||
core.info(common.SELF_HOSTED_NO_AGENT_MESSAGE);
|
||||
return;
|
||||
}
|
||||
if (confg.egress_policy === "block") {
|
||||
try {
|
||||
if (process.env.USER) {
|
||||
chownForFolder(process.env.USER, "/home/agent");
|
||||
}
|
||||
|
||||
const confgStr = JSON.stringify(confg);
|
||||
fs.writeFileSync("/home/agent/block_event.json", confgStr);
|
||||
await sleep(5000);
|
||||
} catch (error) {
|
||||
core.info(`[!] Unable to write block_event.json: ${error}`);
|
||||
}
|
||||
core.info(common.SELF_HOSTED_RUNNER_MESSAGE);
|
||||
|
||||
if (confg.egress_policy === "block") {
|
||||
sendAllowedEndpoints(confg.allowed_endpoints);
|
||||
await sleep(5000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGithubHosted() && process.env.STEP_SECURITY_HARDEN_RUNNER === "true") {
|
||||
fs.appendFileSync(process.env.GITHUB_STATE, `customVMImage=true${EOL}`, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
|
||||
core.info("This job is running on a custom VM image with Harden Runner installed.");
|
||||
|
||||
if (confg.egress_policy === "block") {
|
||||
sendAllowedEndpoints(confg.allowed_endpoints);
|
||||
await sleep(5000);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if (isGithubHosted() && fs.existsSync("/home/agent/agent.status")) {
|
||||
console.log("Agent already installed, skipping installation");
|
||||
return;
|
||||
}
|
||||
|
||||
let _http = new httpm.HttpClient();
|
||||
let statusCode;
|
||||
let statusCode: number | undefined;
|
||||
_http.requestOptions = { socketTimeout: 3 * 1000 };
|
||||
let addSummary = "false";
|
||||
try {
|
||||
@@ -217,6 +307,13 @@ interface MonitorResponse {
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_STATE,
|
||||
`correlation_id=${correlation_id}${EOL}`,
|
||||
{
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`Step Security Job Correlation ID: ${correlation_id}`);
|
||||
if (String(statusCode) === common.STATUS_HARDEN_RUNNER_UNAVAILABLE) {
|
||||
@@ -265,7 +362,7 @@ interface MonitorResponse {
|
||||
process.exit(0);
|
||||
})();
|
||||
|
||||
export function sleep(ms) {
|
||||
export function sleep(ms: number) {
|
||||
return new Promise((resolve) => {
|
||||
setTimeout(resolve, ms);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user