Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions content/actions/concepts/security/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ children:
- /openid-connect
- /artifact-attestations
- /script-injections
- /workflow-dependency-lockfiles
- /compromised-runners
- /kubernetes-admissions-controller
contentType: concepts
Expand Down
68 changes: 68 additions & 0 deletions content/actions/concepts/security/workflow-dependency-lockfiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: Workflow dependency lockfiles
shortTitle: Dependency lockfiles
intro: 'A lockfile pins every action your workflows depend on to a verified commit, so the code you reviewed is the code that runs.'
versions:
feature: actions-workflow-lockfiles
category:
- Secure your workflows
contentType: concepts
---

> [!NOTE] {% data reusables.actions.workflow-lockfiles-preview %}

## Why lock workflow dependencies?

When a workflow references an action by a tag or branch, such as `uses: octo-org/deploy-action@v3` or `uses: octo-org/deploy-action@main`, {% data variables.product.prodname_actions %} resolves that reference to whatever commit the tag or branch points to at run time.

Tags and branches can move, so the code that runs is not guaranteed to be the code that the workflow author originally reviewed. If an upstream maintainer's account is compromised and a tag is repointed, your repositories could run malicious code.

A workflow dependency lockfile pins a commit SHA to every action (including transitive dependencies) and reusable workflow your workflows depend on. This gives you:

* Reproducible builds
* Reviewable dependency changes and {% data variables.product.prodname_dependabot %} version updates
* An auditable inventory of your CI/CD systems' dependencies

## How does the lockfile work?

The lockfile is defined in a repository at `.github/workflows/actions.lock`. It's generated by `gh actions-lock`, a {% data variables.product.prodname_cli %} extension, and should never be edited by hand.

You can onboard a repository's workflows to the lockfile one at a time or all at once. After a workflow is onboarded:

* Every action dependency the workflow uses must be present in the lockfile.
* The locked commit is what runs when the workflow executes, and what allowed actions policies check. This commit must exist within a branch of the source repository.

A lockfile covers the workflows in its own repository. It extends to transitive dependencies (actions that _your action dependencies_ use), **except** for dependencies of reusable workflow calls. A reusable workflow is covered by the lockfile of the repository where it is defined.

## What does the lockfile record?

The lockfile has two sections. `workflows` records which dependencies each onboarded workflow uses, and `dependencies` records which commit each of those references resolves to.

```yaml
# This file is machine-generated by `gh actions-lock`.
# Do not edit by hand; run `gh actions-lock` to update.
version: 'v0.0.2'
workflows:
'.github/workflows/ci.yml':
- 'octo-org/deploy-action@v3.1.0'
dependencies:
'octo-org/deploy-action@v3.1.0':
ref: 'v3.1.0'
commit: 'sha1-...'
owner_id: 44036562
repo_id: 197814629
```

Each dependency records:

* **`ref`**: The reference the workflow uses, such as a tag or branch.
* **`commit`**: The exact commit the reference resolved to. This is the code that will run.
* **`owner_id` and `repo_id`**: The numeric identity of the source repository. {% data variables.product.prodname_actions %} uses these to confirm at run time that the repository behind the name is still the one you locked.

## How do lockfiles compare with SHA pinning?

{% data variables.product.github %} has historically recommended pinning third-party actions to a full-length commit SHA in your workflow files. For repositories that adopt a lockfile, SHA pinning within workflow files is **no longer necessary**. The lockfile is the better option because it supports transitive dependencies, makes workflow files easier to read, and records the identity of the source repository.

## Next steps

To create a lockfile for a repository, see [AUTOTITLE](/actions/how-tos/secure-your-work/lock-workflow-dependencies).
14 changes: 13 additions & 1 deletion content/actions/how-tos/administer/control-workflow-execution.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
title: Controlling who can execute {% data variables.product.prodname_actions %} workflows
title: Governing execution of {% data variables.product.prodname_actions %} workflows
shortTitle: Control workflow execution
intro: Control who can trigger {% data variables.product.prodname_actions %} workflows and which events are permitted to run them across an enterprise, organization, and repository.
versions:
Expand Down Expand Up @@ -78,6 +78,18 @@ Only the allowed actors will be able to run the specified workflows in the targe

Event rules control which events are permitted, such as `push`, `pull_request`, `pull_request_target`, and `workflow_dispatch`.

{% ifversion actions-workflow-lockfiles %}

