{ lib, pkgs, ... }: { name = "reaction-core-plugins"; nodes.server = args: { services.reaction = { enable = true; stopForFirewall = false; runAsRoot = false; settings = import ./settings.nix args; /* # NOTE: When runAsRoot is true, disable run0 settings = { # In the qemu vm `run0 ls` as root prints nothing, so we can't use it # see https://reaction.ppom.me/reference.html#systemd plugins.ipset.systemd = false; plugins.virtual.systemd = false; }; */ }; /* NOTE: - if reaction is run as non-root, the plugins need these capabilities, remove these if runAsRoot is true - CAP_DAC_READ_SEARCH is for journalctl for accessing ssh logs - useful tools: capable (from package bcc), captree, getpcaps (from libpcap) */ systemd.services.reaction.serviceConfig = { CapabilityBoundingSet = [ "CAP_NET_ADMIN" "CAP_NET_RAW" "CAP_DAC_READ_SEARCH" ]; AmbientCapabilities = [ "CAP_NET_ADMIN" "CAP_NET_RAW" "CAP_DAC_READ_SEARCH" ]; }; services.openssh.enable = true; users.users.nixos.isNormalUser = true; # neeeded to establish a ssh connection, by default root login is succeeding without any password }; nodes.client = _: { environment.systemPackages = [ pkgs.sshpass pkgs.libressl.nc ]; }; testScript = { nodes, ... }: # py '' start_all() # Wait for everything to be ready. server.wait_for_unit("multi-user.target") server.wait_for_unit("reaction") server.wait_for_unit("sshd") client_addr = "${(lib.head nodes.client.networking.interfaces.eth1.ipv4.addresses).address}" server_addr = "${(lib.head nodes.server.networking.interfaces.eth1.ipv4.addresses).address}" # Verify there is not ban and the port is reachable from the client. server.succeed(f"reaction show | grep -q {client_addr} || test $? -eq 1") client.succeed(f"nc -w3 -z {server_addr} 22") # Cause authentication failure log entries. for _ in range(2): client.fail(f""" sshpass -p 'wrongpassword' \ ssh -o StrictHostKeyChecking=no \ -o User=nixos \ -o ServerAliveInterval=1 \ -o ServerAliveCountMax=2 \ {server_addr} """) # Verify there is a ban and the port is unreachable from the client. server.sleep(2) output = server.succeed("reaction show") print(output) assert client_addr in output, f"client did not get banned, {client_addr}" client.fail(f"nc -w3 -z {server_addr} 22") # Check that unbanning works output = server.succeed("reaction flush") print(output) client.succeed(f"nc -w3 -z {server_addr} 22") ''; # Debug interactively with: # - nix run .#nixosTests.reaction.driverInteractive -L # - run_tests() interactive.sshBackdoor.enable = true; interactive.nodes.server = { config, ... }: { # not needed, only for manual interactive debugging virtualisation.memorySize = 4096; virtualisation.graphics = false; environment.systemPackages = with pkgs; [ btop sysz sshpass libressl.nc ]; }; meta.maintainers = with lib.maintainers; [ ppom ] ++ lib.teams.ngi.members; }