Harden CI runner and release gate security (#320)

## Summary

- run untrusted pull-request jobs on ephemeral `ubuntu-24.04` runners
- reserve the local backend runner pool for trusted branch pushes
- remove world-writable Docker-socket fallbacks
- pin core GitHub Actions and disable checkout credential persistence
- remove the release-manager PHP parse-error fail-open path

## Why

Pull-request code previously ran on persistent self-hosted runners with
Docker access, and CI contained permission weakening and a release-gate
break-glass success path. Those behaviors were unsafe for autonomous
intake.

## Validation

- workflow YAML parsed
- backend AI workflow outputs are in sync
- pinned action SHAs match the current v4 tags
- `git diff --check`

## Risk and activation

This is an R4 CI/release-policy change. Keep the PR draft for human
review and let required CI prove the hosted-runner path before merge.
This commit is contained in:
Jeppe B
2026-07-22 19:05:55 +02:00
committed by GitHub
parent 677d4700b0
commit 34cf804d75
+35 -49
View File
@@ -19,7 +19,9 @@ concurrency:
jobs: jobs:
php: php:
name: PHP ${{ matrix.suite }} (required) name: PHP ${{ matrix.suite }} (required)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] # Pull requests are untrusted and must use an ephemeral GitHub-hosted runner.
# Trusted branch pushes may use the local backend pool for throughput.
runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-24.04' || 'backend' }}
strategy: strategy:
fail-fast: false fail-fast: false
matrix: matrix:
@@ -29,25 +31,21 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Ensure Docker access - name: Ensure Docker access
run: | run: |
set -euo pipefail set -euo pipefail
if docker ps >/dev/null 2>&1; then docker ps >/dev/null 2>&1 || {
exit 0 echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
fi exit 1
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1) }
if command -v sudo >/dev/null 2>&1; then
sudo -n chmod 666 /var/run/docker.sock
else
chmod 666 /var/run/docker.sock
fi
docker ps >/dev/null
- name: Setup Node.js - name: Setup Node.js
if: ${{ matrix.suite == 'unit' }} if: ${{ matrix.suite == 'unit' }}
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: 22 node-version: 22
@@ -61,7 +59,7 @@ jobs:
- name: Upload PHP suite logs - name: Upload PHP suite logs
if: ${{ failure() }} if: ${{ failure() }}
continue-on-error: true continue-on-error: true
uses: actions/upload-artifact@v4 uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
with: with:
name: php-${{ matrix.suite }}-logs name: php-${{ matrix.suite }}-logs
path: .tmp/ci-logs/${{ matrix.suite }} path: .tmp/ci-logs/${{ matrix.suite }}
@@ -70,14 +68,16 @@ jobs:
edge-agent: edge-agent:
name: Edge Agent (required) name: Edge Agent (required)
runs-on: [self-hosted, Linux, X64, pleno, backend] runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-24.04' || 'backend' }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: 22 node-version: 22
@@ -115,25 +115,21 @@ jobs:
edge-broker: edge-broker:
name: Edge Broker (required) name: Edge Broker (required)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-24.04' || 'backend' }}
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Ensure Docker access - name: Ensure Docker access
run: | run: |
set -euo pipefail set -euo pipefail
if docker ps >/dev/null 2>&1; then docker ps >/dev/null 2>&1 || {
exit 0 echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
fi exit 1
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1) }
if command -v sudo >/dev/null 2>&1; then
sudo -n chmod 666 /var/run/docker.sock
else
chmod 666 /var/run/docker.sock
fi
docker ps >/dev/null
- name: Materialize CI compose env files - name: Materialize CI compose env files
run: | run: |
@@ -147,7 +143,7 @@ jobs:
docker compose -f docker-compose.example.yml config > /dev/null docker compose -f docker-compose.example.yml config > /dev/null
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: 22 node-version: 22
@@ -161,7 +157,7 @@ jobs:
edge-gateway-backend: edge-gateway-backend:
name: Edge Gateway Backend (required) name: Edge Gateway Backend (required)
runs-on: [self-hosted, Linux, X64, pleno, backend, docker] runs-on: ${{ github.event_name == 'pull_request' && 'ubuntu-24.04' || 'backend' }}
env: env:
COMPOSE_FILE: docker-compose.yml:.github/docker-compose.ci.yml COMPOSE_FILE: docker-compose.yml:.github/docker-compose.ci.yml
COMPOSE_PROJECT_NAME: edge-gateway-backend-${{ github.run_id }}-${{ github.run_attempt }} COMPOSE_PROJECT_NAME: edge-gateway-backend-${{ github.run_id }}-${{ github.run_attempt }}
@@ -175,21 +171,17 @@ jobs:
steps: steps:
- name: Checkout - name: Checkout
uses: actions/checkout@v4 uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4
with:
persist-credentials: false
- name: Ensure Docker access - name: Ensure Docker access
run: | run: |
set -euo pipefail set -euo pipefail
if docker ps >/dev/null 2>&1; then docker ps >/dev/null 2>&1 || {
exit 0 echo "Docker is unavailable to the runner identity. Fix the isolated runner configuration; the workflow will not weaken /var/run/docker.sock permissions." >&2
fi exit 1
test -S /var/run/docker.sock || (echo "Docker socket is not available." >&2; exit 1) }
if command -v sudo >/dev/null 2>&1; then
sudo -n chmod 666 /var/run/docker.sock
else
chmod 666 /var/run/docker.sock
fi
docker ps >/dev/null
- name: Allocate CI ports - name: Allocate CI ports
run: | run: |
@@ -240,7 +232,7 @@ jobs:
printf '\nEDGE_PUBLIC_BROKER_URL=http://edge-broker:4300/edge-broker\n' >> .env printf '\nEDGE_PUBLIC_BROKER_URL=http://edge-broker:4300/edge-broker\n' >> .env
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v4 uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4
with: with:
node-version: 22 node-version: 22
@@ -411,12 +403,6 @@ jobs:
exit 0 exit 0
fi fi
if printf '%s' "$response_body" | grep -qi '<b>Parse error</b>'; then
echo "::warning::Release Manager API returned a PHP parse error while recording the gate. Treating this as a break-glass pass so a fix can be deployed."
printf '%s\n' "$response_body"
exit 0
fi
printf '%s\n' "$response_body" printf '%s\n' "$response_body"
echo "Release Manager gate failed with HTTP $http_code." >&2 echo "Release Manager gate failed with HTTP $http_code." >&2
exit 1 exit 1