Artifact Management

Build once, promote many. This chapter covers artifact types, registry hygiene, digest-based promotion, SBOM generation, SLSA attestations, and Cosign signing—with pipeline YAML for GitHub Actions and GitLab CI.

developer devops security SARIF SLSA OIDC

Artifact types in the supply chain

An artifact is any immutable output of a build—container images, JARs, Helm charts, Terraform modules, npm packages. CI produces once; staging and production promote the same digest, never rebuild.

Key concepts

Tool Category When to run Default gate
Container image OCI Primary deployable for K8s CRITICAL gate typical
Helm chart Packaged K8s manifests App + config bundle HIGH gate typical
Binary / JAR Language-specific VM or serverless deploy HIGH gate typical
SBOM SPDX / CycloneDX JSON Vulnerability + license input MEDIUM gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/artifact-types.yml
name: Build artifacts
on: [push]
jobs:
  build:
    runs-on: ubuntu-latest
    outputs:
      digest: ${{ steps.build.outputs.digest }}
    steps:
      - uses: actions/checkout@v4
      - uses: docker/build-push-action@v5
        id: build
        with:
          push: true
          tags: ghcr.io/org/app:${{ github.sha }}
          outputs: type=image,name=ghcr.io/org/app,push=true
.gitlab-ci.yml
build-image:
  stage: build
  image: docker:24
  services: [docker:24-dind]
  script:
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  artifacts:
    reports:
      dotenv: build.env

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store Artifact types in the supply chain metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.

flowchart LR
  SRC["Source commit"] --> CI["CI pipeline"]
  CI --> IMG["OCI image digest"]
  CI --> JAR["JAR / wheel"]
  CI --> CHART["Helm chart .tgz"]
  IMG --> REG["Registry"]
  REG --> GITOPS["GitOps digest pin"]

Artifact repositories & registries

Registries (ECR, GCR, Harbor, GitHub GHCR, GitLab Registry) store OCI artifacts with RBAC, vulnerability scanning, retention, and replication. Treat registries as production systems—with backup, HA, and audit logs.

Key concepts

Tool Category When to run Default gate
Harbor Self-hosted, signing On-prem / air-gap HIGH gate typical
Amazon ECR AWS-native EKS workloads HIGH gate typical
GHCR GitHub packages GHA pipelines MEDIUM gate typical
Artifactory Universal Polyglot enterprises MEDIUM gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/repositories.yml
name: Push to registry
on: [push]
jobs:
  publish:
    runs-on: ubuntu-latest
    permissions:
      contents: read
      packages: write
    steps:
      - uses: docker/login-action@v3
        with:
          registry: ghcr.io
          username: ${{ github.actor }}
          password: ${{ secrets.GITHUB_TOKEN }}
      - uses: docker/build-push-action@v5
        with:
          push: true
          tags: |
            ghcr.io/${{ github.repository }}:${{ github.sha }}
            ghcr.io/${{ github.repository }}:latest
.gitlab-ci.yml
publish:
  stage: publish
  script:
    - docker login -u $CI_REGISTRY_USER -p $CI_REGISTRY_PASSWORD $CI_REGISTRY
    - docker build -t $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA .
    - docker push $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store Artifact repositories & registries metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.

Registry hardening

  • Enable vulnerability scanning on push (Harbor, ECR, GHCR native).
  • Immutable tags for release channels—:latest is for local dev only.
  • Geo-replication for DR; test failover pull quarterly.
  • Audit log shipping to SIEM—who pushed digest sha256:abc?
📦 Real World

SolarWinds-style attacks target build and registry paths—signing and provenance verify publisher, not just image contents.

Artifact promotion patterns

Promotion copies or retags an existing digest through environments—dev → staging → prod—without rebuilding. Rebuilds introduce non-determinism and invalidate scan results.

Key concepts

Tool Category When to run Default gate
Digest pin Deploy by sha256 GitOps CRITICAL gate typical
Retag promotion staging-ok → prod Simple pipelines HIGH gate typical
Cross-registry copy crane skopeo Multi-cloud HIGH gate typical
GitOps bump PR updates image digest ArgoCD/Flux CRITICAL gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/promotion.yml
name: Promote image
on:
  workflow_dispatch:
    inputs:
      digest:
        required: true
jobs:
  promote:
    runs-on: ubuntu-latest
    steps:
      - run: |
          crane copy ghcr.io/org/app@${{ inputs.digest }} ghcr.io/org/app:prod-approved
        env:
          CRANE_USERNAME: ${{ github.actor }}
          CRANE_PASSWORD: ${{ secrets.GITHUB_TOKEN }}
.gitlab-ci.yml
promote-prod:
  stage: deploy
  when: manual
  script:
    - crane copy $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA $CI_REGISTRY_IMAGE:prod
  rules:
    - if: $CI_COMMIT_BRANCH == "main"
