259 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
			
		
		
	
	
			259 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Markdown
		
	
	
		
			Executable File
		
	
	
	
	
| # Stella Ops CI Recipes — (2025‑08‑04)
 | ||
| 
 | ||
| ## 0 · Key variables (export these once)
 | ||
| 
 | ||
| | Variable      | Meaning                                                                                                                           | Typical value                                        |
 | ||
| | ------------- | --------------------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------- |
 | ||
| | `STELLA_URL`  | Host that: ① stores the **CLI** & **SBOM‑builder** images under `/registry` **and** ② receives API calls at `https://$STELLA_URL` | `stella-ops.ci.acme.example`                         |
 | ||
| | `DOCKER_HOST` | How containers reach your Docker daemon (because we no longer mount `/var/run/docker.sock`)                                       | `tcp://docker:2375`                                  |
 | ||
| | `WORKSPACE`   | Directory where the pipeline stores artefacts (SBOM file)                                                                         | `$(pwd)`                                             |
 | ||
| | `IMAGE`       | The image you are building & scanning                                                                                             | `acme/backend:sha-${COMMIT_SHA}`                     |
 | ||
| | `SBOM_FILE`   | Immutable SBOM name – `<image-ref>‑YYYYMMDDThhmmssZ.sbom.json`                                                                    | `acme_backend_sha‑abc123‑20250804T153050Z.sbom.json` |
 | ||
| 
 | ||
| ```bash
 | ||
| export STELLA_URL="stella-ops.ci.acme.example"
 | ||
| export DOCKER_HOST="tcp://docker:2375"               # Jenkins/Circle often expose it like this
 | ||
| export WORKSPACE="$(pwd)"
 | ||
| export IMAGE="acme/backend:sha-${COMMIT_SHA}"
 | ||
| export SBOM_FILE="$(echo "${IMAGE}" | tr '/:+' '__')-$(date -u +%Y%m%dT%H%M%SZ).sbom.json"
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 1 · SBOM creation strategies
 | ||
| 
 | ||
| ### Option A – **Buildx attested SBOM** (preferred if you can use BuildKit)
 | ||
| 
 | ||
| You pass **two build args** so the Dockerfile can run the builder and copy the result out of the build context.
 | ||
| 
 | ||
| ```bash
 | ||
| docker buildx build \
 | ||
|   --build-arg STELLA_SBOM_BUILDER="$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|   --provenance=true --sbom=true \
 | ||
|   --build-arg SBOM_FILE="$SBOM_FILE" \
 | ||
|   -t "$IMAGE" .
 | ||
| ```
 | ||
| 
 | ||
| **If you **cannot** use Buildx, use Option B below.** The older “run a builder stage inside the Dockerfile” pattern is unreliable for producing an SBOM of the final image.
 | ||
| 
 | ||
| ```Dockerfile
 | ||
| 
 | ||
| ARG STELLA_SBOM_BUILDER
 | ||
| ARG SBOM_FILE
 | ||
| 
 | ||
| FROM $STELLA_SBOM_BUILDER as sbom
 | ||
| ARG IMAGE
 | ||
| ARG SBOM_FILE
 | ||
| RUN $STELLA_SBOM_BUILDER build --image $IMAGE --output /out/$SBOM_FILE
 | ||
| 
 | ||
| # ---- actual build stages … ----
 | ||
| FROM alpine:3.20
 | ||
| COPY --from=sbom /out/$SBOM_FILE /     # (optional) keep or discard
 | ||
| 
 | ||
| # (rest of your Dockerfile)
 | ||
| ```
 | ||
| 
 | ||
| ### Option B – **External builder step** (works everywhere; recommended baseline if Buildx isn’t available)
 | ||
| 
 | ||
| *(keep this block if your pipeline already has an image‑build step that you can’t modify)*
 | ||
| 
 | ||
| ```bash
 | ||
| docker run --rm \
 | ||
|   -e DOCKER_HOST="$DOCKER_HOST" \                       # let builder reach the daemon remotely
 | ||
|   -v "$WORKSPACE:/workspace" \                          # place SBOM beside the source code
 | ||
|   "$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|     build --image "$IMAGE" --output "/workspace/${SBOM_FILE}"
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 2 · Scan the image & upload results
 | ||
| 
 | ||
| ```bash
 | ||
| docker run --rm \
 | ||
|   -e DOCKER_HOST="$DOCKER_HOST" \                       # remote‑daemon pointer
 | ||
|   -v "$WORKSPACE/${SBOM_FILE}:/${SBOM_FILE}:ro" \       # mount SBOM under same name at container root
 | ||
|   -e STELLA_OPS_URL="https://${STELLA_URL}" \           # where the CLI posts findings
 | ||
|   "$STELLA_URL/registry/stella-cli:latest" \
 | ||
|     scan --sbom "/${SBOM_FILE}" "$IMAGE"
 | ||
| ```
 | ||
