chopratejas Claude Opus 4.6 (1M context) commited on
Commit
ccb92e5
·
1 Parent(s): 87146ac

Add supply chain hardening: SBOM, cosign signing, pinned digests, Dependabot

Browse files

Enterprise supply chain improvements:
- publish.yml: Generate CycloneDX SBOM and attach to GitHub releases
- docker.yml: Sign all Docker images with Sigstore cosign (keyless OIDC)
- Dockerfile: Pin python:3.11-slim and distroless base images with SHA256
digests to prevent silent upstream changes
- Add .github/dependabot.yml for automated Docker digest, GitHub Actions,
and pip dependency update PRs

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>

.github/dependabot.yml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version: 2
2
+ updates:
3
+ # Docker base image digest updates
4
+ - package-ecosystem: docker
5
+ directory: /
6
+ schedule:
7
+ interval: weekly
8
+ commit-message:
9
+ prefix: "docker"
10
+
11
+ # GitHub Actions version updates
12
+ - package-ecosystem: github-actions
13
+ directory: /
14
+ schedule:
15
+ interval: weekly
16
+ commit-message:
17
+ prefix: "ci"
18
+
19
+ # Python dependency updates (pip)
20
+ - package-ecosystem: pip
21
+ directory: /
22
+ schedule:
23
+ interval: weekly
24
+ commit-message:
25
+ prefix: "deps"
26
+ # Only open PRs for security updates to avoid noise
27
+ open-pull-requests-limit: 5
.github/workflows/docker.yml CHANGED
@@ -12,6 +12,7 @@ env:
12
  permissions:
13
  contents: read
14
  packages: write
 
15
 
16
  jobs:
17
  docker-variant-tags:
@@ -81,3 +82,19 @@ jobs:
81
  set: |
82
  *.cache-from=type=gha
83
  *.cache-to=type=gha,mode=max
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
  permissions:
13
  contents: read
14
  packages: write
15
+ id-token: write # For cosign keyless signing via Sigstore OIDC
16
 
17
  jobs:
18
  docker-variant-tags:
 
82
  set: |
83
  *.cache-from=type=gha
84
  *.cache-to=type=gha,mode=max
85
+
86
+ - name: Install cosign
87
+ uses: sigstore/cosign-installer@v3
88
+
89
+ - name: Sign images with cosign (keyless via Sigstore OIDC)
90
+ env:
91
+ BAKE_META: ${{ steps.bake.outputs.metadata }}
92
+ run: |
93
+ # Extract all pushed image digests from bake metadata and sign each
94
+ echo "$BAKE_META" | jq -r '
95
+ to_entries[].value."containerimage.digest" // empty
96
+ ' | while read -r digest; do
97
+ image="${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}@${digest}"
98
+ echo "Signing ${image}"
99
+ cosign sign --yes "${image}"
100
+ done
.github/workflows/publish.yml CHANGED
@@ -9,7 +9,8 @@ jobs:
9
  runs-on: ubuntu-latest
10
  environment: pypi
11
  permissions:
12
- id-token: write # For trusted publishing
 
13
 
14
  steps:
15
  - uses: actions/checkout@v4
@@ -21,11 +22,23 @@ jobs:
21
 
22
  - name: Install build tools
23
  run: |
24
- python -m pip install --upgrade pip build
25
 
26
  - name: Build package
27
  run: |
28
  python -m build
29
 
 
 
 
 
 
 
 
 
 
 
 
 
30
  - name: Publish to PyPI
31
  uses: pypa/gh-action-pypi-publish@release/v1
 
9
  runs-on: ubuntu-latest
10
  environment: pypi
11
  permissions:
12
+ id-token: write # For trusted publishing
13
+ contents: write # For uploading release assets
14
 
15
  steps:
16
  - uses: actions/checkout@v4
 
22
 
23
  - name: Install build tools
24
  run: |
25
+ python -m pip install --upgrade pip build cyclonedx-bom
26
 
27
  - name: Build package
28
  run: |
29
  python -m build
30
 
31
+ - name: Generate SBOM (CycloneDX)
32
+ run: |
33
+ pip install -e ".[proxy]"
34
+ cyclonedx-py environment \
35
+ --output-format json \
36
+ --outfile dist/headroom-sbom.cdx.json
37
+
38
+ - name: Upload SBOM to release
39
+ uses: softprops/action-gh-release@v2
40
+ with:
41
+ files: dist/headroom-sbom.cdx.json
42
+
43
  - name: Publish to PyPI
