Tessera operations runbook
此内容尚不支持你的语言。
For the on-duty Astra Linux SE administrator maintaining a fleet of
machines with tessera installed. This collects what you do on a
shift — grouped by what triggers the operation:
- regular, on a schedule — monitoring (§1), the daily CRL refresh (§2.2), configuration backup (§4);
- event-driven — CA renewal (§2.1), changing a certificate’s scope
(§2.3), rolling out a cloned image (§2.4), rotating
gost-engineafter an Astra upgrade (§5); - during an incident — security incidents, a lost token, a daemon failure: moved into troubleshooting.md (§3).
Where an operation has a deadline or trigger, it is given in the When field. Logs, МКЦ, and the emergency contact are at the end (§6–§8).
1. Monitoring
Section titled “1. Monitoring”The daemon has no separate health file — the liveness signals are: the systemd state of the unit (
Type=notify+sd_notify), the presence of the IPC socket, and journal entries.
1.1 The systemd service
Section titled “1.1 The systemd service”systemctl is-active tesseraExpected: active. Any other value is an alert. The unit runs in
Type=notify mode: systemd itself sees that the daemon is alive and
restarts it per the Restart= policy.
1.2 The socket
Section titled “1.2 The socket”test -S /run/tessera/monitord.sock && echo OK || echo FAIL1.3 The journal
Section titled “1.3 The journal”Fresh daemon errors over the polling interval:
journalctl -u tessera --since '5 min ago' -p err --no-pager -qEmpty output is normal; any line is a reason to look manually.
1.4 Snippet for a Zabbix UserParameter
Section titled “1.4 Snippet for a Zabbix UserParameter”UserParameter=<key>,<command> — one line per key (Zabbix does not
allow a line break):
UserParameter=tessera.active,systemctl is-active tesseraUserParameter=tessera.socket,test -S /run/tessera/monitord.sock && echo 1 || echo 01.5 Snippet for the Prometheus textfile collector
Section titled “1.5 Snippet for the Prometheus textfile collector”/var/lib/node_exporter/textfile_collector/tessera.prom:
# HELP tessera_up 1 if monitord is active.# TYPE tessera_up gaugetessera_up <0|1># HELP tessera_socket_present 1 if the IPC socket exists.# TYPE tessera_socket_present gaugetessera_socket_present <0|1>Update script (cron every 30 s):
#!/usr/bin/env bashset -eUP=$([[ "$(systemctl is-active tessera)" == "active" ]] && echo 1 || echo 0)SOCK=$([[ -S /run/tessera/monitord.sock ]] && echo 1 || echo 0)TMP=$(mktemp){ echo "# HELP tessera_up 1 if monitord is active." echo "# TYPE tessera_up gauge" echo "tessera_up $UP" echo "# HELP tessera_socket_present 1 if the IPC socket exists." echo "# TYPE tessera_socket_present gauge" echo "tessera_socket_present $SOCK"} > "$TMP"mv "$TMP" /var/lib/node_exporter/textfile_collector/tessera.prom2. Certificate and CRL operations
Section titled “2. Certificate and CRL operations”2.1 Renewing the CA certificate
Section titled “2.1 Renewing the CA certificate”When: 6 months before the current CA expires.
How:
- Generate a new CA in an HSM or a protected segment.
- Sign the new CA with the old one (cross-sign) for a smooth transition.
- Distribute the new
chain.pemto every device:- onto USB media (Mode A) — update
certs/chain.pem; - into
/etc/tessera/ca/bundle.pem(via the organization’s apt repository or ansible/puppet).
- onto USB media (Mode A) — update
- Reissue the user certificates with the new CA pair, preserving the
correct
pam_cert_host_bindingandpam_cert_allowed_rolesextensions in them (see cert-issuance.md). - After the full transition — revoke the old CA via CRL and remove it
from
[trust].anchors.
Verification:
openssl x509 -in /etc/tessera/ca/bundle.pem -noout -enddate2.2 Refreshing the CRL
Section titled “2.2 Refreshing the CRL”When: daily, via cron / a systemd timer.
How:
systemd timer (/etc/systemd/system/tessera-crl-update.timer):
[Unit]Description=tessera daily CRL refresh
[Timer]OnCalendar=dailyPersistent=true
[Install]WantedBy=timers.targetService (/etc/systemd/system/tessera-crl-update.service):
[Unit]Description=tessera CRL refresh
[Service]Type=oneshotExecStart=/usr/local/sbin/tessera-crl-fetch/usr/local/sbin/tessera-crl-fetch is a script that downloads the CRL
over a signed HTTP channel or from a CA share and atomically overwrites
/etc/tessera/crl/*.crl.
Verification:
ls -la /etc/tessera/crl/openssl crl -in /etc/tessera/crl/staff.crl -noout -lastupdate -nextupdate2.3 Changing a certificate’s scope
Section titled “2.3 Changing a certificate’s scope”When: when adding/removing a role or a machine from the scope of a specific certificate.
Because authorization is described in the X.509 extensions themselves
(pam_cert_host_binding, pam_cert_allowed_roles), there is no separate
configuration to update. The lifecycle goes through the CA:
- Revoke the current certificate via CRL (the revocation procedure is in troubleshooting.md §8).
- Reissue the certificate with updated lists in the extensions
(
openssl.cnfrecipes are in cert-issuance.md). - Distribute the new certificate to the user’s USB/token.
- Update the CRL on the endpoints (see §2.2).
monitord does not need to re-read the config — the changes take effect
at the next pam_sm_authenticate.
2.4 Rolling out a cloned image
Section titled “2.4 Rolling out a cloned image”When: you have set up one reference workstation, taken an image, and
are rolling it out across the fleet. On each machine the machine_id /
DMI / hostname are unique and differ from the reference.
Full workflow: docs/clone-image.md — bootstrapping
the reference, finish-bootstrap.sh on the clone, per-host certificate
issuance, Ansible rollout, and troubleshooting.
The short outline for the on-duty operator:
- Reference:
[host_identity].sources = ["override"]+ a bootstrap cert withhost_binding = "installation". - Clone → boot → bootstrap auth works.
- On each workstation:
sudo /usr/share/tessera/finish-bootstrap.sh(or Ansible with--non-interactive). Flip + dump the host_id to USB. - The CA admin issues a per-host certificate by the
hash_hexfrom theactive_under_current_config=yesline (with the CA tool; shipped separately, see clone-image.md §6.1). - The USB with the new
.p12comes back to the workstation — bootstrap is no longer used, and the per-host chain is in effect.
3. Actions during incidents
Section titled “3. Actions during incidents”All incidents and troubleshooting are moved into a single reference — docs/troubleshooting.md:
- §8 Security incidents: a compromised cert, a lost token, CA worst-case, DIGSIG
- §2 USB and tokens: USBGuard, ЗПС, a lost/blocked token
- §3 monitord and the daemon: a failed start, an unreachable socket
- §4 The PAM stack and lockout: replay from rescue.target,
Logout requested but session has no logind id
4. Backing up and restoring the configuration
Section titled “4. Backing up and restoring the configuration”4.1 What to back up
Section titled “4.1 What to back up”/etc/tessera/(config, ca/, crl/);/var/lib/tessera/(root-owned policy/enrollment material and persistent daemon state);/etc/pam.d/(with the.bak.*backup copies).
4.2 What NOT to back up
Section titled “4.2 What NOT to back up”/run/tessera/— runtime (the socket,sessions.json,daemon.lock); created by the unit’sRuntimeDirectory=tesseradirective on every daemon start./var/cache/tessera/— reserved for caches, restored at runtime.
4.3 Commands
Section titled “4.3 Commands”Backup:
sudo tar --acls --xattrs -czf /backup/tessera-$(date +%F).tgz \ /etc/tessera /var/lib/tessera /etc/pam.d /backup/tessera-$(date +%F).tgzRestore:
gpg --decrypt /backup/tessera-2026-05-01.tgz.gpg \ | sudo tar -xzC /sudo systemctl reload tessera5. Rotating gost-engine on an Astra upgrade
Section titled “5. Rotating gost-engine on an Astra upgrade”5.1 When
Section titled “5.1 When”After apt upgrade, when the logs indicate an update of the
gost-engine or libgost-engine package.
5.2 What to check
Section titled “5.2 What to check”openssl engine gost -t# Right after the update it should show [ available ].pamtester sudo alice authenticate# An authentication smoke test after the update.5.3 Rollback
Section titled “5.3 Rollback”If the update broke compatibility:
apt install gost-engine=<previous-version>apt-mark hold gost-enginesudo systemctl restart tessera6. Logs: where to look, what to look for
Section titled “6. Logs: where to look, what to look for”6.1 monitord
Section titled “6.1 monitord”sudo journalctl -u tesserasudo journalctl -u tessera -g 'tessera.monitord'The name
tessera.monitordis kept as an operational ABI: it is used by log aggregators and journalctl-filter templates. The binary and unit themselves are namedtessera, but thetracing targetand the Unix-socket path (/run/tessera/monitord.sock) remain historical — renaming them would break the filters in production.
There are no separate targets like tessera.monitord.start /
.removal / .lock: the daemon has a single tessera.monitord target
with free-form message text. The outcome and event details live in the
message text and the key=value fields, not in the target name. The
daemon’s main targets and examples of real messages (verbatim from the
journal):
tessera.monitord— the daemon lifecycle, udev events, the grace window, action dispatch:starting— the daemon starts;grace window expired, dispatching action(fieldserial=…) — the grace window after media removal has expired, the action goes to the action-runner;grace cancelled(serial=…) — the media was reinserted within the grace window, the action is cancelled;session target updated(session_id=…,new_target=…) —pam_sm_open_sessiondelivered the realXDG_SESSION_ID, and the session’s registry entry is updated from the placeholder target toLogindSession.
tessera.mount— mounting and cleanup of stale mountpoints under the mountpoint base.tessera.daemon.singleton— thedaemon.locksingleton lock.tessera.fly_dm_greeter— redrawing the wallpaper banner.tessera.startup_check— startup config validation.role.audit— role-store events (role_deny,role_session_openwith areason=…field); the target has notessera.prefix.
Media removal from a session with no logind id. In 0.4.0 the action
is not “dropped” (there is no USB-removal action dropped line) — it
fails closed by rebooting the host. This is an ERROR line (field
action=Lock or Logout):
ERROR tessera.monitord: ALERT: USB-removal Logout has no logind id; failing closed with reboot session_id=… target=… pam_user=… pam_service=…It is followed by an INFO tip (the text starts with
tip: pam_sm_open_session pushes XDG_SESSION_ID to monitord) saying you
need to fix the pam_systemd.so / pam_tessera.so ordering in the
session phase. The cause analysis and fix are in
troubleshooting.md §4.
6.2 cdylib (the PAM module)
Section titled “6.2 cdylib (the PAM module)”sudo tail -f /var/log/auth.logsudo journalctl -t pam_tesseraThe PAM module writes to syslog (facility
auth) under the process identifierpam_tessera— hence the-t pam_tesserafilter, not-t tessera. On journald hosts the lines are visible both injournalctl -t pam_tesseraand in/var/log/auth.log.
There are no separate targets like tessera.auth.success /
.fail.<reason> or tessera.cert_scope.* — the authentication outcome
and the denial reason live in the message text and the fields
(error=…, reason=…), not in the target name. The module’s main
targets:
tessera.auth— the entry and result ofpam_sm_authenticate:authentication failed(WARN, theerror=…field carries the denial category);host identity unresolved(ERROR,error=…).
tessera.flow— the step-by-step flow trace:usb devices/partitions enumerated(count=…);trying USB candidate(devnode=…,vid=…,pid=…,fs_type=…);candidate mounted(devnode=…,mountpoint=…);no .p12 on this partition, trying next(mountpoint=…,missing=…);cert chain validated;auth result: success (pkcs12)— success of the PKCS#12 path.
tessera.session—pam_sm_open_session/pam_sm_close_session:open_session: running session_open hooks(session_id=…,pam_user=…);close_session: running session_close hooks(session_id=…).
role.audit— a role denial/grant:role_denywith areason=…field (not_found/not_covered/backend_unavailable/mask_exceeds_ceiling/syntax/system_account),role_session_open.
6.3 Useful grep filters
Section titled “6.3 Useful grep filters”# All failed authentications over a day:sudo journalctl -t pam_tessera --since="1 day ago" \ | grep -F 'authentication failed'
# All role denials (the role-store registry):sudo journalctl -t pam_tessera | grep -F 'role_deny'
# USB-removal events that triggered an action:sudo journalctl -u tessera | grep -F 'grace window expired, dispatching action'
# Fail-closed reboots due to a missing logind id:sudo journalctl -u tessera | grep -F 'failing closed with reboot'
# The step-by-step partition-probing trace on multi-partition media:sudo journalctl -t pam_tessera \ | grep -E 'trying USB candidate|candidate mounted|no \.p12 on this partition'
# A specific user's sessions/denials (the role audit):sudo journalctl -t pam_tessera | grep -E 'role_(deny|session_open)' | grep alice6.4 What is not logged (by policy)
Section titled “6.4 What is not logged (by policy)”- PINs and passphrases —
<redacted>. - Full certificate DNs at the
infolevel — only the CN is shown. At thedebuglevel — the full DN. - The full contents of the
pam_cert_host_binding/pam_cert_allowed_rolesX.509 extensions — at theinfolevel only the matched entry is logged; the full list — at thedebuglevel.
7. МКЦ (MAC integrity)
Section titled “7. МКЦ (MAC integrity)”Activating mandatory integrity control is an optional step, performed by
the operator manually after the package is installed. By default the
tessera.service daemon runs as tessera without
CAP_MAC_ADMIN/PARSEC_CAP_CHMAC. Activation is three operator steps:
- install the drop-in
/usr/share/tessera/systemd/mac-integrity.conf.exampleinto/etc/systemd/system/tessera.service.d/; - install the paired PAM stack
/usr/share/tessera/pam.d/tessera.exampleinto/etc/pam.d/tessera(it usespam_parsec_cap.so+pam_parsec_mac.so); - grant the daemon
PARSEC_CAP_CHMACviausercaps -m "+3" tesserapluspdpl-user --ilevel 63 tessera.
The full activation, verification, and rollback procedure is described in docs/install.md §“МКЦ (MAC integrity): optional activation”.
Session state. The sessions.json registry lives on tmpfs
(/run/tessera/sessions.json, RuntimeDirectory=). It is volatile
across reboot — this is by design: the sshd/login/sudo processes holding
these sessions die on reboot anyway. The singleton lock daemon.lock
lives next to sessions.json (fallback —
/var/lib/tessera/daemon/); the daemon’s persistent state is the
wallpaper backup in /var/lib/tessera/daemon/. The parent
/var/lib/tessera/ remains root-owned because it also contains trusted
roles, tags, and enrollment material.
8. Emergency contact
Section titled “8. Emergency contact”For confidential security reports — see the contacts in README.md.