| 47 |
47 |
|
TARGET_BUS=nvme ./run-vm.sh live # a target that reports `tran: nvme`
|
| 48 |
48 |
|
python3 install_preseeded.py # install from a medium with an answer sheet
|
| 49 |
49 |
|
|
|
50 |
+ |
`KEYBOARD` decides what keyboards the machine has, and exists because the USB
|
|
51 |
+ |
gate's bench tests need hardware no machine here can be. `usr/bin/alloy-usb-gate`
|
|
52 |
+ |
suspends enforcement whenever the count of `ID_INPUT_KEYBOARD` devices reaches
|
|
53 |
+ |
zero, and every physical machine in this tree has an i8042 keyboard soldered to
|
|
54 |
+ |
it that USBGuard has no jurisdiction over, so the count never reaches zero on
|
|
55 |
+ |
real hardware. `ps2` is q35's own i8042 controller and the default. `none` is
|
|
56 |
+ |
`i8042=off`: no keyboard of any kind, which is the machine the gate is supposed
|
|
57 |
+ |
to open for at boot. `usb` is `i8042=off` plus a USB keyboard on an xHCI
|
|
58 |
+ |
controller, so the only keyboard is one USBGuard can take away; its `id=kbd0` is
|
|
59 |
+ |
what `qmp.py cmd device_del '{"id": "kbd0"}'` unplugs, which is the third bench
|
|
60 |
+ |
test and cannot be done from inside the guest.
|
|
61 |
+ |
|
|
62 |
+ |
KEYBOARD=none ./run-vm.sh installed # zero keyboards
|
|
63 |
+ |
KEYBOARD=usb ./run-vm.sh installed # one, and it is removable
|
|
64 |
+ |
|
| 50 |
65 |
|
`TARGET_BUS` decides how the target disk is attached, and it is not a
|
| 51 |
66 |
|
performance knob. A recipe whose disk rule is `single-internal-nvme` cannot be
|
| 52 |
67 |
|
exercised against a virtio disk, whose lsblk `tran` is null, and a rule that
|
| 114 |
129 |
|
then written once in its own syntax with nothing to quote through nushell. Use
|
| 115 |
130 |
|
`sh -c '...'` only when stdin is carrying something else.
|
| 116 |
131 |
|
|
|
132 |
+ |
**`run-vm.sh` re-creates the software TPM whenever swtpm is not running, and
|
|
133 |
+ |
that silently breaks an installed disk that unlocks with one.** The setup call
|
|
134 |
+ |
carries `--overwrite`, and it fires whenever `state/tpm/swtpm-sock` is absent —
|
|
135 |
+ |
which is every time the swtpm daemon has been stopped, not only on a first run.
|
|
136 |
+ |
A new TPM has a new seed, so the sealed key in the LUKS header can no longer be
|
|
137 |
+ |
loaded, and the guest asks for the passphrase instead. What it prints is
|
|
138 |
+ |
`Failed to unseal secret using TPM2: State not recoverable` with
|
|
139 |
+ |
`Esys_Load() ... ErrorCode (0x0000018b)` above it, in the journal of the boot
|
|
140 |
+ |
that prompted. It reads like a broken enrolment rather than a harness that
|
|
141 |
+ |
changed the hardware underneath it. Delete `state/tpm/` deliberately to simulate
|
|
142 |
+ |
a cleared TPM; do not let it happen by accident.
|
|
143 |
+ |
|
|
144 |
+ |
**Re-enrolling a TPM2 keyslot takes two commands, not one.**
|
|
145 |
+ |
`systemd-cryptenroll --wipe-slot=tpm2 --tpm2-device=auto <dev>` answers
|
|
146 |
+ |
`This PCR set is already enrolled, executing no operation` and changes nothing,
|
|
147 |
+ |
because it compares against the existing enrolment before it wipes. Wipe and
|
|
148 |
+ |
enrol separately, and read the output: the pair that works prints `Wiped slot N`
|
|
149 |
+ |
and then `New TPM2 token enrolled as key slot N`.
|
|
150 |
+ |
|
|
151 |
+ |
**A machine with `KEYBOARD=none` cannot answer a LUKS passphrase prompt.**
|
|
152 |
+ |
There is no input device for `vm.py type` to reach, so an encrypted target is
|
|
153 |
+ |
unreachable in that mode and the run stalls at the prompt with no way forward.
|
|
154 |
+ |
Bench-testing the gate against an encrypted disk therefore depends on the TPM
|
|
155 |
+ |
path working. `install_drive.py` leaves encryption off by default, and a target
|
|
156 |
+ |
installed that way is the one to use for keyboard-shaped tests.
|
|
157 |
+ |
|
| 117 |
158 |
|
**The medium's ssh installer needs privilege it does not get on its own.**
|
| 118 |
159 |
|
`alloy install` does not escalate, and an ssh session lands unprivileged, so the
|
| 119 |
160 |
|
wizard draws and answers every step and then fails on the first command that
|