A tool to deploy Confidential Containers (CoCo) applications on Kubernetes.
cococtl is designed primarily for developers to CoCo-fy their applications and test them with Trustee, the Remote Attestation Solution for CoCo. It's not meant for production deployment of CoCo applications.
Read more about CoCo at confidentialcontainers.org.
The tool ships as a single binary (cococtl) and supports two invocation styles:
| Mode | Binary | Invocation |
|---|---|---|
| Standalone CLI | cococtl |
cococtl <command> |
| kubectl plugin | kubectl-coco (symlink) |
kubectl coco <command> |
All commands and flags are identical in both modes. The examples in this README use cococtl; replace with kubectl coco if you prefer the plugin style.
cococtl simplifies the process of transforming regular Kubernetes manifests into CoCo-enabled manifests. It automatically handles:
- RuntimeClass Configuration: Sets the appropriate CoCo runtime
- Secrets Management: Converts K8s secrets to sealed secrets for upload to Trustee KBS via
kbs populate - ImagePullSecrets: Handles private registry credentials with automatic Trustee KBS integration
- InitData Generation: Creates aa.toml, cdh.toml, and policy.rego configurations
- ✅ KBS Management: Deploy in-cluster Trustee KBS or register an external instance; upload resources via
kbs populate - ✅ Automatic Secret Conversion: Detects and converts K8s secrets to sealed format; generates a trustee-secrets.yaml for upload via
kbs populate - ✅ ImagePullSecrets Support: Handles private registry credentials with Trustee KBS integration
- ✅ Secure Access Sidecar: Optional mTLS-secured sidecar for status reporting and secure port forwarding (see sidecar/README.md)
- ✅ Multi-Resource Support: Works with Pod, Deployment, StatefulSet, ReplicaSet, Job, DaemonSet
- ✅ InitData Management: Create, inspect, and validate initdata via the
initdatasubcommand; automatically generated duringapply - ✅ Backup Management: Saves transformed manifests with
-cocosuffix
# Download latest release
OS=$(uname -s | tr '[:upper:]' '[:lower:]')
ARCH=$(uname -m)
if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi
curl -LO "https://github.com/confidential-devhub/cococtl/releases/latest/download/cococtl-${OS}-${ARCH}"
# Install standalone CLI
sudo install -m 0755 cococtl-${OS}-${ARCH} /usr/local/bin/cococtl
# Also install as kubectl plugin (optional)
sudo ln -sf /usr/local/bin/cococtl /usr/local/bin/kubectl-coco
sudo ln -sf /usr/local/bin/cococtl /usr/local/bin/kubectl_complete-coco
# Verify
cococtl --version
kubectl coco --version # if kubectl plugin symlinks were createdSee Installation for detailed options.
Deploy Trustee and create configuration:
cococtl init
# or: kubectl coco initThis creates ~/.kube/coco-config.toml and deploys Trustee to your cluster.
Use --skip-apply to generate the transformed manifest and secrets file without deploying yet:
cococtl apply -f your-app.yaml --skip-apply
# or: kubectl coco apply -f your-app.yaml --skip-applySecrets must be in KBS before the pods start:
cococtl kbs populate -f <app>-trustee-secrets.yaml
# or: kubectl coco kbs populate -f <app>-trustee-secrets.yamlkubectl apply -f your-app-coco.yamlNote: There are some sample manifests under examples folder which you can try.
cococtl applyrunskubectl applyautomatically unless--skip-applyis set. Use--skip-applywhen you need to upload secrets to KBS before the workload starts (recommended for first deployments).
cococtl performs these transformations:
- Sets RuntimeClass to
kata-cc(configurable) - Converts Secrets:
- Detects all secret references (env, envFrom, volumes)
- Creates sealed secrets with
-sealedsuffix - Writes KBS resource references to
<app>-trustee-secrets.yaml(upload withkbs populate) - Updates manifest to use sealed secret names
- Handles ImagePullSecrets:
- Keeps imagePullSecrets in manifest (for CRI-O)
- Writes credentials to
<app>-trustee-secrets.yaml(upload withkbs populate) - Adds KBS URI to initdata CDH configuration
- Falls back to default service account if not specified
- Generates InitData: Creates aa.toml, cdh.toml, policy.rego
- Places Annotations: Correctly adds initdata on pod templates
- Adds Custom Annotations: From your config file
For detailed information, see TRANSFORMATIONS.md.
- Go 1.24+ (for building from source)
- kubectl (for applying manifests)
- Kubernetes cluster with CoCo runtime installed
-
Download the latest release:
OS=$(uname -s | tr '[:upper:]' '[:lower:]') ARCH=$(uname -m) if [ "$ARCH" = "x86_64" ]; then ARCH="amd64"; fi curl -LO "https://github.com/confidential-devhub/cococtl/releases/latest/download/cococtl-${OS}-${ARCH}"
For a specific version:
VERSION=v0.1.0 curl -LO "https://github.com/confidential-devhub/cococtl/releases/download/${VERSION}/cococtl-${OS}-${ARCH}" -
Validate (optional):
curl -LO "https://github.com/confidential-devhub/cococtl/releases/latest/download/cococtl-${OS}-${ARCH}.sha256" echo "$(cat cococtl-${OS}-${ARCH}.sha256)" | sha256sum --check
-
Install:
System-wide (requires sudo):
sudo install -m 0755 cococtl-${OS}-${ARCH} /usr/local/bin/cococtl # Optional: enable as kubectl plugin sudo ln -sf /usr/local/bin/cococtl /usr/local/bin/kubectl-coco sudo ln -sf /usr/local/bin/cococtl /usr/local/bin/kubectl_complete-coco
Or user directory:
mkdir -p ~/.local/bin install -m 0755 cococtl-${OS}-${ARCH} ~/.local/bin/cococtl export PATH=$PATH:~/.local/bin # Add to ~/.bashrc or ~/.zshrc # Optional: enable as kubectl plugin ln -sf ~/.local/bin/cococtl ~/.local/bin/kubectl-coco ln -sf ~/.local/bin/cococtl ~/.local/bin/kubectl_complete-coco
-
Verify:
cococtl --version kubectl coco --version # if kubectl plugin symlinks were created
git clone https://github.com/confidential-devhub/cococtl
cd cococtl
make build
sudo make installmake install installs the cococtl binary and automatically creates the kubectl-coco and kubectl_complete-coco symlinks in $(INSTALL_PATH) (default: /usr/local/bin).
cococtl supports tab completion for bash and zsh.
How completion works — two independent mechanisms:
| What you type | Driven by |
|---|---|
cococtl <TAB> or kubectl-coco <TAB> |
Generated completion script (source once) |
kubectl coco <TAB> |
kubectl calls kubectl_complete-coco directly; needs kubectl's own completion set up |
make install creates the kubectl_complete-coco symlink automatically. If you installed from a release binary, create it manually:
sudo ln -sf /usr/local/bin/cococtl /usr/local/bin/kubectl_complete-cocoStep 1 — Install bash-completion (skip if already done):
# macOS
brew install bash-completion@2
echo '[[ -r "/opt/homebrew/etc/profile.d/bash_completion.sh" ]] && . "/opt/homebrew/etc/profile.d/bash_completion.sh"' >> ~/.bash_profile
source ~/.bash_profile
# Linux (Ubuntu/Debian)
apt-get install bash-completion
# Linux (CentOS/RHEL)
yum install bash-completionStep 2 — Install cococtl completion (covers cococtl and kubectl-coco):
# Current session only
source <(cococtl completion bash)
# Permanent — macOS
cococtl completion bash > $(brew --prefix)/etc/bash_completion.d/cococtl
# Permanent — Linux, system-wide (requires root)
cococtl completion bash | sudo tee /etc/bash_completion.d/cococtl > /dev/null
# Permanent — Linux, current user only (no sudo)
mkdir -p ~/.local/share/bash-completion/completions
cococtl completion bash > ~/.local/share/bash-completion/completions/cococtl
# Then restart your shellStep 3 — Enable kubectl coco <TAB> (if you use the kubectl plugin):
# macOS
kubectl completion bash > $(brew --prefix)/etc/bash_completion.d/kubectl
# Linux, system-wide (requires root)
kubectl completion bash | sudo tee /etc/bash_completion.d/kubectl > /dev/null
# Linux, current user only (no sudo)
kubectl completion bash > ~/.local/share/bash-completion/completions/kubectlVerify the symlink is in PATH:
which kubectl_complete-coco # should resolve to cococtlStep 1 — Enable compinit (skip if already done):
echo "autoload -U compinit; compinit" >> ~/.zshrcStep 2 — Install cococtl completion (covers cococtl and kubectl-coco):
cococtl completion zsh > "${fpath[1]}/_cococtl"Step 3 — Enable kubectl coco <TAB> (if you use the kubectl plugin):
kubectl completion zsh > "${fpath[1]}/_kubectl"Verify the symlink is in PATH, then start a new shell:
which kubectl_complete-coco # should resolve to cococtl
exec zshDeploy Trustee and create configuration (non-interactive by default):
cococtl init
# or: kubectl coco initThis deploys Trustee to your current namespace and creates ~/.kube/coco-config.toml.
Interactive mode:
cococtl init --interactive # or -iWith custom Trustee:
cococtl init --trustee-url https://trustee.example.com:8080The kbs subcommand manages the Trustee Key Broker Service that stores your secrets.
cococtl kbs start --mode k8sDeploys Trustee to the current namespace and saves the admin private key to ~/.kube/coco-kbs-auth. The KBS URL is written to ~/.kube/coco-config.toml for use by subsequent commands.
# With custom namespace
cococtl kbs start --mode k8s --namespace coco-systemcococtl kbs start --mode external --url http://kbs.example.com:8080Records the KBS URL in config without deploying anything. Optionally specify --auth-dir to point at an existing admin key directory.
After cococtl apply generates a *-trustee-secrets.yaml, upload the secrets:
cococtl kbs populate -f app-trustee-secrets.yamlOther input modes:
# From a Kubernetes Secret
cococtl kbs populate --from-k8s-secret my-registry-secret -n my-namespace
# Single file to a specific KBS path
cococtl kbs populate --path default/myapp/password --resource-file /path/to/password.txt
# Direct URL (skips in-cluster port-forward)
cococtl kbs populate --kbs-url http://kbs.example.com:8080 --auth-key /path/to/private.key -f secrets.yamlThe initdata subcommand lets you create, inspect, and validate initdata independently of apply. This is useful for auditing initdata before deployment or generating it for use with external tooling.
Generate the raw initdata TOML from your config and save it to disk:
# From default config, save to ~/.kube/coco-initdata.toml
cococtl initdata create
# With a custom CA certificate (validates cert before embedding)
cococtl initdata create --cacert /path/to/ca.crt
# With a directory of CA certs
cococtl initdata create --capath /etc/ssl/certs
# Custom output path
cococtl initdata create --output /tmp/my-initdata.toml# Show the base64+gzip encoded blob (ready for use as an annotation value)
cococtl initdata dump
# Show the human-readable plaintext TOML
cococtl initdata dump --raw
# Read from a specific file
cococtl initdata dump --file /tmp/my-initdata.tomlThe default output of dump (without --raw) is the value to use for the
io.katacontainers.config.hypervisor.cc_init_data annotation.
# Validate a saved TOML file (checks version, algorithm, required keys, embedded certs)
cococtl initdata validate --file ~/.kube/coco-initdata.toml
# Validate the encoded blob from dump via pipe
cococtl initdata dump | cococtl initdata validateValidation checks:
versionis0.1.0andalgorithmis one ofsha256,sha384,sha512- Required keys
aa.tomlandcdh.tomlare present (policy.regois optional) - Embedded certificates must be CA certificates (
CA:TRUE,keyCertSign); rejected: leaf/non-CA certs, expired or not-yet-valid certs, SHA-1 or MD5 signatures, unknown critical extensions, RSA keys shorter than 1024 bits - All
aa.tomltoken config URLs are consistent withcdh.tomlkbc URL (a warning is printed if any differ)
Basic usage:
cococtl apply -f app.yamlCommon options:
# Only transform, don't apply
cococtl apply -f app.yaml --skip-apply
# Use specific runtime class
cococtl apply -f app.yaml --runtime-class kata-remote
# Add attestation initContainer
cococtl apply -f app.yaml --init-container
# Enable secure access sidecar
cococtl apply -f app.yaml --sidecar
# Disable automatic secret conversion
cococtl apply -f app.yaml --convert-secrets=false
# Use custom config file
cococtl apply -f app.yaml --config /path/to/config.tomlSee TRANSFORMATIONS.md for detailed description on the transformations.
The explain command helps you understand what transformations are applied to your manifests:
# Analyze your manifest
cococtl explain -f your-app.yaml
# View built-in examples
cococtl explain --list-examples
# Learn with interactive examples
cococtl explain --example simple-pod
cococtl explain --example deployment-secrets
cococtl explain --example sidecar-serviceOutput formats:
# Human-readable (default)
cococtl explain -f app.yaml
# Side-by-side diff view
cococtl explain -f app.yaml --format diff
# Markdown for documentation
cococtl explain -f app.yaml --format markdown -o transformations.mdThe explain command provides:
- Educational analysis of each transformation
- Before/after comparisons for secrets, runtime, and initdata
- Learning points explaining why each change is needed
- Interactive examples to explore CoCo concepts
Perfect for learning how CoCo works without making any changes to your cluster.
The secure access sidecar provides mTLS-secured HTTPS access to your CoCo pods.
One-time setup:
cococtl init --enable-sidecarDeploy with sidecar:
# Basic usage
cococtl apply -f app.yaml --sidecar
# Enable port forwarding from primary container
cococtl apply -f app.yaml --sidecar --sidecar-port-forward 8888
# Custom SANs for LoadBalancer or Ingress
cococtl apply -f app.yaml --sidecar \
--sidecar-san-ips=203.0.113.10 \
--sidecar-san-dns=myapp.example.comNote: When --sidecar is enabled, a Kubernetes Service (ClusterIP type) is automatically created with the name <app-name>-sidecar to expose the sidecar's HTTPS port. You can convert it to NodePort or use it with an Ingress for external access.
See sidecar/README.md for detailed configuration and usage.
The configuration file (~/.kube/coco-config.toml) supports:
# Mandatory
trustee_server = 'https://trustee-kbs.default.svc.cluster.local:8080'
runtime_class = 'kata-cc'
# Optional
trustee_ca_cert = '/path/to/ca.crt'
kata_agent_policy = '/path/to/policy.rego'
init_container_image = 'quay.io/fedora/fedora:44'
init_container_cmd = 'curl http://localhost:8006/cdh/resource/default/attestation-status/status'
# Image-related (optional, for CDH [image] section)
container_policy_uri = 'kbs:///default/security-policy/test'
registry_cred_uri = 'kbs:///default/credential/test'
registry_config_uri = 'kbs:///default/registry-configuration/test'
# Custom annotations (optional, only non-empty values applied)
[annotations]
"io.katacontainers.config.runtime.create_container_timeout" = "120"
"io.katacontainers.config.hypervisor.machine_type" = "q35"
# Secure access sidecar (optional)
[sidecar]
enabled = true
image = "ghcr.io/confidential-devhub/coco-sidecar:latest" # Optional: custom sidecar image
https_port = 8443 # Optional: HTTPS port (default: 8443)
forward_port = 8888 # Optional: application port to forward
cpu_limit = "100m" # Optional: CPU limit
memory_limit = "128Mi" # Optional: memory limit
cpu_request = "50m" # Optional: CPU request
memory_request = "64Mi" # Optional: memory requestNote: TLS certificates are auto-generated per-app during cococtl apply --sidecar.
make buildmake testmake cleanApache License 2.0
Contributions are welcome! Please submit issues and pull requests to the repository.