flowchart LR
  BUILD["CI build
single digest"] --> STG["Deploy staging
by digest"]
  STG --> TEST["Integration tests"]
  TEST --> PROMO["Promote digest
crane copy / retag"]
  PROMO --> PROD["Prod GitOps PR
updates digest only"]
⚠️ Pitfall

Rebuilding 'the same' Dockerfile on prod deploy produces a different digest—SBOM and scan results no longer apply.

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store Artifact promotion patterns metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.

SBOM — software bill of materials

SBOMs list every component in an artifact—name, version, supplier, hashes. Fed by build tools (Syft, Trivy, CycloneDX Maven plugin) and consumed by scanners, license tools, and incident response.

Key concepts

Tool Category When to run Default gate
Syft Multi-format SBOM Post-build MEDIUM gate typical
CycloneDX Standard format Language plugins MEDIUM gate typical
Trivy SBOM + scan Container CI HIGH gate typical
Dependency-Track SBOM aggregator Org dashboard MEDIUM gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/sbom.yml
name: SBOM
on: [push]
jobs:
  sbom:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: anchore/sbom-action@v0
        with:
          artifact-name: sbom.spdx.json
          format: spdx-json
      - uses: actions/upload-artifact@v4
        with:
          name: sbom
          path: sbom.spdx.json
.gitlab-ci.yml
sbom:
  stage: build
  image:
    name: anchore/syft:latest
    entrypoint: [""]
  script:
    - syft $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA -o spdx-json=sbom.spdx.json
  artifacts:
    paths: [sbom.spdx.json]
🔬 Under the Hood

NTIA minimum elements require supplier name, component name, version, and unique ID—Syft and CycloneDX plugins emit these by default when built from lockfiles.

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store SBOM metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.

SLSA provenance & supply chain levels

SLSA (Supply-chain Levels for Software Artifacts) defines increasing assurance—provenance, signed attestations, hermetic builds, two-person review. Level 2 is a realistic enterprise target with OIDC + Sigstore.

Key concepts

Tool Category When to run Default gate
SLSA L1 Provenance exists Audit trail LOW gate typical
SLSA L2 Hosted build + signed Enterprise target MEDIUM gate typical
SLSA L3 Hardened platform Regulated industries HIGH gate typical
in-toto Attestation format Policy verify MEDIUM gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/slsa.yml
name: SLSA provenance
on: [push]
jobs:
  provenance:
    permissions:
      id-token: write
      contents: read
      attestations: write
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/attest-build-provenance@v1
        with:
          subject-name: ghcr.io/org/app
          subject-digest: sha256:abc123...
          push-to-registry: true
.gitlab-ci.yml
provenance:
  stage: attest
  image: slsa-framework/slsa-verifier:latest
  script:
    - echo "Generate provenance with GitLab runner OIDC + Sigstore"
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
flowchart TB
  L1["SLSA L1
Provenance"] --> L2["L2
Signed hosted build"]
  L2 --> L3["L3
Hardened hermetic"]
  L3 --> L4["L4
Two-person review"]
🎯 Interview Tip

Most enterprises target SLSA L2: GitHub/GitLab hosted runners + OIDC + signed provenance—not L4 hermetic reproducibility.

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store SLSA provenance & supply chain levels metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.

Image signing with Cosign & policy admission

Signing binds publisher identity to a digest. Cosign + Sigstore keyless signing via OIDC produces short-lived certificates. Cluster admission (Kyverno, Gatekeeper, OCP) verifies signatures before pods start.

Key concepts

Tool Category When to run Default gate
Cosign OCI signing CI post-push CRITICAL gate typical
Sigstore Fulcio OIDC certs Keyless HIGH gate typical
Kyverno verifyImages K8s admission Deploy gate CRITICAL gate typical
Notation Microsoft alternative Azure ACR MEDIUM gate typical
Severity Examples Typical gate policy
CRITICAL RCE, auth bypass, exposed secrets in prod Block merge & deploy immediately
HIGH SQLi, SSRF, privilege escalation paths Block within SLA (24–72h)
MEDIUM XSS, misconfigurations with limited blast radius Track in backlog; fix before next release
LOW Info leaks, best-practice deviations Accept risk or fix opportunistically
.github/workflows/image-signing.yml
name: Sign image
on: [push]
jobs:
  sign:
    runs-on: ubuntu-latest
    permissions:
      id-token: write
      contents: read
      packages: write
    steps:
      - uses: sigstore/cosign-installer@v3
      - run: cosign sign --yes ghcr.io/org/app@${{ github.sha }}
.gitlab-ci.yml
sign-image:
  stage: sign
  image: gcr.io/projectsigstore/cosign:v2.2.0
  script:
    - cosign sign --yes $CI_REGISTRY_IMAGE:$CI_COMMIT_SHA
  id_tokens:
    SIGSTORE_ID_TOKEN:
      aud: sigstore
yaml — Kyverno verifyImages
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
  name: verify-signed-images
spec:
  validationFailureAction: Enforce
  rules:
    - name: verify-cosign
      match:
        any:
          - resources:
              kinds: [Pod]
      verifyImages:
        - imageReferences: ["ghcr.io/org/*"]
          attestors:
            - entries:
                - keyless:
                    subject: "https://github.com/org/repo/.github/workflows/sign.yml@refs/heads/main"
                    issuer: "https://token.actions.githubusercontent.com"
                    rekor:
                      url: https://rekor.sigstore.dev
🔒 Security

Verify signatures at admission—registry compromise or tag mutation cannot bypass Kyverno if deploy references digest.

Operational checklist

  1. Pin deploy manifests to image@sha256:... not floating tags.
  2. Generate SBOM on every main-branch build; upload to Dependency-Track or GUAC.
  3. Sign images before promotion; verify in cluster before prod traffic.
  4. Retention policy: keep prod digests 2+ years for forensic rebuild.
  5. Replicate critical images to DR registry—supply chain continuity during outage.
💡 Pro Tip

Store Image signing with Cosign & policy admission metadata in OCI artifact attachments—SBOM and signatures travel with the image reference.