Merge branch 'main' into int
8
.github/workflows/canary.yml
vendored
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v1
|
||||
- uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # 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
|
||||
|
|
|
|||
2
.github/workflows/code-review.yml
vendored
|
|
@ -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@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: block
|
||||
|
|
|
|||
2
.github/workflows/codeql-analysis.yml
vendored
|
|
@ -41,7 +41,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142
|
||||
with:
|
||||
egress-policy: audit # TODO: change to 'egress-policy: block' after couple of runs
|
||||
|
||||
|
|
|
|||
2
.github/workflows/dependency-review.yml
vendored
|
|
@ -17,7 +17,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
|
|
|||
17
.github/workflows/recurring-int-tests.yml
vendored
|
|
@ -13,7 +13,7 @@ jobs:
|
|||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142
|
||||
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@63c24ba6bd7ba022e95695ff85de572c04a18142
|
||||
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
|
||||
|
|
|
|||
2
.github/workflows/release.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
permissions:
|
||||
contents: write
|
||||
steps:
|
||||
- uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895
|
||||
- uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142
|
||||
with:
|
||||
egress-policy: audit
|
||||
allowed-endpoints:
|
||||
|
|
|
|||
2
.github/workflows/scorecards.yml
vendored
|
|
@ -25,7 +25,7 @@ jobs:
|
|||
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
egress-policy: audit
|
||||
|
||||
|
|
|
|||
11
.github/workflows/test.yml
vendored
|
|
@ -13,10 +13,13 @@ concurrency:
|
|||
group: ${{ github.workflow }}
|
||||
jobs:
|
||||
test:
|
||||
permissions:
|
||||
checks: write
|
||||
pull-requests: write
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Harden Runner
|
||||
uses: step-security/harden-runner@eb238b55efaa70779f274895e782ed17c84f2895 # v2.6.1
|
||||
uses: step-security/harden-runner@63c24ba6bd7ba022e95695ff85de572c04a18142 # v2.7.0
|
||||
with:
|
||||
disable-sudo: true
|
||||
egress-policy: audit
|
||||
|
|
@ -35,3 +38,9 @@ jobs:
|
|||
- name: Run coverage
|
||||
run: npm test -- --coverage
|
||||
- uses: codecov/codecov-action@eaaf4bedf32dbdc6b720b63067d99c4d77d6047d # v3.1.4
|
||||
- name: Publish Test Results
|
||||
uses: step-security/publish-unit-test-result-action@v1
|
||||
if: always()
|
||||
with:
|
||||
files: |
|
||||
reports/*.xml
|
||||
|
|
|
|||
1
.gitignore
vendored
|
|
@ -20,6 +20,7 @@ lib-cov
|
|||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
reports
|
||||
*.lcov
|
||||
|
||||
# nyc test coverage
|
||||
|
|
|
|||
116
README.md
|
|
@ -13,42 +13,35 @@
|
|||
|
||||
</div>
|
||||
|
||||
## GitHub Actions Runtime Security
|
||||
## Harden Github-hosted and self-hosted runners
|
||||
|
||||
Harden-Runner provides runtime security for GitHub-hosted and self-hosted environments.
|
||||
Harden-Runner provides network egress filtering and runtime security for GitHub-hosted and self-hosted runners.
|
||||
|
||||
For self-hosted environments, Harden-Runner supports:
|
||||
|
||||
1. Kubernetes runners setup using Actions Runner Controller (ARC)
|
||||
2. Virtual Machine runners (e.g. on EC2) - both ephemeral and persistent runners are supported
|
||||
|
||||
[](https://youtu.be/fpdwX5hYACo)
|
||||

|
||||
|
||||
## Explore open source projects using Harden-Runner
|
||||
|
||||
| [](https://app.stepsecurity.io/github/cisagov/skeleton-generic/actions/runs/6199340224) | [](https://app.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/5559160177) | [](https://app.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/5546354505) | [](https://app.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/5387101451) | [](https://app.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/5579910614) | [](https://app.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/5581511101) | [](https://app.stepsecurity.io/github/nodejs/node/actions/runs/5563468674) | [](https://app.stepsecurity.io/github/Mastercard/flow/actions/runs/5542112873) |
|
||||
| [](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/6199340224) | **Microsoft**<br>[Explore](https://app.stepsecurity.io/github/microsoft/ebpf-for-windows/actions/runs/5559160177) | **Google**<br>[Explore](https://app.stepsecurity.io/github/GoogleCloudPlatform/functions-framework-ruby/actions/runs/5546354505) | **DataDog**<br>[Explore](https://app.stepsecurity.io/github/DataDog/stratus-red-team/actions/runs/5387101451) | **Intel**<br>[Explore](https://app.stepsecurity.io/github/intel/cve-bin-tool/actions/runs/5579910614) | **Kubernetes**<br>[Explore](https://app.stepsecurity.io/github/kubernetes-sigs/cluster-api-provider-azure/actions/runs/5581511101) | **Node.js**<br>[Explore](https://app.stepsecurity.io/github/nodejs/node/actions/runs/5563468674) | **Mastercard**<br>[Explore](https://app.stepsecurity.io/github/Mastercard/flow/actions/runs/5542112873) |
|
||||
|
||||
## 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. | 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 [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 [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 [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.
|
||||
|
|
@ -57,11 +50,11 @@ Read this [case study](https://infosecwriteups.com/detecting-malware-packages-in
|
|||
|
||||
### GitHub-Hosted Runners
|
||||
|
||||
1. Add `step-security/harden-runner` GitHub Action to your GitHub Actions workflow file as the first step in each job.
|
||||
1. Add the `step-security/harden-runner` GitHub Action to your GitHub Actions workflow file as the first step in each job.
|
||||
|
||||
```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,51 +65,60 @@ 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/5577342236)). 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-5.png" alt="Insights from harden-runner" >
|
||||
<img src="images/network-events.png" alt="Insights from harden-runner" >
|
||||
</p>
|
||||
|
||||
4. Under the insights section, you'll find a Recommended Policy. You can either 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.
|
||||
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-policy1.png" alt="Policy recommended by harden-runner" >
|
||||
<img src="images/recommended-policy.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.
|
||||
|
||||
- To use Harden-Runner in a `Private` repository, you must install the [StepSecurity Actions Security GitHub App](https://github.com/apps/stepsecurity-actions-security).
|
||||
- This is needed to access the GitHub Actions API and to authenticate users to access the insights URL for private repositories.
|
||||
- If you use Harden-Runner GitHub Action in a private repository, the generated insights URL is NOT public. Only those who have access to the repository can view it.
|
||||
|
||||
Read this [case study on how Kapiche uses Harden-Runner](https://www.stepsecurity.io/case-studies/kapiche/) to improve software supply chain security in their private repositories.
|
||||
|
||||
### Self-Hosted Actions Runner Controller (ARC) Runners
|
||||
|
||||
> Explore demo workflows using self-hosted ARC Runner and ARC Harden-Runner [here](https://docs.stepsecurity.io/harden-runner/how-tos/enable-runtime-security-arc).
|
||||
|
||||
Actions Runner Controller (ARC) is a Kubernetes operator that orchestrates and scales self-hosted runners for GitHub Actions.
|
||||
Actions Runner Controller (ARC) is a Kubernetes operator that orchestrates self-hosted runners for GitHub Actions.
|
||||
|
||||
- Instead of adding the Harden-Runner GitHub Action in each workflow, you'll need to install the ARC Harden-Runner daemonset on your Kubernetes cluster.
|
||||
- Upon installation, the ARC Harden-Runner daemonset constantly monitors each workflow run; you do NOT need to add the Harden-Runner GitHub Action to each job for `audit` mode. You do need to add the Harden-Runner GitHub Action for `block` mode.
|
||||
- You can access security insights and runtime detections under the `Runtime Security` tab in your dashboard.
|
||||
- Instead of adding the Harden-Runner GitHub Action in each job, you'll need to install the ARC Harden-Runner daemonset on your Kubernetes cluster.
|
||||
- Upon installation, the ARC Harden-Runner daemonset monitors all jobs run on the cluster; you do NOT need to add the Harden-Runner GitHub Action to each job for `audit` mode. You do need to add the Harden-Runner GitHub Action to jobs where you want to enable `block` mode.
|
||||
- Please email support@stepsecurity.io for instructions on how to install the ARC-Harden-Runner daemonset on your Kubernetes cluster.
|
||||
|
||||
### Self-Hosted VM Runners (e.g. on EC2)
|
||||
|
||||
> Explore demo workflows using self-hosted VM Runners and Harden-Runner [here](https://docs.stepsecurity.io/harden-runner/how-tos/enable-runtime-security-vm).
|
||||
|
||||
- Instead of adding the Harden-Runner GitHub Action in each workflow, you'll need to install the Harden-Runner agent on your runner image (e.g. AMI). This is typically done using packer.
|
||||
- The Harden-Runner agent monitors each job run on the VM, both ephemeral and persistent runners are supported; you do NOT need to add the Harden-Runner GitHub Action to each job for `audit` mode. You do need to add the Harden-Runner GitHub Action for `block` mode.
|
||||
- You can access security insights and runtime detections under the `Runtime Security` tab in your dashboard.
|
||||
|
||||
## Support for Self-Hosted Runners and Private Repositories
|
||||
|
||||
Runtime security for self-hosted runners and private repositories are supported with a commercial license. Check out the [documentation](https://docs.stepsecurity.io/stepsecurity-platform/billing) for more details.
|
||||
|
||||
- Install the [StepSecurity Actions Security GitHub App](https://github.com/apps/stepsecurity-actions-security) to use Harden-Runner GitHub Action for `Private` repositories.
|
||||
- If you use Harden-Runner GitHub Action in a private repository, the generated insights URL is NOT public.
|
||||
- You need to authenticate first to access insights URL for private repository. Only those who have access to the repository can view it.
|
||||
|
||||
Read this [case study on how Kapiche uses Harden-Runner](https://www.stepsecurity.io/case-studies/kapiche/) to improve software supply chain security in their private repositories.
|
||||
- Instead of adding the Harden-Runner GitHub Action in each job, you'll need to install the Harden-Runner agent on your runner image (e.g. AMI). This is typically done using packer or as a post-install step when using the https://github.com/philips-labs/terraform-aws-github-runner project to setup runners.
|
||||
- The Harden-Runner agent monitors all jobs run on the VM, both ephemeral and persistent runners are supported; you do NOT need to add the Harden-Runner GitHub Action to each job for `audit` mode. You do need to add the Harden-Runner GitHub Action to jobs where you want to enable `block` mode.
|
||||
- Please email support@stepsecurity.io for instructions on how to install the Harden-Runner agent on your self-hosted VM runners. This agent is different than the one used for GitHub-hosted runners.
|
||||
|
||||
## Features at a glance
|
||||
|
||||
For details, check out the documentation at https://docs.stepsecurity.io
|
||||
|
||||
### 👀 Monitor egress traffic
|
||||
### 📶 View outbound network traffic
|
||||
|
||||
> Applies to both GitHub-hosted and self-hosted runners
|
||||
|
||||
|
|
@ -124,9 +126,33 @@ Harden-Runner monitors all outbound traffic from each job at the DNS and network
|
|||
|
||||
- After the workflow completes, each outbound call is correlated with each step of the job, and shown in the insights page
|
||||
- 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 past job runs
|
||||
- A filtering (block) egress policy is suggested in the insights page based on the current and past job runs
|
||||
|
||||
### 🚦 Filter egress traffic to allowed endpoints
|
||||
### 🌐 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
|
||||
|
||||
Harden-Runner creates a baseline of outbound traffic for each job during the first few runs that it monitors
|
||||
|
||||
- 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
|
||||
|
||||
|
|
@ -137,7 +163,7 @@ 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>
|
||||
|
||||
### 📁 Detect tampering of source code during build
|
||||
|
|
@ -152,7 +178,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
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ branding:
|
|||
icon: "check-square"
|
||||
color: "green"
|
||||
runs:
|
||||
using: "node16"
|
||||
using: "node20"
|
||||
pre: "dist/pre/index.js"
|
||||
main: "dist/index.js"
|
||||
post: "dist/post/index.js"
|
||||
|
|
|
|||
7188
dist/pre/index.js
vendored
2
dist/pre/index.js.map
vendored
|
Before Width: | Height: | Size: 3.9 MiB |
|
Before Width: | Height: | Size: 39 KiB |
BIN
images/blocked-outbound-call-3.png
Normal file
|
After Width: | Height: | Size: 56 KiB |
BIN
images/file-events.png
Normal file
|
After Width: | Height: | Size: 85 KiB |
BIN
images/https-events.png
Normal file
|
After Width: | Height: | Size: 103 KiB |
|
Before Width: | Height: | Size: 107 KiB |
BIN
images/main.png
Normal file
|
After Width: | Height: | Size: 191 KiB |
BIN
images/network-events.png
Normal file
|
After Width: | Height: | Size: 107 KiB |
|
Before Width: | Height: | Size: 57 KiB |
BIN
images/recommended-policy.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
|
|
@ -1,5 +1,9 @@
|
|||
/** @type {import('ts-jest/dist/types').InitialOptionsTsJest} */
|
||||
export default {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
preset: "ts-jest",
|
||||
testEnvironment: "node",
|
||||
reporters: [
|
||||
"default",
|
||||
["jest-junit", { outputDirectory: "reports", outputName: "report.xml" }],
|
||||
],
|
||||
};
|
||||
|
|
|
|||
4
package-lock.json
generated
|
|
@ -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 =
|
||||
"204c82116e8c0eebf5409bb2b81aa5d96fe32f0c5abc1cb0364ee70937c32056"; // checksum for tls_agent
|
||||
}
|
||||
|
||||
if (checksum !== expectedChecksum) {
|
||||
core.setFailed(
|
||||
`Checksum verification failed, expected ${expectedChecksum} instead got ${checksum}`
|
||||
|
|
|
|||
5
src/configs.ts
Normal file
|
|
@ -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";
|
||||
|
|
@ -9,6 +9,7 @@ export interface Configuration {
|
|||
disable_telemetry: boolean;
|
||||
disable_sudo: boolean;
|
||||
disable_file_monitoring: boolean;
|
||||
is_github_hosted: boolean;
|
||||
private: string;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,7 @@ test("merge configs", async () => {
|
|||
disable_sudo: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
};
|
||||
let policyResponse: PolicyResponse = {
|
||||
owner: "h0x0er",
|
||||
|
|
@ -60,6 +62,7 @@ test("merge configs", async () => {
|
|||
disable_sudo: false,
|
||||
disable_file_monitoring: false,
|
||||
private: "true",
|
||||
is_github_hosted: true,
|
||||
};
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
30
src/setup.ts
|
|
@ -23,6 +23,8 @@ 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";
|
||||
|
||||
(async () => {
|
||||
try {
|
||||
|
|
@ -52,6 +54,7 @@ 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(),
|
||||
};
|
||||
|
||||
let policyName = core.getInput("policy");
|
||||
|
|
@ -143,7 +146,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",
|
||||
});
|
||||
|
|
@ -199,13 +202,28 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
|||
let token = core.getInput("token");
|
||||
let auth = `token ${token}`;
|
||||
|
||||
const downloadPath: string = await tc.downloadTool(
|
||||
`https://step-security-agent.s3.us-west-2.amazonaws.com/refs/heads/${env}/agent`
|
||||
);
|
||||
let downloadPath: string;
|
||||
|
||||
if (await isTLSEnabled(context.repo.owner)) {
|
||||
downloadPath = await tc.downloadTool(
|
||||
"https://packages.stepsecurity.io/github-hosted/harden-runner_1.1.0_linux_amd64.tar.gz"
|
||||
);
|
||||
verifyChecksum(downloadPath, true); // NOTE: verifying tls_agent's checksum, before extracting
|
||||
} else {
|
||||
downloadPath = await tc.downloadTool(
|
||||
`https://step-security-agent.s3.us-west-2.amazonaws.com/refs/heads/${env}/agent`
|
||||
);
|
||||
|
||||
//verifyChecksum(downloadPath, false); // NOTE: verifying agent's checksum, before extracting
|
||||
}
|
||||
|
||||
const extractPath = await tc.extractTar(downloadPath);
|
||||
|
||||
let cmd = "cp",
|
||||
args = [downloadPath, "/home/agent/agent"];
|
||||
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);
|
||||
|
|
@ -247,6 +265,8 @@ import { isArcRunner, sendAllowedEndpoints } from "./arc-runner";
|
|||
} catch (error) {
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
// see https://github.com/ruby/setup-ruby/issues/543
|
||||
process.exit(0);
|
||||
})();
|
||||
|
||||
export function sleep(ms) {
|
||||
|
|
|
|||
29
src/tls-inspect.test.ts
Normal file
|
|
@ -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);
|
||||
});
|
||||
29
src/tls-inspect.ts
Normal file
|
|
@ -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 runnerName = process.env.RUNNER_NAME || "";
|
||||
return runnerName.startsWith("GitHub Actions");
|
||||
}
|
||||