nixos/test-driver: add backdoor based on systemd-ssh-proxy & AF_VSOCK (#392030)
This commit is contained in:
commit
8b3baa1402
@ -63,6 +63,35 @@ using:
|
|||||||
Once the connection is established, you can enter commands in the socat terminal
|
Once the connection is established, you can enter commands in the socat terminal
|
||||||
where socat is running.
|
where socat is running.
|
||||||
|
|
||||||
|
## SSH Access for test machines {#sec-nixos-test-ssh-access}
|
||||||
|
|
||||||
|
An SSH-based backdoor to log into machines can be enabled with
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
name = "…";
|
||||||
|
nodes.machines = { /* … */ };
|
||||||
|
sshBackdoor.enable = true;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
This creates a [vsock socket](https://man7.org/linux/man-pages/man7/vsock.7.html)
|
||||||
|
for each VM to log in with SSH. This configures root login with an empty password.
|
||||||
|
|
||||||
|
When the VMs get started interactively with the test-driver, it's possible to
|
||||||
|
connect to `machine` with
|
||||||
|
|
||||||
|
```
|
||||||
|
$ ssh vsock/3 -o User=root
|
||||||
|
```
|
||||||
|
|
||||||
|
The socket numbers correspond to the node number of the test VM, but start
|
||||||
|
at three instead of one because that's the lowest possible
|
||||||
|
vsock number.
|
||||||
|
|
||||||
|
On non-NixOS systems you'll probably need to enable
|
||||||
|
the SSH config from {manpage}`systemd-ssh-proxy(1)` yourself.
|
||||||
|
|
||||||
## Port forwarding to NixOS test VMs {#sec-nixos-test-port-forwarding}
|
## Port forwarding to NixOS test VMs {#sec-nixos-test-port-forwarding}
|
||||||
|
|
||||||
If your test has only a single VM, you may use e.g.
|
If your test has only a single VM, you may use e.g.
|
||||||
|
|||||||
@ -1823,6 +1823,9 @@
|
|||||||
"sec-test-options-reference": [
|
"sec-test-options-reference": [
|
||||||
"index.html#sec-test-options-reference"
|
"index.html#sec-test-options-reference"
|
||||||
],
|
],
|
||||||
|
"test-opt-sshBackdoor.enable": [
|
||||||
|
"index.html#test-opt-sshBackdoor.enable"
|
||||||
|
],
|
||||||
"test-opt-defaults": [
|
"test-opt-defaults": [
|
||||||
"index.html#test-opt-defaults"
|
"index.html#test-opt-defaults"
|
||||||
],
|
],
|
||||||
@ -1910,6 +1913,9 @@
|
|||||||
"sec-nixos-test-shell-access": [
|
"sec-nixos-test-shell-access": [
|
||||||
"index.html#sec-nixos-test-shell-access"
|
"index.html#sec-nixos-test-shell-access"
|
||||||
],
|
],
|
||||||
|
"sec-nixos-test-ssh-access": [
|
||||||
|
"index.html#sec-nixos-test-ssh-access"
|
||||||
|
],
|
||||||
"sec-nixos-test-port-forwarding": [
|
"sec-nixos-test-port-forwarding": [
|
||||||
"index.html#sec-nixos-test-port-forwarding"
|
"index.html#sec-nixos-test-port-forwarding"
|
||||||
],
|
],
|
||||||
|
|||||||
@ -109,6 +109,11 @@ def main() -> None:
|
|||||||
help="the test script to run",
|
help="the test script to run",
|
||||||
type=Path,
|
type=Path,
|
||||||
)
|
)
|
||||||
|
arg_parser.add_argument(
|
||||||
|
"--dump-vsocks",
|
||||||
|
help="indicates that the interactive SSH backdoor is active and dumps information about it on start",
|
||||||
|
action="store_true",
|
||||||
|
)
|
||||||
|
|
||||||
args = arg_parser.parse_args()
|
args = arg_parser.parse_args()
|
||||||
|
|
||||||
@ -136,6 +141,8 @@ def main() -> None:
|
|||||||
if args.interactive:
|
if args.interactive:
|
||||||
history_dir = os.getcwd()
|
history_dir = os.getcwd()
|
||||||
history_path = os.path.join(history_dir, ".nixos-test-history")
|
history_path = os.path.join(history_dir, ".nixos-test-history")
|
||||||
|
if args.dump_vsocks:
|
||||||
|
driver.dump_machine_ssh()
|
||||||
ptpython.ipython.embed(
|
ptpython.ipython.embed(
|
||||||
user_ns=driver.test_symbols(),
|
user_ns=driver.test_symbols(),
|
||||||
history_filename=history_path,
|
history_filename=history_path,
|
||||||
|
|||||||
@ -11,6 +11,8 @@ from pathlib import Path
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
from unittest import TestCase
|
from unittest import TestCase
|
||||||
|
|
||||||
|
from colorama import Style
|
||||||
|
|
||||||
from test_driver.errors import MachineError, RequestedAssertionFailed
|
from test_driver.errors import MachineError, RequestedAssertionFailed
|
||||||
from test_driver.logger import AbstractLogger
|
from test_driver.logger import AbstractLogger
|
||||||
from test_driver.machine import Machine, NixStartScript, retry
|
from test_driver.machine import Machine, NixStartScript, retry
|
||||||
@ -176,6 +178,19 @@ class Driver:
|
|||||||
)
|
)
|
||||||
return {**general_symbols, **machine_symbols, **vlan_symbols}
|
return {**general_symbols, **machine_symbols, **vlan_symbols}
|
||||||
|
|
||||||
|
def dump_machine_ssh(self) -> None:
|
||||||
|
print("SSH backdoor enabled, the machines can be accessed like this:")
|
||||||
|
print(
|
||||||
|
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)."
|
||||||
|
)
|
||||||
|
names = [machine.name for machine in self.machines]
|
||||||
|
longest_name = len(max(names, key=len))
|
||||||
|
for num, name in enumerate(names, start=3):
|
||||||
|
spaces = " " * (longest_name - len(name) + 2)
|
||||||
|
print(
|
||||||
|
f" {name}:{spaces}{Style.BRIGHT}ssh -o User=root vsock/{num}{Style.RESET_ALL}"
|
||||||
|
)
|
||||||
|
|
||||||
def test_script(self) -> None:
|
def test_script(self) -> None:
|
||||||
"""Run the test script"""
|
"""Run the test script"""
|
||||||
with self.logger.nested("run the VM test script"):
|
with self.logger.nested("run the VM test script"):
|
||||||
|
|||||||
@ -75,6 +75,7 @@ pkgs.lib.throwIf (args ? specialArgs)
|
|||||||
),
|
),
|
||||||
extraPythonPackages ? (_: [ ]),
|
extraPythonPackages ? (_: [ ]),
|
||||||
interactive ? { },
|
interactive ? { },
|
||||||
|
sshBackdoor ? { },
|
||||||
}@t:
|
}@t:
|
||||||
let
|
let
|
||||||
testConfig =
|
testConfig =
|
||||||
|
|||||||
@ -13,6 +13,7 @@ let
|
|||||||
mapAttrs
|
mapAttrs
|
||||||
mkDefault
|
mkDefault
|
||||||
mkIf
|
mkIf
|
||||||
|
mkMerge
|
||||||
mkOption
|
mkOption
|
||||||
mkForce
|
mkForce
|
||||||
optional
|
optional
|
||||||
@ -77,6 +78,14 @@ in
|
|||||||
{
|
{
|
||||||
|
|
||||||
options = {
|
options = {
|
||||||
|
sshBackdoor = {
|
||||||
|
enable = mkOption {
|
||||||
|
default = false;
|
||||||
|
type = types.bool;
|
||||||
|
description = "Whether to turn on the VSOCK-based access to all VMs. This provides an unauthenticated access intended for debugging.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
node.type = mkOption {
|
node.type = mkOption {
|
||||||
type = types.raw;
|
type = types.raw;
|
||||||
default = baseOS.type;
|
default = baseOS.type;
|
||||||
@ -172,10 +181,19 @@ in
|
|||||||
|
|
||||||
passthru.nodes = config.nodesCompat;
|
passthru.nodes = config.nodesCompat;
|
||||||
|
|
||||||
defaults = mkIf config.node.pkgsReadOnly {
|
extraDriverArgs = mkIf config.sshBackdoor.enable [
|
||||||
nixpkgs.pkgs = config.node.pkgs;
|
"--dump-vsocks"
|
||||||
imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
|
];
|
||||||
};
|
|
||||||
|
defaults = mkMerge [
|
||||||
|
(mkIf config.node.pkgsReadOnly {
|
||||||
|
nixpkgs.pkgs = config.node.pkgs;
|
||||||
|
imports = [ ../../modules/misc/nixpkgs/read-only.nix ];
|
||||||
|
})
|
||||||
|
(mkIf config.sshBackdoor.enable {
|
||||||
|
testing.sshBackdoor.enable = true;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@ -87,6 +87,10 @@ in
|
|||||||
machine.switch_root() to leave stage 1 and proceed to stage 2
|
machine.switch_root() to leave stage 1 and proceed to stage 2
|
||||||
'';
|
'';
|
||||||
|
|
||||||
|
sshBackdoor = {
|
||||||
|
enable = mkEnableOption "vsock-based ssh backdoor for the VM";
|
||||||
|
};
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = {
|
config = {
|
||||||
@ -100,6 +104,18 @@ in
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
services.openssh = mkIf config.testing.sshBackdoor.enable {
|
||||||
|
enable = true;
|
||||||
|
settings = {
|
||||||
|
PermitRootLogin = "yes";
|
||||||
|
PermitEmptyPasswords = "yes";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
security.pam.services.sshd = mkIf config.testing.sshBackdoor.enable {
|
||||||
|
allowNullPassword = true;
|
||||||
|
};
|
||||||
|
|
||||||
systemd.services.backdoor = lib.mkMerge [
|
systemd.services.backdoor = lib.mkMerge [
|
||||||
backdoorService
|
backdoorService
|
||||||
{
|
{
|
||||||
@ -175,6 +191,10 @@ in
|
|||||||
# we avoid defining attributes if not possible.
|
# we avoid defining attributes if not possible.
|
||||||
# TODO: refactor such that test-instrumentation can import qemu-vm
|
# TODO: refactor such that test-instrumentation can import qemu-vm
|
||||||
package = lib.mkDefault pkgs.qemu_test;
|
package = lib.mkDefault pkgs.qemu_test;
|
||||||
|
|
||||||
|
options = mkIf config.testing.sshBackdoor.enable [
|
||||||
|
"-device vhost-vsock-pci,guest-cid=${toString (config.virtualisation.test.nodeNumber + 2)}"
|
||||||
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user