Rolling out a workstation fleet from a cloned image
An end-to-end runbook: from preparing the reference image to a per-host
certificate on every production workstation. The scenario applies when a
single Astra SE image is rolled out onto dozens/hundreds of machines (typically
a terminal fleet), and each workstation’s host_id is known only after the
first boot on real hardware.
Companion documents:
- install.md — step-by-step installation of
tessera(performed on the reference machine).- configuration.md — the
config.tomlreference.- cert-issuance.md — certificate structure and issuance.
- operations.md — the operations runbook.
1. Why a bootstrap mode
Section titled “1. Why a bootstrap mode”A tessera certificate is bound to the workstation’s host_id_hash (the
pam_cert_host_binding extension). When a reference image is cloned:
machine_idis identical across all clones (unless reset on first boot);dmi_board_serialis unique to each piece of hardware;hostnameis assigned by the operator/Ansible.
The reference image cannot contain a per-host certificate — it does not exist at
build time. The solution: a bootstrap certificate with a fixed
host_binding = "installation" + a config.toml that resolves host_id to
that same value via [host_identity].sources = ["override"]. Bootstrap passes
auth on any machine deployed from the image. After the first boot the operator
switches the workstation to a real source (dmi_board_serial / machine_id)
and takes a dump — now the real host_id_hash is known, from which the CA
issues the per-host certificate.
2. Preparing the reference image
Section titled “2. Preparing the reference image”The steps are performed once, on the reference machine, before taking the image.
2.1 Installing tessera
Section titled “2.1 Installing tessera”See install.md §1–§9. All sections are performed in full, except the personal USB medium (section 5): instead of the per-user/.p12, a bootstrap chain is placed on the reference machine.
2.2 The bootstrap certificate
Section titled “2.2 The bootstrap certificate”Issued by the CA tools in bootstrap mode (see §6.1). The certificate must contain the extensions:
pam_cert_host_binding = "installation"(a marker string, not a hash);pam_cert_allowed_roles = <service_role>;- the standard
extendedKeyUsage = clientAuth, emailProtection.
emailProtection is required not by tessera but by the stock Astra
validator (openssl CMS_verify) — without this EKU it rejects the chain (see
cert-issuance.md).
2.3 config.toml on the reference machine
Section titled “2.3 config.toml on the reference machine”# /etc/tessera/config.toml (fragment)
[host_identity]sources = ["override"]override = "installation"
[fly_dm_greeter]update_wallpaper = true # see §2.4sources = ["override"] + override = "installation" forces the daemon to
resolve host_id to the string installation on any clone machine — exactly
what is baked into the bootstrap cert.
2.4 Wallpaper banner (optional, recommended on МКЦ-3)
Section titled “2.4 Wallpaper banner (optional, recommended on МКЦ-3)”For the CA admin to issue a per-host certificate, they need each device’s
host_id (§6.2). Normally tessera shows the host_id as an informational
PAM message on the login screen — but the graphical fly-modern greeter under
МКЦ-3 ignores those messages, and the host_id is not visible. The workaround:
the daemon prints the host_id directly onto the JPG background of the login
screen that [background].path in
/etc/X11/fly-dm/fly-modern/settings.ini points at. The mechanics, options,
and diagnostics are in fly-dm-greeter.md; here only what is
done on the reference machine before taking the image.
It is enabled with a single line in config.toml (already added in §2.3):
[fly_dm_greeter]update_wallpaper = trueThe daemon does not edit the theme’s settings.ini — the overlay
(color_overlay), the blur (blur), and the background path stay with the
operator. With a strong overlay or blur enabled the printed text is invisible,
so before taking the image bring settings.ini to a working baseline:
[background]path=/usr/share/wallpapers/fly-default-light.jpgcolor_overlay=0,0,0,30
[background][blur]enable=false2.5 Validating the reference machine
Section titled “2.5 Validating the reference machine”sudo tessera checkIt must return exit 0. On the reference machine the daemon log is expected to
show INFO fly-dm wallpaper update finished (target tessera.fly_dm_greeter)
and host_identity: probe selected with source=Override.
2.6 Taking the image
Section titled “2.6 Taking the image”The standard path (dd, Clonezilla, vSphere template — at the integrator’s
discretion). Before taking it:
- stop
tessera.service(systemctl stop tessera); - clear
/var/lib/tessera/sessions.json(optional, not critical); - do not clear
/etc/machine-id— after switching host_id to the production source (henceforth the flip) it stops being used, but until that moment a consistent override is needed.
3. Rolling a clone out to a production workstation
Section titled “3. Rolling a clone out to a production workstation”The clone boots, the bootstrap chain is in effect — auth works. host_id is
still installation on every machine.
At this stage do not issue per-host certificates:
host_id_hashis not yet known.
4. Flip → production: finish-bootstrap.sh
Section titled “4. Flip → production: finish-bootstrap.sh”The single command the operator runs on each workstation after the first boot:
sudo /usr/share/tessera/finish-bootstrap.sh4.1 What the script does
Section titled “4.1 What the script does”Atomic, single-pass:
- Rewrite
config.toml:[host_identity].sources = ["override"]→["dmi_board_serial", "machine_id"](default);- the
override = "..."line is commented out (#override = "..."); - a copy of the previous config →
/etc/tessera/config.toml.bak.<UTC-ISO8601>.
- Validates the new config:
tessera check. If ERROR — roll back the backup, exit ≠ 0. - Restarts
tessera.service, waits foris-active=activeup to 30 s. - Takes a dump:
tessera dump-host-id --usbwith retries (up to 60 s for the USB to appear, polling every 5 s). Fallback: TSV in/var/lib/tessera/host-ids-<hostname>-<UTC>.tsv.
4.2 Flags
Section titled “4.2 Flags”| Flag | Purpose |
|---|---|
--non-interactive | Skip confirmations. For Ansible. |
--sources "A,B" | Replace the production source list. Or the POST_INSTALL_SOURCES variable. Default: dmi_board_serial,machine_id. |
--no-restart | Rewrite + check only, no restart. For a dry-run. |
--no-dump | Skip step 4. If the operator will take the dump later. |
4.3 Idempotency
Section titled “4.3 Idempotency”The script detects sources = ["override"] in the current config.toml:
- present → runs the full pipeline;
- absent → exit 0 with no changes (the workstation is already flipped).
Safe to re-run in any Ansible rollout.
4.4 TSV dump format
Section titled “4.4 TSV dump format”Columns:
source status hash_hex hash_prefix raw normalized active_under_current_config reasonOne row per known source (not only the configured ones): machine_id,
dmi_board_serial, dmi_system_uuid, dmi_system_serial, hostname, plus
custom_command (if in the config) and always the synthetic override row
(with status=err when the override is not configured). The row with
active_under_current_config=yes is the source the daemon is using right
now. From it the CA admin takes hash_hex.
status ∈ {ok, err}. reason explains err (empty value,
dmi_board_serial = 0 in a VM, custom_command exited 1, etc.).
dump-host-id exits ≠ 0 if all known sources returned empty/error — an
unambiguous “do not issue the certificate until the login is fixed” signal.
5. Returning the USB stick to the reference side
Section titled “5. Returning the USB stick to the reference side”The operator physically brings the USB to the CA admin (or hands over the TSV through a secure channel — these are just hashes, not secrets).
6. The CA side: issuing the per-host certificate
Section titled “6. The CA side: issuing the per-host certificate”6.1 The CA tools
Section titled “6.1 The CA tools”The CA tools (PKI setup, certificate issuance in per-host / wildcard / bootstrap
modes, USB-medium preparation) are not included in the .deb or in this
repository — they must not sit on production workstations. They are shipped
separately; they are kept on the CA machine (HSM/Vault host).
6.2 Issuance
Section titled “6.2 Issuance”The admin reads the active_under_current_config=yes row from the TSV, takes
hash_hex, and issues the per-host certificate with the CA tool.
The certificate receives the extensions:
pam_cert_host_binding = <host_id_hash>(binding to the workstation);pam_cert_allowed_roles = service;pam_cert_max_integrity = <level>if applicable (МКЦ).
6.3 Packing onto the USB
Section titled “6.3 Packing onto the USB”The resulting .p12 is packed onto the operator’s USB stick by the CA tool: the
old .p12s are deleted, the new one is written with permissions 0600, the
medium is unmounted.
Enrollment package (tags + the first bundle). Alongside the per-host .p12,
the CA places an enrollment package on the same returning USB — for a device
that needs tags (group delegation) and/or a role database at rollout. This is
the CA-side contract (format — the device-enrollment change):
- managed (with a server): a signed
manifest.toml(Ed25519) with the device’s tags, the role database, and the CRL pin + the CRL file itself. The signature and the monotonebundle_version(anti-rollback) are the same as forrole-store; tags/roles/CRL are not secret → they travel in the clear (the PIN protects only the.p12). - standalone (without a server): a tags file + role slices under filesystem
permissions (
root:root, dir0755, files0644), unsigned.
The tags/bundle are not secret and grant no access on their own — access is
still through the PIN-protected .p12; Engine does not interpret the tag names
(generic data, handled uniformly without hardcoded keys). A malformed/broken
package → the import is rejected fail-closed, the device stays in its previous
state.
6.4 Tag assignment — the server side
Section titled “6.4 Tag assignment — the server side”A device accepts tags from a trusted source but does not decide them
itself (otherwise the delegation envelope would be bypassed). The
hash_hex → tags mapping is the responsibility of the Control inventory (or the
operator at install): from the TSV dump (hash_hex) the server/operator picks
the device’s tags (region, class, …) and puts them in the signed manifest
(managed) or in the standalone file. An arbitrary local tag config on the device
is not accepted as a source.
7. Returning the USB stick to the workstation
Section titled “7. Returning the USB stick to the workstation”The operator plugs the USB back into the production workstation.
- the bootstrap cert on the stick is erased by step 6.3;
- the per-host cert passes auth →
host_bindingmatcheshost_id_hash; - the bootstrap chain in the trust store remains valid (in case of a repeat flip after a hardware change), but the cert on the USB no longer uses it.
Importing the enrollment package (if present). If the CA placed an
enrollment package on the return (§6.3), import it after finish-bootstrap:
# managed (signed manifest) — the verification key is given by a flagtessera enroll --import /run/media/usb --manifest-pubkey /etc/tessera/ca/manifest.pub# standalone (without a server)tessera enroll --standalone --import /run/media/usbThe import is atomic and idempotent: repeating the same bundle_version is a
no-op, a smaller one is rejected (anti-rollback), a larger one is applied. After
a successful import, tessera check runs automatically; a failure → rollback,
exit ≠ 0 (fail-closed). The report prints host_id (prefix8), the cert serial,
bundle_version, and the mode; a device_enrolled event goes to audit. Without
tags, group-delegated login is rejected (fail-closed), while per-host login by
the cert works.
7.1 Verification on the workstation
Section titled “7.1 Verification on the workstation”journalctl -u tessera -g 'host_identity: probe' -n 20journalctl -u tessera -g 'host_binding' -n 20journalctl -u tessera -g 'device_enrolled' -n 5The first command must show probe selected source=dmi_board_serial (or
whatever is set in --sources), not override. The second —
host_binding match on the next auth session.
8. Troubleshooting
Section titled “8. Troubleshooting”Clone-specific cases (dump-host-id empty, USB does not appear,
active_under_current_config=no, bootstrap cert rejected, repeat flip after a
motherboard swap, wallpaper not updating) — see
troubleshooting.md §7 Clone-image / golden image.
9. Ansible rollout
Section titled “9. Ansible rollout”A minimal playbook fragment:
- name: Finish bootstrap on cloned terminal ansible.builtin.command: cmd: /usr/share/tessera/finish-bootstrap.sh --non-interactive --no-dump register: finish changed_when: "'no changes' not in finish.stdout"
- name: Fetch host_id dump ansible.builtin.command: cmd: tessera dump-host-id --output /tmp/host-ids.tsv changed_when: false
- name: Pull TSV to control node ansible.builtin.fetch: src: /tmp/host-ids.tsv dest: ./host-ids/{{ inventory_hostname }}.tsv flat: trueAfterward the TSV files are aggregated on the CA machine, per-host certificates
are issued in a loop with the CA tool, and the resulting .p12s are distributed
back (over a USB medium or through a secure channel to the workstation).
10. See also
Section titled “10. See also”- install.md §2.4¾ — a short aside about the tooling.
- §2.4 — the wallpaper baseline in detail; on the greeter — fly-dm-greeter.md.
- cert-issuance.md — certificate extensions, per-host vs wildcard vs bootstrap.
- operations.md §2.4 — where this workflow sits in the operations runbook.
- configuration.md — the
[host_identity],[fly_dm_greeter]fields in full.