Policy Store
The Policy Store centrally manages Harden-Runner egress policies for your organization. Instead of writing allowed-endpoints or denied-endpoints inline in each workflow file, you define a policy once, attach it to a scope (workflow, repository, organization, or ARC cluster), and Harden-Runner fetches and enforces it automatically at runtime.
This makes it possible to update egress rules across hundreds of repositories without touching a single workflow file, and to keep an auditable history of every change.
Open Harden Runner → Policy Store from the sidebar to manage all of your organization's policies in one place.

The page has three sections:
Global Block Policy: A red callout at the top of the page surfaces the Global Block Policy, which blocks known-malicious domains and IPs across every workflow run in your organization, even when a policy is in Audit mode. The Global Block Policy is maintained by the StepSecurity SOC and is enforced automatically; you cannot edit or detach it.

Your policies: A counter row below the callout summarizes your organization's policies and acts as a filter:
Total
Every policy in the organization
Block
Policies in Block (enforce) mode
Audit
Policies in Audit (observe) mode
Unattached
Policies that are not attached to any scope and are therefore inactive
Use the Search policies by name input to filter the table further.
Policies table: Each row is one policy:
Policy name
The display name set when the policy was created
Mode
Block or Audit, with a colored dot (red for Block, orange for Audit)
Endpoints
Number of endpoints in the policy, from either the allowed-endpoints or the denied-endpoints list
Attached to
The scope the policy is currently attached to (e.g., Organization, 2 Repositories), or Not attached if it is dormant
The three-dot menu at the end of each row exposes the per-policy actions described in Managing policies.
Key concepts
Policy
A policy is a YAML document that configures Harden-Runner's runtime behavior. It has the same shape as the with: block you would write in a workflow file:
A policy can also include a lockdown-mode section (see Lockdown Mode below).
Allow list and deny list
A policy controls egress in one of two directions. Which one you pick depends on whether you know what the job needs, or you know what it must never reach.
Allow list
allowed-endpoints
Only the listed destinations are reachable. Everything else is blocked.
You have a stable baseline for the job and want the tightest possible policy.
Deny list
denied-endpoints
Only the listed destinations are blocked. Everything else stays reachable.
You want to stop traffic to specific known-bad or unwanted destinations without first building a complete allow list.
A deny list policy sets egress-policy: block and lists the destinations to deny:
Wildcard domains are supported in denied-endpoints, using the same syntax as allowed-endpoints. Ports are not. A denied endpoint is denied on every port, so a deny list entry is written without a port.
A deny list entry can be a domain, a wildcard domain, or an IP address. Write the address on its own line to deny every connection to it.
The trade-off is coverage. An allow list is closed by default, so a destination nobody anticipated is blocked. A deny list is open by default, so it only stops what you have named. A deny list is the faster path to enforcement on a job whose baseline is not yet stable, and it is the right tool for org-wide bans on specific destinations, but it does not replace an allow list as an exfiltration control.
The deny list egress policy requires the Harden-Runner GitHub Action v2.21.0 or later, Self-Hosted VM Harden Runner v1.9.0 or later, or ARC Harden Runner v2.19.0 or later.
On ARC clusters running an earlier version, a deny list policy attached to the cluster scope is not enforced. Upgrade the cluster to v2.19.0 or later, or use an allow list policy until you do.
Scope and attachment
A policy on its own does nothing. To take effect, it must be attached to one or more of the following scopes:
Workflow
A workflow file in a single repository, or a workflow file name across many repositories using a repository wildcard pattern (see below)
Repository
All workflows in a repository
Organization
All repositories in a GitHub organization
Cluster
All jobs running on a specific Actions Runner Controller (ARC) cluster
A policy listed as Not attached in the Policy Store exists but is not applied anywhere. Attach it to a scope to activate it.
Attaching to a workflow file name across repositories
The Workflow scope also accepts a repository wildcard pattern in place of a repository name. This attaches the policy to a workflow file name, and the policy applies in every repository whose name matches the pattern and that runs a workflow file with that name. It is the right tool when the same workflow file, such as a security-scan.yml rolled out from a template, exists in many repositories.
*
All repositories in the organization
service-*
Repositories whose name starts with service-
*-prod
Repositories whose name ends with -prod
*payments*
Repositories whose name contains payments
Pattern rules:
*matches any sequence of characters, including an empty one. A pattern can contain more than one*, but not two in a row (service-**is rejected).Repository name matching is case-insensitive.
Wildcards are supported only in the repository name. The workflow file name must be exact:
security-scan.yml, not*-scan.yml.A pattern attachment must name at least one workflow file. A pattern cannot be attached to entire repositories; use the Organization scope to cover all workflows in all repositories.
Start pattern attachments in Audit mode. A single pattern can match many repositories with very different egress baselines, and a block policy that fits one repository may break the same workflow file in another. Review the audit findings across the matched repositories, then switch the policy to Block.
Precedence
When multiple policies could apply to the same job, Harden-Runner picks the most specific one, in this order:
Workflow (exact repository)
Workflow (repository pattern)
Repository
Organization
Cluster
A policy attached to the workflow in that specific repository always wins. Otherwise a policy attached to the workflow file name through a repository pattern applies; if none, the repository policy; then the organization policy; and finally the cluster policy (for ARC).
If more than one pattern attachment matches the same workflow, the most specific pattern wins: the one with the most non-wildcard characters. For a repository named service-payments, a policy attached through service-* beats one attached through *. If two matching patterns are equally specific, the tie is broken deterministically by policy name.
Policy history
Every change to a policy — content edits, attachments, detachments, and scope modifications — is recorded on the policy's History page. Each entry captures who made the change, when, and what was changed, including a side-by-side diff for content edits. See View policy history for details.
Creating a policy
You can create a policy three ways:
From scratch in the Policy Store
From a baseline, importing endpoints observed during past workflow runs
From the Baseline page, auto-populated with endpoints from a selected source
Creating A New Security Policy In StepSecurity
Learn how to establish a new security policy for your GitHub actions by importing existing configurations from an established repository. This guide simplifies the setup process by utilizing baseline sources to ensure consistent security coverage across your projects.
From scratch
Step 1: Navigate to Harden Runner → Policy Store in the sidebar and click "Create policy"

