0123. SecureBoot, UKI and kernel lockdown are typed options that pick the installer, check each node and never change one in place

Status: Accepted; built; hardware-verified 2026-11-05 (the --wipe-mode ISO reinstall awaits) · Date: 2026-11-04 · Design section: Typed hardening options for managed clusters · Touches: 0023, 0076, 0077, 0108

Decided: TalosCluster.spec.security gains secureBoot (mode: Off | Preferred | Required) and kernelLockdown (integrity | confidentiality), both unset by default. (1) The installer image variant follows the mode: Preferred and Required make Ichor use factory.talos.dev/metal-installer-secureboot/<schematic>:<version> wherever Ichor chooses the installer itself (a Talos upgrade, and the default of an approval that names no --install-image); a node that already runs with SecureBoot always gets the SecureBoot installer, whatever the mode says, because an unsigned UKI would not boot there. (2) The only machine-config effect is that requireSecureBoot of security.diskEncryption defaults to true for a TPM key when mode is Required; the UKI command line, and so the lockdown level, is baked into the boot asset and cannot be set from the machine config on a v1.14 contract, so kernelLockdown is checked, not rendered. (3) The controller reads each node's SecurityState and KernelCmdline resources, records them in TalosMachine.status.security, sets the conditions SecureBoot and KernelLockdown on the machine, and a cluster-level condition of the same name summarises the machines. (4) Preferred and kernelLockdown only warn. Required refuses at two places where refusing is possible: the admission webhook refuses enabling Required on a cluster whose approved machines are not all running with SecureBoot unless the cluster carries ichor.maesh.dev/confirm-secureboot-reinstall: "true", and the controller does not push the first configuration to a maintenance-mode machine that reports SecureBoot off. (5) Ichor never converts an installed node: Talos cannot move a GRUB install to UKI/SecureBoot, and the firmware setting and key enrolment are outside Talos, so the answer for an installed node is reset and add one node at a time (the same path as disk encryption), documented in the hardening guide.

Context

ADR 0108 left "SecureBoot and UKI, kernel lockdown" as Not modelled and tied diskEncryption.requireSecureBoot to the TPM key without a way to ask for SecureBoot itself. The user decided on 2026-11-04 that these ship in v0.1.0. What Talos v1.14 says (docs.siderolabs.com/talos/v1.14, read for this ADR; the SecureBoot page is under Bare Metal Platforms):

  • Boot chain. UEFI firmware loads systemd-boot, which loads the Unified Kernel Image; the UKI bundles kernel, initramfs and command line, and both are signed with a key enrolled in the firmware. With SecureBoot on, the UKI ignores command-line arguments passed at boot and uses the embedded ones; changing them means rebuilding the UKI. SecureBoot is not supported in BIOS mode. "There is no support at the moment to upgrade non-UKI (GRUB-based) Talos installation to use UKI/SecureBoot, so a fresh installation is required."
  • Images. Image Factory serves signed SecureBoot assets for Talos 1.5.0 and later: the ISO metal-amd64-secureboot.iso and the installer factory.talos.dev/metal-installer-secureboot/<schematic>:<version>, which is what the machine configuration must name. The stock metal-installer image installs an unsigned UKI. The docs do not say what happens when a SecureBoot node is upgraded with the plain installer; the signature check would fail at the next boot, so Ichor treats the mix as unsafe.
  • Enrolment. The firmware must be in setup mode on the first boot; the keys are enrolled automatically (the if-safe default), on bare metal the operator chooses "Enroll Secure Boot keys: auto" from the boot menu. This is outside Talos's API and Ichor's reach.
  • State. talosctl get securitystate (--insecure in maintenance mode) shows the SecurityState resource (runtime namespace, id securitystate). In machinery v1.14.5 its spec has secureBoot, bootedWithUKI, moduleSignatureEnforced, ukiSigningKeyFingerprint, pcrSigningKeyFingerprint, selinuxState and fipsState. The kernel command line is the KernelCmdline resource (cmdline).
  • Lockdown. What's new in Talos 1.14: SecureBoot images no longer enable lockdown=confidentiality by default; "they now run with lockdown=integrity enabled implicitly, which is the recommended setting for most users. Add lockdown=confidentiality to the kernel command line through the Image Factory if it is required." (pkg/machinery/kernel forces confidentiality only below 1.14.) With UKI the command line is embedded, and machine.install.extraKernelArgs is rejected together with grubUseUKICmdline, which v1.14 sets for UnattendedInstallConfig. Nothing in SecurityState carries the lockdown level.
  • TPM. TPM disk encryption seals to PCR 7 (SecureBoot state and keys) and a signed PCR 11 policy; "the disk will only unlock if SecureBoot remains enabled with the same key set". Switching SecureBoot on an encrypted node therefore also breaks unlock.

