mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 19:27:03 +01:00
feat(pi): rustscan instead of nmap
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Some checks are pending
dependency freshness / dependencies on newest stable (push) Waiting to run
md-naming / markdown naming convention (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (mypy, mypy) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (pylint, pylint) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (rest, ) (push) Waiting to run
Pre-commit checks / pre-commit (${{ matrix.group }}) (shellcheck, shellcheck) (push) Waiting to run
Python tests / test (push) Waiting to run
repo-contract / repo contract (push) Waiting to run
Shell tests / shell-tests (push) Waiting to run
Shell tests / lib-tests (push) Waiting to run
Shell tests / arch-tests (push) Waiting to run
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2a788b741d
commit
7581a904cd
@ -0,0 +1,13 @@
|
||||
{
|
||||
"title": "Pi flashing: discover SSH hosts with rustscan, not nmap",
|
||||
"objective": "The Pi flash/discovery scripts find SSH hosts on the LAN with `rustscan -a <net> -p 22 -g`, install/check for rustscan instead of nmap, and rustscan is configured (scripts = \"None\") so it never pipes into nmap, which the pacman wrapper blocks.",
|
||||
"acceptance_criteria": [
|
||||
"No nmap references remain in the Pi flashing scripts and rpi_nc_deps checks for rustscan (test_pi_discovery_rustscan.sh)",
|
||||
"fresh-install/main.sh installs fresh-install/rustscan.toml to ~/.rustscan.toml"
|
||||
],
|
||||
"out_of_scope": [
|
||||
"The pacman nmap block and /usr/local/bin/nmap shim (digital-wellbeing repo)",
|
||||
"Live scan of a real LAN"
|
||||
],
|
||||
"verifier": "linux_configuration/tests/test_pi_discovery_rustscan.sh; pre-commit run --files <changed-files>"
|
||||
}
|
||||
@ -1,44 +0,0 @@
|
||||
{
|
||||
"intent": "DNS-over-TLS (stunnel-dot) comes up at every boot, dnsmasq has no ordering cycle, and the service checker stops expecting the removed pc_startup feature and finds the extracted repos under ~/src.",
|
||||
"scope": [
|
||||
"linux_configuration/features/lib/dns_units.sh, dot_resolver_install.sh and their tests",
|
||||
"linux_configuration/periodic_background check_and_enable_services.sh, services_units.sh, services_report.sh and their tests",
|
||||
"Non-goal: the firewall, the blocklist feed, the guard warnings (compulsive-block, LeechBlock, workout-locker) this checker reports"
|
||||
],
|
||||
"changes": [
|
||||
"dnsmasq: bind-dynamic instead of bind-interfaces, so starting before enp6s0 exists no longer exits with 'unknown interface'",
|
||||
"dnsmasq drop-in: drop After/Wants=network-online.target, which cycled with the stock Before=network-online.target",
|
||||
"stunnel-dot.service: Wants=dnsmasq.service instead of Requires=, so one failed dnsmasq start no longer kills DoT until a manual restart",
|
||||
"check_and_enable_services: remove check_startup_monitor (pc_startup was removed in 765fa925) and default HOSTS_BLOCKER/DW/WORKOUT_LOCKER repos to ~/src/<repo>"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "pre-commit run --files <the 11 files> (shell-coverage-ratchet runs the suites in the jail)",
|
||||
"result": "pass",
|
||||
"evidence": "all hooks passed, ratchet 100%"
|
||||
},
|
||||
{
|
||||
"command": "vmbox: dnsmasq with interface=<absent>, bind-interfaces vs bind-dynamic; Requires= vs Wants= on a failing dependency",
|
||||
"result": "pass",
|
||||
"evidence": "bind-interfaces: DIED 'unknown interface lanx0'; bind-dynamic: survived, bound 10.199.0.1:5353 after the interface appeared; Requires=: inactive; Wants=: active"
|
||||
},
|
||||
{
|
||||
"command": "host: setup_dot_resolver.sh + dns_units functions + validate_and_enable",
|
||||
"result": "pass",
|
||||
"evidence": "dnsmasq --test OK; stunnel-dot active on 10.8.0.1:853, TLSv1.3 Verification OK; DNS answers on 127.0.0.1 and 192.168.1.43; no network-online in dnsmasq After="
|
||||
},
|
||||
{
|
||||
"command": "sudo check_and_enable_services.sh --status (host) and vmbox diff against HEAD",
|
||||
"result": "pass",
|
||||
"evidence": "full run, no 'not found'; only diff vs HEAD is the removed PC Startup Monitor section"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"bind-dynamic also listens on addresses that appear later on enp6s0 (intended)",
|
||||
"stunnel now starts even if dnsmasq is down; queries fail until dnsmasq is back, which Restart=always plus the watchdog already handle"
|
||||
],
|
||||
"rollback": [
|
||||
"git revert this commit, then rerun setup_dns_blocker.sh setup and setup_dot_resolver.sh",
|
||||
"Validate with: systemctl is-active dnsmasq stunnel-dot; setup_dot_resolver.sh --check"
|
||||
]
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
{
|
||||
"intent": "Pi flashing scripts discover SSH hosts with rustscan instead of nmap; rustscan runs with scripts = \"None\" so it never calls nmap.",
|
||||
"scope": [
|
||||
"linux_configuration/features/lib/{nc_discover,nc_flash,nc_image,nc_remote,pi_flash_remote,rpi_nc_deps}.sh",
|
||||
"linux_configuration/features/raspberry_pi_flash_sd.sh",
|
||||
"linux_configuration/fresh-install/main.sh, rustscan.toml",
|
||||
"linux_configuration/tests/test_pi_discovery_rustscan.sh",
|
||||
"Not touched: pacman wrapper (digital-wellbeing), live LAN scanning"
|
||||
],
|
||||
"changes": [
|
||||
"nmap ping sweep + port scan replaced by `rustscan -a <net> -p 22 -g | awk '{print $1}'` (greppable output, one IP per line)",
|
||||
"Dependency checks and hint messages name rustscan instead of nmap",
|
||||
"fresh-install/rustscan.toml (scripts = \"None\") copied to ~/.rustscan.toml by main.sh"
|
||||
],
|
||||
"verification": [
|
||||
{
|
||||
"command": "linux_configuration/tests/test_pi_discovery_rustscan.sh",
|
||||
"result": "pass",
|
||||
"evidence": "ok per discovery script, ok rpi_nc_deps dependencies, ok no nmap references"
|
||||
},
|
||||
{
|
||||
"command": "pre-commit run --files <changed-files>",
|
||||
"result": "pass",
|
||||
"evidence": "shellcheck, codespell and secrets hooks passed on the shell files"
|
||||
}
|
||||
],
|
||||
"risks": [
|
||||
"rustscan -g output format change would break the awk IP extraction",
|
||||
"Hosts that drop rustscan's fast SYN probing may be missed versus nmap"
|
||||
],
|
||||
"rollback": [
|
||||
"git revert the commit",
|
||||
"Re-run a Pi discovery and confirm hosts are listed"
|
||||
]
|
||||
}
|
||||
@ -39,13 +39,11 @@ discover_raspberry_pi() {
|
||||
return
|
||||
fi
|
||||
|
||||
# Ping sweep to wake up hosts
|
||||
log_info "Hostname resolution failed, scanning network..."
|
||||
nmap -sn -T4 "$network" &>/dev/null || true
|
||||
|
||||
# Scan for SSH-enabled devices (excluding our IP and known laptop)
|
||||
local ssh_hosts
|
||||
ssh_hosts=$(nmap -p 22 --open -sT -T4 "$network" 2>/dev/null | grep "Nmap scan report" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -vw "$my_ip" | grep -vw "$REMOTE_LAPTOP_IP" 2>/dev/null | sort -u) || true
|
||||
ssh_hosts=$(rustscan -a "$network" -p 22 -g 2>/dev/null | awk '{print $1}' | grep -vw "$my_ip" | grep -vw "$REMOTE_LAPTOP_IP" 2>/dev/null | sort -u) || true
|
||||
|
||||
if [[ -z $ssh_hosts ]]; then
|
||||
die "No new SSH-enabled devices found. Is the Pi connected and booted?"
|
||||
|
||||
@ -67,9 +67,9 @@ ensure_dependencies() {
|
||||
|
||||
local missing_packages=()
|
||||
|
||||
# Check for nmap (fast network scanning)
|
||||
if ! command -v nmap &>/dev/null; then
|
||||
missing_packages+=("nmap")
|
||||
# Check for rustscan (fast network scanning)
|
||||
if ! command -v rustscan &>/dev/null; then
|
||||
missing_packages+=("rustscan")
|
||||
fi
|
||||
|
||||
# Check for sshpass (for initial SSH key setup)
|
||||
|
||||
@ -176,5 +176,5 @@ EOF
|
||||
|
||||
log_success "SD card configured for headless boot!"
|
||||
log_info "Insert the SD card into your Raspberry Pi and power it on."
|
||||
log_info "Find the Pi's IP address from your router or use: nmap -sn 192.168.1.0/24"
|
||||
log_info "Find the Pi's IP address from your router or use: rustscan -a 192.168.1.0/24 -p 22 -g"
|
||||
}
|
||||
|
||||
@ -22,13 +22,11 @@ discover_remote_laptop() {
|
||||
|
||||
log_info "Local IP: $my_ip, Gateway: $gateway, Network: $network"
|
||||
|
||||
# Use nmap for fast parallel SSH port scanning
|
||||
log_info "Scanning network for SSH-enabled devices (using nmap)..."
|
||||
# Use rustscan for fast parallel SSH port scanning
|
||||
log_info "Scanning network for SSH-enabled devices (using rustscan)..."
|
||||
local ssh_hosts
|
||||
# First do a ping sweep to wake up hosts, then scan SSH port
|
||||
nmap -sn -T4 "$network" &>/dev/null || true
|
||||
# Extract IPs from nmap output - grep for report lines then extract IP
|
||||
ssh_hosts=$(nmap -p 22 --open -sT -T4 "$network" 2>/dev/null | grep "Nmap scan report" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -vw "$my_ip" | sort -u)
|
||||
# Greppable output: one "IP -> [22]" line per host with the port open
|
||||
ssh_hosts=$(rustscan -a "$network" -p 22 -g 2>/dev/null | awk '{print $1}' | grep -vw "$my_ip" | sort -u) || true
|
||||
|
||||
if [[ -z $ssh_hosts ]]; then
|
||||
die "No SSH-enabled devices found on network"
|
||||
|
||||
@ -18,11 +18,11 @@ discover_remote_laptop() {
|
||||
local network="${gateway%.*}.0/24"
|
||||
|
||||
log_info "Local IP: $my_ip, Gateway: $gateway, Network: $network"
|
||||
log_info "Scanning network for SSH-enabled devices (using nmap)..."
|
||||
log_info "Scanning network for SSH-enabled devices (using rustscan)..."
|
||||
|
||||
local ssh_hosts
|
||||
nmap -sn -T4 "$network" &> /dev/null || true
|
||||
ssh_hosts=$(nmap -p 22 --open -sT -T4 "$network" 2> /dev/null | grep "Nmap scan report" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -vw "$my_ip" | sort -u)
|
||||
# Greppable output: one "IP -> [22]" line per host with the port open
|
||||
ssh_hosts=$(rustscan -a "$network" -p 22 -g 2>/dev/null | awk '{print $1}' | grep -vw "$my_ip" | sort -u) || true
|
||||
|
||||
if [[ -z $ssh_hosts ]]; then
|
||||
die "No SSH-enabled devices found on network"
|
||||
|
||||
@ -36,8 +36,8 @@ wait_for_apt_lock() {
|
||||
ensure_dependencies() {
|
||||
local missing_packages=()
|
||||
|
||||
if ! command -v nmap &>/dev/null; then
|
||||
missing_packages+=("nmap")
|
||||
if ! command -v rustscan &>/dev/null; then
|
||||
missing_packages+=("rustscan")
|
||||
fi
|
||||
|
||||
if ! command -v sshpass &>/dev/null; then
|
||||
@ -88,10 +88,9 @@ discover_raspberry_pi() {
|
||||
fi
|
||||
|
||||
log_info "Hostname resolution failed, scanning network..."
|
||||
nmap -sn -T4 "$network" &>/dev/null || true
|
||||
|
||||
local ssh_hosts
|
||||
ssh_hosts=$(nmap -p 22 --open -sT -T4 "$network" 2>/dev/null | grep "Nmap scan report" | grep -oP '[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+' | grep -vw "$my_ip" | sort -u) || true
|
||||
ssh_hosts=$(rustscan -a "$network" -p 22 -g 2>/dev/null | awk '{print $1}' | grep -vw "$my_ip" | sort -u) || true
|
||||
|
||||
if [[ -z $ssh_hosts ]]; then
|
||||
die "No SSH-enabled devices found. Is the Pi connected and booted?"
|
||||
|
||||
@ -106,8 +106,8 @@ auto_generate_pi_password() {
|
||||
ensure_dependencies() {
|
||||
local missing_packages=()
|
||||
|
||||
if ! command -v nmap &> /dev/null; then
|
||||
missing_packages+=("nmap")
|
||||
if ! command -v rustscan &>/dev/null; then
|
||||
missing_packages+=("rustscan")
|
||||
fi
|
||||
|
||||
if ! command -v sshpass &>/dev/null; then
|
||||
@ -138,7 +138,6 @@ source "$SCRIPT_DIR/lib/pi_flash_image.sh"
|
||||
# shellcheck source=lib/pi_flash_exec.sh
|
||||
source "$SCRIPT_DIR/lib/pi_flash_exec.sh"
|
||||
|
||||
|
||||
main() {
|
||||
local command="${1:-help}"
|
||||
|
||||
|
||||
@ -169,6 +169,8 @@ if [ ! -d "$HOME/.config/mpv" ]; then
|
||||
mkdir -p "$HOME/.config/mpv"
|
||||
fi
|
||||
cp mpv.conf "$HOME/.config/mpv/mpv.conf"
|
||||
# rustscan must never call nmap (nonfree, blocked by the pacman wrapper)
|
||||
cp rustscan.toml "$HOME/.rustscan.toml"
|
||||
|
||||
if [ ! -d "$HOME/.oh-my-zsh" ]; then
|
||||
yes | sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)"
|
||||
|
||||
4
linux_configuration/fresh-install/rustscan.toml
Normal file
4
linux_configuration/fresh-install/rustscan.toml
Normal file
@ -0,0 +1,4 @@
|
||||
# RustScan config (copied to ~/.rustscan.toml by main.sh).
|
||||
# "None" keeps rustscan from piping open ports into nmap (nonfree, blocked by
|
||||
# the pacman wrapper; the /usr/local/bin/nmap shim would only exit 1).
|
||||
scripts = "None"
|
||||
121
linux_configuration/tests/test_pi_discovery_rustscan.sh
Executable file
121
linux_configuration/tests/test_pi_discovery_rustscan.sh
Executable file
@ -0,0 +1,121 @@
|
||||
#!/bin/bash
|
||||
# Regression test: Raspberry Pi / laptop SSH discovery uses rustscan, never
|
||||
# nmap (nonfree NPSL licence; the pacman wrapper refuses to install it).
|
||||
#
|
||||
# Runs the real discover/ensure_dependencies functions with stubbed
|
||||
# ip/getent/sshpass/rustscan/nmap/pacman on PATH and asserts:
|
||||
# - rustscan is called as `-a <net> -p 22 -g` and nmap is never executed
|
||||
# - our own IP is excluded and the Pi (answering the hostname probe) is found
|
||||
# - ensure_dependencies asks for rustscan, not nmap
|
||||
# - no script in the set still invokes nmap
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SCRIPT_DIR=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
||||
FEATURES="$(cd -- "$SCRIPT_DIR/.." && pwd)/features"
|
||||
|
||||
fail() {
|
||||
printf 'FAIL: %s\n' "$1" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
TMP_DIR=$(mktemp -d)
|
||||
trap 'rm -rf "$TMP_DIR"' EXIT
|
||||
STUBS="$TMP_DIR/stubs"
|
||||
mkdir -p "$STUBS"
|
||||
export STUB_LOG="$TMP_DIR/calls.log"
|
||||
: >"$STUB_LOG"
|
||||
|
||||
stub() { # name; body on stdin
|
||||
{
|
||||
printf '#!/bin/bash\n'
|
||||
cat
|
||||
} >"$STUBS/$1"
|
||||
chmod +x "$STUBS/$1"
|
||||
}
|
||||
stub ip <<'STUB'
|
||||
if [[ $1 == -4 ]]; then
|
||||
echo " inet 192.168.1.5/24 brd 192.168.1.255 scope global eth0"
|
||||
else
|
||||
echo "default via 192.168.1.1 dev eth0"
|
||||
fi
|
||||
STUB
|
||||
stub getent <<'STUB'
|
||||
exit 2
|
||||
STUB
|
||||
stub nmap <<'STUB'
|
||||
echo "nmap $*" >>"$STUB_LOG"
|
||||
exit 1
|
||||
STUB
|
||||
stub rustscan <<'STUB'
|
||||
echo "rustscan $*" >>"$STUB_LOG"
|
||||
printf '192.168.1.5 -> [22]\n192.168.1.43 -> [22]\n192.168.1.26 -> [22]\n'
|
||||
STUB
|
||||
stub pacman <<'STUB'
|
||||
echo "pacman $*" >>"$STUB_LOG"
|
||||
STUB
|
||||
stub sudo <<'STUB'
|
||||
"$@"
|
||||
STUB
|
||||
# sshpass -p PW ssh ... user@IP CMD : only .43 is the Pi.
|
||||
stub sshpass <<'STUB'
|
||||
for a in "$@"; do [[ $a == *@* ]] && ip=${a#*@}; done
|
||||
echo "ssh $ip" >>"$STUB_LOG"
|
||||
if [[ $ip == 192.168.1.43 ]]; then
|
||||
case "${*: -1}" in hostname) echo nextcloud-pi ;; *) echo ok ;; esac
|
||||
fi
|
||||
STUB
|
||||
|
||||
log_info() { :; }
|
||||
log_success() { :; }
|
||||
die() {
|
||||
printf 'die: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
export PI_HOSTNAME=nextcloud-pi PI_USER=pi PI_PASSWORD=pw REMOTE_LAPTOP_IP=192.168.1.17
|
||||
|
||||
# Runs discover_raspberry_pi from the given lib with every external stubbed.
|
||||
check_discover() { # lib file
|
||||
: >"$STUB_LOG"
|
||||
(
|
||||
PATH="$STUBS:$PATH"
|
||||
# Real ensure_dependencies (no-op: rustscan/sshpass stubs are on PATH).
|
||||
# shellcheck source=../features/lib/rpi_nc_deps.sh
|
||||
source "$FEATURES/lib/rpi_nc_deps.sh"
|
||||
# shellcheck source=/dev/null
|
||||
source "$FEATURES/lib/$1"
|
||||
found=$(discover_raspberry_pi)
|
||||
[[ $found == 192.168.1.43 ]] || fail "$1 found '$found', want 192.168.1.43"
|
||||
)
|
||||
grep -qx 'rustscan -a 192.168.1.0/24 -p 22 -g' "$STUB_LOG" ||
|
||||
fail "$1: rustscan not called with '-a 192.168.1.0/24 -p 22 -g': $(tr '\n' '|' <"$STUB_LOG")"
|
||||
if grep -q '^nmap' "$STUB_LOG"; then fail "$1 ran nmap"; fi
|
||||
if grep -qx 'ssh 192.168.1.5' "$STUB_LOG"; then fail "$1 tried our own IP"; fi
|
||||
printf 'ok discover %s\n' "$1"
|
||||
}
|
||||
check_discover nc_discover.sh
|
||||
check_discover rpi_nc_deps.sh
|
||||
|
||||
# --- ensure_dependencies asks for rustscan, not nmap ------------------------
|
||||
: >"$STUB_LOG"
|
||||
rm -f "$STUBS/rustscan" "$STUBS/sshpass" # hide them so the install branch runs
|
||||
(
|
||||
PATH="$STUBS"
|
||||
hash -r
|
||||
# shellcheck source=../features/lib/rpi_nc_deps.sh
|
||||
source "$FEATURES/lib/rpi_nc_deps.sh"
|
||||
ensure_dependencies >/dev/null 2>&1 || true
|
||||
)
|
||||
grep -q '^pacman -S --noconfirm rustscan' "$STUB_LOG" ||
|
||||
fail "ensure_dependencies did not request rustscan: $(tr '\n' '|' <"$STUB_LOG")"
|
||||
grep -E '^pacman .*nmap' "$STUB_LOG" && fail "ensure_dependencies still requests nmap"
|
||||
printf 'ok rpi_nc_deps dependencies\n'
|
||||
|
||||
# --- no remaining nmap invocation in the set --------------------------------
|
||||
for f in lib/nc_discover.sh lib/nc_remote.sh lib/rpi_nc_deps.sh lib/nc_flash.sh \
|
||||
lib/nc_image.sh lib/pi_flash_remote.sh raspberry_pi_flash_sd.sh; do
|
||||
if grep -nE '(^|[^[:alnum:]_-])nmap([^[:alnum:]_-]|$)' "$FEATURES/$f"; then
|
||||
fail "$f still mentions nmap"
|
||||
fi
|
||||
done
|
||||
printf 'ok no nmap references\n'
|
||||
Loading…
Reference in New Issue
Block a user