The `netns-exec` wrapper was created with `group = ""`, which makes the
`security.wrappers` activation run `chown ${allowedUser}:`. GNU chown
resolves the trailing colon to the owner's login group, and because the
operator is defined with `isNormalUser = true`, that group is the shared
group `users`. Together with mode 550, this made the wrapper executable
by every normal user on the host instead of only by `allowedUser`.
The wrapper carries `cap_sys_admin=ep` and the NixOS security wrapper
performs no owner check, so any normal user could enter `nb-joinmarket`,
the only netns that `netns-exec` permits. This exposes joinmarketd's
unauthenticated control port on 127.0.0.1 inside that netns and allows
sending packets from the joinmarket netns address. This can only ever become a
problem if there's a second, normal user on the host.
Use mode 500 so that only `allowedUser` can execute the wrapper. Also
set the group to `root`, so that `users` doesn't silently become an
authorized group again if the mode is ever loosened.
The netns-isolation test asserted this property with
`runuser -u clightning -- netns-exec nb-bitcoind ip a`, which fails
regardless of the file mode, because `clightning` is a system user with
its own group and `nb-bitcoind` is not in netns-exec's allowlist.
Replace it with a check that a normal user is denied by the exec
permissions, and assert the reason for each failure instead of only the
exit status.
The run-tests.sh command is the most convenient and versatile way to run tests.
It leave no traces (outside of /nix/store) on the host system.
run-tests.sh requires Nix with Flakes.
Summary
./run-tests.sh [--scenario|-s <scenario>] [build|vm|debug|container]
See the top of run-tests.sh for a complete documentation.
Test scenarios are defined in tests.nix and tests.py.
Tutorial
Running tests
# Run the basic set of tests. These tests are also run on the GitHub CI server.
./run-tests.sh
# Run the test for scenario `regtest`.
# The test is run via the Nix build system. Successful runs are cached.
./run-tests.sh -s regtest build
./run-tests.sh -s regtest # Shorthand, equivalent
# To test a single service, use its name as a scenario.
./run-tests.sh -s clightning
# When no scenario is specified, scenario `default` is used.
./run-tests.sh build
Debugging
# Start a shell inside a test VM. No tests are executed.
./run-tests.sh -s bitcoind vm
systemctl status bitcoind
# Run a Python NixOS test shell inside a VM.
# See https://nixos.org/manual/nixos/stable/#ssec-machine-objects for available commands.
./run-tests.sh debug
print(succeed("systemctl status bitcoind"))
run_test("bitcoind")
# Start a shell in a container node. Requires systemd and root privileges.
./run-tests.sh container
# In the container shell: Run command in container (with prefix `c`)
c systemctl status bitcoind
# Explore a single feature
./run-tests.sh -s electrs container
# Run a command in a container.
# The container is deleted afterwards.
./run-tests.sh -s clightning container --run c lightning-cli getinfo
# Define a custom scenario
./run-tests.sh --scenario '{
services.clightning.enable = true;
nix-bitcoin.nodeinfo.enable = true;
}' container --run c nodeinfo
Running tests with Flakes
Tests can also be accessed via the nix-bitcoin flake:
# Build test
nix build --no-link ..#tests.default
# Run a node in a VM. No tests are executed.
nix run ..#tests.default.vm
# Run a Python test shell inside a VM node
nix run ..#tests.default.run -- --debug
# Run a node in a container. Requires extra-container, systemd and root privileges
nix run ..#tests.default.container
nix run ..#tests.default.containerLegacy # For NixOS with `system.stateVersion` <22.05
# Run a command in a container
nix run ..#tests.default.container -- --run c nodeinfo
nix run ..#tests.default.containerLegacy -- --run c nodeinfo # For NixOS with `system.stateVersion` <22.05