···556- `teleport` has been upgraded from major version 12 to major version 14. Please see upstream [upgrade instructions](https://goteleport.com/docs/management/operations/upgrading/) and release notes for versions [13](https://goteleport.com/docs/changelog/#1300-050823) and [14](https://goteleport.com/docs/changelog/#1400-092023). Note that Teleport does not officially support upgrades across more than one major version at a time. If you're running Teleport server components, it is recommended to first upgrade to an intermediate 13.x version by setting `services.teleport.package = pkgs.teleport_13`. Afterwards, this option can be removed to upgrade to the default version (14).
557558- The Linux kernel module `msr` (see [`msr(4)`](https://man7.org/linux/man-pages/man4/msr.4.html)), which provides an interface to read and write the model-specific registers (MSRs) of an x86 CPU, can now be configured via `hardware.cpu.x86.msr`.
00
···556- `teleport` has been upgraded from major version 12 to major version 14. Please see upstream [upgrade instructions](https://goteleport.com/docs/management/operations/upgrading/) and release notes for versions [13](https://goteleport.com/docs/changelog/#1300-050823) and [14](https://goteleport.com/docs/changelog/#1400-092023). Note that Teleport does not officially support upgrades across more than one major version at a time. If you're running Teleport server components, it is recommended to first upgrade to an intermediate 13.x version by setting `services.teleport.package = pkgs.teleport_13`. Afterwards, this option can be removed to upgrade to the default version (14).
557558- The Linux kernel module `msr` (see [`msr(4)`](https://man7.org/linux/man-pages/man4/msr.4.html)), which provides an interface to read and write the model-specific registers (MSRs) of an x86 CPU, can now be configured via `hardware.cpu.x86.msr`.
559+560+- There is a new NixOS option when writing NixOS tests `testing.initrdBackdoor`, that enables `backdoor.service` in initrd. Requires `boot.initrd.systemd.enable` to be enabled. Boot will pause in stage 1 at `initrd.target`, and will listen for commands from the `Machine` python interface, just like stage 2 normally does. This enables commands to be sent to test and debug stage 1. Use `machine.switch_root()` to leave stage 1 and proceed to stage 2.
+16
nixos/lib/test-driver/test_driver/machine.py
···1278 def run_callbacks(self) -> None:
1279 for callback in self.callbacks:
1280 callback()
0000000000000000
···1278 def run_callbacks(self) -> None:
1279 for callback in self.callbacks:
1280 callback()
1281+1282+ def switch_root(self) -> None:
1283+ """
1284+ Transition from stage 1 to stage 2. This requires the
1285+ machine to be configured with `testing.initrdBackdoor = true`
1286+ and `boot.initrd.systemd.enable = true`.
1287+ """
1288+ self.wait_for_unit("initrd.target")
1289+ self.execute(
1290+ "systemctl isolate --no-block initrd-switch-root.target 2>/dev/null >/dev/null",
1291+ check_return=False,
1292+ check_output=False,
1293+ )
1294+ self.wait_for_console_text(r"systemd\[1\]:.*Switching root\.")
1295+ self.connected = False
1296+ self.connect()
+93-41
nixos/modules/testing/test-instrumentation.nix
···6with lib;
78let
009 qemu-common = import ../../lib/qemu-common.nix { inherit lib pkgs; };
000000000000000000000000000000000000000000010in
1112{
13000000000000014 config = {
1516- systemd.services.backdoor =
17- { wantedBy = [ "multi-user.target" ];
18- requires = [ "dev-hvc0.device" "dev-${qemu-common.qemuSerialDevice}.device" ];
19- after = [ "dev-hvc0.device" "dev-${qemu-common.qemuSerialDevice}.device" ];
20- script =
21- ''
22- export USER=root
23- export HOME=/root
24- export DISPLAY=:0.0
2526- source /etc/profile
00000000000000000000002728- # Don't use a pager when executing backdoor
29- # actions. Because we use a tty, commands like systemctl
30- # or nix-store get confused into thinking they're running
31- # interactively.
32- export PAGER=
3334- cd /tmp
35- exec < /dev/hvc0 > /dev/hvc0
36- while ! exec 2> /dev/${qemu-common.qemuSerialDevice}; do sleep 0.1; done
37- echo "connecting to host..." >&2
38- stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
39- # The following line is essential since it signals to
40- # the test driver that the shell is ready.
41- # See: the connect method in the Machine class.
42- echo "Spawning backdoor root shell..."
43- # Passing the terminal device makes bash run non-interactively.
44- # Otherwise we get errors on the terminal because bash tries to
45- # setup things like job control.
46- # Note: calling bash explicitly here instead of sh makes sure that
47- # we can also run non-NixOS guests during tests.
48- PS1= exec /usr/bin/env bash --norc /dev/hvc0
49- '';
50- serviceConfig.KillSignal = "SIGHUP";
51- };
5253 # Prevent agetty from being instantiated on the serial device, since it
54 # interferes with the backdoor (writes to it will randomly fail
···104 MaxLevelConsole=debug
105 '';
106107- boot.initrd.systemd.contents."/etc/systemd/journald.conf".text = ''
108- [Journal]
109- ForwardToConsole=yes
110- MaxLevelConsole=debug
111- '';
112-113 systemd.extraConfig = ''
114 # Don't clobber the console with duplicate systemd messages.
115 ShowStatus=no
···122 DefaultTimeoutStartSec=300
123 DefaultDeviceTimeoutSec=300
124 '';
125-126- boot.initrd.systemd.extraConfig = config.systemd.extraConfig;
127128 boot.consoleLogLevel = 7;
129
···6with lib;
78let
9+ cfg = config.testing;
10+11 qemu-common = import ../../lib/qemu-common.nix { inherit lib pkgs; };
12+13+ backdoorService = {
14+ wantedBy = [ "sysinit.target" ];
15+ unitConfig.DefaultDependencies = false;
16+ conflicts = [ "shutdown.target" "initrd-switch-root.target" ];
17+ before = [ "shutdown.target" "initrd-switch-root.target" ];
18+ requires = [ "dev-hvc0.device" "dev-${qemu-common.qemuSerialDevice}.device" ];
19+ after = [ "dev-hvc0.device" "dev-${qemu-common.qemuSerialDevice}.device" ];
20+ script =
21+ ''
22+ export USER=root
23+ export HOME=/root
24+ export DISPLAY=:0.0
25+26+ if [[ -e /etc/profile ]]; then
27+ source /etc/profile
28+ fi
29+30+ # Don't use a pager when executing backdoor
31+ # actions. Because we use a tty, commands like systemctl
32+ # or nix-store get confused into thinking they're running
33+ # interactively.
34+ export PAGER=
35+36+ cd /tmp
37+ exec < /dev/hvc0 > /dev/hvc0
38+ while ! exec 2> /dev/${qemu-common.qemuSerialDevice}; do sleep 0.1; done
39+ echo "connecting to host..." >&2
40+ stty -F /dev/hvc0 raw -echo # prevent nl -> cr/nl conversion
41+ # The following line is essential since it signals to
42+ # the test driver that the shell is ready.
43+ # See: the connect method in the Machine class.
44+ echo "Spawning backdoor root shell..."
45+ # Passing the terminal device makes bash run non-interactively.
46+ # Otherwise we get errors on the terminal because bash tries to
47+ # setup things like job control.
48+ # Note: calling bash explicitly here instead of sh makes sure that
49+ # we can also run non-NixOS guests during tests.
50+ PS1= exec /usr/bin/env bash --norc /dev/hvc0
51+ '';
52+ serviceConfig.KillSignal = "SIGHUP";
53+ };
54+55in
5657{
5859+ options.testing = {
60+61+ initrdBackdoor = lib.mkEnableOption (lib.mdDoc ''
62+ enable backdoor.service in initrd. Requires
63+ boot.initrd.systemd.enable to be enabled. Boot will pause in
64+ stage 1 at initrd.target, and will listen for commands from the
65+ Machine python interface, just like stage 2 normally does. This
66+ enables commands to be sent to test and debug stage 1. Use
67+ machine.switch_root() to leave stage 1 and proceed to stage 2.
68+ '');
69+70+ };
71+72 config = {
7374+ assertions = [
75+ {
76+ assertion = cfg.initrdBackdoor -> config.boot.initrd.systemd.enable;
77+ message = ''
78+ testing.initrdBackdoor requires boot.initrd.systemd.enable to be enabled.
79+ '';
80+ }
81+ ];
08283+ systemd.services.backdoor = backdoorService;
84+85+ boot.initrd.systemd = lib.mkMerge [
86+ {
87+ contents."/etc/systemd/journald.conf".text = ''
88+ [Journal]
89+ ForwardToConsole=yes
90+ MaxLevelConsole=debug
91+ '';
92+93+ extraConfig = config.systemd.extraConfig;
94+ }
95+96+ (lib.mkIf cfg.initrdBackdoor {
97+ # Implemented in machine.switch_root(). Suppress the unit by
98+ # making it a noop without removing it, which would break
99+ # initrd-parse-etc.service
100+ services.initrd-cleanup.serviceConfig.ExecStart = [
101+ # Reset
102+ ""
103+ # noop
104+ "/bin/true"
105+ ];
106107+ services.backdoor = backdoorService;
0000108109+ contents."/usr/bin/env".source = "${pkgs.coreutils}/bin/env";
110+ })
111+ ];
000000000000000112113 # Prevent agetty from being instantiated on the serial device, since it
114 # interferes with the backdoor (writes to it will randomly fail
···164 MaxLevelConsole=debug
165 '';
166000000167 systemd.extraConfig = ''
168 # Don't clobber the console with duplicate systemd messages.
169 ShowStatus=no
···176 DefaultTimeoutStartSec=300
177 DefaultDeviceTimeoutSec=300
178 '';
00179180 boot.consoleLogLevel = 7;
181