diff --git a/scripts/network/network-diagnostics.sh b/scripts/network/network-diagnostics.sh index d2295ed..be48f6e 100755 --- a/scripts/network/network-diagnostics.sh +++ b/scripts/network/network-diagnostics.sh @@ -3,99 +3,160 @@ set -euo pipefail show_help() { cat <<'EOF' -Usage: $(basename "$0") [options] +Usage: network-diagnostics.sh [options] Options: -h, --help Show this help message and exit -Runs a quick network diagnostics suite: - * Checks internet connectivity (curl or wget) - * Performs a DNS lookup for a default host (google.com) - * Pings a default IP (8.8.8.8) - * Checks common ports (80, 443) on the default gateway -The script reports success/failure for each step but never modifies the system. +Description: + Runs a comprehensive non-destructive network diagnostics suite: + * Internet HTTP/HTTPS reachability + * DNS resolution test + * ICMP Ping connectivity + * Gateway detection and reachability + Clearly reports test status using [PASS], [FAIL], [SKIPPED], [UNKNOWN]. EOF exit 0 } -# Default values -host="google.com" -ping_ip="8.8.8.8" -ports="80 443" - -# Parse arguments for arg in "$@"; do case "$arg" in - -h|--help) show_help ;; - *) echo "Unknown option: $arg" >&2; exit 1 ;; + -h|--help) + show_help + ;; + *) + echo "Error: Unknown option: $arg" >&2 + exit 1 + ;; esac done -echo "--- Network diagnostics start ---" +echo "=== Network Diagnostics Suite ===" +echo "" + +PASS_COUNT=0 +FAIL_COUNT=0 +SKIP_COUNT=0 +UNKNOWN_COUNT=0 + +report_pass() { + echo " [PASS] $1" + PASS_COUNT=$((PASS_COUNT + 1)) +} + +report_fail() { + echo " [FAIL] $1" + FAIL_COUNT=$((FAIL_COUNT + 1)) +} + +report_skip() { + echo " [SKIPPED] $1" + SKIP_COUNT=$((SKIP_COUNT + 1)) +} + +report_unknown() { + echo " [UNKNOWN] $1" + UNKNOWN_COUNT=$((UNKNOWN_COUNT + 1)) +} -# 1. Internet connectivity +# 1. Internet Connectivity +echo "[1/4] Checking internet reachability..." if command -v curl >/dev/null 2>&1; then - echo "Testing internet reachability with curl..." - if curl -s -o /dev/null --max-time 5 "https://www.google.com"; then - echo "Internet reachable (curl)." + if curl -s -o /dev/null --connect-timeout 4 --max-time 6 "https://1.1.1.1" || curl -s -o /dev/null --connect-timeout 4 --max-time 6 "https://8.8.8.8"; then + report_pass "Direct IP HTTPS reachability" else - echo "Internet NOT reachable (curl)." + report_fail "Could not reach internet endpoints via HTTPS" fi elif command -v wget >/dev/null 2>&1; then - echo "Testing internet reachability with wget..." - if wget -q --timeout=5 --spider "https://www.google.com"; then - echo "Internet reachable (wget)." + if wget -q --timeout=5 --spider "https://1.1.1.1" || wget -q --timeout=5 --spider "https://8.8.8.8"; then + report_pass "Direct IP HTTPS reachability (wget)" else - echo "Internet NOT reachable (wget)." + report_fail "Could not reach internet endpoints via HTTPS (wget)" fi else - echo "Neither curl nor wget installed; skipping internet test." + report_skip "Neither curl nor wget is installed" fi -# 2. DNS lookup +# 2. DNS Resolution +echo "[2/4] Testing DNS resolution..." +TEST_DOMAINS=("cloudflare.com" "google.com") +DNS_SUCCESS=false + if command -v dig >/dev/null 2>&1; then - echo "Performing DNS lookup for $host..." - dig +short "$host" | head -n 3 || echo "DNS lookup failed." + for dom in "${TEST_DOMAINS[@]}"; do + if dig +short +time=3 +tries=1 "$dom" 2>/dev/null | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'; then + report_pass "Resolved $dom via dig" + DNS_SUCCESS=true + break + fi + done + if ! $DNS_SUCCESS; then + report_fail "DNS resolution failed for test domains via dig" + fi elif command -v nslookup >/dev/null 2>&1; then - echo "Performing DNS lookup for $host..." - nslookup "$host" | awk '/^Address: / {print $2}' | head -n 3 || echo "DNS lookup failed." + for dom in "${TEST_DOMAINS[@]}"; do + if nslookup "$dom" 2>/dev/null | awk '/^Address: / {print $2}' | grep -qE '^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+'; then + report_pass "Resolved $dom via nslookup" + DNS_SUCCESS=true + break + fi + done + if ! $DNS_SUCCESS; then + report_fail "DNS resolution failed for test domains via nslookup" + fi +elif command -v getent >/dev/null 2>&1; then + if getent hosts "${TEST_DOMAINS[0]}" >/dev/null 2>&1; then + report_pass "Resolved ${TEST_DOMAINS[0]} via getent hosts" + else + report_fail "DNS resolution failed via getent" + fi else - echo "Neither dig nor nslookup installed; skipping DNS test." + report_skip "No DNS diagnostic tool (dig/nslookup/getent) available" fi -# 3. Ping test +# 3. ICMP Ping Test +echo "[3/4] Testing ICMP reachability..." if command -v ping >/dev/null 2>&1; then - echo "Pinging $ping_ip..." - if ping -c 2 "$ping_ip" >/dev/null; then - echo "Ping successful." + # Try pinging 1.1.1.1 or 8.8.8.8 (1 packet, 3s timeout) + if ping -c 1 -W 3 1.1.1.1 >/dev/null 2>&1 || ping -c 1 -W 3 8.8.8.8 >/dev/null 2>&1; then + report_pass "ICMP ping to public resolver responded" else - echo "Ping failed." + # ICMP is frequently filtered by cloud firewalls / ISP networks + report_unknown "Ping unacknowledged (ICMP may be filtered by firewall or network policy)" fi else - echo "ping command not found; skipping ping test." + report_skip "ping utility is not installed" fi -# 4. Port checks on default gateway (if reachable) +# 4. Default Gateway Reachability +echo "[4/4] Checking default gateway..." +GATEWAY="" if command -v ip >/dev/null 2>&1; then - gateway=$(ip route | awk '/default/ {print $3}' | head -n1 || true) - if [[ -n "$gateway" ]]; then - echo "Default gateway detected: $gateway" - for p in $ports; do - echo "Checking TCP port $p on $gateway..." - if command -v nc >/dev/null 2>&1; then - nc -z -w2 "$gateway" "$p" && echo "Port $p open" || echo "Port $p closed" - elif command -v timeout >/dev/null 2>&1 && command -v bash >/dev/null 2>&1; then - timeout 2 bash -c "/dev/null 2>&1; then + if ping -c 1 -W 2 "$GATEWAY" >/dev/null 2>&1; then + report_pass "Default gateway ($GATEWAY) is responsive to ping" + else + report_unknown "Default gateway ($GATEWAY) detected but does not respond to ping" + fi else - echo "Could not determine default gateway; skipping port checks." + report_pass "Default gateway detected: $GATEWAY (ping tool not available to verify)" fi else - echo "ip command not found; cannot determine default gateway." + report_skip "No default gateway route detected in routing table" fi -echo "--- Network diagnostics end ---" +echo "" +echo "=== Diagnostic Summary ===" +echo "Passed: $PASS_COUNT" +echo "Failed: $FAIL_COUNT" +echo "Skipped: $SKIP_COUNT" +echo "Unknown: $UNKNOWN_COUNT" + +if [[ $FAIL_COUNT -gt 0 ]]; then + exit 1 +fi +exit 0 diff --git a/scripts/process/kill-process.sh b/scripts/process/kill-process.sh index 7bea548..3feab01 100755 --- a/scripts/process/kill-process.sh +++ b/scripts/process/kill-process.sh @@ -3,58 +3,100 @@ set -euo pipefail show_help() { cat <<'EOF' -Usage: $(basename "$0") [options] +Usage: kill-process.sh [options] Options: -h, --help Show this help message and exit -s, --signal Signal to send (default: SIGTERM) -f, --force Skip confirmation prompt (use with caution) -Safely terminates a process identified by PID or name. By default the script -asks for confirmation before sending the signal. It validates the PID and -ensures the target is not the current shell. +Description: + Safely terminates a process identified by PID or name. + - Prompts for confirmation unless -f / --force is given. + - Safeguards against terminating init (PID 1), current shell ($$), + or parent process ($PPID). EOF exit 0 } -# Default values signal="SIGTERM" force="no" +target="" -# Parse arguments while (( "$#" )); do case "$1" in - -h|--help) show_help;; + -h|--help) + show_help + ;; -s|--signal) - signal="$2" - shift 2 + if [[ -n "${2-}" && "${2-}" != -* ]]; then + signal="$2" + shift + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi ;; -f|--force) force="yes" - shift + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 ;; *) - target="$1" - shift + if [[ -z "$target" ]]; then + target="$1" + else + echo "Error: Unexpected additional argument: $1" >&2 + exit 1 + fi ;; esac + shift done -if [[ -z "${target:-}" ]]; then - echo "Error: PID or process name is required." - show_help +if [[ -z "$target" ]]; then + echo "Error: PID or process name is required." >&2 + echo "Run '$0 --help' for usage." >&2 + exit 1 +fi + +# Validate signal +if ! kill -l "$signal" >/dev/null 2>&1; then + echo "Error: Invalid signal '$signal'." >&2 + exit 1 fi +declare -a pids=() + # Resolve target to PID(s) if [[ "$target" =~ ^[0-9]+$ ]]; then - read -ra pids <<< "$target" + if [[ "$target" -eq 1 ]]; then + echo "Error: Refusing to send signal to PID 1 (init/systemd)." >&2 + exit 1 + fi + if [[ "$target" -eq 0 ]]; then + echo "Error: Refusing to send signal to PID 0." >&2 + exit 1 + fi + if [[ "$target" -eq $$ || "$target" -eq $PPID ]]; then + echo "Error: Refusing to send signal to self ($$) or parent ($PPID)." >&2 + exit 1 + fi + pids=("$target") else - # Use pgrep to find matching processes (exclude this script) - mapfile -t pids < <(pgrep -f "${target}" | grep -vw $$ || true) + # Use pgrep to find matching processes, filtering out self and parent + while IFS= read -r matched_pid; do + [[ -z "$matched_pid" ]] && continue + if [[ "$matched_pid" -ne $$ && "$matched_pid" -ne $PPID && "$matched_pid" -ne 1 ]]; then + pids+=("$matched_pid") + fi + done < <(pgrep -f "${target}" || true) fi if [[ ${#pids[@]} -eq 0 ]]; then - echo "No matching processes found for '$target'." + echo "No matching eligible processes found for '$target'." exit 1 fi @@ -70,7 +112,8 @@ fi for pid in "${pids[@]}"; do if ! kill -0 "$pid" 2>/dev/null; then - echo "Process $pid no longer exists."; continue + echo "Process $pid no longer exists." + continue fi echo "Sending $signal to PID $pid..." kill -s "$signal" "$pid" || echo "Failed to send signal to $pid" diff --git a/scripts/ssh/ssh-key-setup.sh b/scripts/ssh/ssh-key-setup.sh index 9827d09..88d97b7 100755 --- a/scripts/ssh/ssh-key-setup.sh +++ b/scripts/ssh/ssh-key-setup.sh @@ -1,5 +1,4 @@ #!/usr/bin/env bash - set -euo pipefail show_help() { @@ -10,11 +9,15 @@ Options: -h, --help Show this help message and exit -t, --type TYPE Key type to generate (ed25519 or rsa; default: ed25519) -c, --comment TEXT Key comment (default: user@hostname) - -f, --force Overwrite existing key if present (with confirmation) + -f, --force Allow overwriting existing key (backs up original) + -y, --yes Skip confirmation prompts (requires -f if key exists) + -p, --passphrase P Specify key passphrase (empty string for no passphrase) Description: - Checks existing SSH keys and optionally generates a new secure key pair. - Defaults to modern Ed25519 keys and never overwrites existing keys without confirmation. + Inspects existing SSH keys and generates a modern, secure SSH key pair. + - Defaults to Ed25519 (or RSA-4096 if rsa is requested). + - Never overwrites an existing key without confirmation and automatic backup. + - Ensures correct file permissions (0700 for ~/.ssh, 0600 for private key). EOF exit 0 } @@ -22,37 +25,85 @@ EOF KEY_TYPE="ed25519" COMMENT="" FORCE=false +AUTOYES=false +PASSPHRASE="" +PASSPHRASE_SET=false while (( "$#" )); do case "$1" in - -h|--help) show_help;; + -h|--help) + show_help + ;; -t|--type) - shift - KEY_TYPE="${1:-ed25519}" + if [[ -n "${2-}" && "${2-}" != -* ]]; then + KEY_TYPE="${2,,}" + shift + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi ;; -c|--comment) - shift - COMMENT="${1:-}" + if [[ -n "${2-}" && "${2-}" != -* ]]; then + COMMENT="$2" + shift + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + -f|--force) + FORCE=true + ;; + -y|--yes) + AUTOYES=true + ;; + -p|--passphrase) + if [[ $# -ge 2 ]]; then + PASSPHRASE="$2" + PASSPHRASE_SET=true + shift + else + echo "Error: Argument for $1 is missing" >&2 + exit 1 + fi + ;; + -*) + echo "Error: Unknown option: $1" >&2 + exit 1 + ;; + *) + echo "Error: Unexpected argument: $1" >&2 + exit 1 ;; - -f|--force) FORCE=true;; - *) echo "Unknown option: $1" >&2; exit 1;; esac shift done -if [[ -z "$COMMENT" ]]; then - if command -v hostname >/dev/null 2>&1; then - hname=$(hostname) - else - hname=$(uname -n) - fi - COMMENT="$(whoami)@$hname" +# Validate key type +case "$KEY_TYPE" in + ed25519|rsa) ;; + *) + echo "Error: Unsupported key type '$KEY_TYPE'. Supported types: ed25519, rsa." >&2 + exit 1 + ;; +esac + +if ! command -v ssh-keygen >/dev/null 2>&1; then + echo "Error: 'ssh-keygen' utility is not installed." >&2 + exit 1 fi -SSH_DIR="$HOME/.ssh" +SSH_DIR="${HOME:-.}/.ssh" mkdir -p "$SSH_DIR" chmod 700 "$SSH_DIR" +if [[ -z "$COMMENT" ]]; then + HNAME="$(hostname 2>/dev/null || uname -n)" + UNAME="$(whoami 2>/dev/null || echo "user")" + COMMENT="${UNAME}@${HNAME}" +fi + TARGET_KEY="$SSH_DIR/id_$KEY_TYPE" echo "=== SSH Key Setup ===" @@ -64,32 +115,61 @@ for pub in "$SSH_DIR"/*.pub; do existing_keys=$((existing_keys + 1)) fi done +if [[ $existing_keys -eq 0 ]]; then + echo " (No existing public keys found)" +fi +echo "" if [[ -f "$TARGET_KEY" ]]; then - echo "Target key '$TARGET_KEY' already exists." - if [[ "$FORCE" == false ]]; then - echo "Use -f / --force to regenerate this key." - exit 0 + echo "Warning: Target key '$TARGET_KEY' already exists." + if ! $FORCE; then + echo "Aborted: Use -f / --force to allow overwriting this key." >&2 + exit 1 fi - read -rp "Are you sure you want to overwrite '$TARGET_KEY'? [y/N]: " confirm - case "$confirm" in - [yY]|[yY][eE][sS]) ;; - *) echo "Operation cancelled."; exit 0;; - esac + + if ! $AUTOYES; then + read -r -p "Overwrite '$TARGET_KEY'? Existing key will be backed up. [y/N]: " confirm + case "$confirm" in + [yY]|[yY][eE][sS]) ;; + *) + echo "Operation cancelled." + exit 0 + ;; + esac + fi + + # Create backup before overwrite + BACKUP_TIME="$(date +%Y%m%d_%H%M%S)" + cp -p "$TARGET_KEY" "${TARGET_KEY}.bak_${BACKUP_TIME}" + if [[ -f "${TARGET_KEY}.pub" ]]; then + cp -p "${TARGET_KEY}.pub" "${TARGET_KEY}.pub.bak_${BACKUP_TIME}" + fi + echo "Original key backed up to '${TARGET_KEY}.bak_${BACKUP_TIME}'" + rm -f "$TARGET_KEY" "${TARGET_KEY}.pub" fi -if ! command -v ssh-keygen >/dev/null 2>&1; then - echo "Error: 'ssh-keygen' command not found. Install openssh-client." >&2 - exit 1 +declare -a GEN_OPTS=(-t "$KEY_TYPE" -C "$COMMENT" -f "$TARGET_KEY") +if [[ "$KEY_TYPE" == "rsa" ]]; then + GEN_OPTS+=(-b 4096) +fi + +if $PASSPHRASE_SET; then + GEN_OPTS+=(-N "$PASSPHRASE") fi echo "Generating $KEY_TYPE SSH key pair..." -ssh-keygen -t "$KEY_TYPE" -C "$COMMENT" -f "$TARGET_KEY" +ssh-keygen "${GEN_OPTS[@]}" + +chmod 600 "$TARGET_KEY" +chmod 644 "${TARGET_KEY}.pub" echo "" echo "Key successfully generated!" echo "Private key: $TARGET_KEY" echo "Public key : $TARGET_KEY.pub" echo "" +echo "Public key fingerprint:" +ssh-keygen -lf "${TARGET_KEY}.pub" +echo "" echo "Public key contents:" -cat "$TARGET_KEY.pub" +cat "${TARGET_KEY}.pub" diff --git a/scripts/storage/disk-health.sh b/scripts/storage/disk-health.sh index 01a9729..aadfcc4 100755 --- a/scripts/storage/disk-health.sh +++ b/scripts/storage/disk-health.sh @@ -3,44 +3,110 @@ set -euo pipefail show_help() { cat <<'EOF' -Usage: $(basename "$0") [options] +Usage: disk-health.sh [options] [device] Options: -h, --help Show this help message and exit -Scans all attached block devices for SMART health status using smartctl. -If smartctl is not installed, the script will inform the user. +Description: + Inspects S.M.A.R.T. health status of disk block devices using smartctl. + If [device] is specified (e.g. /dev/sda or /dev/nvme0n1), checks only + that device. Otherwise scans attached physical disks. + Note: Requires root/sudo privileges to query raw drive hardware. EOF exit 0 } -# Parse arguments +TARGET_DEV="" + for arg in "$@"; do case "$arg" in - -h|--help) show_help ;; - *) echo "Unknown option: $arg" >&2; exit 1 ;; + -h|--help) + show_help + ;; + -*) + echo "Error: Unknown option: $arg" >&2 + exit 1 + ;; + *) + if [[ -z "$TARGET_DEV" ]]; then + TARGET_DEV="$1" + else + echo "Error: Unexpected additional argument: $1" >&2 + exit 1 + fi + ;; esac done if ! command -v smartctl >/dev/null 2>&1; then - echo "smartctl not found. Please install smartmontools to use this script." + echo "smartctl not found. Please install smartmontools (e.g. pacman -S smartmontools, apt install smartmontools)." >&2 exit 1 fi -# Identify block devices (exclude partitions) -devices=$(lsblk -dn -o NAME,TYPE | awk '$2=="disk" {print "/dev/"$1}') -if [[ -z "$devices" ]]; then - echo "No block devices found." +SUDO_CMD=() +if [[ $EUID -ne 0 ]]; then + if command -v sudo >/dev/null 2>&1; then + SUDO_CMD=(sudo) + else + echo "Warning: Running as non-root without sudo. SMART queries may fail with permission denied." >&2 + fi +fi + +declare -a DISKS=() + +if [[ -n "$TARGET_DEV" ]]; then + if [[ ! -b "$TARGET_DEV" ]]; then + echo "Error: Device '$TARGET_DEV' is not a valid block device." >&2 + exit 1 + fi + DISKS=("$TARGET_DEV") +else + # Discover physical disks using lsblk if available, otherwise check /sys/block + if command -v lsblk >/dev/null 2>&1; then + while IFS= read -r dev; do + [[ -n "$dev" ]] && DISKS+=("/dev/$dev") + done < <(lsblk -dn -o NAME,TYPE 2>/dev/null | awk '$2=="disk" && $1 !~ /^(loop|zram|ram)/ {print $1}' || true) + fi + + if [[ ${#DISKS[@]} -eq 0 && -d /sys/block ]]; then + for node in /sys/block/*; do + devname="$(basename "$node")" + if [[ "$devname" =~ ^(sd[a-z]|nvme[0-9]+n[0-9]+|hd[a-z]|vd[a-z]) ]]; then + DISKS+=("/dev/$devname") + fi + done + fi +fi + +if [[ ${#DISKS[@]} -eq 0 ]]; then + echo "No physical block devices detected for SMART health checking." exit 0 fi -for dev in $devices; do - echo "=== $dev ===" - # Run health check; ignore non-SMART capable devices - if smartctl -i "$dev" | grep -q "SMART support is: Available"; then - smartctl -H "$dev" || true +echo "=== S.M.A.R.T. Disk Health Check ===" +echo "" + +for dev in "${DISKS[@]}"; do + echo "--- Device: $dev ---" + + # Check if SMART is supported and enabled + smart_info="$("${SUDO_CMD[@]}" smartctl -i "$dev" 2>&1 || true)" + + if echo "$smart_info" | grep -qi "SMART support is: Available"; then + echo "SMART: Supported" + + # Query health status + health_output="$("${SUDO_CMD[@]}" smartctl -H "$dev" 2>&1 || true)" + if echo "$health_output" | grep -qiE "PASSED|OK"; then + echo "Status: [HEALTHY] Self-Assessment Test Result: PASSED" + elif echo "$health_output" | grep -qi "FAILED"; then + echo "Status: [CRITICAL WARNING] Self-Assessment Test Result: FAILED!" + else + echo "Status: [UNKNOWN] Check detailed smartctl output." + fi else - echo "SMART not supported on $dev" + echo "SMART: Not supported or unavailable on this device (e.g. virtualized, container, or USB enclosure)." fi - echo + echo "" done