Skip to content

GitHub Actions

Scorecards

Scorecards from the Open Source Security Foundation is a good starting point to learn about security best practices for GitHub Actions.

Permissions and environments

Use the permissions key in workflows to specify least privilege permissions for GITHUB_TOKEN. You can find a lot of examples at GitHub. For example:

    permissions:
      contents: write
      pull-requests: write

Continuous delivery jobs (for example running Terraform) should run isolated in its own job with scoped permissions and environments to reduce the risk of supply-chain attacks and adheres to the principle of least privilege.

🔴 In the example below, both hashicorp/setup-terraform and terraform-linters/setup-tflint will be able to assume the same AWS IAM role because they originate from a job that has an environment named production. This trust condition is checked by the IAM policy.1

jobs:

  terraform:

    name: Lint and apply Terraform configuration

    permissions:
      id-token: write
      contents: read

    environment: production

    steps:
      - uses: aws-actions/configure-aws-credentials@sha-1
      - uses: terraform-linters/setup-tflint@sha-1
      - uses: hashicorp/setup-terraform@sha-1

🟢 Only aws-actions/configure-aws-credentials and hashicorp/setup-terraform can use the IAM role:

jobs:

  tflint:

    name: Lint Terraform configuration

    permissions:
      contents: read

    environment: production

    steps:
      - uses: terraform-linters/setup-tflint@sha-1

  tflint:

    name: Lint and Terraform configuration

    permissions:
      id-token: write
      contents: read

    steps:
      - uses: aws-actions/configure-aws-credentials@sha-1
      - uses: hashicorp/setup-terraform@sha-1

Pin actions

GitHub Actions should be pinned to a commit hash. This is because Git tags are mutable in practice.[^1] There's no need to fork an action for security purposes as long as you follow this recommendation. Use Dependabot to keep on top of security updates. When pinning to a SHA-1 hash it's good practice to comment with the corresponding version number:

        uses: actions/checkout@e2f20e631ae6d7dd3b768f56a5d2af784dd54791 # v2.5.0

Use fine-grained personal access tokens for cross-repository access

🟢 Set up fine-grained personal access tokens (PAT) on a machine user (such as okctl-bot). Consider making the token available as a organization-level secret, which reduces the need to create duplicate secrets.

🔴 Avoid using PATs attached to private GitHub accounts.

SSH deploy keys for cross-repository access

🔴 Don't use SSH deploy keys unless you have a special reason. Use fine-grained personal access tokens instead. This eliminates the need of using a third-party action.

If there is no other option, use webfactory/ssh-agent to load SSH deploy keys in a GitHub Actions workflow.

      - name: Load golden-path-iac SSH deploy key
        uses: webfactory/ssh-agent@fc49353b67b2b7c1e0e6a600572d01a69f2672dd # v0.5.4
        with:
            ssh-private-key: ${{ secrets.GOLDEN_PATH_IAC_PRIVATE_DEPLOY_KEY }}

Signing commits in workflows with GnuPG

Use crazy-max/ghaction-import-gpg to sign commits in a GitHub Actions workflow.

      - name: Import GPG key
        uses: crazy-max/ghaction-import-gpg@111c56156bcc6918c056dbef52164cfa583dc549 # v5.2.0
        with:
          gpg_private_key: ${{ secrets.GPG_PRIVATE_KEY }}
          passphrase: ${{ secrets.GPG_PASSPHRASE }}

          # Username and email is inferred from GPG key metadata
          git_user_signingkey: true
          git_commit_gpgsign: true
          git_config_global: true

See also