Skip to content

Latest commit

 

History

History
184 lines (146 loc) · 7.12 KB

File metadata and controls

184 lines (146 loc) · 7.12 KB

gh-2fa-auditor GitHub Action

Run gh-2fa-auditor from a workflow to list members of an organization (or a single repository) without 2FA, export the report as an artifact, and optionally fail the workflow when violators are detected.

Published as a Docker container action (action.yml + Dockerfile) — consumers only need a single uses: line.

Quick start

name: 2FA Audit
on:
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - uses: mingcheng/gh-2fa-auditor@v1
        with:
          command: audit-org
          org: my-org
          github-token: ${{ secrets.ADMIN_ORG_PAT }}

By default the report is written to 2fa-report.json under $GITHUB_WORKSPACE, ready for actions/upload-artifact.

Inputs

Name Required Default Description
command yes audit-org audit-org, audit-repo, or cache-clear.
org for audit-* — Organization login.
repo for audit-repo — Repository name (without owner/).
format no json table, json, or csv.
output no auto Report path (relative paths resolve under $GITHUB_WORKSPACE).
no-cache no false Bypass the on-disk request cache.
cache-ttl no 300 Override cache TTL (seconds).
cache-file no — Custom cache file for cache-clear.
fail-on-violators no false Exit 1 when ≥ 1 violator is found (requires format: json).
github-token for audit-* — PAT with admin:org. See Permissions & token.

When format: table is used without output, the report is printed to the runner's log and no file is produced.

Outputs

Name Description
report-path Absolute path to the generated report file (empty when no file emitted).
violator-count Number of 2FA violators. Populated only when format: json.
- id: audit
  uses: mingcheng/gh-2fa-auditor@v1
  with:
    command: audit-org
    org: my-org
    github-token: ${{ secrets.ADMIN_ORG_PAT }}

- run: |
    echo "Found ${{ steps.audit.outputs.violator-count }} violator(s)."
    echo "Report at ${{ steps.audit.outputs.report-path }}."

Permissions & token

The audit endpoints require an organization-admin identity. The default ${{ github.token }} is repo-scoped and does not carry admin:org — the action will fail fast if you try to use it.

Use one of:

  1. A classic PAT with the admin:org scope.
  2. A fine-grained PAT with Organization administration: Read (and Members: Read) on the target org.
  3. A GitHub App installation token with Organization administration permission.

Pass it via github-token; it is exported as GITHUB_TOKEN inside the container and never logged.

Recipes

Nightly org-wide audit + artifact

name: Nightly 2FA Audit
on:
  schedule: [{ cron: '0 2 * * *' }]
  workflow_dispatch:

jobs:
  audit:
    runs-on: ubuntu-latest
    steps:
      - id: audit
        uses: mingcheng/gh-2fa-auditor@v1
        with:
          command: audit-org
          org: my-org
          github-token: ${{ secrets.ADMIN_ORG_PAT }}

      - uses: actions/upload-artifact@v4
        if: steps.audit.outputs.report-path != ''
        with:
          name: 2fa-report-${{ github.run_id }}
          path: ${{ steps.audit.outputs.report-path }}
          retention-days: 30

Pull-request compliance gate

Block merges whenever one of a repo's collaborators is missing 2FA.

name: 2FA Gate
on:
  pull_request:
    branches: [main]

jobs:
  gate:
    runs-on: ubuntu-latest
    steps:
      - uses: mingcheng/gh-2fa-auditor@v1
        with:
          command: audit-repo
          org: my-org
          repo: ${{ github.event.repository.name }}
          fail-on-violators: 'true'
          github-token: ${{ secrets.ADMIN_ORG_PAT }}

Per-repo audit + Slack notification

- id: audit
  uses: mingcheng/gh-2fa-auditor@v1
  with:
    command: audit-repo
    org: my-org
    repo: payments
    github-token: ${{ secrets.ADMIN_ORG_PAT }}

- if: steps.audit.outputs.violator-count != '0'
  uses: slackapi/slack-github-action@v1
  with:
    payload: |
      { "text": ":warning: ${{ steps.audit.outputs.violator-count }} user(s) without 2FA have access to `payments`." }
  env:
    SLACK_WEBHOOK_URL: ${{ secrets.SLACK_WEBHOOK }}

CSV report committed back to the repo

- uses: actions/checkout@v4

- uses: mingcheng/gh-2fa-auditor@v1
  with:
    command: audit-org
    org: my-org
    format: csv
    output: reports/2fa-latest.csv
    github-token: ${{ secrets.ADMIN_ORG_PAT }}

- run: |
    git config user.name  'github-actions[bot]'
    git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
    git add reports/2fa-latest.csv
    git diff --cached --quiet || git commit -m 'chore: refresh 2FA audit report'
    git push

Troubleshooting

Symptom Cause / fix
Missing required scope: admin:org The token lacks admin:org. Use a classic PAT with that scope or an equivalent fine-grained PAT.
Input 'github-token' is required Set github-token to a secret reference, e.g. ${{ secrets.ADMIN_ORG_PAT }}.
Workflow succeeds but violator-count is empty The counter is only populated when format: json.
Action fails on a personal-account repo GitHub does not expose 2FA outside Organizations — move the repo into an organization first.
Cache appears stale The container cache is ephemeral and resets every run. Use no-cache: 'true' to make this explicit.
docker: ... linux/amd64 Container actions require Linux runners (runs-on: ubuntu-latest).

See also