Skip to content

Commit 8b3baa1

Browse files
authored
nixos/test-driver: add backdoor based on systemd-ssh-proxy & AF_VSOCK (#392030)
2 parents 907e98d + 8869265 commit 8b3baa1

File tree

7 files changed

+100
-4
lines changed

7 files changed

+100
-4
lines changed

nixos/doc/manual/development/running-nixos-tests-interactively.section.md

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,35 @@ using:
6363
Once the connection is established, you can enter commands in the socat terminal
6464
where socat is running.
6565

66+
## SSH Access for test machines {#sec-nixos-test-ssh-access}
67+
68+
An SSH-based backdoor to log into machines can be enabled with
69+
70+
```nix
71+
{
72+
name = "…";
73+
nodes.machines = { /* … */ };
74+
sshBackdoor.enable = true;
75+
}
76+
```
77+
78+
This creates a [vsock socket](https://man7.org/linux/man-pages/man7/vsock.7.html)
79+
for each VM to log in with SSH. This configures root login with an empty password.
80+
81+
When the VMs get started interactively with the test-driver, it's possible to
82+
connect to `machine` with
83+
84+
```
85+
$ ssh vsock/3 -o User=root
86+
```
87+
88+
The socket numbers correspond to the node number of the test VM, but start
89+
at three instead of one because that's the lowest possible
90+
vsock number.
91+
92+
On non-NixOS systems you'll probably need to enable
93+
the SSH config from {manpage}`systemd-ssh-proxy(1)` yourself.
94+
6695
## Port forwarding to NixOS test VMs {#sec-nixos-test-port-forwarding}
6796

6897
If your test has only a single VM, you may use e.g.

nixos/doc/manual/redirects.json

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1823,6 +1823,9 @@
18231823
"sec-test-options-reference": [
18241824
"index.html#sec-test-options-reference"
18251825
],
1826+
"test-opt-sshBackdoor.enable": [
1827+
"index.html#test-opt-sshBackdoor.enable"
1828+
],
18261829
"test-opt-defaults": [
18271830
"index.html#test-opt-defaults"
18281831
],
@@ -1910,6 +1913,9 @@
19101913
"sec-nixos-test-shell-access": [
19111914
"index.html#sec-nixos-test-shell-access"
19121915
],
1916+
"sec-nixos-test-ssh-access": [
1917+
"index.html#sec-nixos-test-ssh-access"
1918+
],
19131919
"sec-nixos-test-port-forwarding": [
19141920
"index.html#sec-nixos-test-port-forwarding"
19151921
],

nixos/lib/test-driver/src/test_driver/__init__.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ def main() -> None:
109109
help="the test script to run",
110110
type=Path,
111111
)
112+
arg_parser.add_argument(
113+
"--dump-vsocks",
114+
help="indicates that the interactive SSH backdoor is active and dumps information about it on start",
115+
action="store_true",
116+
)
112117

113118
args = arg_parser.parse_args()
114119

@@ -136,6 +141,8 @@ def main() -> None:
136141
if args.interactive:
137142
history_dir = os.getcwd()
138143
history_path = os.path.join(history_dir, ".nixos-test-history")
144+
if args.dump_vsocks:
145+
driver.dump_machine_ssh()
139146
ptpython.ipython.embed(
140147
user_ns=driver.test_symbols(),
141148
history_filename=history_path,

nixos/lib/test-driver/src/test_driver/driver.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111
from typing import Any
1212
from unittest import TestCase
1313

14+
from colorama import Style
15+
1416
from test_driver.errors import MachineError, RequestedAssertionFailed
1517
from test_driver.logger import AbstractLogger
1618
from test_driver.machine import Machine, NixStartScript, retry
@@ -176,6 +178,19 @@ def subtest(name: str) -> Iterator[None]:
176178
)
177179
return {**general_symbols, **machine_symbols, **vlan_symbols}
178180

181+
def dump_machine_ssh(self) -> None:
182+
print("SSH backdoor enabled, the machines can be accessed like this:")
183+
print(
184+
f"{Style.BRIGHT}Note:{Style.RESET_ALL} this requires {Style.BRIGHT}systemd-ssh-proxy(1){Style.RESET_ALL} to be enabled (default on NixOS 25.05 and newer)."
185+
)
186+
names = [machine.name for machine in self.machines]
187+
longest_name = len(max(names, key=len))
188+
for num, name in enumerate(names, start=3):
189+
spaces = " " * (longest_name - len(name) + 2)
190+
print(
191+
f" {name}:{spaces}{Style.BRIGHT}ssh -o User=root vsock/{num}{Style.RESET_ALL}"
192+
)
193+
179194
def test_script(self) -> None:
180195
"""Run the test script"""
181196
with self.logger.nested("run the VM test script"):

nixos/lib/testing-python.nix

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ pkgs.lib.throwIf (args ? specialArgs)
7575
),
7676
extraPythonPackages ? (_: [ ]),
7777
interactive ? { },
78+
sshBackdoor ? { },
7879
}@t:
7980
let
8081
testConfig =