Decision

Fields (append-only). spec.security.secureBoot.mode (required inside the struct, enum Off | Preferred | Required) and spec.security.kernelLockdown (enum integrity | confidentiality, optional). Unset or Off renders and behaves exactly as before; the render test proves byte equality for a control plane and a worker.

Installer variant. One function, upgrade.InstallerImage(schematic, version, secureBoot), builds the reference. The Talos upgrade (installerImage in talos_upgrade.go) passes secureBoot true when the node reports SecureBoot, or when the mode is not Off and the node booted from a UKI (bootedWithUKI); a GRUB-installed node keeps the stock installer and its machine condition says it needs a reinstall to get SecureBoot. A mode of Off with a node that runs SecureBoot is reported as a mismatch (the condition), not obeyed, for the safety reason above. The approval default (ApproveMachine, so the CLI and UI alike) fills install.image with the SecureBoot installer for the cluster's schematic and spec.talosVersion when the mode is not Off, the machine names no image and a disk is given; an explicit --install-image is kept, and the TalosMachine webhook warns when Required meets an explicit image whose path does not contain installer-secureboot (Ichor cannot tell a mirror of a custom-key installer from a stock one, so it is a warning, not a refusal). spec.schematic stays the cluster-wide schematic; Ichor builds no schematics and has no Image Factory client, so a lockdown of confidentiality needs the user to create a schematic with extraKernelArgs: [lockdown=confidentiality] and set spec.schematic.id.

Rendered config. diskEncryption.type: tpm with requireSecureBoot unset renders checkSecurebootStatusOnEnroll: true when the mode is Required (an explicit false stays). Nothing else is rendered: no extraKernelArgs, no grubUseUKICmdline. Validation adds: kernelLockdown and secureBoot need nothing from the version contract (the options are about the boot asset), and diskEncryption.requireSecureBoot: true with a secureBoot.mode of Off is admitted with a warning (it will fail on enrolment unless the node is already SecureBoot).

Verification. TalosClient.SecurityState reads the two resources (a missing SecurityState is an error to the caller, a missing KernelCmdline leaves the command line empty). status.security holds secureBoot, bootedWithUKI, moduleSignatureEnforced, ukiSigningKeyFingerprint and kernelLockdown, where the level is derived: the last lockdown= word of the command line, else integrity when SecureBoot is on (the implicit level of the docs above), else none. The derived level has to be confirmed on hardware against /sys/kernel/security/lockdown. It is written on every reconcile that reaches the node, as the schematic is. The machine conditions exist only while the matching option is set. SecureBoot: True when the node runs SecureBoot; False with reason SecureBootOff, NotUKI or ModeOffButSecureBoot otherwise, the message saying what to do. KernelLockdown: True when the level is at least the expected one, False with the observed level otherwise, Unknown while unread. The cluster-level SecureBoot and KernelLockdown conditions are True when every approved machine's condition is True, False naming the machines that are not, Unknown while none has been read; the cluster reconciler recomputes them when a TalosMachine changes.

Refusing and warning. Per the rule that Ichor hardens itself strictly and only warns about the managed cluster, Preferred and kernelLockdown never refuse. For Required: (a) the TalosCluster webhook, when mode becomes Required on a cluster with approved machines that are not all known to run SecureBoot, refuses unless ichor.maesh.dev/confirm-secureboot-reinstall: "true" is set, the message listing the machines and the path; (b) the machine reconciler, before the first configuration of a machine in maintenance mode, reads SecurityState over the insecure API and, when SecureBoot is off, holds the machine with Ready=False reason SecureBootRequired and pushes nothing (the controller cannot make the node SecureBoot; the message says to boot the SecureBoot ISO with the firmware in setup mode). A node that is already installed and without SecureBoot is only reported (SecureBoot=False), never reconfigured or wiped.

