mirror of
https://github.com/kuhyx/testsAndMisc.git
synced 2026-11-10 18:07:06 +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"
|
||||
|
||||
@ -6,136 +6,136 @@
|
||||
# and the variables defined above the source line.
|
||||
|
||||
discover_remote_laptop() {
|
||||
log_info "Auto-discovering remote laptop on local network..."
|
||||
log_info "Auto-discovering remote laptop on local network..."
|
||||
|
||||
ensure_dependencies
|
||||
ensure_dependencies
|
||||
|
||||
local my_ip
|
||||
my_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)(?!127\.)\d+(\.\d+){3}' | head -1)
|
||||
local my_ip
|
||||
my_ip=$(ip -4 addr show | grep -oP '(?<=inet\s)(?!127\.)\d+(\.\d+){3}' | head -1)
|
||||
|
||||
local gateway
|
||||
gateway=$(ip route | grep default | awk '{print $3}' | head -1)
|
||||
local network="${gateway%.*}.0/24"
|
||||
local gateway
|
||||
gateway=$(ip route | grep default | awk '{print $3}' | head -1)
|
||||
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 "Local IP: $my_ip, Gateway: $gateway, Network: $network"
|
||||
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)
|
||||
local ssh_hosts
|
||||
# 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"
|
||||
fi
|
||||
if [[ -z $ssh_hosts ]]; then
|
||||
die "No SSH-enabled devices found on network"
|
||||
fi
|
||||
|
||||
local host_count
|
||||
host_count=$(echo "$ssh_hosts" | wc -l)
|
||||
log_info "Found $host_count SSH-enabled device(s): $(echo "$ssh_hosts" | tr '\n' ' ')"
|
||||
local host_count
|
||||
host_count=$(echo "$ssh_hosts" | wc -l)
|
||||
log_info "Found $host_count SSH-enabled device(s): $(echo "$ssh_hosts" | tr '\n' ' ')"
|
||||
|
||||
local common_users=("$REMOTE_LAPTOP_USER" "kuchy" "kuhy" "$(whoami)" "pi" "user" "admin")
|
||||
local users=()
|
||||
for u in "${common_users[@]}"; do
|
||||
local is_dup=0
|
||||
for existing in "${users[@]}"; do
|
||||
if [[ $u == "$existing" ]]; then
|
||||
is_dup=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ $is_dup -eq 0 ]]; then
|
||||
users+=("$u")
|
||||
fi
|
||||
done
|
||||
local common_users=("$REMOTE_LAPTOP_USER" "kuchy" "kuhy" "$(whoami)" "pi" "user" "admin")
|
||||
local users=()
|
||||
for u in "${common_users[@]}"; do
|
||||
local is_dup=0
|
||||
for existing in "${users[@]}"; do
|
||||
if [[ $u == "$existing" ]]; then
|
||||
is_dup=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ $is_dup -eq 0 ]]; then
|
||||
users+=("$u")
|
||||
fi
|
||||
done
|
||||
|
||||
log_info "Will try usernames: ${users[*]}"
|
||||
log_info "Will try usernames: ${users[*]}"
|
||||
|
||||
local found_laptop=""
|
||||
local found_user=""
|
||||
local idx=0
|
||||
local found_laptop=""
|
||||
local found_user=""
|
||||
local idx=0
|
||||
|
||||
for ip in $ssh_hosts; do
|
||||
idx=$((idx + 1))
|
||||
for ip in $ssh_hosts; do
|
||||
idx=$((idx + 1))
|
||||
|
||||
if [[ $ip == "$gateway" ]]; then
|
||||
log_info "[$idx/$host_count] Skipping $ip (gateway)"
|
||||
continue
|
||||
fi
|
||||
if [[ $ip == "$gateway" ]]; then
|
||||
log_info "[$idx/$host_count] Skipping $ip (gateway)"
|
||||
continue
|
||||
fi
|
||||
|
||||
log_info "[$idx/$host_count] $ip - Trying SSH key access with common usernames..."
|
||||
log_info "[$idx/$host_count] $ip - Trying SSH key access with common usernames..."
|
||||
|
||||
for try_user in "${users[@]}"; do
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=2 -o StrictHostKeyChecking=accept-new "${try_user}@${ip}" "echo ok" 2> /dev/null | grep -q "ok"; then
|
||||
log_success "[$idx/$host_count] $ip - SSH key access confirmed with user '$try_user'!"
|
||||
found_user="$try_user"
|
||||
for try_user in "${users[@]}"; do
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=2 -o StrictHostKeyChecking=accept-new "${try_user}@${ip}" "echo ok" 2>/dev/null | grep -q "ok"; then
|
||||
log_success "[$idx/$host_count] $ip - SSH key access confirmed with user '$try_user'!"
|
||||
found_user="$try_user"
|
||||
|
||||
log_info "[$idx/$host_count] $ip - Checking for SD card..."
|
||||
local has_sd
|
||||
has_sd=$(ssh -o BatchMode=yes -o ConnectTimeout=2 "${try_user}@${ip}" "lsblk -d -o NAME,RM,TRAN 2>/dev/null | grep -E '1.*(usb|mmc)' | head -1" 2> /dev/null || true)
|
||||
log_info "[$idx/$host_count] $ip - Checking for SD card..."
|
||||
local has_sd
|
||||
has_sd=$(ssh -o BatchMode=yes -o ConnectTimeout=2 "${try_user}@${ip}" "lsblk -d -o NAME,RM,TRAN 2>/dev/null | grep -E '1.*(usb|mmc)' | head -1" 2>/dev/null || true)
|
||||
|
||||
if [[ -n $has_sd ]]; then
|
||||
log_success "[$idx/$host_count] $ip - Found SD card: $has_sd"
|
||||
found_laptop="$ip"
|
||||
break 2
|
||||
else
|
||||
log_warning "[$idx/$host_count] $ip - No SD card detected, saving as fallback..."
|
||||
if [[ -z $found_laptop ]]; then
|
||||
found_laptop="$ip"
|
||||
fi
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
if [[ -n $has_sd ]]; then
|
||||
log_success "[$idx/$host_count] $ip - Found SD card: $has_sd"
|
||||
found_laptop="$ip"
|
||||
break 2
|
||||
else
|
||||
log_warning "[$idx/$host_count] $ip - No SD card detected, saving as fallback..."
|
||||
if [[ -z $found_laptop ]]; then
|
||||
found_laptop="$ip"
|
||||
fi
|
||||
fi
|
||||
break
|
||||
fi
|
||||
done
|
||||
done
|
||||
|
||||
if [[ -z $found_laptop ]] || [[ -z $found_user ]]; then
|
||||
log_warning "No device with passwordless SSH found using common usernames."
|
||||
if [[ -z $found_laptop ]] || [[ -z $found_user ]]; then
|
||||
log_warning "No device with passwordless SSH found using common usernames."
|
||||
|
||||
found_laptop=$(echo "$ssh_hosts" | grep -vw "$gateway" | head -1)
|
||||
found_laptop=$(echo "$ssh_hosts" | grep -vw "$gateway" | head -1)
|
||||
|
||||
if [[ -z $found_laptop ]]; then
|
||||
die "Could not find any suitable SSH-enabled device"
|
||||
fi
|
||||
if [[ -z $found_laptop ]]; then
|
||||
die "Could not find any suitable SSH-enabled device"
|
||||
fi
|
||||
|
||||
log_info "Found SSH host at $found_laptop but need credentials."
|
||||
read -r -p "Enter username for $found_laptop: " found_user
|
||||
log_info "Found SSH host at $found_laptop but need credentials."
|
||||
read -r -p "Enter username for $found_laptop: " found_user
|
||||
|
||||
if [[ -z $found_user ]]; then
|
||||
die "No username provided"
|
||||
fi
|
||||
fi
|
||||
if [[ -z $found_user ]]; then
|
||||
die "No username provided"
|
||||
fi
|
||||
fi
|
||||
|
||||
REMOTE_LAPTOP_IP="$found_laptop"
|
||||
REMOTE_LAPTOP_USER="$found_user"
|
||||
log_success "Selected remote laptop: ${REMOTE_LAPTOP_USER}@${REMOTE_LAPTOP_IP}"
|
||||
REMOTE_LAPTOP_IP="$found_laptop"
|
||||
REMOTE_LAPTOP_USER="$found_user"
|
||||
log_success "Selected remote laptop: ${REMOTE_LAPTOP_USER}@${REMOTE_LAPTOP_IP}"
|
||||
|
||||
save_config
|
||||
save_config
|
||||
}
|
||||
|
||||
setup_ssh_key_to_remote() {
|
||||
local remote_host="$1"
|
||||
local remote_user="$2"
|
||||
local remote_host="$1"
|
||||
local remote_user="$2"
|
||||
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=5 "${remote_user}@${remote_host}" "echo 'SSH key works'" 2> /dev/null; then
|
||||
log_success "SSH key authentication to ${remote_user}@${remote_host} already configured"
|
||||
return 0
|
||||
fi
|
||||
if ssh -o BatchMode=yes -o ConnectTimeout=5 "${remote_user}@${remote_host}" "echo 'SSH key works'" 2>/dev/null; then
|
||||
log_success "SSH key authentication to ${remote_user}@${remote_host} already configured"
|
||||
return 0
|
||||
fi
|
||||
|
||||
log_info "Setting up SSH key authentication to ${remote_user}@${remote_host}..."
|
||||
log_info "Setting up SSH key authentication to ${remote_user}@${remote_host}..."
|
||||
|
||||
if [[ ! -f ~/.ssh/id_rsa.pub ]] && [[ ! -f ~/.ssh/id_ed25519.pub ]]; then
|
||||
log_info "Generating SSH key..."
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N "" -q
|
||||
fi
|
||||
if [[ ! -f ~/.ssh/id_rsa.pub ]] && [[ ! -f ~/.ssh/id_ed25519.pub ]]; then
|
||||
log_info "Generating SSH key..."
|
||||
ssh-keygen -t ed25519 -f ~/.ssh/id_ed25519 -N "" -q
|
||||
fi
|
||||
|
||||
log_info "Copying SSH key to remote host (you may be prompted for password)..."
|
||||
log_info "Copying SSH key to remote host (you may be prompted for password)..."
|
||||
|
||||
if command -v ssh-copy-id &> /dev/null; then
|
||||
ssh-copy-id -o StrictHostKeyChecking=no "${remote_user}@${remote_host}"
|
||||
else
|
||||
local pub_key
|
||||
pub_key=$(cat ~/.ssh/id_ed25519.pub 2> /dev/null || cat ~/.ssh/id_rsa.pub)
|
||||
ssh -o StrictHostKeyChecking=no "${remote_user}@${remote_host}" "mkdir -p ~/.ssh && echo '$pub_key' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
fi
|
||||
if command -v ssh-copy-id &>/dev/null; then
|
||||
ssh-copy-id -o StrictHostKeyChecking=no "${remote_user}@${remote_host}"
|
||||
else
|
||||
local pub_key
|
||||
pub_key=$(cat ~/.ssh/id_ed25519.pub 2>/dev/null || cat ~/.ssh/id_rsa.pub)
|
||||
ssh -o StrictHostKeyChecking=no "${remote_user}@${remote_host}" "mkdir -p ~/.ssh && echo '$pub_key' >> ~/.ssh/authorized_keys && chmod 600 ~/.ssh/authorized_keys"
|
||||
fi
|
||||
|
||||
log_success "SSH key authentication configured"
|
||||
log_success "SSH key authentication configured"
|
||||
}
|
||||
|
||||
@ -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?"
|
||||
|
||||
@ -14,8 +14,8 @@ CONFIG_FILE="${SCRIPT_DIR}/.raspberry_pi.conf"
|
||||
|
||||
# Load configuration from gitignored config file if it exists
|
||||
if [[ -f $CONFIG_FILE ]]; then
|
||||
# shellcheck source=/dev/null
|
||||
source "$CONFIG_FILE"
|
||||
# shellcheck source=/dev/null
|
||||
source "$CONFIG_FILE"
|
||||
fi
|
||||
|
||||
# Configuration - Customize these values (or set in config file)
|
||||
@ -38,34 +38,34 @@ NC='\033[0m'
|
||||
|
||||
# All log functions output to stderr so they don't interfere with function return values
|
||||
log_info() {
|
||||
echo -e "${BLUE}[INFO]${NC} $1" >&2
|
||||
echo -e "${BLUE}[INFO]${NC} $1" >&2
|
||||
}
|
||||
|
||||
log_success() {
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
|
||||
echo -e "${GREEN}[SUCCESS]${NC} $1" >&2
|
||||
}
|
||||
|
||||
log_warning() {
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
|
||||
echo -e "${YELLOW}[WARNING]${NC} $1" >&2
|
||||
}
|
||||
|
||||
log_error() {
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
echo -e "${RED}[ERROR]${NC} $1" >&2
|
||||
}
|
||||
|
||||
die() {
|
||||
log_error "$1"
|
||||
exit 1
|
||||
log_error "$1"
|
||||
exit 1
|
||||
}
|
||||
|
||||
check_root() {
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
die "This script must be run as root. Use: sudo $0"
|
||||
fi
|
||||
if [[ $EUID -ne 0 ]]; then
|
||||
die "This script must be run as root. Use: sudo $0"
|
||||
fi
|
||||
}
|
||||
|
||||
save_config() {
|
||||
cat > "$CONFIG_FILE" << EOF
|
||||
cat >"$CONFIG_FILE" <<EOF
|
||||
# Raspberry Pi Setup - Auto-generated config
|
||||
# This file is gitignored and stores discovered settings
|
||||
|
||||
@ -81,22 +81,22 @@ PI_TIMEZONE="${PI_TIMEZONE}"
|
||||
# Generated passwords (KEEP THIS FILE SECURE!)
|
||||
PI_PASSWORD="${PI_PASSWORD}"
|
||||
EOF
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
log_info "Configuration saved to $CONFIG_FILE"
|
||||
chmod 600 "$CONFIG_FILE"
|
||||
log_info "Configuration saved to $CONFIG_FILE"
|
||||
}
|
||||
|
||||
generate_password() {
|
||||
local length="${1:-16}"
|
||||
local chars
|
||||
chars=$(dd if=/dev/urandom bs=256 count=1 2> /dev/null | tr -dc 'A-Za-z0-9!@#$%&*' | cut -c1-"$length")
|
||||
echo "$chars"
|
||||
local length="${1:-16}"
|
||||
local chars
|
||||
chars=$(dd if=/dev/urandom bs=256 count=1 2>/dev/null | tr -dc 'A-Za-z0-9!@#$%&*' | cut -c1-"$length")
|
||||
echo "$chars"
|
||||
}
|
||||
|
||||
auto_generate_pi_password() {
|
||||
if [[ -z $PI_PASSWORD ]]; then
|
||||
PI_PASSWORD=$(generate_password 16)
|
||||
log_info "Auto-generated Pi password (will be saved to config file)"
|
||||
fi
|
||||
if [[ -z $PI_PASSWORD ]]; then
|
||||
PI_PASSWORD=$(generate_password 16)
|
||||
log_info "Auto-generated Pi password (will be saved to config file)"
|
||||
fi
|
||||
}
|
||||
|
||||
# =============================================================================
|
||||
@ -104,31 +104,31 @@ auto_generate_pi_password() {
|
||||
# =============================================================================
|
||||
|
||||
ensure_dependencies() {
|
||||
local missing_packages=()
|
||||
local missing_packages=()
|
||||
|
||||
if ! command -v nmap &> /dev/null; then
|
||||
missing_packages+=("nmap")
|
||||
fi
|
||||
if ! command -v rustscan &>/dev/null; then
|
||||
missing_packages+=("rustscan")
|
||||
fi
|
||||
|
||||
if ! command -v sshpass &> /dev/null; then
|
||||
missing_packages+=("sshpass")
|
||||
fi
|
||||
if ! command -v sshpass &>/dev/null; then
|
||||
missing_packages+=("sshpass")
|
||||
fi
|
||||
|
||||
if [[ ${#missing_packages[@]} -gt 0 ]]; then
|
||||
log_info "Installing missing packages: ${missing_packages[*]}"
|
||||
if [[ ${#missing_packages[@]} -gt 0 ]]; then
|
||||
log_info "Installing missing packages: ${missing_packages[*]}"
|
||||
|
||||
if command -v pacman &> /dev/null; then
|
||||
sudo pacman -S --noconfirm "${missing_packages[@]}"
|
||||
elif command -v apt-get &> /dev/null; then
|
||||
sudo apt-get update && sudo apt-get install -y "${missing_packages[@]}"
|
||||
elif command -v dnf &> /dev/null; then
|
||||
sudo dnf install -y "${missing_packages[@]}"
|
||||
else
|
||||
die "Could not detect package manager. Please install manually: ${missing_packages[*]}"
|
||||
fi
|
||||
if command -v pacman &>/dev/null; then
|
||||
sudo pacman -S --noconfirm "${missing_packages[@]}"
|
||||
elif command -v apt-get &>/dev/null; then
|
||||
sudo apt-get update && sudo apt-get install -y "${missing_packages[@]}"
|
||||
elif command -v dnf &>/dev/null; then
|
||||
sudo dnf install -y "${missing_packages[@]}"
|
||||
else
|
||||
die "Could not detect package manager. Please install manually: ${missing_packages[*]}"
|
||||
fi
|
||||
|
||||
log_success "Dependencies installed"
|
||||
fi
|
||||
log_success "Dependencies installed"
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck source=lib/pi_flash_remote.sh
|
||||
@ -138,29 +138,28 @@ 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}"
|
||||
local command="${1:-help}"
|
||||
|
||||
case "$command" in
|
||||
local)
|
||||
phase_flash_local
|
||||
;;
|
||||
remote)
|
||||
phase_flash_remote
|
||||
;;
|
||||
execute-remote)
|
||||
phase_execute_remote "${2:-}"
|
||||
;;
|
||||
help | --help | -h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
case "$command" in
|
||||
local)
|
||||
phase_flash_local
|
||||
;;
|
||||
remote)
|
||||
phase_flash_remote
|
||||
;;
|
||||
execute-remote)
|
||||
phase_execute_remote "${2:-}"
|
||||
;;
|
||||
help | --help | -h)
|
||||
show_help
|
||||
;;
|
||||
*)
|
||||
log_error "Unknown command: $command"
|
||||
show_help
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
main "$@"
|
||||
|
||||
@ -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