DEV Community

Mike Anderson
Mike Anderson

Posted on

GitHub Organization Security Hardening: Exact Controls and Step-by-Step Setup Guide

GitHub is part of the engineering control plane. It controls source code, CI/CD workflows, secrets, packages, release automation, infrastructure-as-code, and production deployment paths.

This guide is written as an implementation runbook.

For each control, you will see:

  • Control objective
  • Exact GitHub setting
  • What to select
  • Validation
  • Evidence to retain

Note: Some settings require GitHub Enterprise Cloud, GitHub Advanced Security, or organization owner permissions. If a setting is not visible, confirm your GitHub plan and your permission level.


1. Rollout Order

Use this rollout order. Do not start with advanced scanning before identity, repository governance, and Actions restrictions are under control.

Phase 1 — Identity and organization governance
1. Enforce SSO.
2. Enforce phishing-resistant MFA/passkeys at the IdP.
3. Require GitHub-local MFA/passkeys for direct-auth/fallback accounts, break-glass accounts, outside collaborators, and service accounts where applicable.
4. Require hardware-backed SSH keys for privileged Git push/pull where feasible.
5. Reduce organization owners.
6. Set organization base permissions to No permission.
7. Restrict outside collaborators.
8. Restrict repository creation, deletion, transfer, visibility change, and private forking.