| 
 | ||
| The CLI returns **exit 0** if policies pass, **>0** if blocked — perfect for failing the job.
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 3 · CI templates
 | ||
| 
 | ||
| Below are minimal, cut‑and‑paste snippets.
 | ||
| **Feel free to delete Option B** if you adopt Option A.
 | ||
| 
 | ||
| ### 3.1 Jenkins (Declarative Pipeline)
 | ||
| 
 | ||
| ```groovy
 | ||
| pipeline {
 | ||
|   agent { docker { image 'docker:25' args '--privileged' } }  // gives us /usr/bin/docker
 | ||
|   environment {
 | ||
|     STELLA_URL = 'stella-ops.ci.acme.example'
 | ||
|     DOCKER_HOST = 'tcp://docker:2375'
 | ||
|     IMAGE = "acme/backend:${env.BUILD_NUMBER}"
 | ||
|     SBOM_FILE = "acme_backend_${env.BUILD_NUMBER}-${new Date().format('yyyyMMdd\'T\'HHmmss\'Z\'', TimeZone.getTimeZone('UTC'))}.sbom.json"
 | ||
|   }
 | ||
|   stages {
 | ||
|     stage('Build image + SBOM (Option A)') {
 | ||
|       steps {
 | ||
|         sh '''
 | ||
|           docker build \
 | ||
|             --build-arg STELLA_SBOM_BUILDER="$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|             --build-arg SBOM_FILE="$SBOM_FILE" \
 | ||
|             -t "$IMAGE" .
 | ||
|         '''
 | ||
|       }
 | ||
|     }
 | ||
|     /* ---------- Option B fallback (when you must keep the existing build step as‑is) ----------
 | ||
|     stage('SBOM builder (Option B)') {
 | ||
|       steps {
 | ||
|         sh '''
 | ||
|           docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|             -v "$WORKSPACE:/workspace" \
 | ||
|             "$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|               build --image "$IMAGE" --output "/workspace/${SBOM_FILE}"
 | ||
|         '''
 | ||
|       }
 | ||
|     }
 | ||
|     ------------------------------------------------------------------------------------------ */
 | ||
|     stage('Scan & upload') {
 | ||
|       steps {
 | ||
|         sh '''
 | ||
|           docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|             -v "$WORKSPACE/${SBOM_FILE}:/${SBOM_FILE}:ro" \
 | ||
|             -e STELLA_OPS_URL="https://$STELLA_URL" \
 | ||
|             "$STELLA_URL/registry/stella-cli:latest" \
 | ||
|               scan --sbom "/${SBOM_FILE}" "$IMAGE"
 | ||
|         '''
 | ||
|       }
 | ||
|     }
 | ||
|   }
 | ||
| }
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ### 3.2 CircleCI `.circleci/config.yml`
 | ||
| 
 | ||
| ```yaml
 | ||
| version: 2.1
 | ||
| jobs:
 | ||
|   stella_scan:
 | ||
|     docker:
 | ||
|       - image: cimg/base:stable           # baremetal image with Docker CLI
 | ||
|     environment:
 | ||
|       STELLA_URL: stella-ops.ci.acme.example
 | ||
|       DOCKER_HOST: tcp://docker:2375      # Circle’s “remote Docker” socket
 | ||
|     steps:
 | ||
|       - checkout
 | ||
| 
 | ||
|       - run:
 | ||
|           name: Compute vars
 | ||
|           command: |
 | ||
|             echo 'export IMAGE="acme/backend:${CIRCLE_SHA1}"' >> $BASH_ENV
 | ||
|             echo 'export SBOM_FILE="$(echo acme/backend:${CIRCLE_SHA1} | tr "/:+" "__")-$(date -u +%Y%m%dT%H%M%SZ).sbom.json"' >> $BASH_ENV
 | ||
|       - run:
 | ||
|           name: Build image + SBOM (Option A)
 | ||
|           command: |
 | ||
|             docker build \
 | ||
|               --build-arg STELLA_SBOM_BUILDER="$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|               --build-arg SBOM_FILE="$SBOM_FILE" \
 | ||
|               -t "$IMAGE" .
 | ||
|       # --- Option B fallback (when you must keep the existing build step as‑is) ---
 | ||
|       #- run:
 | ||
|       #    name: SBOM builder (Option B)
 | ||
|       #    command: |
 | ||
|       #      docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|       #        -v "$PWD:/workspace" \
 | ||
|       #        "$STELLA_URL/registry/stella-sbom-builder:latest" \
 | ||
|       #          build --image "$IMAGE" --output "/workspace/${SBOM_FILE}"
 | ||
|       - run:
 | ||
|           name: Scan
 | ||
|           command: |
 | ||
