Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
17d0e2bd7d | ||
|
|
bb112d061b | ||
|
|
f4f3f445f3 | ||
|
|
7a946b57df | ||
|
|
75a01c2f00 | ||
|
|
53413f1a91 | ||
|
|
f086349bfa | ||
|
|
b9c325d8ce | ||
|
|
808a771f66 | ||
|
|
71714298b4 | ||
|
|
9ff9d14760 | ||
|
|
ac5fa0194e | ||
|
|
a4aa98b93c | ||
|
|
6c3b1c91e8 | ||
|
|
3498091f2b | ||
|
|
63a88e2cd7 | ||
|
|
07e5965e5b | ||
|
|
63c24ba6bd | ||
|
|
95691d3d1c | ||
|
|
6339621ce7 | ||
|
|
4a63cdab74 |
@@ -22,7 +22,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v1
|
||||
- uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v1
|
||||
with:
|
||||
egress-policy: audit
|
||||
allowed-endpoints:
|
||||
@@ -41,3 +41,9 @@ jobs:
|
||||
env:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
canary: true
|
||||
|
||||
- name: Canary TLS test
|
||||
uses: docker://ghcr.io/step-security/integration-test/int:latest
|
||||
env:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
canary-tls: true
|
||||
|
||||
@@ -11,7 +11,7 @@ jobs:
|
||||
pull-requests: read
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
|
||||
@@ -41,7 +41,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10
|
||||
with:
|
||||
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
|
||||
|
||||
|
||||
@@ -17,11 +17,11 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
- name: 'Checkout Repository'
|
||||
uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1
|
||||
- name: 'Dependency Review'
|
||||
uses: actions/dependency-review-action@7bbfa034e752445ea40215fff1c3bf9597993d3f # v3.1.3
|
||||
uses: actions/dependency-review-action@0c155c5e8556a497adf53f2c18edabf945ed8e70 # v4.3.2
|
||||
|
||||
@@ -13,7 +13,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10
|
||||
with:
|
||||
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
|
||||
|
||||
@@ -22,3 +22,18 @@ jobs:
|
||||
env:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
canary: true
|
||||
|
||||
int-tls-tests:
|
||||
name: int tls tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10
|
||||
with:
|
||||
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
|
||||
|
||||
- name: Canary test
|
||||
uses: docker://ghcr.io/step-security/integration-test/int:latest
|
||||
env:
|
||||
PAT: ${{ secrets.PAT }}
|
||||
canary-tls: true
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
- uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10
|
||||
with:
|
||||
egress-policy: audit
|
||||
allowed-endpoints:
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@f086349bfa2bd1361f7909c78558e816508cdc10 # v2.8.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: audit
|
||||
|
||||
@@ -17,42 +17,30 @@
|
||||
|
||||
Harden-Runner provides network egress filtering and runtime security for GitHub-hosted and self-hosted runners.
|
||||
|
||||
For self-hosted environments, Harden-Runner supports:
|
||||
Learn how Harden-Runner works through the video below, which shows how it detected a supply chain attack on a Google open-source project.
|
||||
|
||||
1. Kubernetes runners setup using Actions Runner Controller (ARC)
|
||||
2. Virtual Machine runners (e.g. on EC2) - both ephemeral and persistent runners are supported
|
||||
<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>
|
||||
|
||||
[](https://youtu.be/fpdwX5hYACo)
|
||||
## 3,000+ open source projects use Harden-Runner
|
||||
|
||||
## Explore open source projects using Harden-Runner
|
||||
|
||||
| [](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/7588528684) | [](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/Mastercard/flow/actions/runs/7539664931) |
|
||||
| [](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/7588528684) | [](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://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/7588528684) | **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) | **Mastercard**<br>[Explore](https://app.stepsecurity.io/github/Mastercard/flow/actions/runs/7539664931) |
|
||||
|
||||
## Hands-On Tutorials
|
||||
|
||||
You can use [GitHub Actions Goat](https://github.com/step-security/github-actions-goat) to try Harden-Runner. You only need a GitHub Account and a web browser.
|
||||
|
||||
Hands-on Tutorials for GitHub Actions Runtime Security:
|
||||
|
||||
1. [Filter Egress Network Traffic](https://github.com/step-security/github-actions-goat/blob/main/docs/Solutions/RestrictOutboundTraffic.md)
|
||||
2. [Detect File Tampering](https://github.com/step-security/github-actions-goat/blob/main/docs/Solutions/MonitorSourceCode.md)
|
||||
| **CISA**<br>[Explore](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/7588528684) | **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) |
|
||||
|
||||
## Why
|
||||
|
||||
Compromised workflows, dependencies, and build tools typically make outbound calls to exfiltrate credentials, or may tamper source code, dependencies, or artifacts during the build.
|
||||
There are two main threats from compromised workflows, dependencies, and build tools in a CI/CD environment:
|
||||
1. Exfiltration of CI/CD credentials and source code
|
||||
2. Tampering of source code, dependencies, or artifacts during the build to inject a backdoor
|
||||
|
||||
Harden-Runner monitors process, file, and network activity to:
|
||||
|
||||
| | Countermeasure | Prevent Security Breach |
|
||||
| --- | ----------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| 1. | Detect anomalous traffic and block egress traffic at the DNS (Layer 7) and network layers (Layers 3 and 4) to prevent exfiltration of code and CI/CD credentials | To prevent the [Codecov breach](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/ExfiltratingCICDSecrets.md) scenario |
|
||||
| 2. | Detect if source code is being tampered during the build process to inject a backdoor | To detect the [SolarWinds incident](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/TamperingDuringBuild.md) scenario |
|
||||
| 1. | Monitor and block outbound network traffic at the DNS, HTTPS (Layer 7), and network layers (Layers 3 and 4) to prevent exfiltration of code and CI/CD credentials | To prevent the [Codecov breach](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/ExfiltratingCICDSecrets.md) scenario |
|
||||
| 2. | Detect if source code is being tampered during the build process to inject a backdoor | To detect the [XZ Utils](https://www.stepsecurity.io/blog/analysis-of-backdoored-xz-utils-build-process-with-harden-runner) and [SolarWinds incident](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/TamperingDuringBuild.md) scenario |
|
||||
| 3. | Detect poisoned workflows and compromised dependencies | To detect [Dependency confusion](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/ExfiltratingCICDSecrets.md#dependency-confusion-attacks) and [Malicious dependencies](https://github.com/step-security/github-actions-goat/blob/main/docs/Vulnerabilities/ExfiltratingCICDSecrets.md#compromised-dependencies) |
|
||||
|
||||
Read this [case study](https://infosecwriteups.com/detecting-malware-packages-in-github-actions-7b93a9985635) on how Harden-Runner detected malicious packages in the NPM registry.
|
||||
|
||||
## How
|
||||
|
||||
### GitHub-Hosted Runners
|
||||
@@ -61,7 +49,7 @@ Read this [case study](https://infosecwriteups.com/detecting-malware-packages-in
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
- uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
```
|
||||
@@ -72,18 +60,27 @@ Read this [case study](https://infosecwriteups.com/detecting-malware-packages-in
|
||||
<img src="images/buildlog1.png" alt="Link in build log" >
|
||||
</p>
|
||||
|
||||
3. Click on the link ([example link](https://app.stepsecurity.io/github/microsoft/msquic/actions/runs/7588119198)). You will see a process monitor view of network and file events correlated with each step of the job.
|
||||
3. Click on the link ([example link](https://app.stepsecurity.io/github/step-security/github-actions-goat/actions/runs/7704454287)). You will see a process monitor view of network and file events correlated with each step of the job.
|
||||
|
||||
<p align="left">
|
||||
<img src="images/insights.png" alt="Insights from harden-runner" >
|
||||
<img src="images/network-events1.png" alt="Insights from harden-runner" >
|
||||
</p>
|
||||
|
||||
4. Under the process monitor view, you'll find a recommended block policy based on outbound calls aggregated from the current and past runs of the job. You can update your workflow file with this policy, or alternatively, use the [Policy Store](https://docs.stepsecurity.io/harden-runner/how-tos/block-egress-traffic#2-add-the-policy-using-the-policy-store) to apply the policy without modifying the workflow file. From now on, any outbound calls not in the allowed list will be blocked.
|
||||
4. In the `Recommended Policy` tab, you'll find a recommended block policy based on outbound calls aggregated from the current and past runs of the job. You can update your workflow file with this policy, or alternatively, use the [Policy Store](https://docs.stepsecurity.io/harden-runner/how-tos/block-egress-traffic#2-add-the-policy-using-the-policy-store) to apply the policy without modifying the workflow file. From now on, any outbound calls not in the allowed list will be blocked.
|
||||
|
||||
<p align="left">
|
||||
<img src="images/rec-policy.png" alt="Policy recommended by harden-runner" >
|
||||
<img src="images/recommended-policy1.png" alt="Policy recommended by harden-runner" >
|
||||
</p>
|
||||
|
||||
## Hands-On Tutorials
|
||||
|
||||
You can use [GitHub Actions Goat](https://github.com/step-security/github-actions-goat) to try Harden-Runner. You only need a GitHub Account and a web browser.
|
||||
|
||||
Hands-on Tutorials for GitHub Actions Runtime Security:
|
||||
|
||||
1. [Filter Egress Network Traffic](https://github.com/step-security/github-actions-goat/blob/main/docs/Solutions/RestrictOutboundTraffic.md)
|
||||
2. [Detect File Tampering](https://github.com/step-security/github-actions-goat/blob/main/docs/Solutions/MonitorSourceCode.md)
|
||||
|
||||
## Support for Self-Hosted Runners and Private Repositories
|
||||
|
||||
Hardening for self-hosted runners and GitHub-hosted runners used in private repositories is supported with a commercial license. Check out the [documentation](https://docs.stepsecurity.io/stepsecurity-platform/billing) for more details.
|
||||
@@ -116,7 +113,7 @@ Actions Runner Controller (ARC) is a Kubernetes operator that orchestrates self-
|
||||
|
||||
For details, check out the documentation at https://docs.stepsecurity.io
|
||||
|
||||
### 👀 View outbound network traffic
|
||||
### 📶 View outbound network traffic
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
|
||||
@@ -126,6 +123,20 @@ Harden-Runner monitors all outbound traffic from each job at the DNS and network
|
||||
- For self-hosted runners, no changes are needed to workflow files to monitor egress traffic
|
||||
- A filtering (block) egress policy is suggested in the insights page based on the current and past job runs
|
||||
|
||||
### 🌐 View outbound HTTPS traffic
|
||||
|
||||
> Applies to GitHub-hosted and self-hosted VM runners
|
||||
|
||||
Harden-Runner can monitor outbound HTTPS requests. This feature is supported with a commercial license.
|
||||
|
||||
- HTTPS events are monitored using eBPF (no MITM proxy is used)
|
||||
- If a HTTP PUT/ POST/ PATCH call is made to `github.com` or `api.github.com` hosts to a HTTP Path with a different organization than where the workflow is running, the call is marked as anomalous
|
||||
- As of now, only HTTPS calls to `github.com` and `api.github.com` hosts are monitoried.
|
||||
|
||||
<p align="left">
|
||||
<img src="images/https-events.png" alt="Policy recommended by harden-runner" >
|
||||
</p>
|
||||
|
||||
### ⚠️ Detect anomalous outbound network traffic
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
@@ -135,6 +146,7 @@ Harden-Runner creates a baseline of outbound traffic for each job during the fir
|
||||
- After the baseline is created, any new outbound destinations are marked as anomalous in the insights page
|
||||
- You can view the list of all anomalous outbound network traffic in the `Runtime detections` page on the dashboard
|
||||
|
||||
|
||||
### 🚦 Filter outbound network traffic to allowed endpoints
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
@@ -146,7 +158,35 @@ Once allowed endpoints are set in the policy in the workflow file, or in the [Po
|
||||
- Wildcard domains are supported, e.g. you can add `*.data.mcr.microsoft.com:443` to the allowed list, and egress traffic will be allowed to `eastus.data.mcr.microsoft.com:443` and `westus.data.mcr.microsoft.com:443`
|
||||
|
||||
<p align="left">
|
||||
<img src="images/blocked-outbound-call-2.png" alt="Policy recommended by harden-runner" >
|
||||
<img src="images/blocked-outbound-call-3.png" alt="Policy recommended by harden-runner" >
|
||||
</p>
|
||||
|
||||
### 📋 View the name and path of every file written during the build process
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
|
||||
View the name and path of every file that was written during the build process. This feature is supported with a commercial license.
|
||||
|
||||
- Harden-Runner tracks every file written to the GitHub Actions working directory during the build process.
|
||||
- In the insights page in the `File Write Events` tab you can see a file explorer view of each file that was written to.
|
||||
- Clicking on any file reveals a list of processes that wrote to it, providing complete transparency.
|
||||
|
||||
<p align="left">
|
||||
<img src="images/file-write-events.png" alt="View the name and path of every file written during the build process" >
|
||||
</p>
|
||||
|
||||
### 🔄 View process names and arguments
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
|
||||
View process names, PIDs, and process arguments. This feature is supported with a commercial license.
|
||||
|
||||
- Harden-Runner tracks every process that is run during the build process.
|
||||
- Clicking on any file reveals a list of processes that wrote to it.
|
||||
- You can walk up the process tree and view process arguments to understand the build process and detect suspicious activity.
|
||||
|
||||
<p align="left">
|
||||
<img src="images/process-events-3.png" alt="View process names and arguments" >
|
||||
</p>
|
||||
|
||||
### 📁 Detect tampering of source code during build
|
||||
@@ -161,7 +201,7 @@ Harden-Runner monitors file writes and can detect if a file is overwritten.
|
||||
- For self-hosted runners, no changes are needed to workflow files for file monitoring
|
||||
|
||||
<p align="left">
|
||||
<img src="images/file-overwritten.png" alt="Policy recommended by harden-runner" >
|
||||
<img src="images/file-events.png" alt="Policy recommended by harden-runner" >
|
||||
</p>
|
||||
|
||||
### 🚫 Run your job without sudo access
|
||||
|
||||
|
Before Width: | Height: | Size: 3.9 MiB |
|
Before Width: | Height: | Size: 39 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 632 KiB |
|
After Width: | Height: | Size: 85 KiB |
|
After Width: | Height: | Size: 79 KiB |
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 99 KiB |
|
After Width: | Height: | Size: 191 KiB |
|
After Width: | Height: | Size: 134 KiB |
|
After Width: | Height: | Size: 87 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
After Width: | Height: | Size: 188 KiB |
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "step-security-harden-runner",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"lockfileVersion": 2,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "step-security-harden-runner",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"license": "Apache License 2.0",
|
||||
"dependencies": {
|
||||
"@actions/cache": "^3.1.4",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "step-security-harden-runner",
|
||||
"version": "2.6.1",
|
||||
"version": "2.7.0",
|
||||
"description": "Security agent for GitHub-hosted runner: block egress traffic & detect code overwrite to prevent breaches",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
@@ -2,16 +2,21 @@ import * as core from "@actions/core";
|
||||
import * as crypto from "crypto";
|
||||
import * as fs from "fs";
|
||||
|
||||
export function verifyChecksum(downloadPath: string) {
|
||||
export function verifyChecksum(downloadPath: string, is_tls: boolean) {
|
||||
const fileBuffer: Buffer = fs.readFileSync(downloadPath);
|
||||
const checksum: string = crypto
|
||||
.createHash("sha256")
|
||||
.update(fileBuffer)
|
||||
.digest("hex"); // checksum of downloaded file
|
||||
|
||||
const expectedChecksum: string =
|
||||
let expectedChecksum: string =
|
||||
"ceb925c78e5c79af4f344f08f59bbdcf3376d20d15930a315f9b24b6c4d0328a"; // checksum for v0.13.5
|
||||
|
||||
if (is_tls) {
|
||||
expectedChecksum =
|
||||
"846ae66c6cfab958fe61736cec0b58bdb7651b36af04c279405c7114675d7033"; // checksum for tls_agent
|
||||
}
|
||||
|
||||
if (checksum !== expectedChecksum) {
|
||||
core.setFailed(
|
||||
`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
|
||||
|
||||
@@ -62,6 +62,13 @@ import { arcCleanUp, isArcRunner, removeStepPolicyFiles } from "./arc-runner";
|
||||
console.log(content);
|
||||
}
|
||||
|
||||
const daemonLog = "/home/agent/daemon.log";
|
||||
if (fs.existsSync(daemonLog)) {
|
||||
console.log("daemonLog:");
|
||||
var content = fs.readFileSync(daemonLog, "utf-8");
|
||||
console.log(content);
|
||||
}
|
||||
|
||||
var status = "/home/agent/agent.status";
|
||||
if (fs.existsSync(status)) {
|
||||
console.log("status:");
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import * as core from "@actions/core";
|
||||
import * as fs from "fs";
|
||||
import { STEPSECURITY_WEB_URL } from "./configs";
|
||||
|
||||
export function printInfo(web_url) {
|
||||
console.log(
|
||||
@@ -47,11 +48,11 @@ export const processLogLine = (
|
||||
};
|
||||
|
||||
export async function addSummary() {
|
||||
if (process.env.STATE_monitorStatusCode !== "200") {
|
||||
if (process.env.STATE_addSummary !== "true") {
|
||||
return;
|
||||
}
|
||||
|
||||
const web_url = "https://app.stepsecurity.io";
|
||||
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";
|
||||
@@ -172,4 +173,4 @@ export const HARDEN_RUNNER_UNAVAILABLE_MESSAGE =
|
||||
"Sorry, we are currently experiencing issues with the Harden Runner installation process. It is currently unavailable.";
|
||||
|
||||
export const ARC_RUNNER_MESSAGE =
|
||||
"Workflow is currently being executed in ARC based runner";
|
||||
"Workflow is currently being executed in ARC based runner";
|
||||
@@ -0,0 +1,5 @@
|
||||
export const STEPSECURITY_ENV = "agent"; // agent or int
|
||||
|
||||
export const STEPSECURITY_API_URL = `https://${STEPSECURITY_ENV}.api.stepsecurity.io/v1`;
|
||||
|
||||
export const STEPSECURITY_WEB_URL = "https://app.stepsecurity.io";
|
||||
@@ -1,6 +1,7 @@
|
||||
import * as common from "./common";
|
||||
import * as core from "@actions/core";
|
||||
import isDocker from "is-docker";
|
||||
import { STEPSECURITY_WEB_URL } from "./configs";
|
||||
|
||||
(async () => {
|
||||
if (process.platform !== "linux") {
|
||||
@@ -28,7 +29,7 @@ import isDocker from "is-docker";
|
||||
"Telemetry will not be sent to StepSecurity API as disable-telemetry is set to true"
|
||||
);
|
||||
} else {
|
||||
var web_url = "https://app.stepsecurity.io";
|
||||
var web_url = STEPSECURITY_WEB_URL;
|
||||
common.printInfo(web_url);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -9,7 +9,9 @@ export interface Configuration {
|
||||
disable_telemetry: boolean;
|
||||
disable_sudo: boolean;
|
||||
disable_file_monitoring: boolean;
|
||||
is_github_hosted: boolean;
|
||||
private: string;
|
||||
is_debug: boolean;
|
||||
}
|
||||
|
||||
export interface PolicyResponse {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import nock from "nock";
|
||||
import { API_ENDPOINT, fetchPolicy, mergeConfigs } from "./policy-utils";
|
||||
import { fetchPolicy, mergeConfigs } from "./policy-utils";
|
||||
import { Configuration, PolicyResponse } from "./interfaces";
|
||||
import { STEPSECURITY_API_URL } from "./configs";
|
||||
|
||||
test("success: fetching policy", async () => {
|
||||
let owner = "h0x0er";
|
||||
@@ -14,7 +15,7 @@ test("success: fetching policy", async () => {
|
||||
disable_sudo: false,
|
||||
disable_file_monitoring: false,
|
||||
};
|
||||
const policyScope = nock(`${API_ENDPOINT}`)
|
||||
const policyScope = nock(`${STEPSECURITY_API_URL}`)
|
||||
.get(`/github/${owner}/actions/policies/${policyName}`)
|
||||
.reply(200, response);
|
||||
|
||||
@@ -37,6 +38,8 @@ test("merge configs", async () => {
|
||||
disable_sudo: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
is_debug: false,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
owner: "h0x0er",
|
||||
@@ -60,6 +63,8 @@ test("merge configs", async () => {
|
||||
disable_sudo: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
is_debug: false,
|
||||
};
|
||||
|
||||
localConfig = mergeConfigs(localConfig, policyResponse);
|
||||
|
||||
@@ -1,19 +1,17 @@
|
||||
import { HttpClient } from "@actions/http-client";
|
||||
import { PolicyResponse, Configuration } from "./interfaces";
|
||||
|
||||
export const API_ENDPOINT = "https://agent.api.stepsecurity.io/v1";
|
||||
import { STEPSECURITY_API_URL } from "./configs";
|
||||
|
||||
export async function fetchPolicy(
|
||||
owner: string,
|
||||
policyName: string,
|
||||
idToken: string
|
||||
): Promise<PolicyResponse> {
|
||||
|
||||
if (idToken === "") {
|
||||
throw new Error("[PolicyFetch]: id-token in empty");
|
||||
}
|
||||
|
||||
let policyEndpoint = `${API_ENDPOINT}/github/${owner}/actions/policies/${policyName}`;
|
||||
let policyEndpoint = `${STEPSECURITY_API_URL}/github/${owner}/actions/policies/${policyName}`;
|
||||
|
||||
let httpClient = new HttpClient();
|
||||
|
||||
@@ -25,24 +23,24 @@ export async function fetchPolicy(
|
||||
let err = undefined;
|
||||
|
||||
let retry = 0;
|
||||
while(retry < 3){
|
||||
try{
|
||||
console.log(`Attempt: ${retry+1}`)
|
||||
while (retry < 3) {
|
||||
try {
|
||||
console.log(`Attempt: ${retry + 1}`);
|
||||
response = await httpClient.getJson<PolicyResponse>(
|
||||
policyEndpoint,
|
||||
headers
|
||||
);
|
||||
break;
|
||||
}catch(e){
|
||||
err = e
|
||||
} catch (e) {
|
||||
err = e;
|
||||
}
|
||||
retry += 1
|
||||
retry += 1;
|
||||
await sleep(1000);
|
||||
}
|
||||
|
||||
if(response === undefined && err !== undefined){
|
||||
throw new Error(`[Policy Fetch] ${err}`)
|
||||
}else{
|
||||
if (response === undefined && err !== undefined) {
|
||||
throw new Error(`[Policy Fetch] ${err}`);
|
||||
} else {
|
||||
return response.result;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,13 @@ import * as cache from "@actions/cache";
|
||||
import { getCacheEntry } from "@actions/cache/lib/internal/cacheHttpClient";
|
||||
import * as utils from "@actions/cache/lib/internal/cacheUtils";
|
||||
import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
import { STEPSECURITY_API_URL, STEPSECURITY_WEB_URL } from "./configs";
|
||||
import { isGithubHosted, isTLSEnabled } from "./tls-inspect";
|
||||
|
||||
interface MonitorResponse {
|
||||
runner_ip_address?: string;
|
||||
monitoring_started?: boolean;
|
||||
}
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
@@ -36,9 +43,8 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
}
|
||||
|
||||
var correlation_id = uuidv4();
|
||||
var env = "agent";
|
||||
var api_url = `https://${env}.api.stepsecurity.io/v1`;
|
||||
var web_url = "https://app.stepsecurity.io";
|
||||
var api_url = STEPSECURITY_API_URL;
|
||||
var web_url = STEPSECURITY_WEB_URL;
|
||||
|
||||
let confg: Configuration = {
|
||||
repo: process.env["GITHUB_REPOSITORY"],
|
||||
@@ -52,6 +58,8 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
disable_sudo: core.getBooleanInput("disable-sudo"),
|
||||
disable_file_monitoring: core.getBooleanInput("disable-file-monitoring"),
|
||||
private: context?.payload?.repository?.private || false,
|
||||
is_github_hosted: isGithubHosted(),
|
||||
is_debug: core.isDebug(),
|
||||
};
|
||||
|
||||
let policyName = core.getInput("policy");
|
||||
@@ -143,7 +151,7 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
|
||||
const runnerName = process.env.RUNNER_NAME || "";
|
||||
core.info(`RUNNER_NAME: ${runnerName}`);
|
||||
if (!runnerName.startsWith("GitHub Actions")) {
|
||||
if (!isGithubHosted()) {
|
||||
fs.appendFileSync(process.env.GITHUB_STATE, `selfHosted=true${EOL}`, {
|
||||
encoding: "utf8",
|
||||
});
|
||||
@@ -169,11 +177,19 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
let _http = new httpm.HttpClient();
|
||||
let statusCode;
|
||||
_http.requestOptions = { socketTimeout: 3 * 1000 };
|
||||
let addSummary = "false";
|
||||
try {
|
||||
const resp: httpm.HttpClientResponse = await _http.get(
|
||||
`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`
|
||||
const monitorRequestData = {
|
||||
correlation_id: correlation_id,
|
||||
job: process.env["GITHUB_JOB"],
|
||||
};
|
||||
const resp = await _http.postJson<MonitorResponse>(
|
||||
`${api_url}/github/${process.env["GITHUB_REPOSITORY"]}/actions/runs/${process.env["GITHUB_RUN_ID"]}/monitor`,
|
||||
monitorRequestData
|
||||
);
|
||||
statusCode = resp.message.statusCode; // adding error code to check whether agent is getting installed or not.
|
||||
|
||||
const responseData = resp.result;
|
||||
statusCode = resp.statusCode; // adding error code to check whether agent is getting installed or not.
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_STATE,
|
||||
`monitorStatusCode=${statusCode}${EOL}`,
|
||||
@@ -181,9 +197,21 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
|
||||
if (statusCode === 200 && responseData) {
|
||||
console.log(`Runner IP Address: ${responseData.runner_ip_address}`);
|
||||
addSummary = responseData.monitoring_started ? "true" : "false";
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(`error in connecting to ${api_url}: ${e}`);
|
||||
}
|
||||
fs.appendFileSync(
|
||||
process.env.GITHUB_STATE,
|
||||
`addSummary=${addSummary}${EOL}`,
|
||||
{
|
||||
encoding: "utf8",
|
||||
}
|
||||
);
|
||||
|
||||
console.log(`Step Security Job Correlation ID: ${correlation_id}`);
|
||||
if (String(statusCode) === common.STATUS_HARDEN_RUNNER_UNAVAILABLE) {
|
||||
@@ -199,18 +227,30 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
let token = core.getInput("token");
|
||||
let auth = `token ${token}`;
|
||||
|
||||
const downloadPath: string = await tc.downloadTool(
|
||||
"https://github.com/step-security/agent/releases/download/v0.13.5/agent_0.13.5_linux_amd64.tar.gz",
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
let downloadPath: string;
|
||||
|
||||
if (await isTLSEnabled(context.repo.owner)) {
|
||||
downloadPath = await tc.downloadTool(
|
||||
"https://packages.stepsecurity.io/github-hosted/harden-runner_1.2.0_linux_amd64.tar.gz"
|
||||
);
|
||||
verifyChecksum(downloadPath, true); // NOTE: verifying tls_agent's checksum, before extracting
|
||||
} else {
|
||||
downloadPath = await tc.downloadTool(
|
||||
"https://github.com/step-security/agent/releases/download/v0.13.5/agent_0.13.5_linux_amd64.tar.gz",
|
||||
undefined,
|
||||
auth
|
||||
);
|
||||
|
||||
verifyChecksum(downloadPath, false); // NOTE: verifying agent's checksum, before extracting
|
||||
}
|
||||
|
||||
verifyChecksum(downloadPath); // NOTE: verifying agent's checksum, before extracting
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
|
||||
let cmd = "cp",
|
||||
args = [path.join(extractPath, "agent"), "/home/agent/agent"];
|
||||
|
||||
cp.execFileSync(cmd, args);
|
||||
|
||||
cp.execSync("chmod +x /home/agent/agent");
|
||||
|
||||
fs.writeFileSync("/home/agent/agent.json", confgStr);
|
||||
@@ -252,6 +292,7 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
||||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
// see https://github.com/ruby/setup-ruby/issues/543
|
||||
process.exit(0);
|
||||
})();
|
||||
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
import nock from "nock";
|
||||
import { STEPSECURITY_API_URL } from "./configs";
|
||||
import { isTLSEnabled } from "./tls-inspect";
|
||||
|
||||
test("tls-inspect enabled", async () => {
|
||||
let owner = "h0x0er";
|
||||
let expected = true;
|
||||
|
||||
const resp = nock(`${STEPSECURITY_API_URL}`)
|
||||
.get(`/github/${owner}/actions/tls-inspection-status`)
|
||||
.reply(200, "");
|
||||
|
||||
let got = await isTLSEnabled(owner);
|
||||
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
|
||||
test("tls-inspect not enabled", async () => {
|
||||
let owner = "step-security";
|
||||
let expected = false;
|
||||
|
||||
const resp = nock(`${STEPSECURITY_API_URL}`)
|
||||
.get(`/github/${owner}/actions/tls-inspection-status`)
|
||||
.reply(401, "");
|
||||
|
||||
let got = await isTLSEnabled(owner);
|
||||
|
||||
expect(got).toEqual(expected);
|
||||
});
|
||||
@@ -0,0 +1,29 @@
|
||||
import { HttpClient } from "@actions/http-client";
|
||||
import { STEPSECURITY_API_URL } from "./configs";
|
||||
import * as core from "@actions/core";
|
||||
|
||||
export async function isTLSEnabled(owner: string): Promise<boolean> {
|
||||
let tlsStatusEndpoint = `${STEPSECURITY_API_URL}/github/${owner}/actions/tls-inspection-status`;
|
||||
let httpClient = new HttpClient();
|
||||
httpClient.requestOptions = { socketTimeout: 3 * 1000 };
|
||||
core.info(`[!] Checking TLS_STATUS: ${owner}`);
|
||||
let isEnabled = false;
|
||||
try {
|
||||
let resp = await httpClient.get(tlsStatusEndpoint);
|
||||
if (resp.message.statusCode === 200) {
|
||||
isEnabled = true;
|
||||
core.info(`[!] TLS_ENABLED: ${owner}`);
|
||||
} else {
|
||||
core.info(`[!] TLS_NOT_ENABLED: ${owner}`);
|
||||
}
|
||||
} catch (e) {
|
||||
core.info(`[!] Unable to check TLS_STATUS`);
|
||||
}
|
||||
|
||||
return isEnabled;
|
||||
}
|
||||
|
||||
export function isGithubHosted() {
|
||||
const runnerEnvironment = process.env.RUNNER_ENVIRONMENT || "";
|
||||
return runnerEnvironment === "github-hosted";
|
||||
}
|
||||