Reinstall, honestly. Changing the SecureBoot mode never touches an installed node: the installer image of a running node cannot be changed by configuration (machine.install and UnattendedInstallConfig are install-time-only in pkg/fieldmeta), the firmware keys are not Talos's, and the TPM key is bound to the SecureBoot state. The guide describes the path per node: set the mode and the annotation, ichor node reset NODE --override --confirm NODE, boot the machine from the SecureBoot ISO of the same schematic and Talos version (firmware in setup mode, SecureBoot enabled), approve it again (the default image is now the SecureBoot installer), wait for SecureBoot=True, next node; control planes one at a time with etcd quorum intact and a fresh etcd backup.

Surfaces. The UI's Talos details (System section) gains the SecureBoot, UKI, module-signature and lockdown fields from the same two resources (live read); the machine and cluster detail pages and ichor machine get / ichor cluster get show the conditions as they do every condition. No new CLI command.

Rejected

  • Rendering lockdown= through machine.install.extraKernelArgs. Rejected twice: it is refused with grubUseUKICmdline, which the v1.14 install document sets, and with SecureBoot the UKI ignores boot-time arguments, so the node would not obey it. Only the boot asset can carry it.
  • Calling Image Factory to create a schematic with the lockdown argument. Ichor has no Image Factory client or egress, the controller does not pull installers (ADR 0115), and a schematic is a supply-chain input the user should name. spec.schematic.id already carries it.
  • mode as a boolean. Preferred is the useful state for a mixed fleet and for a first rollout; a boolean would force either nothing or a refusal.
  • Making Required block every reconcile of a non-SecureBoot node. The node cannot be fixed by configuration, and blocking would stop security patches (firewall, audit) reaching exactly the machines that need attention. It is reported and refused at the two points where refusing prevents a mistake.
  • Obeying Off on a SecureBoot node. It would install an unsigned UKI that the firmware refuses at the next boot.
  • Reading /sys/kernel/security/lockdown through the file API. Precise, but a file read per node per reconcile needs a broader Talos role than the COSI reads Ichor already does, and whether the path is readable through Read is unconfirmed. Kept as the hardware check of the derived level.

Risks

  • The derived lockdown level assumes the kernel honours an embedded lockdown= and applies integrity under SecureBoot as the v1.14 notes say; hardware must confirm it.
  • Whether the secureboot installer honours extraKernelArgs of its schematic is not documented (the Image Factory page says kernel arguments are ignored for installer images and apply to ISO and PXE assets); if it does not, confidentiality has to come from a custom-key imager build and KernelLockdown=False will say so.
  • Preferred on a cluster of GRUB-installed nodes changes nothing for them (by design) and the approval default changes the installer for new nodes only.

Needs hardware

Steps, in order of cost:

  1. Negative path on the existing harness (no firmware change, cheap). On a BIOS node of the harness, set spec.security.secureBoot.mode: Required on a cluster without machines, add a machine booted from the stock ISO and approve it: expect Ready=False reason SecureBootRequired and no configuration stored (status.configStoredAt empty). Set Preferred: it is configured, status.security has secureBoot: false, bootedWithUKI: false (a BIOS install is GRUB) and the machine condition SecureBoot=False reason NotUKI; the cluster condition summarises it. With kernelLockdown: integrity, KernelLockdown=False (level none) and the status.security.kernelLockdown value agree with talosctl read /sys/kernel/security/lockdown (to confirm the derived level and that the path is readable). The webhook: with the machine up, Required without the annotation is refused naming the machine.
  2. Positive path (needs a different node spec). Cluster-wide Preferred and kernelLockdown: integrity, one node with firmware.bootloader.efi.secureBoot: false (setup mode), features.smm.enabled: true, persistent EFI variables and devices.tpm.persistent: true, booted from metal-amd64-secureboot.iso of the cluster's schematic and version, approved through ichor machine approve --install-disk ... (check spec.install.image is the metal-installer-secureboot reference), expect secureBoot: true, bootedWithUKI: true, SecureBoot=True, KernelLockdown=True (integrity). Then an ichor upgrade talos to the next patch: the Upgrade call must carry the SecureBoot installer (talosctl events or the machine's installer image) and the node must boot again. With diskEncryption.type: tpm and Required the STATE volume must show the TPM key and enrolment must have checked SecureBoot.
  3. Lockdown confidentiality. An Image Factory schematic with extraKernelArgs: [lockdown=confidentiality], used for the ISO and as spec.schematic: confirm the secureboot installer and the booted UKI both carry it (the risk above) and KernelLockdown=True.