### Require lockfile

The **Require lockfile** protection blocks runs of workflows with `uses:` dependencies on other actions, unless the dependencies are pinned in a dependency lockfile. This ensures that the targeted workflows run predictable, vetted code, rather than whatever a tag or branch points to at run time. See [AUTOTITLE](/actions/concepts/security/workflow-dependency-lockfiles).

Generate and verify lockfiles in the repositories you're targeting before you set this protection to **Active**. Otherwise, you'll block workflow runs in repositories that aren't ready. For a staged rollout, see [AUTOTITLE](/actions/how-tos/administer/enforce-workflow-lockfiles).

You can keep **Require actions to be pinned to a full-length commit SHA** enabled when adopting lockfiles. For dependencies covered by a lockfile, {% data variables.product.github %} evaluates this requirement using the locked commit SHA. Keeping the policy enabled during a staged rollout preserves SHA-pinning enforcement for workflows that have not yet adopted lockfiles.

{% endif %}

## Evaluating policies

You can view policy insights to see workflow runs that have been blocked (for active policies) or would have been blocked (for "evaluate" policies). This is a good way to check that policies are working as intended and not causing unnecessary friction.
Expand Down
84 changes: 84 additions & 0 deletions content/actions/how-tos/administer/enforce-workflow-lockfiles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
title: Enforcing lockfiles for action dependencies
shortTitle: Enforce workflow lockfiles
intro: 'Require workflow dependency lockfiles across your repositories, and automatically keep the files up to date.'
versions:
feature: actions-workflow-lockfiles
permissions: 'Repository administrators, organization owners, and enterprise owners'
product: '{% data variables.product.prodname_ghe_cloud %}'
contentType: how-tos
---

> [!NOTE] {% data reusables.actions.workflow-lockfiles-preview %}

Requiring lockfiles gives you consistent, auditable assurance that every workflow runs vetted code, without asking each team to adopt the practice on its own. For an introduction, see [AUTOTITLE](/actions/concepts/security/workflow-dependency-lockfiles).

This guide helps you run a pilot in selected repositories and set up automations to keep the lockfiles up to date. When you're confident the lockfiles are working as intended and not introducing developer friction, you can enforce the **Require lockfile** policy.

## Prerequisites

Before you enforce workflow lockfiles and create processes to keep them up to date, the lockfile must exist in your repositories. For instructions, see [AUTOTITLE](/actions/how-tos/secure-your-work/lock-workflow-dependencies).

## 1. Pilot the "require lockfile" policy

>[!IMPORTANT] **Evaluate** mode for policies is only available on {% data variables.product.prodname_enterprise %}. Do not enable this policy in **Active** mode until you're confident you have systems in place to keep the lockfile up to date for all targeted workflows.

The **Require lockfile** policy blocks runs of targeted workflows that include action dependencies but are **not** onboarded to the repository's lockfile. To configure the policy, see [AUTOTITLE](/actions/how-tos/administer/control-workflow-execution).

Run a pilot before enforcing this policy in all repositories. You should:

* Scope the policy to specific repositories that have onboarded to lockfiles.
* Target specific workflows that have been onboarded to the lockfile. Do this until all workflows are onboarded _and_ you have processes in place to ensure new workflows are onboarded automatically.
* Start with the policy in **Evaluate** mode and analyze the results under **Policy insights**

## 2. Keep lockfiles current

Enforcement only works if lockfiles stay up to date. Add automations so that developers do not need to remember to run the lock command every time they create or edit workflows.

The following sections give examples for how to prevent stale lockfiles at different points of the development lifecycle.

* Keep the file up to date by enabling {% data variables.product.prodname_dependabot %} and creating a workflow to run the lock command.
* Add a skill and pre-commit hook to shorten the feedback loop for developers.

