Skip to content

Container Security #107

Container Security

Container Security #107

name: Container Security
on:
pull_request:
branches:
- develop
- main
push:
branches:
- develop
- main
schedule:
- cron: "43 9 * * 1"
workflow_dispatch: {}
permissions:
contents: read
concurrency:
group: provider-container-security-${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
cancel-in-progress: ${{ github.event_name == 'pull_request' }}
jobs:
runtime-smoke:
name: Hardened Runtime Smoke
runs-on: ubuntu-latest
timeout-minutes: 20
permissions:
contents: read
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Test synthetic private-network conformance
run: make docker-conformance
- name: Test source providers without external network access
run: make docker-source-smoke
image-scan:
name: Scan ${{ matrix.provider }} Image
runs-on: ubuntu-latest
timeout-minutes: 25
permissions:
actions: read
contents: read
security-events: write
strategy:
fail-fast: false
matrix:
provider:
- synthetic
- getcomics
- annas-archive
- libgen
include:
- provider: synthetic
dockerfile: docker/Dockerfile.synthetic
- provider: getcomics
dockerfile: docker/Dockerfile.getcomics
- provider: annas-archive
dockerfile: docker/Dockerfile.annas-archive
- provider: libgen
dockerfile: docker/Dockerfile.libgen
env:
IMAGE: pullbox-provider-${{ matrix.provider }}:security-${{ github.run_id }}
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Build final runtime image
run: |
docker build \
--file "${{ matrix.dockerfile }}" \
--target runtime \
--tag "${IMAGE}" \
.
- name: Produce machine-readable Grype report
id: scan-json
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
image: ${{ env.IMAGE }}
fail-build: false
severity-cutoff: negligible
output-format: json
- name: Reject unreviewed High or Critical findings
env:
GRYPE_REPORT: ${{ steps.scan-json.outputs.json }}
run: |
python .github/scripts/verify-container-vulnerability-baseline.py \
--report "$GRYPE_REPORT" \
--baseline .github/security/container-vulnerability-baseline.json \
--image "${{ matrix.provider }}"
- name: Produce SARIF report
id: scan-sarif
if: always()
uses: anchore/scan-action@e1165082ffb1fe366ebaf02d8526e7c4989ea9d2 # v7.4.0
with:
image: ${{ env.IMAGE }}
fail-build: false
severity-cutoff: high
output-format: sarif
- name: Filter reviewed findings from SARIF
id: filter-sarif
if: always() && steps.scan-sarif.outcome == 'success'
env:
GRYPE_REPORT: ${{ steps.scan-json.outputs.json }}
GRYPE_SARIF: ${{ steps.scan-sarif.outputs.sarif }}
run: |
python .github/scripts/filter-reviewed-container-sarif.py \
--sarif "$GRYPE_SARIF" \
--report "$GRYPE_REPORT" \
--baseline .github/security/container-vulnerability-baseline.json \
--image "${{ matrix.provider }}" \
--output "${RUNNER_TEMP}/${{ matrix.provider }}-actionable.sarif"
- name: Upload actionable SARIF
if: always() && steps.filter-sarif.outcome == 'success' && (github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository)
uses: github/codeql-action/upload-sarif@f205ea1c3313d32999d8d6a48b4f6530d4437b38 # v4.37.4
with:
sarif_file: ${{ runner.temp }}/${{ matrix.provider }}-actionable.sarif
category: container-${{ matrix.provider }}
- name: Upload Grype JSON report
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: grype-${{ matrix.provider }}-report
path: ${{ steps.scan-json.outputs.json }}
if-no-files-found: ignore
retention-days: 14
multiarch-build:
name: Build ${{ matrix.provider }} for AMD64 and ARM64
runs-on: ubuntu-latest
timeout-minutes: 30
permissions:
contents: read
strategy:
fail-fast: false
matrix:
provider:
- synthetic
- getcomics
- annas-archive
- libgen
include:
- provider: synthetic
dockerfile: docker/Dockerfile.synthetic
- provider: getcomics
dockerfile: docker/Dockerfile.getcomics
- provider: annas-archive
dockerfile: docker/Dockerfile.annas-archive
- provider: libgen
dockerfile: docker/Dockerfile.libgen
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up QEMU
uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
with:
platforms: arm64
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
- name: Build both supported architectures without publishing
run: |
docker buildx build \
--file "${{ matrix.dockerfile }}" \
--target runtime \
--platform linux/amd64,linux/arm64 \
--output "type=oci,dest=${RUNNER_TEMP}/${{ matrix.provider }}.oci" \
.
required:
name: Container Security Required
if: always()
needs:
- runtime-smoke
- image-scan
- multiarch-build
runs-on: ubuntu-latest
timeout-minutes: 5
permissions:
contents: read
env:
RUNTIME_RESULT: ${{ needs.runtime-smoke.result }}
SCAN_RESULT: ${{ needs.image-scan.result }}
MULTIARCH_RESULT: ${{ needs.multiarch-build.result }}
steps:
- name: Verify required container jobs
run: |
if [[ "$RUNTIME_RESULT" != "success" || \
"$SCAN_RESULT" != "success" || \
"$MULTIARCH_RESULT" != "success" ]]; then
echo "One or more required container checks failed."
exit 1
fi