Using Pepr to Enforce Software Supply Chain Security

Ingredients

Recipe

Step 1. Use NPX and the pepr tool to create a Pepr module. In layman's terms, a Pepr module is a self-contained TypeScript project. In the context of Pepr, a Pepr module is a collection of "capabilities." NOTE: the pepr tool will also initialize your TypeScript project as a Git repository.

npx pepr init

If you haven't installed the pepr tool before, NPX will prompt you for permission before installing and then running it.

Need to install the following packages:
pepr@2.0.0
Ok to proceed? (y)

When prompted, press enter values similar to below.

✔ Enter a name for the new Pepr module. This will create a new directory based on the name.
 … attestations
✔ (Recommended) Enter a description for the new Pepr module.
 … Block pod creation requests that don't have container attestations.
✔ How do you want Pepr to handle errors encountered during K8s operations?
 … Reject the operation
✔ Enter a unique identifier for the new Pepr module.
 … 1

Step 2. Change directories to the Pepr module you just created. The main entrypoint to the Pepr module is the pepr.ts file.

cd attestations

Step 3. Replace the content of the pepr.ts file located in the root of your Pepr module with the code below.

import { PeprModule } from "pepr";
import cfg from "./package.json";
import { Attestations } from "./capabilities/attestations";

new PeprModule(cfg, [
  Attestations,
]);

Step 4. Create a file called attestations.ts in the capabilities folder and add the content below to it.

import {
  Capability,
  a,
} from "pepr";

export const Attestations = new Capability({
  name: "attestations",
  description: "Block pod creation requests that don't have container attestations.",
  namespaces: [],
});

const { When } = Attestations;

When(a.Namespace)
  .IsCreated()
  .Mutate(ns => ns.RemoveLabel("remove-me"));

Step 5. Run the command below to deploy your Pepr module onto your Kubernetes cluster.

npx pepr deploy

Step 6. Change directories to your previous working directory.

cd ..

Step 7. Create a file called sonic-k8s-manifest.yaml and add the content below to it.

# sonic-k8s-manifest.yaml

---
apiVersion: apps/v1
kind: Deployment
metadata:
  namespace: sonic
  name: sonic
  labels:
    app.kubernetes.io/component: frontend
    remove-me: please
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/component: frontend
  template:
    metadata:
      labels:
        app.kubernetes.io/component: frontend
    spec:
      containers:
        - name: frontend
          image: dazdaz/sonic:latest
          imagePullPolicy: Always
          ports:
            - containerPort: 8080

---
apiVersion: v1
kind: Service
metadata:
  namespace: sonic
  name: frontend
  labels:
    app.kubernetes.io/component: frontend
spec:
  type: ClusterIP
  selector:
    app.kubernetes.io/component: frontend
  ports:
    - port: 8080

Step 8. Create a file called sonic-uds-package.yaml and add the content below to it.

# sonic-uds-package.yaml

---
apiVersion: uds.dev/v1alpha1
kind: Package
metadata:
  namespace: sonic
  name: sonic
spec:
  network:
    expose:    
      - service: frontend
        selector:
          app.kubernetes.io/component: frontend
        host: sonic
        port: 8080
  sso:
    - name: Sonic
      clientId: sonic
      redirectUris:
        - https://sonic.uds.dev/login
      enableAuthserviceSelector:
        app.kubernetes.io/component: frontend

Step 9. Create a file called zarf.yaml and add the content below to it.

# zarf.yaml

---
kind: ZarfPackageConfig
metadata:
  name: sonic
  version: 0.1.0
  annotations:
    dev.uds.title: Sonic
components:
  - name: sonic-container-image
    required: true
    images:
      - docker.io/dazdaz/sonic:latest
  - name: sonic-k8s-manifest
    required: true
    manifests:
      - name: sonic-k8s-manifest
        namespace: sonic
        files:
          - sonic-k8s-manifest.yaml
  - name: sonic-uds-manifest
    required: true
    manifests:
      - name: sonic-uds-manifest
        namespace: sonic
        files:
          - sonic-uds-package.yaml

Step 10. Create a file called Makefile and add the content below to it. NOTE: this specific Makefile has hardcoded values for the Zarf package, name, architecture, and version.

# ---------------------------------------------------------
# Set the default target.
# ---------------------------------------------------------

.DEFAULT_GOAL := create-and-deploy

# ---------------------------------------------------------
# Create and deploy the Zarf package.
# ---------------------------------------------------------

.PHONY: create-and-deploy
.SILENT: create-and-deploy

create-and-deploy: remove
    uds zarf package create --confirm &&\
    uds zarf package deploy zarf-package-sonic-amd64-0.1.0.tar.zst --confirm

# ---------------------------------------------------------
# Remove the Zarf package.
# ---------------------------------------------------------

.PHONY: remove
.SILENT: remove

remove: 
    uds zarf package remove sonic --confirm || true
    uds zarf tools kubectl delete namespace sonic || true

Step 11. Create and deploy your Zarf package.

Make

Cleaning Up

When you're done, remove your Zarf package from your Kubernetes cluster and then delete the .zst file in your current working directory.

make remove