nixos/lib/testing/nodes.nix

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ let
1313
mapAttrs
1414
mkDefault
1515
mkIf
16+
mkMerge
1617
mkOption
1718
mkForce
1819
optional
@@ -77,6 +78,14 @@ in
7778
{
7879

7980
options = {
81+
sshBackdoor = {
82+
enable = mkOption {
83+
default = false;
84+
type = types.bool;
85+
description = "Whether to turn on the VSOCK-based access to all VMs. This provides an unauthenticated access intended for debugging.";
86+
};
87+
};
88+
8089
node.type = mkOption {
8190
type = types.raw;
8291
default = baseOS.type;
@@ -172,10 +181,19 @@ in
172181

173182
passthru.nodes = config.nodesCompat;
174183

175-
defaults = mkIf config.node.pkgsReadOnly {
176-
nixpkgs.pkgs = config.node.pkgs;
177-
imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
178-
};
184+
extraDriverArgs = mkIf config.sshBackdoor.enable [
185+
"--dump-vsocks"
186+
];
187+
188+
defaults = mkMerge [
189+
(mkIf config.node.pkgsReadOnly {
190+
nixpkgs.pkgs = config.node.pkgs;
191+
imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
192+
})
193+
(mkIf config.sshBackdoor.enable {
194+
testing.sshBackdoor.enable = true;
195+
})
196+
];
179197

180198
};
181199
}

nixos/modules/testing/test-instrumentation.nix

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,10 @@ in
8787
machine.switch_root() to leave stage 1 and proceed to stage 2
8888
'';
8989

90+
sshBackdoor = {
91+
enable = mkEnableOption "vsock-based ssh backdoor for the VM";
92+
};
93+
9094
};
9195

9296
config = {
@@ -100,6 +104,18 @@ in
100104
}
101105
];
102106

107+
services.openssh = mkIf config.testing.sshBackdoor.enable {
108+
enable = true;
109+
settings = {
110+
PermitRootLogin = "yes";
111+
PermitEmptyPasswords = "yes";
112+
};
113+
};
114+
115+
security.pam.services.sshd = mkIf config.testing.sshBackdoor.enable {
116+
allowNullPassword = true;
117+
};
118+
103119
systemd.services.backdoor = lib.mkMerge [
104120
backdoorService
105121
{
@@ -175,6 +191,10 @@ in
175191
# we avoid defining attributes if not possible.
176192
# TODO: refactor such that test-instrumentation can import qemu-vm
177193
package = lib.mkDefault pkgs.qemu_test;
194+
195+
options = mkIf config.testing.sshBackdoor.enable [
196+
"-device vhost-vsock-pci,guest-cid=${toString (config.virtualisation.test.nodeNumber + 2)}"
197+
];
178198
};
179199
};
180200

0 commit comments

Comments
 (0)