For the complete documentation index, see llms.txt. This page is also available as Markdown.

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:

Filter
Shows

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:

Column
Description

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.

Approach
YAML key
Behavior
Use it when

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.

In public.registry.com:443 the port is stripped and ignored, and all traffic to public.registry.com is denied on every port. The same applies to an IP address: 203.0.113.10:443 denies every connection to 203.0.113.10, on every port. Port-level granularity is only available in allowed-endpoints, where the port is part of the entry.

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.

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:

Scope
Applies to

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.

On ARC, self-hosted VMs, and GitHub-hosted custom VMs, the StepSecurity pre-job hook is what requests and applies the attached policy at runtime. Without the hook configured on the runner, an attached policy is never applied to jobs in those environments. See Runner Job Hooks.

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.

Pattern
Applies the policy to the workflow file in

*

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.

Pattern attachments are evaluated at runtime, so repositories created after the attachment are covered automatically. A new repository whose name matches the pattern picks up the policy on its first workflow run, with no further action needed.

Precedence

When multiple policies could apply to the same job, Harden-Runner picks the most specific one, in this order:

  1. Workflow (exact repository)

  2. Workflow (repository pattern)

  3. Repository

  4. Organization

  5. 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.

When a reusable workflow is called, the calling repository's policy is applied, not the policy of the repository that hosts the reusable workflow. This means every repository gets its own enforcement even when sharing workflows. Pattern attachments follow the same rule: the attachment's workflow file name is matched against the calling workflow's file name, and the repository pattern against the calling repository's 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:

  1. From scratch in the Policy Store

  2. From a baseline, importing endpoints observed during past workflow runs

  3. 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.

Importing from a baseline populates the list with endpoints Harden-Runner has actually observed. That is what you want for an allow list. For a deny list, the endpoints you want are the ones a baseline would not contain, so type them in directly.

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:

  1. Go to Harden-Runner → Installation and select GitHub-Hosted Custom VM.

  2. Copy the API key.

  3. Store it as an organization-level secret named STEP_SECURITY_API_KEY so every repository in the organization can use it.

The API key only grants access to the Policy Store. It does not grant access to any other StepSecurity APIs.

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:

Action
Description

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:

  1. In the Policy Store, click the three-dot menu next to the policy.

  2. Select View history.

Event types:

Event
Shown when

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 is currently available only for ARC clusters

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

Detection
Description

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.

Use the most specific scope possible when creating exemptions. For instance, if only one workflow in a repository needs to run privileged containers, attach the exemption policy at the workflow level rather than disabling lockdown for the entire repository.

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.

Job Markdown
  • 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

Network Events Tab

Last updated

Was this helpful?