As built

  • pkg/secureboot (the shared rules: installer reference, lockdown level and comparison, machine and cluster conditions), api/v1alpha1 (TalosClusterSecurity.SecureBoot, KernelLockdown, TalosMachineStatus.Security, the condition types and the confirm annotation), pkg/render/security.go (the TPM default), internal/controller/security_state.go (ReadSecurityState, recordSecurity, the first-configuration gate), internal/controller/cluster_security.go (BootSecurityReconciler, its own controller so a machine's status change does not start a discovery sweep), internal/controller/talos_upgrade.go (the variant), internal/webhook/v1alpha1/security_options.go and talosmachine_webhook.go, internal/uiapi (ApproveMachine's default and the details fields, talosdetails.proto fields 13 to 17), ui/src/components/TalosDetails.tsx, docs/guides/hardening.md.
  • Deviations from the text above: the first-configuration hold sets Ready=False with reason SecureBootRequired (there is no separate Provisioned write for a refusal); the CLI has no new command, ichor machine get and ichor cluster get print the conditions and ichor machine approve --install-image documents the default.
  • status.security is recorded on every reconcile that reaches the node, for every cluster, so enabling Required later can be checked against it; the conditions exist only while the option is set (and a SecureBoot condition also appears under mode Off when a node runs SecureBoot).
  • Tests: pkg/secureboot (the rules), pkg/render (TestSecureBootRender: the options alone render the same bytes, Required and the TPM key), api/v1alpha1 (enums), internal/controller (TestReadSecurityState against an in-memory COSI state, the machine reconcile, the maintenance gate, the upgrade variant, the cluster conditions), internal/webhook/v1alpha1 (confirmation, warnings, installer image), internal/uiapi (approval default, details mapping). The hardware steps above were not run.

Amendment 2026-11-05: hardware findings of lane C

Four findings, fixed in the same batch:

  • The fingerprint field. Talos v1.14.5 fills pcrSigningKeyFingerprint of SecurityState and leaves ukiSigningKeyFingerprint empty (both exist in the machinery type, the first one is the one a node reports), so status.security and the UI showed no key. status.security.pcrSigningKeyFingerprint (and talosdetails.proto field 18) now carry it, and the UI row reads "PCR signing key". ukiSigningKeyFingerprint stays, filled if a node ever reports it (CRD fields are append-only); it is documented as normally empty.
  • Reset and add from the ISO. ichor node reset wipes only STATE and EPHEMERAL (ADR 0042), so a machine whose system disk comes first in the boot order boots its installed UKI again and the SecureBoot ISO reinstalls nothing. The reset operation gained spec.node.wipeMode (system-disk or all, ichor node reset --wipe-mode, field 7 of NodeActionRequest): the removal flow then sends Talos a ResetRequest with that Mode and no partitions named, which erases the whole system disk (all also the user disks). Same guards as any reset: --override, the typed machine name. The hardening guide's "Moving an installed cluster" uses --wipe-mode system-disk. Not offered: user-disks (it cannot make a node installable).
  • Rediscovery after a reset is covered in ADR 0108's amendment of the same date.
  • Approving with no sign-in is covered there too.

Needs hardware: ichor node reset --wipe-mode system-disk on the SecureBoot node with the ISO attached, then approval, expecting a fresh install from the ISO (bootedWithUKI, secureBoot true, pcrSigningKeyFingerprint set).

Search the docs

Search pages and headings