From 690005de06c1a52441b6b3aa2c166d79bda10749 Mon Sep 17 00:00:00 2001 From: Matthew Kilgore Date: Sat, 27 Dec 2025 18:46:14 -0500 Subject: [PATCH] Harden all github actions --- .github/ISSUE_TEMPLATE/internal.md | 10 ++++ .../workflows/docker-publish-hardened.yaml | 8 ++-- .../workflows/docker-publish-rootless.yaml | 32 ++++++------- .github/workflows/docker-publish.yaml | 32 ++++++------- .github/workflows/issue-gatekeeper.yml | 46 +++++++++++++++++++ 5 files changed, 92 insertions(+), 36 deletions(-) create mode 100644 .github/ISSUE_TEMPLATE/internal.md create mode 100644 .github/workflows/issue-gatekeeper.yml diff --git a/.github/ISSUE_TEMPLATE/internal.md b/.github/ISSUE_TEMPLATE/internal.md new file mode 100644 index 00000000..acc76d64 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/internal.md @@ -0,0 +1,10 @@ +--- +name: "🛠️ Internal / Developer Issue" +about: "Unstructured issue for project members only. Outside contributors: please use a standard template." +title: "[INT]: " +labels: ["internal"] +assignees: [] +--- + +**Summary:** +[Write here] \ No newline at end of file diff --git a/.github/workflows/docker-publish-hardened.yaml b/.github/workflows/docker-publish-hardened.yaml index ac8547df..03e05357 100644 --- a/.github/workflows/docker-publish-hardened.yaml +++ b/.github/workflows/docker-publish-hardened.yaml @@ -56,7 +56,7 @@ jobs: ACTIONS_STEP_DEBUG: true - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Prepare run: | @@ -123,7 +123,7 @@ jobs: annotations: ${{ steps.meta.outputs.annotations }} - name: Attest platform-specific images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -216,7 +216,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest GHCR images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -240,7 +240,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest Dockerhub images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: subject-name: docker.io/${{ env.DOCKERHUB_REPO }} diff --git a/.github/workflows/docker-publish-rootless.yaml b/.github/workflows/docker-publish-rootless.yaml index 004a4040..b2765779 100644 --- a/.github/workflows/docker-publish-rootless.yaml +++ b/.github/workflows/docker-publish-rootless.yaml @@ -60,7 +60,7 @@ jobs: ACTIONS_STEP_DEBUG: true - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Prepare run: | @@ -75,40 +75,40 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f with: images: | name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }} name=${{ env.GHCR_REPO }} - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 with: image: ghcr.io/sysadminsmedia/binfmt:latest - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 with: driver-opts: | image=ghcr.io/sysadminsmedia/buildkit:master - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 with: context: . # Explicitly specify the build context file: ./Dockerfile.rootless # Explicitly specify the Dockerfile @@ -125,7 +125,7 @@ jobs: annotations: ${{ steps.meta.outputs.annotations }} - name: Attest platform-specific images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -139,7 +139,7 @@ jobs: touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: name: digests-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* @@ -159,35 +159,35 @@ jobs: steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 with: path: /tmp/digests pattern: digests-* merge-multiple: true - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 with: driver-opts: | image=ghcr.io/sysadminsmedia/buildkit:master - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f with: images: | name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }} @@ -218,7 +218,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest GHCR images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -242,7 +242,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest Dockerhub images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: subject-name: docker.io/${{ env.DOCKERHUB_REPO }} diff --git a/.github/workflows/docker-publish.yaml b/.github/workflows/docker-publish.yaml index 6fcc67d6..9bf0b657 100644 --- a/.github/workflows/docker-publish.yaml +++ b/.github/workflows/docker-publish.yaml @@ -54,7 +54,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v4 + uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 - name: Prepare run: | @@ -70,40 +70,40 @@ jobs: - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f with: images: | name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }} name=${{ env.GHCR_REPO }} - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up QEMU - uses: docker/setup-qemu-action@v3 + uses: docker/setup-qemu-action@29109295f81e9208d7d86ff1c6c12d2833863392 with: image: ghcr.io/sysadminsmedia/binfmt:latest - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 with: driver-opts: | image=ghcr.io/sysadminsmedia/buildkit:latest - name: Build and push by digest id: build - uses: docker/build-push-action@v6 + uses: docker/build-push-action@263435318d21b8e681c14492fe198d362a7d2c83 with: platforms: ${{ matrix.platform }} labels: ${{ steps.meta.outputs.labels }} @@ -118,7 +118,7 @@ jobs: annotations: ${{ steps.meta.outputs.annotations }} - name: Attest platform-specific images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -132,7 +132,7 @@ jobs: touch "/tmp/digests/${digest#sha256:}" - name: Upload digest - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 with: name: digests-${{ env.PLATFORM_PAIR }} path: /tmp/digests/* @@ -152,35 +152,35 @@ jobs: steps: - name: Download digests - uses: actions/download-artifact@v4 + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 with: path: /tmp/digests pattern: digests-* merge-multiple: true - name: Login to Docker Hub - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: username: ${{ secrets.DOCKER_USERNAME }} password: ${{ secrets.DOCKER_PASSWORD }} - name: Login to GHCR - uses: docker/login-action@v3 + uses: docker/login-action@184bdaa0721073962dff0199f1fb9940f07167d1 with: registry: ghcr.io username: ${{ github.actor }} password: ${{ secrets.GITHUB_TOKEN }} - name: Set up Docker Buildx - uses: docker/setup-buildx-action@v3 + uses: docker/setup-buildx-action@e468171a9de216ec08956ac3ada2f0791b6bd435 with: driver-opts: | image=ghcr.io/sysadminsmedia/buildkit:master - name: Docker meta id: meta - uses: docker/metadata-action@v5 + uses: docker/metadata-action@c1e51972afc2121e065aed6d45c65596fe445f3f with: images: | name=${{ env.DOCKERHUB_REPO }},enable=${{ github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/') }} @@ -209,7 +209,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest GHCR images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: github.event_name != 'pull_request' with: subject-name: ${{ env.GHCR_REPO }} @@ -233,7 +233,7 @@ jobs: echo "digest=$digest" >> $GITHUB_OUTPUT - name: Attest Dockerhub images - uses: actions/attest-build-provenance@v1 + uses: actions/attest-build-provenance@00014ed6ed5efc5b1ab7f7f34a39eb55d41aa4f8 if: (github.event_name == 'schedule' || startsWith(github.ref, 'refs/tags/')) with: subject-name: docker.io/${{ env.DOCKERHUB_REPO }} diff --git a/.github/workflows/issue-gatekeeper.yml b/.github/workflows/issue-gatekeeper.yml new file mode 100644 index 00000000..18885653 --- /dev/null +++ b/.github/workflows/issue-gatekeeper.yml @@ -0,0 +1,46 @@ +name: Issue Gatekeeper +on: + issues: + types: [ opened ] + +jobs: + check-permissions: + runs-on: ubuntu-latest + steps: + - name: Verify Internal Template Use + uses: actions/github-script@v7 + with: + script: | + const { owner, repo } = context.repo; + const issue_number = context.issue.number; + const actor = context.payload.sender.login; + + // 1. Get user permission level + const { data: perms } = await github.rest.repos.getCollaboratorPermissionLevel({ + owner, + repo, + username: actor + }); + + const isMember = ['admin', 'write'].includes(perms.permission); + const body = context.payload.issue.body || ""; + + // 2. Check if they used the internal template (or if the issue is blank) + // We detect this by checking for our specific template string or the 'internal' label + const usedInternal = context.payload.issue.labels.some(l => l.name === 'internal'); + + if (usedInternal && !isMember) { + await github.rest.issues.createComment({ + owner, + repo, + issue_number, + body: `@${actor}, the "Internal" template is restricted to project members. Please use one of the standard bug or feature templates for this repository.` + }); + + await github.rest.issues.update({ + owner, + repo, + issue_number, + state: 'closed' + }); + } \ No newline at end of file