For ready-to-copy implementations of the workflow and the skill, see the [gh-actions-lock](https://github.com/github/gh-actions-lock) repository.

### {% data variables.product.prodname_dependabot %} version updates

When {% data variables.product.prodname_dependabot %} bumps an action version, it regenerates the matching lockfile entry in the same pull request.

You don't need any configuration beyond having {% data variables.product.prodname_dependabot %} manage {% data variables.product.prodname_actions %} dependencies. See [AUTOTITLE](/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/auto-update-actions).

{% data variables.product.prodname_dependabot %} will **not** onboard a workflow that has no lockfile entries yet, so this option maintains coverage rather than extending it.

### A dedicated {% data variables.product.prodname_actions %} workflow

When someone adds or edits a workflow, run another workflow that runs the lock command. Trigger the workflow on changes to `.github/workflows/**` and to any `action.yml` or `action.yaml` file, then give it two jobs:

* An **update** job that regenerates the lockfile on push and commits the result. This job needs `contents: write` permissions, and your branch protection rules or rulesets must allow {% data variables.product.prodname_actions %} to push to the branches it runs on.
* A **verify** job that runs `gh actions-lock --verify` on pull requests. This should exit with an error if the lockfile is stale, making it useful for a required check.

### A {% data variables.product.prodname_copilot_short %} skill

When {% data variables.product.prodname_copilot_short %} edits a workflow file, tell it to run the command to update the lockfile in the same changeset.

The skill should tell {% data variables.product.prodname_copilot_short %} to regenerate the lockfile with the CLI whenever it changes a workflow or a local action, and never to edit the lockfile directly. It only covers edits made with {% data variables.product.prodname_copilot_short %}, so treat it as a convenience rather than a control. See [AUTOTITLE](/copilot/concepts/agents/about-agent-skills).

### A pre-commit hook

This catches a stale lockfile before a commit exists, which is the earliest feedback a developer can get.

Use `gh actions-lock --verify-local --no-fix` in the hook. It works offline, doesn't need authentication, and is fast enough to run on every commit. This command confirms that every dependency has a lockfile entry, but does not re-verify the pins against {% data variables.product.github %}.

## 3. Set up a review process for lockfile changes

A lockfile is only a security control if someone reads the diff. A changed commit SHA tells you that the code your workflows run has changed.

1. Add `.github/workflows/actions.lock` to your `CODEOWNERS` file so that the team responsible for build security is automatically requested for review. See [AUTOTITLE](/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners).
1. Configure a branch protection rule or repository ruleset to require approval from a code owner.
1. Tell reviewers what to look for. For guidance, see [AUTOTITLE](/actions/how-tos/secure-your-work/lock-workflow-dependencies#reviewing-lockfile-changes).

## 4. Enforce the policy

When you're confident that lockfiles are in place and will stay up to date, change the enforcement status of the **Require lockfile** policy to **Active**, and ensure all onboarded repositories and workflows are targeted.

Leave the verification from step 2 in place. The policy will block a workflow run that has no valid lockfile, but a verification check tells an author their lockfile is stale while they're still working on the pull request.
1 change: 1 addition & 0 deletions content/actions/how-tos/administer/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ versions:
ghec: '*'
children:
- /control-workflow-execution
- /enforce-workflow-lockfiles
- /view-metrics
- /reuse-namespaces-on-ghecom
redirect_from:
Expand Down
1 change: 1 addition & 0 deletions content/actions/how-tos/secure-your-work/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ versions:
ghes: '*'
ghec: '*'
children:
- /lock-workflow-dependencies
- /use-artifact-attestations
- /security-harden-deployments
contentType: how-tos
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
---
title: Locking action dependencies in a repository's workflows
shortTitle: Lock workflow dependencies
intro: 'Generate a lockfile that pins every action your workflows use to a verified commit, and keep it current as your dependencies change.'
versions:
feature: actions-workflow-lockfiles
permissions: 'People with write access to a repository'
product: '{% data variables.product.prodname_ghe_cloud %}'
contentType: how-tos
---

> [!NOTE] {% data reusables.actions.workflow-lockfiles-preview %}

Locking your workflows' dependencies ensures that the actions you reference contain the same code that was originally reviewed. For more information, see [AUTOTITLE](/actions/concepts/security/workflow-dependency-lockfiles).

## 1. Check your action references

Creating a lockfile pins your dependencies to commits based on the current branch or tag reference. Make sure these are the versions you want to lock in place.

Check that each `uses` reference in your workflows points to a supported, current version of the action. For best practices, see [AUTOTITLE](/actions/reference/security/secure-use#using-third-party-actions).

If any of your workflows call a reusable workflow in another repository, check those references too. Dependencies of reusable workflows are covered by the lockfile of the repository where the reusable workflow is defined. If your organization maintains shared workflows, we recommend onboarding those repositories first. See [AUTOTITLE](/actions/concepts/workflows-and-actions/reusing-workflow-configurations).

We recommend letting {% data variables.product.prodname_dependabot %} manage your {% data variables.product.prodname_actions %} dependencies. If it's not already enabled, enable it before you create the lockfile. See [AUTOTITLE](/code-security/how-tos/secure-your-supply-chain/secure-your-dependencies/auto-update-actions).

## 2. Install the `actions-lock` extension

1. Install the [{% data variables.product.prodname_cli %}](https://cli.github.com/).

1. Install the extension.

```bash copy
gh extension install github/gh-actions-lock
```

> [!TIP] To update the extension later, run `gh extension upgrade gh-actions-lock`.

## 3. Generate the lockfile

From the root of your repository, run the extension with no arguments. It will scan every workflow under `.github/workflows/`, pin each resolvable action to a commit SHA, and write the lockfile.

```bash copy
gh actions-lock
```

To onboard a single workflow instead of the whole directory, pass its path.

```bash copy
gh actions-lock .github/workflows/ci.yml
```

If the extension reports that it has skipped a workflow because local path actions aren't supported, this means a `uses: ./…` reference in that workflow didn't resolve to a real action file. Local paths resolve from the repository root, **not** from the directory of the file that contains them, so a path that looks right next to its own file may not resolve. Correct the path and run the command again.

For the full set of options, run `gh actions-lock --help`.

## 4. Review the changes and commit

The extension changes more than the lockfile, so review the full diff before you commit. It also rewrites your workflow `uses` references to the specific version it resolved, so a reference such as `octo-org/deploy-action@v3` can become `octo-org/deploy-action@v3.1.0`. This makes the reference more specific without changing the commit it resolves to.

If your repository, organization, or enterprise only allows specified actions, check the allowlist before you commit. After a workflow is onboarded, the allowlist is checked against locked commit SHAs, because a tag can be moved to different code but a locked SHA can't. Entries pinned to a tag, such as `{% data reusables.actions.action-checkout %}`, no longer match. Use a wildcard or the full commit SHA instead. See [AUTOTITLE](/repositories/managing-your-repositorys-settings-and-features/enabling-features-for-your-repository/managing-github-actions-settings-for-a-repository#allowing-select-actions-and-reusable-workflows-to-run).

References to actions in your own repository are rewritten too, from `uses: ./…` to the equivalent `uses: $/…`, in both your workflows and your in-repository `action.yml` files. A `$/…` reference always resolves to the running commit, so it's inherently pinned and needs no lockfile entry.

## 5. Keep the lockfile current

Keep the lockfile up to date with your repository's workflow files and security updates.

1. **Re-run the extension when you change dependencies.** Run `gh actions-lock` again whenever you add a workflow that has `uses` dependencies, or add or remove `uses` dependencies in an existing workflow.
1. **Let {% data variables.product.prodname_dependabot %} handle version bumps.** When {% data variables.product.prodname_dependabot %} opens a version update for an action in a workflow you've onboarded, it will also regenerate the matching lockfile entry in the same pull request, so your workflow file and lockfile can't drift apart.

{% data variables.product.prodname_dependabot %} will **not** onboard a workflow for you. If it bumps a dependency in a workflow with no lockfile entry, it just reports that onboarding is required.

1. **Re-resolve references that have moved.** References to branches or partial versions, such as `main` or `v3`, are trusted from the lockfile and aren't re-resolved on a normal run. If the extension reports an error because a tag now points to a different upstream commit, check the upstream repository's history first. If the move was legitimate, re-resolve the reference with `gh actions-lock --relock`. If the commit you originally locked is no longer reachable at all, for example because the upstream repository force-pushed or deleted it, `--relock` will still report an error. To accept the new commit, run `gh actions-lock --relock --accept-moved`.

### Reviewing lockfile changes

Review changes to the lockfile like any other dependency change:

* **A changed `commit`** is the code your workflows will run. Treat it the way you'd treat any other dependency version bump.
* **A changed `owner_id` or `repo_id`** should be rare. It means the repository behind that name is **not** the one you originally locked, so investigate before you merge.

If a lockfile looks wrong, fix your workflow files and regenerate it. Never edit the lockfile manually.

## Next steps

Once you have generated a lockfile for a repository, ensure it's enforced and up to date without developers needing to remember to run commands. See [AUTOTITLE](/actions/how-tos/administer/enforce-workflow-lockfiles).
Loading
Loading