Step 2: Enter a policy name and choose the enforcement mode (audit and block)

Step 3: Choose Allow list or Deny list, then add endpoints manually or import them from a baseline.
The toggle above the endpoint box controls which kind of list the policy defines. Allow list produces an allowed-endpoints policy, where only the listed destinations are reachable. Deny list produces a denied-endpoints policy, where only the listed destinations are blocked and everything not listed stays reachable. See Allow list and deny list for the trade-offs, including the minimum agent version each deployment option needs.
Enter one endpoint per line. The count below the box confirms how many endpoints the policy currently holds, and the Live preview panel on the right shows the exact policy.yml that will be saved, along with the resolved mode, endpoint count, and lockdown state.

Step 4. Optionally enable lockdown-mode configuration (see Lockdown Mode).
Step 5. Click Add Policy.

The policy is created in the Not attached state. See Attaching a policy to a scope to activate it.
From a baseline (during creation)
During Step 3 above, click Import Endpoints from Baseline instead of typing endpoints manually.
Choose a baseline source:
Organization — endpoints observed across all repositories in the organization
Repository — endpoints observed in a single repository
ARC Cluster — endpoints observed on a specific ARC cluster
Job — endpoints observed in a single job's history
Local file — upload a file with endpoints

Click Import Endpoints to populate the policy, then Add Policy to save.

From the Baseline page
Step 1: Navigate to Harden Runner → Baseline

Step 2: Select a job, repository, ARC cluster, or organization, then click Create Policy

Step 3: You will be redirected to the new policy page with endpoints automatically populated

Step 4: (Optional) Click Export Endpoints to download the list as a text file

Step 5: Click Create Policy to save

Attaching a policy to a scope
A newly-created policy is inactive until you attach it.
Step 1: In the Policy Store, click the three-dot menu next to the policy and select "Attach policy"

Step 2. Choose the scope type — Cluster, Organization, Repository, or Workflow — and select the specific target.