44
  uses: pypa/gh-action-pypi-publish@release/v1
.gitignore CHANGED
@@ -8,6 +8,9 @@ scripts/*
8
  # Swift SDK (separate repo)
9
  swift/
10
 
 
 
 
11
  # Audit/scan outputs (contain security findings — never commit)
12
  bandit_result.txt
13
  pip_audit_result.txt
 
8
  # Swift SDK (separate repo)
9
  swift/
10
 
11
+ # Local planning docs (never commit)
12
+ ENTERPRISE_HARDENING.md
13
+
14
  # Audit/scan outputs (contain security findings — never commit)
15
  bandit_result.txt
16
  pip_audit_result.txt
Dockerfile CHANGED
@@ -1,10 +1,14 @@
1
  ARG PYTHON_VERSION=3.11
2
  ARG UV_VERSION=0.6.17
 
 
 
 
3
  ARG DISTROLESS_IMAGE=gcr.io/distroless/python3-debian13
4
  ARG PYTHON_SITE_PACKAGES=/usr/local/lib/python${PYTHON_VERSION}/site-packages
5
 
6
  # ---- Build stage: compile native extensions, build wheel ----
7
- FROM python:${PYTHON_VERSION}-slim AS builder
8
 
9
  ARG UV_VERSION
10
 
@@ -32,7 +36,7 @@ RUN --mount=type=cache,target=/root/.cache/uv \
32
  uv pip install --system --no-deps --reinstall-package headroom-ai .
33
 
34
  # ---- Runtime stage (python-slim): supports root/nonroot via build arg ----
35
- FROM python:${PYTHON_VERSION}-slim AS runtime-slim-base
36
 
37
  ARG RUNTIME_USER=nonroot
38
  ARG PYTHON_SITE_PACKAGES
@@ -69,7 +73,7 @@ HEALTHCHECK --interval=30s --timeout=5s --start-period=20s --retries=3 \
69
  ENTRYPOINT ["headroom", "proxy"]
70
  CMD ["--host", "0.0.0.0", "--port", "8787"]
71
 
72
- FROM ${DISTROLESS_IMAGE} AS runtime-slim
73
 
74
  ARG RUNTIME_USER=nonroot
75
  ARG PYTHON_SITE_PACKAGES
 
1
  ARG PYTHON_VERSION=3.11
2
  ARG UV_VERSION=0.6.17
3
+ # Pinned 2026-04-15. Update via Dependabot or: docker pull python:3.11-slim
4
+ ARG PYTHON_DIGEST=sha256:233de06753d30d120b1a3ce359d8d3be8bda78524cd8f520c99883bfe33964cf
5
+ # Pinned 2026-04-15. Update via Dependabot or: docker pull gcr.io/distroless/python3-debian13
6
+ ARG DISTROLESS_DIGEST=sha256:ed3a4beb46f8f8baac068743ba1b1f95ea3f793422129cf6dd23967f779b6018
7
  ARG DISTROLESS_IMAGE=gcr.io/distroless/python3-debian13
8
  ARG PYTHON_SITE_PACKAGES=/usr/local/lib/python${PYTHON_VERSION}/site-packages
9
 
10
  # ---- Build stage: compile native extensions, build wheel ----
11
+ FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS builder
12
 
13
  ARG UV_VERSION
14
 
 
36
  uv pip install --system --no-deps --reinstall-package headroom-ai .
37
 
38
  # ---- Runtime stage (python-slim): supports root/nonroot via build arg ----
39
+ FROM python:${PYTHON_VERSION}-slim@${PYTHON_DIGEST} AS runtime-slim-base
40
 
41
  ARG RUNTIME_USER=nonroot
42
  ARG PYTHON_SITE_PACKAGES
 
73
  ENTRYPOINT ["headroom", "proxy"]
74
  CMD ["--host", "0.0.0.0", "--port", "8787"]
75
 
76
+ FROM ${DISTROLESS_IMAGE}@${DISTROLESS_DIGEST} AS runtime-slim
77
 
78
  ARG RUNTIME_USER=nonroot
79
  ARG PYTHON_SITE_PACKAGES