Install this skill
npx skills add mukul975/Anthropic-Cybersecurity-SkillsFramework mappings
MITRE ATT&CK
Overview
Cosign is a Sigstore tool for signing, verifying, and attaching metadata to container images and OCI artifacts. It supports both key-based and keyless (OIDC) signing, integrates with Fulcio (certificate authority) and Rekor (transparency log), and enables supply chain security for container images.
When to Use
- When deploying or configuring implementing image provenance verification with cosign capabilities in your environment
- When establishing security controls aligned to compliance requirements
- When building or improving security architecture for this domain
- When conducting security assessments that require this implementation
Prerequisites
- Cosign CLI installed
- Docker or Podman for building images
- OCI-compliant container registry (Docker Hub, GHCR, GCR, ECR)
- OIDC provider account (GitHub, Google, Microsoft) for keyless signing
Installing Cosign
# Install via Go
go install github.com/sigstore/cosign/v2/cmd/cosign@latest
# Install via Homebrew
brew install cosign
# Install via script
curl -O -L "https://github.com/sigstore/cosign/releases/latest/download/cosign-linux-amd64"
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
sudo chmod +x /usr/local/bin/cosign
# Verify installation
cosign versionKey-Based Signing
Generate Key Pair
# Generate cosign key pair (creates cosign.key and cosign.pub)
cosign generate-key-pair
# Generate key pair stored in KMS
cosign generate-key-pair --kms awskms:///alias/cosign-key
cosign generate-key-pair --kms gcpkms://projects/PROJECT/locations/LOCATION/keyRings/KEYRING/cryptoKeys/KEY
cosign generate-key-pair --kms hashivault://transit/keys/cosignSign Image with Key
# Sign an image
cosign sign --key cosign.key ghcr.io/myorg/myapp:v1.0.0
# Sign with annotations
cosign sign --key cosign.key \
-a "build-id=12345" \
-a "git-sha=$(git rev-parse HEAD)" \
ghcr.io/myorg/myapp:v1.0.0Verify Image with Key
# Verify signature
cosign verify --key cosign.pub ghcr.io/myorg/myapp:v1.0.0
# Verify with annotation check
cosign verify --key cosign.pub \
-a "build-id=12345" \
ghcr.io/myorg/myapp:v1.0.0Keyless Signing (OIDC)
Sign with Keyless (Interactive)
# Keyless sign - opens browser for OIDC auth
cosign sign ghcr.io/myorg/myapp:v1.0.0
# The signature, certificate, and Rekor entry are created automaticallySign with Keyless (CI/CD - Non-Interactive)
# GitHub Actions (uses OIDC token automatically)
cosign sign ghcr.io/myorg/myapp:v1.0.0 \
--yes
# With explicit identity token
cosign sign ghcr.io/myorg/myapp:v1.0.0 \
--identity-token=$(cat /var/run/sigstore/cosign/oidc-token) \
--yesVerify Keyless Signature
# Verify by email identity
cosign verify ghcr.io/myorg/myapp:v1.0.0 \
--certificate-identity=builder@example.com \
--certificate-oidc-issuer=https://accounts.google.com
# Verify by GitHub Actions workflow
cosign verify ghcr.io/myorg/myapp:v1.0.0 \
--certificate-identity=https://github.com/myorg/myrepo/.github/workflows/build.yml@refs/heads/main \
--certificate-oidc-issuer=https://token.actions.githubusercontent.com
# Verify with regex matching
cosign verify ghcr.io/myorg/myapp:v1.0.0 \
--certificate-identity-regexp=".*@example.com" \
--certificate-oidc-issuer=https://accounts.google.comAttestations (SLSA Provenance)
Attach SBOM Attestation
# Generate SBOM
syft ghcr.io/myorg/myapp:v1.0.0 -o cyclonedx-json > sbom.cdx.json
# Attach SBOM as attestation
cosign attest --key cosign.key \
--type cyclonedx \
--predicate sbom.cdx.json \
ghcr.io/myorg/myapp:v1.0.0
# Verify attestation
cosign verify-attestation --key cosign.pub \
--type cyclonedx \
ghcr.io/myorg/myapp:v1.0.0Attach Vulnerability Scan Attestation
# Run scan and save results
grype ghcr.io/myorg/myapp:v1.0.0 -o json > vuln-scan.json
# Attach scan results as attestation
cosign attest --key cosign.key \
--type vuln \
--predicate vuln-scan.json \
ghcr.io/myorg/myapp:v1.0.0SLSA Provenance Attestation
# Attach SLSA provenance
cosign attest --key cosign.key \
--type slsaprovenance \
--predicate provenance.json \
ghcr.io/myorg/myapp:v1.0.0
# Verify SLSA provenance
cosign verify-attestation --key cosign.pub \
--type slsaprovenance \
ghcr.io/myorg/myapp:v1.0.0CI/CD Integration
GitHub Actions
name: Sign and Publish
on:
push:
tags: ['v*']
permissions:
contents: read
packages: write
id-token: write # Required for keyless signing
jobs:
build-sign:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v3
- name: Login to GHCR
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and push
id: build
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.ref_name }}
- name: Sign image (keyless)
run: |
cosign sign --yes \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}
- name: Generate and attach SBOM
run: |
syft ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }} -o cyclonedx-json > sbom.json
cosign attest --yes \
--type cyclonedx \
--predicate sbom.json \
ghcr.io/${{ github.repository }}@${{ steps.build.outputs.digest }}Kubernetes Admission Enforcement
Policy Controller (Sigstore)
# Install policy-controller
helm repo add sigstore https://sigstore.github.io/helm-charts
helm install policy-controller sigstore/policy-controller \
--namespace cosign-system --create-namespace# Enforce signed images in namespace
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "ghcr.io/myorg/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://token.actions.githubusercontent.com
subjectRegExp: "https://github.com/myorg/.*"
ctlog:
url: https://rekor.sigstore.devKyverno Integration
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: Enforce
rules:
- name: verify-cosign-signature
match:
any:
- resources:
kinds: ["Pod"]
verifyImages:
- imageReferences:
- "ghcr.io/myorg/*"
attestors:
- entries:
- keyless:
subject: "https://github.com/myorg/*"
issuer: "https://token.actions.githubusercontent.com"
rekor:
url: https://rekor.sigstore.devTransparency Log (Rekor)
# Search Rekor for image signatures
rekor-cli search --email builder@example.com
# Get specific entry
rekor-cli get --uuid <entry-uuid>
# Verify entry inclusion
cosign verify ghcr.io/myorg/myapp:v1.0.0 \
--certificate-identity=builder@example.com \
--certificate-oidc-issuer=https://accounts.google.comBest Practices
- Use keyless signing in CI/CD for automated pipelines
- Sign by digest not by tag for immutable references
- Attach SBOM attestations alongside signatures
- Enforce signatures at admission with policy-controller or Kyverno
- Use OIDC identity verification instead of just key verification
- Store keys in KMS (AWS KMS, GCP KMS, HashiCorp Vault) for key-based signing
- Verify the full chain: signature + certificate + Rekor inclusion
- Include build metadata as annotations on signatures
Source materials
References and resources
Everything below is rendered for inspection. Script files are read-only and never run.
References 3
api-reference.md1.9 KB
API Reference: Implementing Image Provenance Verification with Cosign
Cosign CLI Commands
# Sign image (keyless with OIDC)
cosign sign --yes IMAGE_REF
# Sign with key
cosign sign --key cosign.key IMAGE_REF
# Verify (keyless)
cosign verify --certificate-identity USER --certificate-oidc-issuer ISSUER IMAGE_REF
# Verify with key
cosign verify --key cosign.pub IMAGE_REF
# Attach attestation
cosign attest --predicate sbom.json --type spdxjson IMAGE_REF
# Verify attestation
cosign verify-attestation --type spdxjson IMAGE_REF
# Get signature location
cosign triangulate IMAGE_REFSigstore Components
| Component | Purpose |
|---|---|
| Cosign | Sign and verify images |
| Fulcio | Short-lived certificate authority |
| Rekor | Transparency log |
| policy-controller | Kubernetes admission |
Attestation Types
| Type | Predicate | Use Case |
|---|---|---|
custom |
Custom JSON | General |
spdxjson |
SPDX SBOM | Software bill of materials |
cyclonedxjson |
CycloneDX SBOM | Alt SBOM format |
slsaprovenance |
SLSA Provenance | Build provenance |
vuln |
Vulnerability scan | Scan results |
Kyverno Policy (Kubernetes Admission)
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-images
spec:
validationFailureAction: Enforce
rules:
- name: verify-cosign
match:
any:
- resources: { kinds: [Pod] }
verifyImages:
- imageReferences: ["ghcr.io/org/*"]
attestors:
- entries:
- keyless:
subject: "*@org.com"
issuer: "https://token.actions.githubusercontent.com"References
- Sigstore: https://sigstore.dev/
- Cosign Docs: https://docs.sigstore.dev/cosign/signing/overview/
- SLSA Framework: https://slsa.dev/
- Kyverno Image Verification: https://kyverno.io/docs/writing-policies/verify-images/
standards.md1.3 KB
Standards and References - Image Provenance with Cosign
SLSA Framework (Supply-chain Levels for Software Artifacts)
- Level 1: Documentation of build process
- Level 2: Source version controlled + signed provenance
- Level 3: Hardened build platform, non-falsifiable provenance
- Level 4: Two-party review, hermetic reproducible builds
NIST SP 800-190
- Section 4.1: Image vulnerabilities - Verify image integrity before deployment
- Section 5.1: Image security - Cryptographic signing and verification
Executive Order 14028 (Improving the Nation's Cybersecurity)
- Section 4(e): SBOM requirements for software supply chain
- Section 4(g): Software supply chain security guidelines
Sigstore Components
| Component | Purpose | URL |
|---|---|---|
| Cosign | Container signing/verification | https://github.com/sigstore/cosign |
| Fulcio | Free code signing CA | https://fulcio.sigstore.dev |
| Rekor | Transparency log | https://rekor.sigstore.dev |
| policy-controller | K8s admission enforcement | https://github.com/sigstore/policy-controller |
Compliance Mappings
PCI DSS v4.0
- Req 6.3.2: Develop software securely with integrity verification
SOC 2
- CC8.1: Change management with cryptographic verification
FedRAMP
- SA-12: Supply chain protection
- SI-7: Software, firmware, and information integrity
workflows.md1.2 KB
Workflow - Image Provenance with Cosign
Phase 1: Setup
- Install cosign CLI
- Choose signing method: keyless (recommended) or key-based
- If key-based: generate keys, store private key in KMS
- Configure CI/CD OIDC token for keyless signing
Phase 2: Build Pipeline Integration
- Build container image
- Push to registry (by digest)
- Sign image with cosign (keyless or key-based)
- Generate SBOM with syft
- Attach SBOM as attestation
- Attach vulnerability scan as attestation
Phase 3: Admission Enforcement
- Deploy policy-controller or Kyverno
- Create ClusterImagePolicy requiring signatures
- Test with signed image (should pass)
- Test with unsigned image (should be denied)
- Enable enforcement in production namespaces
Phase 4: Verification
# Manual verification
cosign verify --certificate-identity=CI_IDENTITY \
--certificate-oidc-issuer=ISSUER \
IMAGE@DIGEST
# Verify SBOM attestation
cosign verify-attestation --type cyclonedx \
--certificate-identity=CI_IDENTITY \
--certificate-oidc-issuer=ISSUER \
IMAGE@DIGESTPhase 5: Monitoring
- Check Rekor transparency log for audit trail
- Monitor admission controller deny events
- Alert on unsigned image deployment attempts
Scripts 2
agent.py7.5 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""Agent for verifying container image provenance using Sigstore Cosign."""
import json
import argparse
import subprocess
from datetime import datetime
def run_cosign(args_list):
"""Run a cosign CLI command and return output."""
cmd = ["cosign"] + args_list
result = subprocess.run(cmd, capture_output=True, text=True, timeout=120)
return {
"stdout": result.stdout.strip(),
"stderr": result.stderr.strip(),
"returncode": result.returncode,
}
def verify_image(image_ref, key=None, certificate_identity=None, certificate_oidc_issuer=None):
"""Verify a container image signature with Cosign."""
args = ["verify"]
if key:
args.extend(["--key", key])
elif certificate_identity and certificate_oidc_issuer:
args.extend(["--certificate-identity", certificate_identity,
"--certificate-oidc-issuer", certificate_oidc_issuer])
args.append(image_ref)
result = run_cosign(args)
verified = result["returncode"] == 0
attestations = []
if verified and result["stdout"]:
try:
attestations = json.loads(result["stdout"])
except json.JSONDecodeError:
pass
return {
"image": image_ref,
"verified": verified,
"attestations": attestations if isinstance(attestations, list) else [attestations],
"error": result["stderr"] if not verified else None,
}
def verify_attestation(image_ref, predicate_type, key=None):
"""Verify an in-toto attestation attached to an image."""
args = ["verify-attestation", "--type", predicate_type]
if key:
args.extend(["--key", key])
args.append(image_ref)
result = run_cosign(args)
return {
"image": image_ref,
"predicate_type": predicate_type,
"verified": result["returncode"] == 0,
"output": result["stdout"][:500] if result["stdout"] else None,
"error": result["stderr"] if result["returncode"] != 0 else None,
}
def sign_image(image_ref, key=None, keyless=False):
"""Sign a container image with Cosign."""
args = ["sign"]
if key:
args.extend(["--key", key])
elif keyless:
args.append("--yes")
args.append(image_ref)
result = run_cosign(args)
return {
"image": image_ref,
"signed": result["returncode"] == 0,
"error": result["stderr"] if result["returncode"] != 0 else None,
}
def triangulate_image(image_ref):
"""Get the signature and attestation image references."""
result = run_cosign(["triangulate", image_ref])
return {
"image": image_ref,
"signature_ref": result["stdout"] if result["returncode"] == 0 else None,
}
def audit_registry_images(images_list, key=None, identity=None, issuer=None):
"""Audit multiple container images for valid signatures."""
results = []
for image in images_list:
result = verify_image(image, key=key, certificate_identity=identity,
certificate_oidc_issuer=issuer)
result["severity"] = "INFO" if result["verified"] else "HIGH"
results.append(result)
signed = sum(1 for r in results if r["verified"])
return {
"total_images": len(results),
"signed": signed,
"unsigned": len(results) - signed,
"signing_rate": round(signed / len(results) * 100, 1) if results else 0,
"details": results,
}
def generate_kyverno_policy(image_patterns, key=None, identity=None, issuer=None):
"""Generate Kyverno ClusterPolicy for image verification."""
policy = {
"apiVersion": "kyverno.io/v1",
"kind": "ClusterPolicy",
"metadata": {"name": "verify-image-signatures"},
"spec": {
"validationFailureAction": "Enforce",
"webhookTimeoutSeconds": 30,
"rules": [{
"name": "verify-cosign-signature",
"match": {"any": [{"resources": {"kinds": ["Pod"]}}]},
"verifyImages": [{
"imageReferences": image_patterns,
"attestors": [{
"entries": [{
"keyless": {
"subject": identity or "*",
"issuer": issuer or "https://token.actions.githubusercontent.com",
}
}] if not key else [{"keys": {"publicKeys": key}}]
}],
}],
}],
},
}
return policy
def generate_cosign_ci_workflow(image_ref, registry):
"""Generate GitHub Actions workflow for Cosign signing."""
return f"""name: Sign Container Image
on:
push:
branches: [main]
jobs:
sign:
runs-on: ubuntu-latest
permissions:
contents: read
id-token: write
packages: write
steps:
- uses: actions/checkout@v4
- uses: sigstore/cosign-installer@v3
- name: Login to registry
uses: docker/login-action@v3
with:
registry: {registry}
- name: Build and push
run: |
docker build -t {image_ref} .
docker push {image_ref}
- name: Sign image (keyless)
run: cosign sign --yes {image_ref}
- name: Verify signature
run: cosign verify --certificate-identity-regexp=".*" --certificate-oidc-issuer="https://token.actions.githubusercontent.com" {image_ref}
"""
def main():
parser = argparse.ArgumentParser(description="Cosign Image Provenance Agent")
parser.add_argument("--verify", help="Image to verify")
parser.add_argument("--sign", help="Image to sign")
parser.add_argument("--audit", nargs="+", help="Images to audit for signatures")
parser.add_argument("--key", help="Cosign public key for verification")
parser.add_argument("--identity", help="Certificate identity for keyless verification")
parser.add_argument("--issuer", help="OIDC issuer for keyless verification")
parser.add_argument("--gen-policy", nargs="+", help="Image patterns for Kyverno policy")
parser.add_argument("--gen-workflow", help="Image ref for CI workflow generation")
parser.add_argument("--output", default="cosign_provenance_report.json")
args = parser.parse_args()
report = {"generated_at": datetime.utcnow().isoformat(), "results": {}}
if args.verify:
result = verify_image(args.verify, key=args.key,
certificate_identity=args.identity,
certificate_oidc_issuer=args.issuer)
report["results"]["verification"] = result
status = "VERIFIED" if result["verified"] else "FAILED"
print(f"[+] {args.verify}: {status}")
if args.audit:
result = audit_registry_images(args.audit, key=args.key,
identity=args.identity, issuer=args.issuer)
report["results"]["audit"] = result
print(f"[+] Audit: {result['signed']}/{result['total_images']} signed")
if args.gen_policy:
policy = generate_kyverno_policy(args.gen_policy, key=args.key,
identity=args.identity, issuer=args.issuer)
report["results"]["kyverno_policy"] = policy
print("[+] Kyverno policy generated")
if args.gen_workflow:
workflow = generate_cosign_ci_workflow(args.gen_workflow, "ghcr.io")
report["results"]["workflow"] = workflow
print("[+] CI workflow generated")
with open(args.output, "w") as f:
json.dump(report, f, indent=2, default=str)
print(f"[+] Report saved to {args.output}")
if __name__ == "__main__":
main()
process.py6.0 KB
Display-only source. This catalog never executes bundled scripts.
#!/usr/bin/env python3
"""
Cosign Image Provenance Manager - Sign, verify, and audit container
image signatures using Sigstore Cosign.
"""
import json
import subprocess
import sys
import argparse
from pathlib import Path
def run_cosign(args: list) -> dict:
"""Execute cosign command and return output."""
cmd = ["cosign"] + args
result = subprocess.run(cmd, capture_output=True, text=True)
return {
"stdout": result.stdout,
"stderr": result.stderr,
"returncode": result.returncode,
}
def sign_image(image: str, key: str = None, annotations: dict = None, keyless: bool = False) -> bool:
"""Sign a container image."""
args = ["sign"]
if key:
args.extend(["--key", key])
if keyless:
args.append("--yes")
if annotations:
for k, v in annotations.items():
args.extend(["-a", f"{k}={v}"])
args.append(image)
result = run_cosign(args)
if result["returncode"] == 0:
print(f"Successfully signed: {image}")
return True
else:
print(f"Failed to sign: {result['stderr']}", file=sys.stderr)
return False
def verify_image(image: str, key: str = None, identity: str = None,
issuer: str = None) -> dict:
"""Verify a container image signature."""
args = ["verify"]
if key:
args.extend(["--key", key])
if identity:
args.extend(["--certificate-identity", identity])
if issuer:
args.extend(["--certificate-oidc-issuer", issuer])
args.append(image)
result = run_cosign(args)
verified = result["returncode"] == 0
signatures = []
if verified and result["stdout"].strip():
try:
signatures = json.loads(result["stdout"])
except json.JSONDecodeError:
pass
return {
"image": image,
"verified": verified,
"signatures": signatures,
"error": result["stderr"] if not verified else None,
}
def verify_attestation(image: str, att_type: str, key: str = None,
identity: str = None, issuer: str = None) -> dict:
"""Verify an attestation on a container image."""
args = ["verify-attestation", "--type", att_type]
if key:
args.extend(["--key", key])
if identity:
args.extend(["--certificate-identity", identity])
if issuer:
args.extend(["--certificate-oidc-issuer", issuer])
args.append(image)
result = run_cosign(args)
return {
"image": image,
"type": att_type,
"verified": result["returncode"] == 0,
"output": result["stdout"],
"error": result["stderr"] if result["returncode"] != 0 else None,
}
def audit_images(images: list, key: str = None, identity: str = None,
issuer: str = None) -> list:
"""Audit multiple images for valid signatures."""
results = []
for image in images:
result = verify_image(image, key=key, identity=identity, issuer=issuer)
results.append(result)
return results
def generate_report(audit_results: list) -> str:
"""Generate markdown audit report."""
signed = sum(1 for r in audit_results if r["verified"])
total = len(audit_results)
report = f"""# Image Signature Audit Report
**Total Images:** {total}
**Signed:** {signed}
**Unsigned:** {total - signed}
## Results
| Image | Signed | Signatures | Error |
|-------|--------|------------|-------|
"""
for r in audit_results:
status = "YES" if r["verified"] else "NO"
sig_count = len(r.get("signatures", []))
error = r.get("error", "")[:50] if r.get("error") else "-"
report += f"| `{r['image']}` | {status} | {sig_count} | {error} |\n"
return report
def main():
parser = argparse.ArgumentParser(description="Cosign Image Provenance Manager")
subparsers = parser.add_subparsers(dest="command")
sign_cmd = subparsers.add_parser("sign", help="Sign an image")
sign_cmd.add_argument("image", help="Image reference")
sign_cmd.add_argument("--key", help="Signing key path")
sign_cmd.add_argument("--keyless", action="store_true", help="Use keyless signing")
sign_cmd.add_argument("--annotation", "-a", action="append", help="key=value annotations")
verify_cmd = subparsers.add_parser("verify", help="Verify image signature")
verify_cmd.add_argument("image", help="Image reference")
verify_cmd.add_argument("--key", help="Public key path")
verify_cmd.add_argument("--identity", help="Certificate identity")
verify_cmd.add_argument("--issuer", help="OIDC issuer")
audit_cmd = subparsers.add_parser("audit", help="Audit multiple images")
audit_cmd.add_argument("--images-file", required=True, help="File with image refs (one per line)")
audit_cmd.add_argument("--key", help="Public key path")
audit_cmd.add_argument("--identity", help="Certificate identity")
audit_cmd.add_argument("--issuer", help="OIDC issuer")
audit_cmd.add_argument("--report", help="Output report path")
args = parser.parse_args()
if args.command == "sign":
annotations = {}
if args.annotation:
for a in args.annotation:
k, v = a.split("=", 1)
annotations[k] = v
sign_image(args.image, key=args.key, annotations=annotations,
keyless=args.keyless)
elif args.command == "verify":
result = verify_image(args.image, key=args.key,
identity=args.identity, issuer=args.issuer)
print(json.dumps(result, indent=2))
sys.exit(0 if result["verified"] else 1)
elif args.command == "audit":
images = Path(args.images_file).read_text().strip().split("\n")
results = audit_images(images, key=args.key,
identity=args.identity, issuer=args.issuer)
report = generate_report(results)
if args.report:
Path(args.report).write_text(report)
print(f"Report written to {args.report}")
else:
print(report)
else:
parser.print_help()
if __name__ == "__main__":
main()
Assets 1
template.mdtext/markdown · 1.3 KBKeep exploring