With the Workflow scope, you can select a specific repository, or use a repository wildcard pattern such as * or service-* to attach the policy to a workflow file name across all matching repositories. See Attaching to a workflow file name across repositories.
You can attach the same policy to multiple scopes, and a single scope can only have one policy attached at a time (replacing the current attachment if you attach a new one).
Applying policies at runtime
Creating and attaching a policy is not enough on its own — the runner also needs to know to fetch policies from the Policy Store. How that's configured depends on the runner type.
GitHub-Hosted runners
For GitHub-hosted runners, enable policy fetching in your workflow file:
To get your API key:
Go to Harden-Runner → Installation and select GitHub-Hosted Custom VM.
Copy the API key.
Store it as an organization-level secret named
STEP_SECURITY_API_KEYso every repository in the organization can use it.
When this step runs, Harden-Runner looks up the attached policy following the precedence rules and enforces it. No other workflow changes are needed.
GitHub-Hosted Custom VMs and Self-Hosted runners
For custom VMs and self-hosted runners, policy fetching is configured on the runner itself, so no workflow changes are needed.
Step 1. Configure the pre-job hook:
Go to Harden-Runner → Installation → Runner Job Hooks.
Follow the provided script to install the hook on your runner.
Step 2. Create and attach policies as described above. The runner will fetch and enforce the attached policy at the start of each job.
Managing policies
The three-dot menu next to each policy in the Policy Store provides the following actions:
View policy
Read-only view of the policy YAML
Edit policy
Modify the policy YAML content (egress policy, allowed or denied endpoints, lockdown settings)
Modify attachment
Change the scope a policy is attached to
Detach policy
Remove all attachments. The policy is preserved but becomes inactive
View history
See the full audit trail of changes to this policy
Delete policy
Permanently remove the policy. All attachments are removed first
Edit a policy
Click Edit policy to open the YAML editor. You can edit the policy content directly, or click Import Endpoints from Baseline to add endpoints from an observed baseline. Click Update Policy to save.
Saved changes are applied on the next workflow run that fetches the policy.
View policy history
The Policy History page shows a timeline of every change made to a policy — content updates, attachments, detachments, and scope modifications — so you can audit exactly what changed, when, and who made the change.
To open the history page:
In the Policy Store, click the three-dot menu next to the policy.
Select View history.

Event types:
Policy Created
The policy was first added to the Policy Store
Policy Updated
The policy's YAML content was edited — displayed with a side-by-side diff highlighting added (green) and removed (red) lines
Policy Attached
The policy was attached to a new scope, or an existing attachment was modified (e.g., changed from specific workflows to entire repo)
Policy Detached
All or some attachments were removed
Each entry includes:
The type of change (with an icon indicating whether it was a content edit or an attachment change)
The user who made the change
Timestamp (absolute and relative)
Details of what changed:
For content changes, a side-by-side diff of the policy YAML
For attachment changes, the scopes added (green) and removed (red), and any transitions (e.g.,
specific workflows → entire repo)
From the history page, you can click Edit Policy in the top right to jump directly to the editor.
Lockdown Mode
Lockdown Mode provides automatic blocking of CI/CD jobs when critical security threats are detected in real-time.
How Lockdown Mode Works
When Lockdown Mode is active for a workflow, Harden-Runner continuously monitors the job at runtime. If a detection matching one of the configured detection types is triggered:
The running job is immediately terminated.
A notification is sent with details about the blocked threat, including the detection type, job ID, and workflow run.
The detection event is recorded in the StepSecurity dashboard and forwarded to any configured integrations (e.g., Webhook, Slack, S3).
Configuring Lockdown Mode
To enable Lockdown Mode for your workflows:
Navigate to the Policy Store
Create a new policy or edit an existing one
Add the lockdown configuration using the following syntax:
Attach the policy to your desired scope (cluster, organization, repository, or workflow)
Supported Detection Types
Privileged-Container
Blocks containers running with elevated privileges
Runner-Worker-Memory-Read
Blocks unauthorized memory reading attempts
Reverse-Shell
Blocks reverse shell connection attempts
Note: When lockdown mode is enabled and a threat is detected, the job will be immediately terminated and you will receive a notification with details about the blocked threat.
Exempting Workflows from Lockdown Mode
In some cases, you may need to exempt specific workflows from Lockdown Mode — for example, workflows that intentionally run privileged containers for testing or infrastructure provisioning.
To exempt a workflow, create a separate policy with lockdown mode disabled:
Then attach this policy to the specific scope you want to exempt. The exemption policy can be applied at any level:
Workflow level — Disables lockdown for a single workflow.
Repository level — Disables lockdown for all workflows in a repository.
Organization level — Disables lockdown for all workflows in an organization.
Example: Policy Enforcement
Suppose you create a policy that only allows specific endpoints

During a workflow run, if the job attempts to call a domain not on the allowlist, the request will be automatically blocked.

On the Network Events tab of the Insights page, you can see that the policy was responsible for blocking the request and causing the run to fail

Last updated
Was this helpful?