Zero Trust Architecture in Development Pipelines

Zero Trust Architecture in Development Pipelines

Zero trust principles increasingly apply to CI/CD pipelines and development environments. Traditional pipeline security assumes trusted networks and authenticated users have broad access. Zero trust pipelines verify every action, validate every artifact, and assume breach at every stage. This approach significantly reduces the blast radius of compromised credentials or malicious insiders.

# Zero Trust CI/CD Pipeline Configuration
apiVersion: tekton.dev/v1beta1
kind: Pipeline
metadata:
  name: zero-trust-pipeline
spec:
  params:
    - name: repo-url
      type: string
    - name: revision
      type: string
      
  workspaces:
    - name: shared-data
    - name: docker-credentials
    - name: signing-keys
    
  tasks:
    # Verify source integrity
    - name: verify-source
      taskRef:
        name: git-verify-commit-signature
      params:
        - name: url
          value: $(params.repo-url)
        - name: revision
          value: $(params.revision)
      workspaces:
        - name: output
          workspace: shared-data
          
    # Identity verification for each step
    - name: build-container
      taskRef:
        name: buildah
      params:
        - name: IMAGE
          value: $(params.image-name)
        - name: BUILDER_IMAGE
          value: registry.access.redhat.com/ubi8/buildah:latest
      workspaces:
        - name: source
          workspace: shared-data
      runAfter:
        - verify-source
      # Workload identity for fine-grained permissions
      serviceAccountName: build-bot
      
    # Binary authorization
    - name: generate-sbom
      taskRef:
        name: syft-generate-sbom
      params:
        - name: image
          value: $(params.image-name)
      workspaces:
        - name: source
          workspace: shared-data
      runAfter:
        - build-container
        
    # Attestation generation
    - name: create-attestation
      taskRef:
        name: generate-slsa-provenance
      params:
        - name: image
          value: $(params.image-name)
        - name: artifact-uri
          value: $(tasks.build-container.results.IMAGE_URL)
      workspaces:
        - name: source
          workspace: shared-data
        - name: signing-keys
          workspace: signing-keys
      runAfter:
        - generate-sbom
        
    # Policy validation before deployment
    - name: validate-policies
      taskRef:
        name: conftest-verify
      params:
        - name: policies
          value:
            - must-have-sbom
            - must-be-signed
            - no-critical-vulnerabilities
            - approved-base-images
      runAfter:
        - create-attestation
        
    # Deployment with verification
    - name: deploy
      taskRef:
        name: kustomize-deploy
      params:
        - name: manifest-dir
          value: k8s/
        - name: image
          value: $(params.image-name)@$(tasks.build-container.results.IMAGE_DIGEST)
      workspaces:
        - name: source
          workspace: shared-data
      runAfter:
        - validate-policies
      # Separate identity for deployment
      serviceAccountName: deploy-bot
      
---
# Policy enforcement for zero trust
apiVersion: v1
kind: ConfigMap
metadata:
  name: zero-trust-policies
data:
  admission-policy.rego: |
    package kubernetes.admission

    import future.keywords.contains
    import future.keywords.if
    import future.keywords.in

    # Deny unsigned images
    deny[msg] {
        input.request.kind.kind == "Pod"
        container := input.request.object.spec.containers[_]
        not container.image contains "@sha256:"
        msg := sprintf("Container %s uses mutable image tag", [container.name])
    }

    # Require attestations
    deny[msg] {
        input.request.kind.kind == "Pod"
        container := input.request.object.spec.containers[_]
        not has_valid_attestation(container.image)
        msg := sprintf("Container %s lacks required attestation", [container.name])
    }

    # Enforce workload identity
    deny[msg] {
        input.request.kind.kind == "Pod"
        not input.request.object.spec.serviceAccountName
        msg := "Pods must specify a service account"
    }

    # Network policies required
    deny[msg] {
        input.request.kind.kind == "Deployment"
        namespace := input.request.namespace
        not has_network_policy(namespace)
        msg := "Namespace must have NetworkPolicy defined"
    }

    has_valid_attestation(image) {
        attestation := data.attestations[image]
        attestation.signature_verified == true
        attestation.policy_compliance == true
        time.now_ns() < attestation.expires_at
    }

    has_network_policy(namespace) {
        policy := data.kubernetes.networkpolicies[namespace][_]
        policy.spec.podSelector.matchLabels
    }

Workload identity and ephemeral credentials eliminate long-lived secrets in pipelines. Each pipeline stage receives just-in-time credentials with minimal permissions required for that specific task. These credentials expire automatically after use, preventing credential theft or reuse. Cloud providers' workload identity systems integrate seamlessly with Kubernetes service accounts and CI/CD platforms.