GitHub - meigma/blob-argo-cmp: An Argo CD Config Management Plugin (CMP) for verified manifest delivery from OCI blob archives.

4 min read Original article ↗

Blob Argo CMP

An Argo CD Config Management Plugin (CMP) for verified manifest delivery from OCI blob archives.

Overview

Blob CMP integrates with the Meigma Blob system to provide cryptographically verified manifest delivery to Argo CD. The plugin verifies blob archives against configurable policies (Sigstore signatures and/or SLSA provenance) before extracting Kubernetes manifests.

Features

  • Sigstore verification: Verify keyless signatures from GitHub Actions and other OIDC providers
  • SLSA provenance: Validate SLSA build provenance attestations
  • Flexible policy configuration: Per-application inline policies or centralized policy management
  • Combined verification: Require multiple verification types (e.g., both Sigstore AND SLSA)
  • Caching: Built-in caching for tag-to-digest resolution and verified archives

Pushing Manifests

Use blob-action to push Kubernetes manifests to an OCI registry with optional Sigstore signing:

- name: Push manifests
  id: push
  uses: meigma/blob-action@v1
  with:
    action: push
    ref: ghcr.io/${{ github.repository }}/manifests:${{ github.sha }}
    path: ./manifests
    sign: "true"  # Enable Sigstore keyless signing

- name: Generate SLSA attestation
  uses: actions/attest-build-provenance@v2
  with:
    subject-name: ghcr.io/${{ github.repository }}/manifests
    subject-digest: ${{ steps.push.outputs.digest }}
    push-to-registry: true

See the integration test workflow for a complete working example.

Installation

1. Deploy the Policy ConfigMap

The policy ConfigMap configures verification requirements and caching:

kubectl apply -f deploy/policy-configmap.yaml

Edit the ConfigMap to configure your verification policies before applying.

2. (Optional) Deploy the Plugin ConfigMap

The plugin configuration is baked into the Docker image, so this step is only needed if you want to customize the plugin parameters:

kubectl apply -f deploy/configmap.yaml

3. Patch argocd-repo-server

Add the CMP sidecar container to the repo-server deployment:

kubectl patch deployment argocd-repo-server -n argocd --patch-file deploy/patch-repo-server.yaml

Or use kubectl edit to add the sidecar container manually. See the Argo CD CMP documentation for details.

Usage

Note: The plugin is named blob-cmp with version v1.0. When referencing it in Applications, use the combined name blob-cmp-v1.0.

Basic Application (Inline Policy)

apiVersion: argoproj.io/v1alpha1
kind: Application
metadata:
  name: my-app
spec:
  source:
    repoURL: https://github.com/placeholder/repo
    path: .
    plugin:
      name: blob-cmp-v1.0
      parameters:
        - name: blob-ref
          string: "ghcr.io/myorg/manifests:v1.2.3"
        - name: policy
          string: |
            type: sigstore
            sigstore:
              issuer: "https://token.actions.githubusercontent.com"
              subject: "https://github.com/myorg/repo/.github/workflows/release.yaml@refs/heads/main"
  destination:
    server: https://kubernetes.default.svc
    namespace: my-app

Policy Types

Sigstore

Verify keyless signatures:

type: sigstore
sigstore:
  issuer: "https://token.actions.githubusercontent.com"
  subject: "https://github.com/myorg/repo/.github/workflows/release.yaml@refs/heads/main"

SLSA

Verify SLSA provenance:

type: slsa
slsa:
  repository: "myorg/repo"
  branches: ["main"]
  tags: ["v*"]

Combined (All)

Require both Sigstore AND SLSA:

type: all
policies:
  - type: sigstore
    sigstore:
      issuer: "https://token.actions.githubusercontent.com"
      subject: "https://github.com/myorg/repo/.github/workflows/release.yaml@refs/heads/main"
  - type: slsa
    slsa:
      repository: "myorg/repo"
      branches: ["main"]

Combined (Any)

Require Sigstore OR SLSA:

type: any
policies:
  - type: sigstore
    sigstore:
      issuer: "https://issuer1"
      subject: "https://subject1"
  - type: sigstore
    sigstore:
      issuer: "https://issuer2"
      subject: "https://subject2"

None

Skip verification (for internal registries):

Central Policy Configuration

For environments where inline policies are not desired, configure policies centrally in /etc/blob-cmp/policy.yaml:

apiVersion: blob-cmp.meigma.io/v1alpha1
kind: PolicyConfig

# Enable caching for improved performance
cache:
  enabled: true
  directory: /tmp/blob-cache
  refTTL: 5m  # How long to cache tag->digest mappings

# Set to false to reject inline policies
allowInlinePolicy: true

defaults:
  requireVerification: true
  patterns: ["*.yaml", "*.yml", "*.json"]

policies:
  default:
    type: sigstore
    sigstore:
      issuer: "https://token.actions.githubusercontent.com"
      subject: "https://github.com/myorg/configs/.github/workflows/*"

  repositories:
    - match: "ghcr.io/myorg/prod-*"
      policy:
        type: all
        policies:
          - type: sigstore
            sigstore:
              issuer: "https://token.actions.githubusercontent.com"
              subject: "https://github.com/myorg/prod-configs/.github/workflows/release.yaml@refs/heads/main"
          - type: slsa
            slsa:
              repository: "myorg/prod-configs"
              branches: ["main"]

    - match: "internal.registry.io/*"
      policy:
        type: none

Using Central Config (No Inline Policy)

apiVersion: argoproj.io/v1alpha1
kind: Application
spec:
  source:
    plugin:
      name: blob-cmp-v1.0
      parameters:
        - name: blob-ref
          string: "ghcr.io/myorg/manifests:v1.2.3"
        # No policy parameter - uses central config lookup

Parameters

Parameter Required Description
blob-ref Yes OCI reference to blob archive (e.g., ghcr.io/org/repo:tag)
policy No Inline policy YAML. Overrides central config if set.
patterns No Glob patterns for manifest files (default: *.yaml, *.yml, *.json)

Exit Codes

Code Meaning
0 Success
1 Verification failed (policy violation)
2 Configuration error
3 Network/registry error
4 Invalid archive format

Complete Example

The integration test workflow demonstrates a complete end-to-end setup:

  1. Push manifests to GHCR with Sigstore signing using blob-action
  2. Generate SLSA attestation using actions/attest-build-provenance
  3. Deploy Argo CD with the CMP sidecar in a Kind cluster
  4. Create an Application that pulls and verifies the manifests
  5. Verify deployment succeeds with verified manifests
  6. Negative test confirms unsigned images are rejected

The workflow configures a policy requiring both Sigstore AND SLSA verification:

policies:
  repositories:
    - match: "ghcr.io/myorg/*"
      policy:
        type: all
        policies:
          - type: sigstore
            sigstore:
              issuer: "https://token.actions.githubusercontent.com"
              subject: "https://github.com/myorg/repo/.github/workflows/release.yml@refs/heads/main"
          - type: slsa
            slsa:
              repository: "myorg/repo"

Building

# Build binary
make build

# Run tests
make test

# Build Docker image
make docker VERSION=v1.0.0

License

Licensed under either of Apache License, Version 2.0 or MIT license at your option.