Phase 2 — Repository controls
9. Classify repositories.
10. Create organization ruleset for default branches.
11. Create repo-level rulesets for develop, release/*, and hotfix/* where needed.
12. Add CODEOWNERS for workflows, IaC, deployment, and dependency paths.
13. Add push rulesets for risky file paths and file types where supported.

Phase 3 — CI/CD and supply chain
14. Restrict GitHub Actions to approved actions.
15. Set GITHUB_TOKEN default permissions to read-only.
16. Require explicit workflow permissions.
17. Protect workflow files.
18. Replace long-lived cloud secrets with OIDC.
19. Use GitHub Environments for production deployments.

Phase 4 — Security features, runners, and monitoring
20. Enable secret scanning and push protection.
21. Enable CodeQL/code scanning.
22. Enable Dependabot and dependency review.
23. Secure self-hosted runners.
24. Restrict OAuth Apps, GitHub Apps, PATs, deploy keys, webhooks, and service accounts.
25. Export audit logs or run scheduled audit review.
26. Create a GitHub incident response playbook.
Enter fullscreen mode Exit fullscreen mode

2. Organization Governance and Ownership

Control Objective

Define who owns GitHub security settings, repositories, CI/CD controls, bypasses, exceptions, tokens, runners, and monitoring.

Exact GitHub Setting

GitHub does not provide one single "ownership" setting. Implement ownership using:

Organization → Teams
Repository → Settings → Collaborators and teams
Repository → About → Description / Website / Topics
Organization → Settings → Custom properties, if available
External CMDB / GRC register
Enter fullscreen mode Exit fullscreen mode

What to Do

  1. Go to Organization → Teams.
  2. Create or confirm these control-owner teams:
security-admins
security-readonly
platform-admins
devsecops
release-managers
breakglass-admins
Enter fullscreen mode Exit fullscreen mode
  1. For every repository, define:
Business owner
Technical owner
Repository classification: Critical / High / Medium / Low
Default branch
Production deployment: Yes / No
Uses GitHub Actions: Yes / No
Uses self-hosted runners: Yes / No
Uses cloud deployment credentials: Yes / No
Enter fullscreen mode Exit fullscreen mode
  1. If GitHub custom properties are available:
Organization → Settings → Custom properties
Enter fullscreen mode Exit fullscreen mode

Create these properties:

business_owner
technical_owner
classification
data_classification
production_deploying_repo
uses_self_hosted_runner
uses_cloud_oidc
exception_required
Enter fullscreen mode Exit fullscreen mode
  1. Require repository owners to maintain those values.

Required Security Decision

Security owns policy and exceptions.
Platform owns organization guardrails.
Repo owners own repo-level implementation.
Release governance owns production/hotfix bypass approval.
SOC/SecOps owns monitoring and incident triage.
Enter fullscreen mode Exit fullscreen mode

Validation

Review 10 random repositories and confirm each has an owner, classification, and default branch defined.

Evidence to Retain

Team list
Repository ownership register
Custom properties export
Exception approval workflow
Quarterly owner review
Enter fullscreen mode Exit fullscreen mode

3. SSO, Passkeys, and MFA

Control Objective

Use the corporate identity provider as the primary control for GitHub organization access.

Exact GitHub Setting

Organization or Enterprise
→ Settings
→ Authentication security
→ SAML single sign-on
Enter fullscreen mode Exit fullscreen mode

What to Select

Select or configure:

Enable SAML authentication
Enforce SAML single sign-on for the organization
Enable SCIM provisioning, if available
Enter fullscreen mode Exit fullscreen mode

At the identity provider, enforce:

Phishing-resistant MFA / passkeys
Device compliance, if available
Group-based provisioning
Automatic deprovisioning
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Authentication security.
  2. Under SAML single sign-on, configure the IdP metadata.
  3. Test SSO with a non-owner test account.
  4. Select Enforce SAML single sign-on.
  5. Configure SCIM provisioning if your plan and IdP support it.
  6. Map IdP groups to GitHub teams.
  7. Remove unmanaged users that are not tied to corporate identity.
  8. Confirm leaver process disables GitHub access through the IdP.

Important Clarification

If SSO uses passkeys or phishing-resistant MFA at the IdP, GitHub-local MFA is not the main control for normal employee access.

Use this standard:

Primary browser access control:
- SSO enforced
- IdP phishing-resistant MFA/passkey enforced

GitHub-local MFA/passkey still required for:
- Break-glass accounts
- Organization owners that can authenticate directly to GitHub
- Outside collaborators
- Service accounts where applicable
- Any account not fully controlled by Enterprise Managed Users
Enter fullscreen mode Exit fullscreen mode

Validation

  1. Try accessing an organization repo from a member account.
  2. Confirm GitHub redirects to the IdP.
  3. Disable a test user in the IdP.
  4. Confirm the user loses organization access.
  5. Confirm break-glass accounts have hardware-backed MFA/passkeys.

Evidence to Retain

SSO enforcement screenshot
IdP MFA/passkey policy
SCIM configuration
IdP group-to-GitHub team mapping
Break-glass account register
Access review evidence
Enter fullscreen mode Exit fullscreen mode

4. Hardware-Backed SSH Keys for Git Push/Pull

Control Objective

Protect Git push/pull access from stolen SSH private keys.

Exact GitHub Setting

User-level setting:

User account
→ Settings
→ SSH and GPG keys
→ New SSH key
Enter fullscreen mode Exit fullscreen mode

SSO authorization:

User account
→ Settings
→ SSH and GPG keys
→ Configure SSO
→ Authorize for the organization
Enter fullscreen mode Exit fullscreen mode

Required Security Standard

Privileged users should use YubiKey/FIDO2-backed SSH keys where feasible.
SSH keys must be unique per user.
Shared SSH keys are not allowed.
SSH keys must be authorized for SSO before accessing organization repos.
Inactive users' SSH keys must be removed during offboarding.
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup for Users

  1. Insert the YubiKey or supported hardware security key.
  2. Generate a FIDO2-backed SSH key:
ssh-keygen -t ed25519-sk -C "user@company.com"
Enter fullscreen mode Exit fullscreen mode
  1. For a resident/discoverable key, where operationally appropriate:
ssh-keygen -t ed25519-sk -O resident -C "user@company.com"
Enter fullscreen mode Exit fullscreen mode
  1. Copy the public key:
cat ~/.ssh/id_ed25519_sk.pub
Enter fullscreen mode Exit fullscreen mode
  1. Go to GitHub → Settings → SSH and GPG keys → New SSH key.
  2. Paste the public key.
  3. Save the key.
  4. Click Configure SSO.
  5. Click Authorize for the organization.
  6. Test access:
ssh -T git@github.com
Enter fullscreen mode Exit fullscreen mode
  1. Set the repository remote to SSH:
git remote set-url origin git@github.com:ORG/REPO.git
Enter fullscreen mode Exit fullscreen mode

What GitHub Cannot Fully Enforce

GitHub does not provide a simple organization setting that says "only allow YubiKey-backed SSH keys." Enforce this through:

Privileged-user standard
Access reviews
Device/security key rollout
Offboarding process
SSH key audit
Enter fullscreen mode Exit fullscreen mode

Validation

Ask privileged users to show that their SSH keys are -sk backed keys, or collect SSH key inventory through GitHub APIs where available.

Evidence to Retain

SSH key review report
Privileged user hardware-backed SSH attestation
SSO-authorized SSH key evidence
Offboarding removal evidence
Enter fullscreen mode Exit fullscreen mode

5. Reduce Organization Owners

Control Objective

Limit blast radius from privileged GitHub compromise.

Exact GitHub Setting

Organization
→ People
→ Filter by Role: Owner
Enter fullscreen mode Exit fullscreen mode

What to Do

  1. Go to Organization → People.
  2. Filter by Role: Owner.
  3. Remove owner role from anyone who does not need organization-wide administration.
  4. Use repository Admin, Maintain, or custom roles instead of org owner for normal repo administration.
  5. Keep a small named break-glass group.
  6. Set an alert for new organization owner additions.

Required Standard

No shared owner accounts.
No routine engineering work from org-owner accounts.
Org owners reviewed monthly.
Break-glass access reviewed and tested.
Enter fullscreen mode Exit fullscreen mode

Validation

Confirm every org owner has an approved reason.

Evidence to Retain

Org owner export/screenshot
Monthly owner review
Approval for owner additions
Audit log for owner changes
Enter fullscreen mode Exit fullscreen mode

6. Set Base Permission to No Permission

Control Objective

Prevent accidental default access to every repository.

Exact GitHub Setting

Organization
→ Settings
→ Member privileges
→ Base permissions
Enter fullscreen mode Exit fullscreen mode

What to Select

Select:

No permission
Enter fullscreen mode Exit fullscreen mode

Do not select:

Write
Admin
Enter fullscreen mode Exit fullscreen mode

Use Read only if your organization deliberately allows broad internal visibility.

Step-by-Step Setup

  1. Go to Organization → Settings.
  2. Open Member privileges.
  3. Find Base permissions.
  4. Select No permission.
  5. Save changes.
  6. Confirm repository access is granted through teams.

Validation

Pick a normal member not assigned to any team and confirm they do not automatically get repo access.

Evidence to Retain

Base permission setting screenshot
Team access report
Direct access exception list
Enter fullscreen mode Exit fullscreen mode

7. Use Teams for Repository Access

Control Objective

Prevent direct user-to-repository permission sprawl.

Exact GitHub Setting

Organization
→ Teams
Enter fullscreen mode Exit fullscreen mode

Repository access:

Repository
→ Settings
→ Collaborators and teams
Enter fullscreen mode Exit fullscreen mode

What to Do

  1. Go to Organization → Teams.
  2. Create teams by application/platform and role.

Recommended pattern:

app-payments-readonly
app-payments-developers
app-payments-maintainers
app-payments-admins
platform-admins
security-readonly
security-admins
release-managers
Enter fullscreen mode Exit fullscreen mode
  1. Go to each repository.
  2. Open Settings → Collaborators and teams.
  3. Add teams, not individual users.
  4. Remove direct user grants unless they are approved exceptions.
  5. Map teams to IdP groups if SSO/SCIM supports it.

Permission Guidance

Read: auditors, security read-only, non-contributing stakeholders
Triage: issue/PR triage without code write
Write: active developers
Maintain: repo maintainers without full admin
Admin: small repo admin group only
Enter fullscreen mode Exit fullscreen mode

Validation

Run an access review:

List direct collaborators.
List teams with Admin/Maintain.
Confirm each has owner approval.
Remove stale users and teams.
Enter fullscreen mode Exit fullscreen mode

Evidence to Retain

Team inventory
Repository team access export
Direct collaborator list
Access review sign-off
Enter fullscreen mode Exit fullscreen mode

8. Restrict Outside Collaborators

Control Objective

Control external user access.

Exact GitHub Setting

Organization
→ Settings
→ Member privileges
→ Outside collaborators
Enter fullscreen mode Exit fullscreen mode

What to Configure

  1. Restrict who can invite outside collaborators.
  2. Require approval for outside collaborator access.
  3. Require a sponsor and expiry date.
  4. Require GitHub-local MFA/passkey.
  5. Review monthly.

Step-by-Step Setup

  1. Go to Organization → Settings → Member privileges.
  2. Locate outside collaborator settings.
  3. Restrict invitation rights to org owners or approved admins.
  4. Export current outside collaborators.
  5. Remove unused or expired users.
  6. Create an approval workflow requiring:
Sponsor
Business justification
Repository list
Permission level
Expiry date
Data sensitivity
Enter fullscreen mode Exit fullscreen mode

Validation

Review all outside collaborators and confirm each has an active sponsor and expiry date.

Evidence to Retain

Outside collaborator export
Approval tickets
Expiry register
Monthly review evidence
Removal tickets
Enter fullscreen mode Exit fullscreen mode

9. Restrict Repository Creation, Deletion, Transfer, Visibility, and Forking

Control Objective

Prevent uncontrolled repositories, accidental public exposure, and unauthorized transfers.

Exact GitHub Setting

Organization
→ Settings
→ Member privileges
Enter fullscreen mode Exit fullscreen mode

What to Configure

In Member privileges, configure:

Repository creation: Restrict to approved owners/platform teams
Default repository visibility: Private or Internal
Repository deletion: Restrict
Repository transfer: Restrict
Repository visibility changes: Restrict
Private repository forking: Disabled by default
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Member privileges.
  2. Find repository creation settings.
  3. Disable unrestricted member repository creation.
  4. Set default visibility to Private or Internal.
  5. Restrict repository deletion.
  6. Restrict repository transfer.
  7. Restrict visibility changes.
  8. Disable private repository forking unless there is an approved business need.
  9. Create a repository onboarding template requiring:
Owner
Classification
Default branch
CODEOWNERS
Ruleset
Security scanning
Actions policy
Secrets review
Enter fullscreen mode Exit fullscreen mode

Validation

Create a test non-admin user and confirm they cannot create/delete/transfer/change visibility unless approved.

Evidence to Retain

Member privilege settings screenshot/export
Repo creation approvals
Visibility change approvals
Public repo approvals
Deletion/transfer approvals
Forking exceptions
Enter fullscreen mode Exit fullscreen mode

10. Repository Classification

Control Objective

Apply stronger controls to repositories that can impact production, data, secrets, cloud infrastructure, or deployments.

Exact GitHub Setting

Use custom properties if available:

Organization
→ Settings
→ Custom properties
Enter fullscreen mode Exit fullscreen mode

Repository view:

Repository
→ Settings
→ General
→ Custom properties, if available
Enter fullscreen mode Exit fullscreen mode

What to Configure

Create custom properties:

classification: Critical / High / Medium / Low
business_owner
technical_owner
data_classification
production_deploying_repo: yes/no
uses_github_actions: yes/no
uses_self_hosted_runner: yes/no
uses_cloud_credentials: yes/no
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Custom properties.
  2. Create the properties above.
  3. Apply them to all repositories.
  4. Mark these as Critical:
Production deployment repos
Infrastructure-as-code repos
IAM/security automation repos
Kubernetes/GitOps repos
CI/CD template repos
Payment/authentication/customer-data repos
Package publishing repos
Enter fullscreen mode Exit fullscreen mode
  1. Require stricter rulesets and reviews for Critical/High repos.

Validation

Export repo list and confirm every repo has classification and owner values.

Evidence to Retain

Repository classification export
Owner mapping
Critical repo list
Control coverage report
Enter fullscreen mode Exit fullscreen mode

11. Organization Ruleset for Default Branch Protection

Control Objective

Protect the source-of-truth branch across all repositories without hard-coding branch names.

Exact GitHub Setting

Organization
→ Settings
→ Rules
→ Rulesets
→ New ruleset
→ New branch ruleset
Enter fullscreen mode Exit fullscreen mode

What to Select

Ruleset name: Org - Default Branch Protection
Target: Branch
Enforcement status: Active
Repository targeting: All repositories, or selected repositories if phased rollout is needed
Branch targeting: Include default branch
Enter fullscreen mode Exit fullscreen mode

Export/API equivalent:

~DEFAULT_BRANCH
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Rules → Rulesets.
  2. Click New ruleset.
  3. Select New branch ruleset.
  4. Enter name:
Org - Default Branch Protection
Enter fullscreen mode Exit fullscreen mode
  1. Set Enforcement status to Active.

For phased rollout, use Evaluate first, then change to Active after testing.

  1. Under repository targeting, select:
All repositories
Enter fullscreen mode Exit fullscreen mode

or for rollout:

Only selected repositories
Enter fullscreen mode Exit fullscreen mode
  1. Under branch targeting, add:
Include default branch
Enter fullscreen mode Exit fullscreen mode
  1. Do not add these to the global default ruleset unless specifically required:
refs/heads/main
refs/heads/master
refs/heads/develop
refs/heads/hotfix/*
Enter fullscreen mode Exit fullscreen mode
  1. Enable these rules:
Restrict deletions
Block force pushes / non-fast-forward updates
Require a pull request before merging
Required approvals: 1 minimum
Dismiss stale pull request approvals when new commits are pushed
Require review from Code Owners
Require conversation resolution before merging
Require status checks to pass, where checks exist
Require signed commits, where operationally supported
Require linear history, if using squash/rebase
Enter fullscreen mode Exit fullscreen mode
  1. Set bypass actors:
Do not add bypass actors by default.
Do not add all repo admins.
Do not add workflow bots.
Do not add broad engineering teams.
Enter fullscreen mode Exit fullscreen mode
  1. Save the ruleset.

Required Status Checks

For Critical/High repos, require checks such as:

Build
Unit tests
CodeQL / SAST
SCA / dependency scan
Secret scan validation, if implemented as CI
Container scan, if applicable
IaC scan, if applicable
Enter fullscreen mode Exit fullscreen mode

Default Branch Hygiene

Before enforcement, ask repo owners to confirm:

The default branch is correct.
The default branch is the source-of-truth branch.
Deployment and release workflows use the expected branch.
Non-default branches have separate rulesets if needed.
Enter fullscreen mode Exit fullscreen mode

Validation

  1. Create a test branch.
  2. Open a PR to the default branch.
  3. Confirm direct push to default branch is blocked.
  4. Confirm PR approval is required.
  5. Confirm CODEOWNER review is required when protected files change.
  6. Confirm force push and deletion are blocked.

Evidence to Retain

Ruleset export
Target repositories list
Bypass actor list
Required status checks list
Sample blocked direct push
Sample PR showing required approvals and checks
Default branch inventory
Enter fullscreen mode Exit fullscreen mode

12. Repository Rulesets for develop, release/, and hotfix/

Control Objective

Protect non-default operational branches without forcing the same branching model across all repositories.

Exact GitHub Setting

Repository
→ Settings
→ Rules
→ Rulesets
→ New ruleset
→ New branch ruleset
Enter fullscreen mode Exit fullscreen mode

Branch Ruleset Decision

Branch Level Use
Default branch Organization ruleset Applies to all repos
develop Repository-level or repo-scoped org ruleset Only where develop is used
release/* Repository-level or repo-scoped org ruleset Only where release branches are used
hotfix/* Repository-level or repo-scoped org ruleset Only where emergency hotfix branches are used

12.1 develop Branch Ruleset

What to Select

Ruleset name: Repo - Develop Branch Protection
Target: Branch
Enforcement status: Active
Branch targeting: refs/heads/develop
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Rules → Rulesets.
  2. Click New ruleset.
  3. Select New branch ruleset.
  4. Name it:
Repo - Develop Branch Protection
Enter fullscreen mode Exit fullscreen mode
  1. Set enforcement to Active.
  2. Under branch targeting, add:
refs/heads/develop
Enter fullscreen mode Exit fullscreen mode
  1. Enable:
Restrict deletions
Block force pushes
Require a pull request before merging
Required approvals: 1
Dismiss stale approvals
Require conversation resolution
Require status checks, where applicable
Require CODEOWNER review for Critical/High repos
Enter fullscreen mode Exit fullscreen mode
  1. Optional, only if workflow supports it:
Require signed commits
Require linear history
Enter fullscreen mode Exit fullscreen mode
  1. Do not add broad bypass actors.

Validation

Try direct push to develop. It should be blocked.

Evidence to Retain

Develop ruleset export
Required check list
Bypass actor list
Exception record if bypass exists
Enter fullscreen mode Exit fullscreen mode

12.2 release/* Branch Ruleset

What to Select

Ruleset name: Repo - Release Branch Protection
Target: Branch
Enforcement status: Active
Branch targeting: refs/heads/release/*
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Rules → Rulesets.
  2. Click New branch ruleset.
  3. Name it:
Repo - Release Branch Protection
Enter fullscreen mode Exit fullscreen mode
  1. Set enforcement to Active.
  2. Add branch pattern:
refs/heads/release/*
Enter fullscreen mode Exit fullscreen mode
  1. Enable:
Restrict deletions
Block force pushes by default
Require a pull request before merging
Required approvals: 1 or 2 for Critical repos
Dismiss stale approvals
Require conversation resolution
Require status checks
Require CODEOWNER review for sensitive paths
Enter fullscreen mode Exit fullscreen mode
  1. Add required checks:
Build
Unit tests
Integration tests
CodeQL / SAST
SCA / dependency scan
Container scan, if applicable
IaC scan, if applicable
Release dry run, if applicable
Enter fullscreen mode Exit fullscreen mode
  1. Bypass actors must be limited to:
Approved release managers
Approved platform admins
Approved security/release break-glass actors
Enter fullscreen mode Exit fullscreen mode
  1. Do not give every repo admin release bypass by default.

Validation

Open a PR into release/test. Confirm approvals and checks are required.

Evidence to Retain

Release ruleset export
Release manager list
Bypass approval
Required checks evidence
Release change ticket
Enter fullscreen mode Exit fullscreen mode

12.3 hotfix/* Branch Ruleset

What to Select

Ruleset name: Repo - Hotfix Branch Protection
Target: Branch
Enforcement status: Active
Branch targeting: refs/heads/hotfix/*
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Rules → Rulesets.
  2. Click New branch ruleset.
  3. Name it:
Repo - Hotfix Branch Protection
Enter fullscreen mode Exit fullscreen mode
  1. Set enforcement to Active.
  2. Add branch pattern:
refs/heads/hotfix/*
Enter fullscreen mode Exit fullscreen mode
  1. Enable:
Restrict deletions
Block force pushes by default
Require a pull request where feasible
Required approvals: 1
Require status checks where feasible
Require conversation resolution where feasible
Require CODEOWNER review for sensitive paths
Enter fullscreen mode Exit fullscreen mode
  1. Configure bypass only if emergency force-push is truly required.

Hotfix Bypass Model

Use this ownership model:

Repo admin / engineering owner:
- Implements and maintains the hotfix ruleset.

Security / Release Governance:
- Approves bypass model and bypass actors.

Approved break-glass users:
- Execute emergency bypass only when justified, logged, and reviewed.
Enter fullscreen mode Exit fullscreen mode

What Not to Do

Do not add all repo admins as always-bypass.
Do not add a workflow bot as global bypass.
Do not allow developer teams to self-approve bypass.
Do not leave emergency bypass without expiry/review.
Enter fullscreen mode Exit fullscreen mode

Validation

Run a hotfix tabletop:

Create hotfix branch.
Attempt direct force push.
Confirm blocked.
Create emergency bypass request.
Confirm approval path.
Confirm audit log captures bypass.
Confirm post-use review occurs.
Enter fullscreen mode Exit fullscreen mode

Evidence to Retain

Hotfix ruleset export
Break-glass approver list
Emergency change ticket
Audit log for bypass use
Post-hotfix review
Enter fullscreen mode Exit fullscreen mode

13. Push Rulesets for High-Risk Files

Control Objective

Block risky files from being pushed to private/internal repositories and their fork network where supported.

Exact GitHub Setting

Organization
→ Settings
→ Rules
→ Rulesets
→ New ruleset
→ New push ruleset
Enter fullscreen mode Exit fullscreen mode

Repository-level:

Repository
→ Settings
→ Rules
→ Rulesets
→ New push ruleset
Enter fullscreen mode Exit fullscreen mode

What to Configure

Use push rulesets to block known risky file paths, extensions, and large files where supported.

Recommended blocked patterns:

.env
.env.*
*.pem
*.key
*.p12
*.pfx
id_rsa
id_dsa
id_ecdsa
id_ed25519
**/secrets/**
**/private/**
*.tfstate
*.tfstate.*
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Rules → Rulesets.
  2. Click New ruleset.
  3. Select New push ruleset.
  4. Name it:
Org - High Risk File Push Protection
Enter fullscreen mode Exit fullscreen mode
  1. Set enforcement to Evaluate first.
  2. Target private/internal repositories.
  3. Add file path or extension restrictions.
  4. Review violations for false positives.
  5. Change enforcement to Active.

Validation

Attempt to push a test file such as:

test.pem
.env.test
Enter fullscreen mode Exit fullscreen mode

The push should be blocked once active.

Evidence to Retain

Push ruleset export
Blocked pattern list
Violation logs
Exception list
Enter fullscreen mode Exit fullscreen mode

14. CODEOWNERS for Sensitive Paths

Control Objective

Require the correct owners to approve changes to CI/CD, infrastructure, deployment, and dependency files.

Exact GitHub Setting

Create a CODEOWNERS file:

Repository
→ Add file
→ Create new file
→ .github/CODEOWNERS
Enter fullscreen mode Exit fullscreen mode

Then enforce it through:

Organization or repository ruleset
→ Require review from Code Owners
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. In the repository, create:
.github/CODEOWNERS
Enter fullscreen mode Exit fullscreen mode
  1. Add owners for sensitive paths:
# GitHub workflow control plane
.github/workflows/**      @org/platform-security @org/devsecops
.github/actions/**        @org/platform-security @org/devsecops

# Infrastructure as Code
terraform/**              @org/cloud-platform @org/security
terragrunt/**             @org/cloud-platform @org/security
cloudformation/**         @org/cloud-platform @org/security

# Kubernetes and deployment
k8s/**                    @org/platform-engineering @org/security
helm/**                   @org/platform-engineering @org/security
argocd/**                 @org/platform-engineering @org/security
deploy/**                 @org/platform-engineering @org/security
scripts/deploy/**         @org/platform-engineering @org/security

# Container and build files
Dockerfile                @org/appsec
docker/**                 @org/appsec

# Dependencies
package.json              @org/appsec
package-lock.json         @org/appsec
yarn.lock                 @org/appsec
pnpm-lock.yaml            @org/appsec
Gemfile                   @org/appsec
Gemfile.lock              @org/appsec
pom.xml                   @org/appsec
build.gradle              @org/appsec
requirements.txt          @org/appsec
go.mod                    @org/appsec
go.sum                    @org/appsec
Enter fullscreen mode Exit fullscreen mode
  1. Commit the file through PR.
  2. Ensure the branch/ruleset has Require review from Code Owners enabled.
  3. Test with a PR modifying .github/workflows/test.yml.

Validation

A PR modifying a protected path should require the listed CODEOWNER team.

Evidence to Retain

CODEOWNERS file
Ruleset with CODEOWNER review enabled
Test PR showing CODEOWNER requirement
Quarterly CODEOWNERS review
Enter fullscreen mode Exit fullscreen mode

15. GitHub Actions Organization Policy

Control Objective

Prevent unreviewed third-party code from running in CI/CD.

Exact GitHub Setting

Organization
→ Settings
→ Actions
→ General
Enter fullscreen mode Exit fullscreen mode

What to Select

Under Policies, configure:

Allow GitHub Actions to run: Enabled only where needed
Actions permissions: Allow selected actions and reusable workflows
Enter fullscreen mode Exit fullscreen mode

Preferred policy:

Allow actions created by GitHub
Allow Marketplace verified creators only if your risk tolerance allows it
Allow specified actions and reusable workflows
Block all other public actions
Enter fullscreen mode Exit fullscreen mode

If using enterprise-owned internal actions, allow:

Actions and reusable workflows in your enterprise or organization
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Actions → General.
  2. Under Actions permissions, do not select Allow all actions and reusable workflows.
  3. Select the most restrictive option available, usually:
Allow selected actions and reusable workflows
Enter fullscreen mode Exit fullscreen mode
  1. Allow GitHub-owned actions if required.
  2. In the allowlist, add approved actions only.

Example allowlist:

actions/checkout@v4
actions/setup-node@v4
actions/setup-python@v5
ruby/setup-ruby@v1
github/codeql-action/*
aws-actions/configure-aws-credentials@v4
Enter fullscreen mode Exit fullscreen mode

For third-party community actions, prefer exact version or SHA:

kentaro-m/auto-assign-action@v2.0.2
madrapps/jacoco-report@v1.7.2
Enter fullscreen mode Exit fullscreen mode

For high-risk workflows, require full commit SHA.

Do Not Allow

*@*
owner/*
third-party/action@*
unreviewed/action@main
unreviewed/action@master
Enter fullscreen mode Exit fullscreen mode

Required Review Before Approving an Action

Before adding a third-party action to the allowlist, check:

Maintainer reputation
Recent commits/releases
Open issues mentioning compromise
Required permissions
Whether it runs arbitrary scripts
Whether it handles secrets
Whether it writes to PRs, issues, packages, releases, or workflows
Whether SHA pinning is required
Enter fullscreen mode Exit fullscreen mode

Validation

Create a test workflow using an unapproved action. It should fail with an Actions policy error.

Evidence to Retain

Organization Actions policy screenshot/export
Approved actions register
Rejected action test result
Action approval tickets
Enter fullscreen mode Exit fullscreen mode

16. GITHUB_TOKEN Workflow Permissions

Control Objective

Reduce blast radius from compromised workflows or malicious Actions.

Exact GitHub Setting

Organization
→ Settings
→ Actions
→ General
→ Workflow permissions
Enter fullscreen mode Exit fullscreen mode

Repository override, if needed:

Repository
→ Settings
→ Actions
→ General
→ Workflow permissions
Enter fullscreen mode Exit fullscreen mode

What to Select

Select:

Read repository contents and packages permissions
Enter fullscreen mode Exit fullscreen mode

Uncheck or disable where possible:

Allow GitHub Actions to create and approve pull requests
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Actions → General.
  2. Scroll to Workflow permissions.
  3. Select:
Read repository contents and packages permissions
Enter fullscreen mode Exit fullscreen mode
  1. Do not select:
Read and write permissions
Enter fullscreen mode Exit fullscreen mode

unless required by exception.

  1. Disable:
Allow GitHub Actions to create and approve pull requests
Enter fullscreen mode Exit fullscreen mode

unless explicitly approved.

  1. Require every workflow to declare permissions:.

Secure default:

permissions:
  contents: read
Enter fullscreen mode Exit fullscreen mode

PR comment workflow:

permissions:
  contents: read
  pull-requests: write
  issues: write
Enter fullscreen mode Exit fullscreen mode

Cloud OIDC workflow:

permissions:
  contents: read
  id-token: write
Enter fullscreen mode Exit fullscreen mode

Do not allow:

permissions: write-all
Enter fullscreen mode Exit fullscreen mode

Validation

Scan workflows for:

permissions: write-all
contents: write
actions: write
id-token: write
secrets write/admin permissions
Enter fullscreen mode Exit fullscreen mode

Every write permission should have justification.

Evidence to Retain

Workflow permission setting screenshot
Workflow permissions inventory
Approved write-permission exceptions
Enter fullscreen mode Exit fullscreen mode

17. Protect Workflow Files

Control Objective

Prevent unauthorized CI/CD logic changes.

Exact GitHub Setting

Use CODEOWNERS and rulesets.

CODEOWNERS file:

.github/CODEOWNERS
Enter fullscreen mode Exit fullscreen mode

Protected paths:

.github/workflows/**
.github/actions/**
Enter fullscreen mode Exit fullscreen mode

Ruleset:

Require review from Code Owners
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Add CODEOWNERS:
.github/workflows/**   @org/platform-security @org/devsecops
.github/actions/**     @org/platform-security @org/devsecops
Enter fullscreen mode Exit fullscreen mode
  1. Enable CODEOWNER review in default branch ruleset.
  2. Require PR before merge.
  3. Require status checks where available.
  4. Review any workflow change for:
New or changed third-party actions
New write permissions
New id-token: write
New secrets
New self-hosted runner labels
pull_request_target
workflow_run
repository_dispatch
release/package publishing
Enter fullscreen mode Exit fullscreen mode

Validation

Open a PR changing .github/workflows/build.yml. It must require platform/security CODEOWNER review.

Evidence to Retain

CODEOWNERS file
Workflow change PR approval
Workflow review checklist
Enter fullscreen mode Exit fullscreen mode

18. OIDC for Cloud Deployments

Control Objective

Remove long-lived AWS/Azure/GCP credentials from GitHub secrets.

GitHub Setting

Repository
→ Settings
→ Secrets and variables
→ Actions
Enter fullscreen mode Exit fullscreen mode

Cloud side:

AWS IAM / Azure Entra ID / GCP IAM
→ OIDC trust configuration
Enter fullscreen mode Exit fullscreen mode

What to Do

Replace static cloud keys with GitHub OIDC.

AWS Step-by-Step Setup

  1. In AWS IAM, create or confirm an OIDC provider for:
https://token.actions.githubusercontent.com
Enter fullscreen mode Exit fullscreen mode
  1. Create an IAM role for GitHub deployment.
  2. Scope the trust policy tightly.

Example trust condition:

{
  "Condition": {
    "StringEquals": {
      "token.actions.githubusercontent.com:aud": "sts.amazonaws.com"
    },
    "StringLike": {
      "token.actions.githubusercontent.com:sub": "repo:ORG/REPO:environment:production"
    }
  }
}
Enter fullscreen mode Exit fullscreen mode
  1. In GitHub, create an environment:
Repository → Settings → Environments → production
Enter fullscreen mode Exit fullscreen mode
  1. Require reviewers for production.
  2. Update workflow:
permissions:
  contents: read
  id-token: write

steps:
  - uses: actions/checkout@v4

  - name: Configure AWS credentials
    uses: aws-actions/configure-aws-credentials@v4
    with:
      role-to-assume: arn:aws:iam::123456789012:role/github-prod-deploy
      aws-region: ap-southeast-1
Enter fullscreen mode Exit fullscreen mode
  1. Remove static AWS keys from GitHub secrets after migration.

Required OIDC Scope

Use at least one of:

repo:ORG/REPO:environment:production
repo:ORG/REPO:ref:refs/heads/main
repo:ORG/REPO:ref:refs/heads/release/*
Enter fullscreen mode Exit fullscreen mode

Do not allow:

repo:ORG/*
*
Enter fullscreen mode Exit fullscreen mode

Validation

  1. Run deployment workflow.
  2. Confirm it assumes the AWS role via OIDC.
  3. Confirm static cloud secrets are removed.
  4. Confirm another repo/branch cannot assume the role.

Evidence to Retain

AWS IAM trust policy
GitHub workflow file
GitHub environment configuration
CloudTrail AssumeRoleWithWebIdentity event
Removed static secret evidence
Enter fullscreen mode Exit fullscreen mode

19. GitHub Environments for Production

Control Objective

Separate code merge permission from production deployment permission.

Exact GitHub Setting

Repository
→ Settings
→ Environments
→ New environment
Enter fullscreen mode Exit fullscreen mode

What to Select

For production:

Environment name: production
Required reviewers: Enabled
Prevent self-review: Enabled, if available
Deployment branches and tags: Selected branches and tags
Environment secrets: Production-only
Admin bypass: Disabled or tightly restricted, if available
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Environments.
  2. Click New environment.
  3. Name it:
production
Enter fullscreen mode Exit fullscreen mode
  1. Enable Required reviewers.
  2. Add release managers or platform/security approvers.
  3. Enable Prevent self-review if available.
  4. Under deployment branches/tags, select:
Selected branches and tags
Enter fullscreen mode Exit fullscreen mode
  1. Add allowed patterns:
main
release/*
hotfix/*
v*
Enter fullscreen mode Exit fullscreen mode
  1. Add production secrets only at the environment level.
  2. Update workflows to use:
environment: production
Enter fullscreen mode Exit fullscreen mode

Validation

Trigger a production deployment. GitHub should pause for environment approval.

Evidence to Retain

Environment configuration screenshot
Reviewer list
Deployment approval logs
Environment secrets inventory
Enter fullscreen mode Exit fullscreen mode

20. Secret Scanning and Push Protection

Control Objective

Detect and block secrets before they enter repositories.

Exact GitHub Setting

Organization level, if available:

Organization
→ Settings
→ Code security and analysis
Enter fullscreen mode Exit fullscreen mode

Repository level:

Repository
→ Settings
→ Code security and analysis
Enter fullscreen mode Exit fullscreen mode

What to Enable

Enable:

Secret scanning
Push protection
Validity checks, if available
Non-provider patterns, if available
Custom patterns for internal secrets
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Code security and analysis.
  2. Enable Secret scanning.
  3. Enable Push protection.
  4. Add custom patterns for internal secrets.
  5. Apply to all repositories where available.
  6. For any repo that does not inherit org settings, go to Repository → Settings → Code security and analysis and enable there.

Custom patterns to add:

Internal API keys
Private registry tokens
Service account tokens
Database credentials
Legacy cloud keys
Webhook signing secrets
Internal JWT signing secrets
Enter fullscreen mode Exit fullscreen mode

Secret Alert Response

For every real secret alert:

1. Revoke or rotate the secret immediately.
2. Review logs for abuse.
3. Remove the secret from active code.
4. Decide whether history cleanup is required.
5. Add prevention control.
6. Close the alert with evidence.
Enter fullscreen mode Exit fullscreen mode

Validation

Attempt to push a known test secret pattern in a test repo. Push protection should block it.

Evidence to Retain

Secret scanning setting
Push protection setting
Custom pattern list
Secret alert tickets
Rotation evidence
Enter fullscreen mode Exit fullscreen mode

21. CodeQL / Code Scanning

Control Objective

Find insecure code before production release.

Exact GitHub Setting

Repository:

Repository
→ Security
→ Code scanning
→ Set up code scanning
Enter fullscreen mode Exit fullscreen mode

or:

Repository
→ Settings
→ Code security and analysis
→ Code scanning
Enter fullscreen mode Exit fullscreen mode

Organization-level security configurations, if available:

Organization
→ Settings
→ Code security and analysis
→ Configurations
Enter fullscreen mode Exit fullscreen mode

What to Enable

For supported languages:

CodeQL default setup
Enter fullscreen mode Exit fullscreen mode

For custom build/language needs:

CodeQL advanced setup
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to the repository.
  2. Open Security → Code scanning.
  3. Click Set up code scanning.
  4. Select CodeQL.
  5. Use Default setup where supported.
  6. If build customization is needed, choose Advanced setup.
  7. Confirm a CodeQL workflow is created.
  8. Go to the default branch ruleset.
  9. Add CodeQL/code scanning as a required status check for Critical/High repos.

Required Severity Handling

Critical: Block release or approved exception required.
High: Fix before production release or within SLA.
Medium: Assign owner and backlog.
Low/Informational: Triage and tune if noisy.
Enter fullscreen mode Exit fullscreen mode

Validation

Confirm Security → Code scanning alerts shows results after a workflow run.

Evidence to Retain

Code scanning configuration
CodeQL workflow
Required check list
Alert dashboard
Remediation tickets
Enter fullscreen mode Exit fullscreen mode

22. Dependabot, Dependency Review, and SBOM

Control Objective

Reduce vulnerable dependency and supply-chain risk.

Exact GitHub Setting

Repository
→ Settings
→ Code security and analysis
Enter fullscreen mode Exit fullscreen mode

What to Enable

Enable:

Dependency graph
Dependabot alerts
Dependabot security updates
Dependency review
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Code security and analysis.
  2. Enable Dependency graph.
  3. Enable Dependabot alerts.
  4. Enable Dependabot security updates.
  5. Enable Dependency review, if available.
  6. Add .github/dependabot.yml.

Example:

version: 2
updates:
  - package-ecosystem: "npm"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "bundler"
    directory: "/"
    schedule:
      interval: "weekly"

  - package-ecosystem: "github-actions"
    directory: "/"
    schedule:
      interval: "weekly"
Enter fullscreen mode Exit fullscreen mode
  1. For Critical/High repos, require dependency review as a status check.

Validation

Confirm Dependabot creates alerts or security PRs when vulnerable dependencies exist.

Evidence to Retain

Dependabot settings
Dependabot PRs
Dependency alert list
Dependency review check
SBOM artifact, if generated
Enter fullscreen mode Exit fullscreen mode

23. Release, Tag, Package, and Artifact Security

Control Objective

Protect releases, tags, packages, and artifacts from unauthorized modification.

Exact GitHub Settings

Tag ruleset:

Repository or Organization
→ Settings
→ Rules
→ Rulesets
→ New tag ruleset
Enter fullscreen mode Exit fullscreen mode

Packages:

Repository or Organization
→ Packages
Enter fullscreen mode Exit fullscreen mode

Environments:

Repository
→ Settings
→ Environments
Enter fullscreen mode Exit fullscreen mode

What to Configure

Create a tag ruleset.

Ruleset name: Release Tag Protection
Target: Tag
Tag pattern: v*
Enforcement: Active
Enter fullscreen mode Exit fullscreen mode

Enable:

Restrict deletions
Block force updates
Require signed tags, where operationally supported
Restrict who can create/update/delete matching tags
Enter fullscreen mode Exit fullscreen mode

For package publishing workflows:

Limit packages: write permission
Require environment approval for production publishing
Use OIDC where supported
Use artifact attestation/provenance where available
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Repository → Settings → Rules → Rulesets.
  2. Click New ruleset.
  3. Select New tag ruleset.
  4. Target:
v*
Enter fullscreen mode Exit fullscreen mode
  1. Enable deletion/update protections.
  2. Restrict creation/update to release managers or release workflow.
  3. Review workflows that publish packages.
  4. Limit workflow token permissions:
permissions:
  contents: read
  packages: write
  id-token: write
Enter fullscreen mode Exit fullscreen mode

Only include packages: write in jobs that publish packages.

Validation

Try deleting or moving a protected release tag as a non-approved user. It should fail.

Evidence to Retain

Tag ruleset export
Release workflow
Package permissions
Release approval logs
Artifact attestation/provenance
Enter fullscreen mode Exit fullscreen mode

24. Personal Access Tokens, Fine-Grained Tokens, and Service Accounts

Control Objective

Prevent unmanaged tokens from accessing organization repositories and APIs.

Exact GitHub Setting

Organization
→ Settings
→ Personal access tokens
→ Settings
Enter fullscreen mode Exit fullscreen mode

Review:

Fine-grained tokens
Tokens (classic)
Enter fullscreen mode Exit fullscreen mode

Fine-Grained PATs — What to Select

Select:

Require administrator approval
Enter fullscreen mode Exit fullscreen mode

Set maximum lifetime if available:

90 days for normal use
30 days for high-risk use
7 days for temporary troubleshooting
Enter fullscreen mode Exit fullscreen mode

Classic PATs — What to Select

Select the most restrictive available option:

Restrict access via personal access tokens (classic)
Enter fullscreen mode Exit fullscreen mode

or equivalent setting that prevents classic PATs from accessing organization resources by default.

Step-by-Step Setup

  1. Go to Organization → Settings → Personal access tokens → Settings.
  2. Open Fine-grained tokens.
  3. Select Require administrator approval.
  4. Configure maximum token lifetime where available.
  5. Open Tokens (classic).
  6. Restrict classic PAT access to organization resources.
  7. Review existing token access.
  8. Revoke unused, broad, or orphaned tokens.
  9. Migrate automations to GitHub Apps, OIDC, GITHUB_TOKEN, or fine-grained PATs.
  10. Require exception approval for any classic PAT.

Fine-Grained PAT Approval Criteria

Approve only if:

Selected repositories only
Minimum permissions only
Expiry set
Named owner
Business justification
Storage location documented
Repo owner approval for write access
Security approval for workflow/admin/org permissions
Enter fullscreen mode Exit fullscreen mode

Do Not Approve

No-expiry tokens
Classic PATs with broad repo scope
Tokens owned by former employees
Tokens stored on laptops/runners
Tokens with workflow/admin/org permissions without Security approval
Enter fullscreen mode Exit fullscreen mode

Validation

Submit a test fine-grained PAT request for org access. It should require administrator approval.

Evidence to Retain

PAT policy screenshot/export
Fine-grained PAT approval list
Classic PAT exception list
Revoked token list
Token review report
Service account register
Enter fullscreen mode Exit fullscreen mode

25. OAuth Apps, GitHub Apps, Deploy Keys, and Webhooks

Control Objective

Prevent unmanaged integrations from accessing organization data or triggering automation.

Exact GitHub Settings

OAuth Apps:

Organization
→ Settings
→ Third-party access
→ OAuth app access restrictions
Enter fullscreen mode Exit fullscreen mode

GitHub Apps:

Organization
→ Settings
→ GitHub Apps
Enter fullscreen mode Exit fullscreen mode

Deploy keys:

Repository
→ Settings
→ Deploy keys
Enter fullscreen mode Exit fullscreen mode

Webhooks:

Repository
→ Settings
→ Webhooks
Enter fullscreen mode Exit fullscreen mode

What to Configure

OAuth Apps:

Restrict third-party application access
Require approval before organization access
Enter fullscreen mode Exit fullscreen mode

GitHub Apps:

Approve only required permissions
Install only on selected repositories
Avoid organization-wide installation unless justified
Enter fullscreen mode Exit fullscreen mode

Deploy keys:

Remove unused deploy keys
Avoid write-enabled deploy keys
Require owner and justification
Enter fullscreen mode Exit fullscreen mode

Webhooks:

Require HTTPS
Require webhook secret
Validate target ownership
Review event subscriptions
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Third-party access.
  2. Enable OAuth App access restrictions.
  3. Review approved OAuth Apps.
  4. Remove unused or over-scoped apps.
  5. Go to Organization → Settings → GitHub Apps.
  6. Review installed GitHub Apps.
  7. Confirm each app has owner, purpose, repo scope, and permission scope.
  8. Go to each Critical/High repo.
  9. Review Deploy keys and Webhooks.
  10. Remove unused keys/hooks.
  11. Add webhook secrets where missing.

Validation

Pick five installed apps and confirm each has least privilege and selected repository scope.

Evidence to Retain

OAuth App inventory
GitHub App inventory
Deploy key list
Webhook list
Permission review
Quarterly review record
Enter fullscreen mode Exit fullscreen mode

26. Self-Hosted Runner Security

Control Objective

Prevent GitHub Actions jobs from becoming a pivot into internal infrastructure or production systems.

Exact GitHub Setting

Organization
→ Settings
→ Actions
→ Runners
→ Runner groups
Enter fullscreen mode Exit fullscreen mode

Repository-level runner view:

Repository
→ Settings
→ Actions
→ Runners
Enter fullscreen mode Exit fullscreen mode

Required Runner Groups

Create separate runner groups:

runner-public-low-trust
runner-internal-build
runner-internal-test
runner-production-deploy
runner-security-tools
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Actions → Runners.
  2. Click Runner groups.
  3. Create runner-internal-build.
  4. Restrict it to selected internal repositories.
  5. Create runner-production-deploy.
  6. Restrict it to production deployment repositories only.
  7. Do not allow public repositories to use privileged self-hosted runners.
  8. Do not allow sandbox or experimental repos to use production runners.
  9. Prefer ephemeral or just-in-time runners for production deployment jobs.
  10. Install EDR/host monitoring on runner hosts.
  11. Restrict runner network egress.

Runner Group Access Standard

Production runners:
- Only production deployment repos
- No public repos
- No fork PRs
- No sandbox repos
- No broad internal network access

Build runners:
- Internal build repos only
- No production secrets
- Limited network egress

Security runners:
- Security-owned repos only
- Separate credentials
- Monitored heavily
Enter fullscreen mode Exit fullscreen mode

Block Dangerous Workflow Patterns

Review workflows using:

pull_request_target
workflow_run
repository_dispatch
self-hosted runner labels on PR workflows
secrets exposed to PR workflows
Enter fullscreen mode Exit fullscreen mode

Host Hardening

Apply:

Patch runner OS regularly
Run runner service as dedicated low-privilege user
Do not run as root/admin unless justified
Clean workspace after jobs
Do not store long-lived credentials locally
Restrict interactive login
Install EDR
Enable network logs
Use separate hosts for separate trust zones
Enter fullscreen mode Exit fullscreen mode

Network Controls

Restrict outbound access to:

GitHub
Required package registries
Artifact repository
Cloud deployment endpoints
Required internal services only
Enter fullscreen mode Exit fullscreen mode

Block or restrict:

Broad internal RFC1918 access
Cloud metadata access where possible
Production databases unless explicitly required
Lateral movement paths
Enter fullscreen mode Exit fullscreen mode

Validation

  1. Confirm a sandbox repo cannot target runner-production-deploy.
  2. Confirm public repos cannot use privileged runners.
  3. Confirm fork PR workflows do not run on privileged self-hosted runners.
  4. Confirm production runner has limited network access.

Evidence to Retain

Runner group configuration
Allowed repository list per runner group
Runner host hardening checklist
EDR coverage report
Network rules
Runner job logs
Runner group access review
Enter fullscreen mode Exit fullscreen mode

27. Repository Forking and Pull Request Safety

Control Objective

Prevent untrusted fork workflows from accessing secrets, write tokens, or privileged runners.

Exact GitHub Settings

Organization:

Organization
→ Settings
→ Member privileges
→ Repository forking
Enter fullscreen mode Exit fullscreen mode

Repository:

Repository
→ Settings
→ Actions
→ General
Enter fullscreen mode Exit fullscreen mode

What to Configure

Private repository forking: Disabled by default
Fork pull request workflows: Require approval where available
Secrets to fork PRs: Do not expose
Privileged self-hosted runners: Do not allow for fork PRs
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Organization → Settings → Member privileges.
  2. Disable private repository forking unless approved.
  3. For public repos, go to Repository → Settings → Actions → General.
  4. Require approval for workflows from outside collaborators/forks where available.
  5. Review all workflows using pull_request_target.
  6. Do not checkout untrusted fork code in privileged contexts.
  7. Do not use self-hosted privileged runners for fork PR workflows.

Validation

Open a fork PR and confirm secrets and privileged runners are not available.

Evidence to Retain

Forking policy screenshot
Fork workflow approval setting
pull_request_target workflow review
Runner access review
Enter fullscreen mode Exit fullscreen mode

28. Audit Logs and Monitoring

Control Objective

Detect high-risk GitHub changes and support investigations.

Exact GitHub Setting

Audit log:

Enterprise or Organization
→ Settings
→ Audit log
Enter fullscreen mode Exit fullscreen mode

Log streaming, where available:

Enterprise
→ Settings
→ Audit log
→ Log streaming
Enter fullscreen mode Exit fullscreen mode

What to Configure

Enable audit log streaming to SIEM/log lake where available
If streaming is unavailable, schedule audit log export/review
Create alerts for high-risk GitHub events
Enter fullscreen mode Exit fullscreen mode

Events to Alert On

Organization owner added
SSO enforcement changed
MFA/passkey requirement changed
Repository made public
Repository transferred
Repository deleted
Ruleset changed
Ruleset bypass actor added
Branch protection disabled
Secret scanning disabled
Push protection disabled
GitHub Actions policy changed
Workflow file changed
Self-hosted runner added
Runner group changed
OAuth App approved
GitHub App installed
PAT policy changed
Fine-grained PAT approved
Classic PAT exception created
Production environment reviewer removed
Deployment secret changed
Release or tag modified unexpectedly
Enter fullscreen mode Exit fullscreen mode

Step-by-Step Setup

  1. Go to Enterprise or Organization → Settings → Audit log.
  2. Configure streaming if available.
  3. Send logs to SIEM.
  4. Build detections for the events above.
  5. Create a weekly review process if streaming is not available.
  6. Assign SOC/SecOps ownership for triage.

Validation

Make a low-risk test change, such as adding/removing a test runner group or modifying a test ruleset. Confirm the event reaches SIEM.

Evidence to Retain

Audit log streaming configuration
SIEM ingestion proof
Detection rules
Alert tickets
Scheduled review records
Enter fullscreen mode Exit fullscreen mode

29. GitHub Incident Response Playbook

Control Objective

Prepare for GitHub compromise, token leakage, malicious workflow changes, and release tampering.

Required Playbooks

Create response steps for:

Compromised developer account
Compromised organization owner
Leaked secret
Malicious workflow change
Malicious GitHub Action
Compromised self-hosted runner
Unauthorized repository visibility change
Malicious release or tag
Unauthorized production deployment
Compromised PAT or OAuth App
Suspicious repo clone or mass download
Enter fullscreen mode Exit fullscreen mode

Containment Actions

Suspend user
Revoke PAT/fine-grained token
Remove OAuth/GitHub App access
Disable workflow
Rotate secrets
Revoke cloud OIDC trust or cloud role access
Disable deployment environment
Remove runner from runner group
Block compromised runner host
Revert malicious commits
Lock affected branches
Archive evidence
Enter fullscreen mode Exit fullscreen mode

Evidence to Preserve

Audit logs
Workflow run logs
PR history
Commit history
Release/tag metadata
Deployment logs
Runner logs
Secret scanning alerts
Cloud access logs
Package publish logs
Token approval/revocation logs
Enter fullscreen mode Exit fullscreen mode

Validation

Run one tabletop exercise per year covering:

Leaked production secret
Malicious workflow change
Compromised GitHub App/PAT
Compromised self-hosted runner
Enter fullscreen mode Exit fullscreen mode

Evidence to Retain

GitHub IR playbook
Tabletop report
Incident ticket
Timeline
Containment log
Post-incident review
Control improvement plan
Enter fullscreen mode Exit fullscreen mode

30. Exception and Bypass Governance

Control Objective

Prevent security exceptions from becoming permanent hidden risk.

Required Action

Every exception must be approved, time-bound, owned, logged, and reviewed.

Required Exception Fields

Control being bypassed
Repository or organization scope
Business justification
Risk impact
Requested actor/team/app
Requested permission
Start date
Expiry date
Compensating controls
Approver
Review cadence
Rollback plan
Enter fullscreen mode Exit fullscreen mode

Bypass Rules

No permanent broad bypass by default.
No workflow bot global bypass by default.
No repo-admin self-approved bypass for production-impacting branches.
Security or release governance approves bypass actors for hotfix and production workflows.
All bypass use must be logged and reviewed after use.
Enter fullscreen mode Exit fullscreen mode

Exact GitHub Locations to Review Bypass

Rulesets:

Organization or Repository
→ Settings
→ Rules
→ Rulesets
→ Select ruleset
→ Bypass list / Bypass actors
Enter fullscreen mode Exit fullscreen mode

Environments:

Repository
→ Settings
→ Environments
→ production
→ Deployment protection / reviewers / admin bypass
Enter fullscreen mode Exit fullscreen mode

Actions:

Organization
→ Settings
→ Actions
→ General
Enter fullscreen mode Exit fullscreen mode

Validation

Review all bypass actors monthly and remove stale entries.

Evidence to Retain

Exception tickets
Risk acceptances
Bypass actor export
Audit logs
Expiry review
Closure evidence
Enter fullscreen mode Exit fullscreen mode

31. Minimum Secure GitHub Baseline Checklist

Identity and Access
[ ] SSO enforced
[ ] IdP phishing-resistant MFA/passkeys enforced
[ ] GitHub-local MFA/passkeys required for direct-auth/fallback accounts, break-glass accounts, outside collaborators, and service accounts where applicable
[ ] Hardware-backed SSH keys required where feasible for privileged Git push/pull
[ ] SSH keys and tokens authorized for SSO where required
[ ] Org owners minimized
[ ] Org owners reviewed monthly
[ ] Base permission set to No permission
[ ] Team-based access implemented
[ ] Direct user-to-repo access minimized
[ ] Outside collaborators restricted and reviewed

Organization Governance
[ ] Repo creation restricted
[ ] Repo deletion restricted
[ ] Repo transfer restricted
[ ] Visibility changes restricted
[ ] Private repo forking disabled by default
[ ] Repository classification completed
[ ] Repository owners assigned

Branch, Tag, and Push Rulesets
[ ] Org ruleset protects default branch
[ ] Default branch hygiene validated
[ ] PR required before merge
[ ] Approval required
[ ] CODEOWNER review required for Critical repos
[ ] Stale reviews dismissed
[ ] Conversation resolution required
[ ] Required status checks configured
[ ] Force push blocked
[ ] Branch deletion blocked
[ ] Bypass actors minimized
[ ] Repo-level develop/release/hotfix rulesets created where needed
[ ] Release tag ruleset created where needed
[ ] Push ruleset blocks risky file types/paths where supported

CODEOWNERS
[ ] .github/workflows/** protected
[ ] .github/actions/** protected
[ ] IaC paths protected
[ ] Kubernetes/deployment paths protected
[ ] Package/dependency files protected
[ ] CODEOWNERS reviewed quarterly

GitHub Actions
[ ] Actions restricted to approved sources
[ ] No third-party @* approvals
[ ] High-risk Actions pinned to full SHA
[ ] Default GITHUB_TOKEN read-only
[ ] Explicit workflow permissions required
[ ] write-all prohibited unless approved
[ ] pull_request_target workflows reviewed
[ ] Workflow changes require CODEOWNER review

Secrets and Cloud Access
[ ] Secret scanning enabled
[ ] Push protection enabled
[ ] Custom secret patterns configured
[ ] Static cloud keys removed where possible
[ ] OIDC configured for cloud deployments
[ ] Production environments protected
[ ] Production secrets scoped to environments

Application and Dependency Security
[ ] Code scanning enabled
[ ] Code scanning required checks configured for Critical/High repos
[ ] Dependabot alerts enabled
[ ] Dependabot security updates enabled
[ ] Dependency review enabled
[ ] Critical/high findings tracked to closure
[ ] SBOM generated for Critical/High production services where feasible

Runners
[ ] Runner groups segmented by trust level
[ ] Privileged runners restricted to approved repos
[ ] Untrusted fork PRs blocked from privileged runners
[ ] Ephemeral runners used where feasible
[ ] Runner network access restricted
[ ] Runner hosts hardened and monitored

Tokens and Integrations
[ ] Classic PATs restricted
[ ] Fine-grained PATs require approval
[ ] Token maximum lifetime configured where available
[ ] Service accounts inventoried and reviewed
[ ] OAuth Apps reviewed
[ ] GitHub Apps reviewed
[ ] Deploy keys reviewed
[ ] Webhooks reviewed and protected with secrets

Release and Package Security
[ ] Release branches protected
[ ] Release tags protected
[ ] Package publishing restricted
[ ] Artifact provenance/attestation used where available
[ ] Release approvals logged

Monitoring and IR
[ ] Audit logs exported or reviewed
[ ] High-risk events monitored
[ ] GitHub IR playbook created
[ ] Exceptions are time-bound and reviewed
[ ] Bypass usage reviewed after each use
Enter fullscreen mode Exit fullscreen mode

32. Repository Rollout Ticket Template

Title:
Implement GitHub security baseline for <repo/org>

Scope:
<organization or repository name>

Repository classification:
Critical / High / Medium / Low

Exact actions:
[ ] Confirm business owner
[ ] Confirm technical owner
[ ] Confirm default branch
[ ] Confirm repository classification
[ ] Confirm production deployment status
[ ] Apply org default branch ruleset
[ ] Add repo-level develop ruleset if used
[ ] Add repo-level release/* ruleset if used
[ ] Add repo-level hotfix/* ruleset if used
[ ] Add release tag ruleset if repo creates releases
[ ] Add push ruleset for risky file types/paths if supported
[ ] Add CODEOWNERS for workflows, IaC, deployment, dependencies
[ ] Enable secret scanning
[ ] Enable push protection
[ ] Enable CodeQL/code scanning
[ ] Enable Dependabot alerts
[ ] Enable Dependabot security updates
[ ] Enable dependency review
[ ] Review workflows for permissions
[ ] Remove unapproved Actions
[ ] Set explicit workflow permissions
[ ] Replace cloud secrets with OIDC where possible
[ ] Configure GitHub Environment for production
[ ] Review PATs, deploy keys, GitHub Apps, OAuth Apps, and webhooks
[ ] Review runner usage and runner group access
[ ] Confirm audit monitoring

Evidence required:
- Ruleset export
- CODEOWNERS file
- Code security settings screenshot/export
- Actions policy compliance evidence
- Workflow permission review
- Approved Actions list
- Secrets/token review
- Runner group review
- Environment approval settings
- Exception records

Approvers:
Engineering owner:
Platform owner:
Security owner:
Release owner, if production:

Due date:
Enter fullscreen mode Exit fullscreen mode

33. Final Operating Standard

GitHub security should be implemented as enforceable settings, not informal advice.

The minimum operating standard is:

SSO enforced.
IdP passkeys/phishing-resistant MFA enforced.
Hardware-backed SSH for privileged Git access where feasible.
Org owners minimized.
Base permissions set to No permission.
Default branch protected by org ruleset.
develop/release/hotfix protected only where used.
CODEOWNERS required for workflow/IaC/deployment paths.
GitHub Actions restricted to approved actions.
GITHUB_TOKEN read-only by default.
Workflow permissions explicit.
Static cloud keys replaced with OIDC.
Production deployments protected by environments.
Secret scanning and push protection enabled.
Code scanning and Dependabot enabled.
Self-hosted runners isolated by trust level.
PATs, apps, deploy keys, and webhooks governed.
Audit logs monitored.
Exceptions time-bound and reviewed.
Enter fullscreen mode Exit fullscreen mode

The goal is not to slow engineering down. The goal is to remove unsafe paths while keeping normal development predictable, auditable, and secure.

Top comments (0)