|             docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|               -v "$PWD/${SBOM_FILE}:/${SBOM_FILE}:ro" \
 | ||
|               -e STELLA_OPS_URL="https://$STELLA_URL" \
 | ||
|               "$STELLA_URL/registry/stella-cli:latest" \
 | ||
|                 scan --sbom "/${SBOM_FILE}" "$IMAGE"
 | ||
| workflows:
 | ||
|   stella:
 | ||
|     jobs: [stella_scan]
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ### 3.3 Gitea Actions `.gitea/workflows/stella.yml`
 | ||
| 
 | ||
| *(Gitea 1.22+ ships native Actions compatible with GitHub syntax)*
 | ||
| 
 | ||
| ```yaml
 | ||
| name: Stella Scan
 | ||
| on: [push]
 | ||
| 
 | ||
| jobs:
 | ||
|   stella:
 | ||
|     runs-on: ubuntu-latest
 | ||
|     env:
 | ||
|       STELLA_URL: ${{ secrets.STELLA_URL }}
 | ||
|       DOCKER_HOST: tcp://docker:2375       # provided by the docker:dind service
 | ||
|     services:
 | ||
|       docker:
 | ||
|         image: docker:dind
 | ||
|         options: >-
 | ||
|           --privileged
 | ||
|     steps:
 | ||
|       - uses: actions/checkout@v4
 | ||
| 
 | ||
|       - name: Compute vars
 | ||
|         id: vars
 | ||
|         run: |
 | ||
|           echo "IMAGE=ghcr.io/${{ gitea.repository }}:${{ gitea.sha }}" >> $GITEA_OUTPUT
 | ||
|           echo "SBOM_FILE=$(echo ghcr.io/${{ gitea.repository }}:${{ gitea.sha }} | tr '/:+' '__')-$(date -u +%Y%m%dT%H%M%SZ).sbom.json" >> $GITEA_OUTPUT
 | ||
| 
 | ||
|       - name: Build image + SBOM (Option A)
 | ||
|         run: |
 | ||
|           docker build \
 | ||
|             --build-arg STELLA_SBOM_BUILDER="${STELLA_URL}/registry/stella-sbom-builder:latest" \
 | ||
|             --build-arg SBOM_FILE="${{ steps.vars.outputs.SBOM_FILE }}" \
 | ||
|             -t "${{ steps.vars.outputs.IMAGE }}" .
 | ||
| 
 | ||
|       # --- Option B fallback (when you must keep the existing build step as‑is) ---
 | ||
|       #- name: SBOM builder (Option B)
 | ||
|       #  run: |
 | ||
|       #    docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|       #      -v "$(pwd):/workspace" \
 | ||
|       #      "${STELLA_URL}/registry/stella-sbom-builder:latest" \
 | ||
|       #        build --image "${{ steps.vars.outputs.IMAGE }}" --output "/workspace/${{ steps.vars.outputs.SBOM_FILE }}"
 | ||
| 
 | ||
|       - name: Scan
 | ||
|         run: |
 | ||
|           docker run --rm -e DOCKER_HOST="$DOCKER_HOST" \
 | ||
|             -v "$(pwd)/${{ steps.vars.outputs.SBOM_FILE }}:/${{ steps.vars.outputs.SBOM_FILE }}:ro" \
 | ||
|             -e STELLA_OPS_URL="https://${STELLA_URL}" \
 | ||
|             "${STELLA_URL}/registry/stella-cli:latest" \
 | ||
|               scan --sbom "/${{ steps.vars.outputs.SBOM_FILE }}" "${{ steps.vars.outputs.IMAGE }}"
 | ||
| ```
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ## 4 · Troubleshooting cheat‑sheet
 | ||
| 
 | ||
| | Symptom                               | Root cause                  | First things to try                                             |
 | ||
| | ------------------------------------- | --------------------------- | --------------------------------------------------------------- |
 | ||
| | `no such host $STELLA_URL`            | DNS typo or VPN outage      | `ping $STELLA_URL` from runner                                  |
 | ||
| | `connection refused` when CLI uploads | Port 443 blocked            | open firewall / check ingress                                   |
 | ||
| | `failed to stat /<sbom>.json`         | SBOM wasn’t produced        | Did Option A actually run builder? If not, enable Option B      |
 | ||
| | `registry unauthorized`               | Runner lacks registry creds | `docker login $STELLA_URL/registry` (store creds in CI secrets) |
 | ||
| | Non‑zero scan exit                    | Blocking vuln/licence       | Open project in Ops UI → triage or waive                        |
 | ||
| 
 | ||
| ---
 | ||
| 
 | ||
| ### Change log
 | ||
| 
 | ||
| * **2025‑08‑04** – Variable clean‑up, removed Docker‑socket & cache mounts, added Jenkins / CircleCI / Gitea examples, clarified Option B comment.
 |