Skip to content

Turn on instruction signing

For manual deployments, follow the steps below. The Ubuntu .deb setup creates the keys, approves its server request and starts signing automatically.

An instruction is the signed message the server sends a device saying which images to stage and how. This page gives the server the key that signs them. Do it before you onboard a device: until it is done, IOx onboarding reports instruction signing is not initialized. Other instruction-production failures report instruction bootstrap unavailable; check signing status and producer health before retrying.

Before you start

  • The server runs in the layout you chose: Install on one Docker host, Install on separate Docker hosts or Install on Kubernetes.
  • The two root keys exist, each with a passphrase. Keep them with separate holders or in your chosen protected local storage. See Create keys and approve signing requests.
  • The person who holds root key A can run one command. That private key and its passphrase stay with that person.
  • The server has no signing key yet: check with docker exec iris iris-instructions --status. Reuse a working key and stop here; a key from another deployment needs an approved server-state migration.

Start signing instructions

The steps are the same in every layout. Only the way you reach the container changes.

For each approval below, the key holder can use iris-key-setup instead of the ssh-keygen command. Choose Approve server request, then Server signing key. Select iris-online.pub and save the public approval as iris-online-cert.pub. For keys kept on the deployment host, run iris-key-setup --local.

Warning

Copy the public half of the signing key out of the config volume, never from the runtime directory $IRIS_RUN: docker cp cannot read it, so the export reports success and the file never reaches the host.

On one Docker host

1. Generate the online key and copy its public half to the host. It lands in ~/iris-online.pub as one ssh-ed25519 … line.

docker exec iris iris-instructions --generate-online-key
docker cp iris:/etc/iris/instr/signing-key.pub ~/iris-online.pub

2. Hand this command to the person who holds root key A. It prompts for the passphrase and writes ~/iris-online-cert.pub. Wait for that file.

ssh-keygen -q -s ~/iris-custody/root-a -I iris-online -n iris-server \
  -V +0s:+30d ~/iris-online.pub

3. Import the certificate, turn on the producer (the part of the server that stamps each instruction), and read the status back as JSON.

docker cp ~/iris-online-cert.pub iris:/etc/iris/instr/signing-key-cert.pub
docker exec iris iris-instructions --import-certificate \
  /etc/iris/instr/signing-key-cert.pub
docker exec iris iris-instr-key initialize
docker exec iris iris-instructions --status

On separate Docker hosts

Run the same commands on the server host. If the container is not named iris, find its name with docker ps --format '{{.Names}}' and use that.

On Kubernetes

Run the same sequence through kubectl, with /data/config in place of /etc/iris and the server pod's name for <server-pod>. The ssh-keygen line is still the root key holder's to run:

kubectl -n iris exec deployment/iris-seed-server -c iris -- \
  iris-instructions --generate-online-key
kubectl -n iris cp iris/<server-pod>:/data/config/instr/signing-key.pub \
  ~/iris-online.pub -c iris
ssh-keygen -q -s ~/iris-custody/root-a -I iris-online -n iris-server \
  -V +0s:+30d ~/iris-online.pub
kubectl -n iris cp ~/iris-online-cert.pub \
  iris/<server-pod>:/data/config/instr/signing-key-cert.pub -c iris
kubectl -n iris exec deployment/iris-seed-server -c iris -- \
  iris-instructions --import-certificate /data/config/instr/signing-key-cert.pub
kubectl -n iris exec deployment/iris-seed-server -c iris -- \
  iris-instr-key initialize
kubectl -n iris exec deployment/iris-seed-server -c iris -- \
  iris-instructions --status

Verify

Signing is ready when the status reports enabled: true with signing_refused: false and the producer on. Then onboard one device.

Reading the status

A new deployment prints something close to this:

{"enabled":true,"signing_refused":false,"state":"keylist_missing",
 "certificate_days_to_expiry":29,"roots_configured":2,
 "root_ceremony_overdue":"critical","root_quorum_degraded":true,
 "roots_attested_180d":0}
  • state: keylist_missing: the keylist is the signed list of withdrawn keys the server sends to devices. Onboarding works without it; install one for production through the Console root attestation procedure. Complete it independently with both root holders.
  • root_ceremony_overdue, root_quorum_degraded and roots_attested_180d: nobody has attested these root keys yet. Expected on a new deployment.
  • certificate_days_to_expiry: the server stops signing with seven days or fewer left, so a 30-day certificate lasts about three weeks.

Do not read the two booleans alone as readiness: state: invalid or state: error still needs investigation.

Next steps