Manage the allowlist
Read, derive, and publish signed workload policy through an attested CDS connection, then check the served policy and node propagation.
Use c8s allowlist to admit workloads before deploying them and to update their launch
policy. c8s publishes each change as one complete, operator-signed document. For the
workload model and enforcement boundaries, see
The image allowlist.
Before you begin
Have these inputs available:
- The
c8sCLI, OpenSSL for key generation,sha256sumfor export checks,jqfor grant-only edits, andcraneonPATHwith registry access when deriving or inspecting images. - A reachable Certificate Distribution Service (CDS) endpoint and its independently approved image/configuration pin, obtained through Obtaining launch measurements.
- For writes, the private operator key whose public half the CDS configuration authorizes.
- For derivation,
deploy.json: your Pod, Deployment, StatefulSet, or DaemonSet as JSON, with every application init and main container image pinned to its platform manifest digest. - For propagation checks, authorized access to each node's plugin logs, or a deployment operator who can supply per-node refresh records containing the timestamp, digest, and version. Health-only records cannot establish the document digest.
This guide calls the measured c8s node image baked nodes (--cvm-mode=bare-metal).
Chart-managed nodes use --cvm-mode=gke or --cvm-mode=aks; the chart installs their
plugin into the host. The launch-bundle examples use a directory named bundle.
Reads need no operator credential, but network reads still verify the endpoint. derive,
lint, and inspect-image do not contact CDS; derivation, image inspection, and online lint
can read registries.
The CLI reference lists the command options.
Create the operator credential
For a new CDS configuration, generate a private key and its public PEM:
umask 077
openssl ecparam -name prime256v1 -genkey -noout -out operator.key
openssl ec -in operator.key -pubout -out operator.pubKeep operator.key private. Add operator.pub to the measured c8s-cds/v1 configuration
through Install c8s. CDS verifies the exact
configuration bytes before serving and refuses a configuration with no operator keys.
Changing the authorized key set requires a new measured configuration, corresponding CDS pin,
and the authorized CDS lifecycle procedure. Setting Helm's cds.operatorKeys alone does not
change the external VM's authority.
For baked nodes, c8s node launch-config new authorizes the generated bundle/server.key,
or the existing key supplied through its --operator-key. Use that private key for writes.
The generator's required --cds-allowlist-seed supplies component entries; optional
--tenant-workloads supplies a full c8s.allowlist/v1 document of tenant entries with distinct
names. For chart-managed nodes, generate the component seed using the exact release,
namespace, image tag, values, and component options used for installation.
The measured CDS seed initializes served policy; live edits use the signed commands below. The node's base allowlist independently admits bootstrap components. Removing a served entry cannot revoke an image still admitted by that base. See Seeding and bootstrap.
Reach the allowlist API
Use the external CDS address supplied to installation or launch generation and the pin that binds its image and launch configuration:
URL='https://<CDS_HOST>:<PORT>'
PIN='bundle/cds-pin.json'
c8s allowlist list --url "$URL" --image-policy-file "$PIN"Replace <CDS_HOST> and <PORT> with that CDS endpoint. Set PIN to your retained CDS pin;
bundle/cds-pin.json is the baked-node launch-bundle example, while the installation guide creates
cds-bootstrap/cds-pin.json. The connection verifies Remote-Attestation TLS (RA-TLS).
A successful command prints the served version and workload table.
Alternatively, use a router front door whose discovery document reports
public_tls.mode=cds and whose allowlist route is enabled. Set URL to
https://<ROUTER_HOST>, replacing <ROUTER_HOST> with that front door's hostname, and set
PIN to your approved router policy, such as router-pin.json.
bundle/server.json is suitable only when it names the node identity hosting that router.
The router verifies its connection to CDS against the separate CDS pin.
Allowlist commands also accept --measurements or --measurements-file for a router launch
digest. Use those instead of --image-policy-file, never as a substitute for the complete
CDS image/configuration pin on the direct path. Discovery verification failures are fatal;
WebPKI and ACME router certificates do not provide the discovery binding this CLI requires.
Use the direct CDS path for those front doors. Unpinned HTTPS is refused, including reads.
Plain HTTP requires --insecure, which skips attestation and is for development only.
Keep the selected URL and PIN for the remaining allowlist commands.
Read it
c8s allowlist list --url "$URL" --image-policy-file "$PIN"
c8s allowlist get vllm-llama --url "$URL" --image-policy-file "$PIN"
c8s allowlist export allowlist.json --url "$URL" --image-policy-file "$PIN"
c8s allowlist diff allowlist.json --url "$URL" --image-policy-file "$PIN"Replace vllm-llama with the workload entry's name. list prints the version and a summary;
-o json prints the document without that version heading. get prints one entry as formatted
JSON. export writes the complete canonical bytes, suitable for backup or upload; retain that
file unchanged when using its digest as a policy pin.
diff compares a complete document with live policy; --exit-code returns nonzero for a
reported difference. Its field comparison covers labels and container policies, but omits
secret grants and egress. Inspect those fields in the JSON directly.
Change workload policy
Use apply to replace selected entries while preserving the other entries in the live
document. Each supplied entry replaces its entire previous value, including grants and egress.
The operator key signs the complete resulting canonical document.
Derive the entry
For an application with no non-platform mounts:
c8s allowlist derive vllm-llama deploy.json --env=any --mounts=deny > entry.jsonvllm-llama is the name you choose for this workload. The output is a bare name-keyed map,
which apply accepts. derive pins effective argv, consulting the image's baked entrypoint
and command through crane when needed. It drops injected c8s containers and reports them
on stderr. Re-derive when application images or commands change.
Choose --env=any, --env=deny, or --env-file env.json. The file maps every derived init
and main container name to its environment policy. For a container whose complete launch
environment consists of PATH and HOSTNAME, save this shape as env.json:
{
"<APP_CONTAINER>": {
"policy": "match",
"variables": {
"PATH": { "exact": "<EXPECTED_PATH>" },
"HOSTNAME": { "present": true }
}
}
}Replace <APP_CONTAINER> with its manifest name and <EXPECTED_PATH> with the approved
image/runtime value. Add every other environment name the image and runtime introduce, and
an entry for each other derived container. match requires the complete name set; use
exact for fixed values and present only for values permitted to vary. Set the pod template's
enableServiceLinks: false. See
Environment policy for constraints.
For volumes, replace --mounts=deny with --mounts-file mounts.json, a JSON map covering
every derived init and main container by name. For one container mounting a ConfigMap, save
this as mounts.json:
{
"<APP_CONTAINER>": {
"policy": "exact",
"rules": [{ "destination": "/mnt/c8s-data/config", "kind": "data" }]
}
}Replace <APP_CONTAINER> with the manifest's container name and the destination with its
actual mount path. ConfigMap, Secret, and persistent-volume data mounts belong under
/mnt/c8s-data/; other mount kinds have their own rules. See
Mount policy.
Private mesh credentials belong to injected platform containers; application rules do not
include /etc/c8s/certs.
Lint and review
c8s allowlist lint entry.json --strict
c8s allowlist apply entry.json --dry-run --url "$URL" --image-policy-file "$PIN"Require lint success and review the JSON as well as the displayed diff. Offline lint accepts
the same document or entry-map shapes as apply; --strict makes warnings fatal. It checks
unlaunchable policies, admission widened by an unconstrained duplicate entry, ambiguous or
shadowed entries, and unsafe grants or mount/search-path combinations. A standalone
unconstrained entry need not produce a warning. --online additionally checks digests in their
registries.
The dry run checks collisions with the live document and sends no write.
When replacing an existing entry, retain its intended grants and egress in entry.json.
derive emits only its requested grant fields and has no egress flag.
Publish the change
c8s allowlist apply entry.json \
--url "$URL" --image-policy-file "$PIN" --operator-key operator.keyUse the authorized key from the credential step; for a generated baked-node bundle, substitute
bundle/server.key. C8S_OPERATOR_KEY supplies the key path when --operator-key is absent.
Success prints applied vllm-llama.
add, apply, edit, and delete read the current version and retry conflicts up to five
conditional replacement attempts. Each attempt can resend HTTP requests for 429 or 503
responses. They preserve concurrent changes to other entries; entries named by your operation
take your requested value. upload refuses a conflict so you can rerun it and review the new
diff. If a response is uncertain, the CLI reads back the document; follow its diagnostic and
check served policy before retrying when that read also fails.
Confirm the served policy
c8s allowlist get vllm-llama --url "$URL" --image-policy-file "$PIN"
c8s allowlist export allowlist-after.json --url "$URL" --image-policy-file "$PIN" &&
sha256sum allowlist-after.jsonCheck the intended fields in the export's workloads.vllm-llama, including secrets and
egress; concurrent writes can change the document between get and export. Retain the
unchanged export and the printed SHA-256 hash. Prefix the 64 hexadecimal characters with
sha256: to form the exact document digest used for node propagation.
If the export lacks your intended policy, resolve that change before proceeding.
Each successful replacement advances the CDS version, even when the document bytes match.
A separate list read reports the version at that read; it does not pair a version with this
export. Record each node's version from the same refresh event that names the export digest.
Edit, delete, or replace the document
To edit one entry in $EDITOR and confirm its publication:
c8s allowlist edit vllm-llama \
--url "$URL" --image-policy-file "$PIN" --operator-key operator.keyTo remove the named entry:
c8s allowlist delete vllm-llama \
--url "$URL" --image-policy-file "$PIN" --operator-key operator.keyOn baked nodes (--cvm-mode=bare-metal), replace the full document with an edited
allowlist.json export:
c8s allowlist upload allowlist.json --cvm-mode=bare-metal \
--url "$URL" --image-policy-file "$PIN" --operator-key operator.keyWhen a confirmed edit cannot be published, the CLI reports a saved file for retry with apply
and the same flags.
For chart-managed nodes, use --cvm-mode=gke or --cvm-mode=aks. Those modes require image
labels naming armtls-mesh, nri-image-policy, attestation-api, and nginx.
--cvm-mode=bare-metal omits the two measured host services, nri-image-policy and
attestation-api. Lint errors always block upload;
--strict also blocks warnings. Review component requirements before overriding them with
--require or --force.
add <IMAGE_DIGEST> <IMAGE_REF> creates an unconstrained one-container entry named from the
image basename and digest prefix. <IMAGE_DIGEST> is the sha256: platform manifest digest;
<IMAGE_REF> is the corresponding image reference used as a label. Use it only when every
launch policy may be any; derive and apply provide the pinned policy needed for grants
and egress.
For a custom API client, all mutations use PUT /allowlist with the complete document.
Convert GET's weak ETag W/"N" to the strong numeric If-Match: "N"; a mismatch or invalid
condition, including *, returns 412. Omitting the condition makes the write unconditional.
Only exact canonical request bytes retain the operator token as a document signature.
Use the API contract when implementing a client.
Grant secret paths
Every container in an entry with a secret grant must pin its argv and mounts. Environment
policy may remain any, although lint warns about grants with unpinned environment values
and --strict makes that warning fatal.
For an application consuming injected secret files, its exact mount rules include
{ "destination": "/run/c8s/secrets", "kind": "emptyDir" } plus its actual application mounts.
Containers without such mounts use deny. Proven read-only nodeRootFile rules can qualify;
arbitrary host paths and GPU sockets cannot. Follow
Application secrets for consumer setup and release
diagnosis.
Derive a new entry with a grant
With deploy.json, env.json, and mounts.json prepared as above, including each consuming
container's secret mount, derive the complete entry:
c8s allowlist derive vllm-llama deploy.json \
--env-file env.json --mounts-file mounts.json \
--secret-read '/tenant-a/model/**' > entry.jsonReplace /tenant-a/model/** with the intended absolute secret-store path or subtree.
Then follow Lint and review, publish with apply, and confirm the served
policy. Derivation creates a new entry; use the next procedure to preserve an existing one.
Change only an existing grant
Fetch the current entry and wrap it in the name-keyed map that lint and apply accept.
This example replaces only secrets, retaining all other fields from that read:
c8s allowlist get vllm-llama --url "$URL" --image-policy-file "$PIN" > live-entry.json &&
jq '{"vllm-llama": .} | .["vllm-llama"].secrets = {"policy":"allow","read":["/tenant-a/model/**"]}' \
live-entry.json > grant-entry.json &&
c8s allowlist lint grant-entry.jsonUse your workload name in both the command and JSON key, and replace the read path with its
intended grant. Review grant-entry.json and all lint findings. Coordinate writers to this
same entry: apply replaces its whole value, so a later concurrent edit to that name can be
overwritten. Publish the reviewed file and read it back:
c8s allowlist apply grant-entry.json \
--url "$URL" --image-policy-file "$PIN" --operator-key operator.key &&
c8s allowlist get vllm-llama --url "$URL" --image-policy-file "$PIN"Use apply for grant-only edits: edit treats them as no changes, while apply can print
unchanged and still write the grant. Set the grant's policy to deny, or remove it from the
replacement entry, to deny subsequent release under that entry. Previously delivered secret
bytes remain with their recipients.
Release rechecks current policy against the sandbox's attested admission inventory and needs a unique matching workload with a qualifying grant. The inventory retains admitted launches, including exited or removed containers. Every required main-container policy needs a matching record; matching does not establish current liveness or distinguish init from main lifecycle.
Egress uses the same argv and mount pinning requirements. Review
Egress and workload composition
before adding exits: the sandbox's retained admission records must satisfy every main-container
policy of one unshadowed entry. Foreign additions are refused, and changed or removed exits
require pod recreation. An exit supplies no payload encryption or inspection.
Use apply and read back egress-only edits too.
Check node propagation
Obtain a recent pull loop: allowlist refreshed or initial allowlist pulled from CDS
record for every target node whose digest equals sha256:<EXPORT_SHA256>, replacing
<EXPORT_SHA256> with the hash of the reviewed allowlist-after.json. Record the node,
timestamp, digest, and version from that same event. Inspect later refresh records for
superseding policy. These are operational diagnostics; a separate CDS list version is not
the export's version.
On baked nodes, an administrator with authorized host-local access can run:
sudo journalctl --boot --identifier=nri-image-policy --output=cat
sudo curl --unix-socket /run/nri-image-policy/health.sock \
--silent --show-error --max-time 5 http://localhost/healthzThe baked plugin sends its records to /dev/log with tag nri-image-policy and serves health
on the shown Unix socket. These commands require journalctl, curl, and permission to read
the node journal and socket. If host access is unavailable, obtain the records from your
deployment operator through the administrative channel arranged in the prerequisites.
On chart-managed nodes, the installer deploys the plugin onto the host; its DaemonSet's
plugin-ready logs are not the plugin's refresh records. The generated configuration uses
plugin stdout for logging, so ask the deployment operator for the host runtime's collection
of that stream. Its plugin.health_addr supplies the health address; chart defaults use
the same socket above, derived from nriImagePolicy.hostPaths.runtimeDir and
nriImagePolicy.healthSocketName. If the runtime does not retain the stream, the digest-based
propagation check remains unverified until the operator supplies refresh records.
The health response reports allowlist_version and any allowlist_stall, but carries no
document digest. It supplements the matching log event; it cannot replace that check.
CDS publication alone does not establish that every node has applied the document.
Pull failures retain the previous policy. During a plugin process lifetime, versions below its applied version are rejected; equal versions can be applied again. Restart establishes a new baseline. Rechecking running containers depends on enforcement configuration and can fail to stop a container; a version check alone does not prove revocation. See Enforcement.
Keep any required audit archive separately. CDS serves retained publication objects and signatures; HA pruning targets the newest 1,000 entries and 64 MiB while preserving the current document. Removed objects and signatures become unavailable.
Node policy measurement is optional and Intel TDX-only; AMD SEV-SNP has no policy-measurement
effect. The stock baked image leaves it disabled, and Helm refuses opt-in for node.baked.
Supported enabled configurations require journal/API mounts and retain the old policy if
measurement fails. For that deployment path, follow
Verify a deployment.
Verify who can write
The CDS pin binds the measured configuration's operator keys. Optionally compare its reported
key set with your independently trusted public-key bundle using
c8s cds verify --operator-keys.
That command uses the direct CDS URL and CDS pin even when allowlist operations use a router.
The comparison supplements the image/configuration pin.
Getting an image's digest
Use a platform-specific manifest digest and put the same digest-pinned reference in the
workload manifest. For an amd64 workload, with registry access configured for crane:
crane digest --platform linux/amd64 '<IMAGE_REF>'
c8s allowlist inspect-image '<IMAGE_REPOSITORY>@sha256:<64_HEX_DIGEST>'<IMAGE_REF> is the image tag or reference to resolve; <IMAGE_REPOSITORY> is its repository
without the tag, and <64_HEX_DIGEST> is the returned digest's hexadecimal part. Choose the
platform that matches the node. inspect-image prints the digest and baked entrypoint/command
needed to review argv. A multi-platform index and its platform manifest are different
objects; admission matches the exact digest reported by the runtime.
Automating the allowlist
Give automation a private key already authorized by the measured CDS configuration, using distinct keys per cluster. Keep the key in your credential system and expose it as a protected file only to the release job. Retirement follows the CDS configuration and lifecycle change; editing a Helm value does not retire CDS authority.
For a reviewed entry.json, the endpoint variables above, and that key mounted at
/run/secrets/operator.key:
c8s allowlist lint entry.json --strict &&
c8s allowlist apply entry.json \
--url "$URL" --image-policy-file "$PIN" \
--operator-key /run/secrets/operator.keyRequire command success, read the entry back, and retain the reviewed canonical export, its SHA-256 digest, and matching per-node refresh records. The CLI's request token lasts 60 seconds and binds the method, path, and exact body; the private key remains the authority for future writes.
See also
- The image allowlist — workload policy and enforcement.
- CDS HTTP API → Allowlist — document writes and retained history.
- CLI reference — commands and options.