diff --git a/config/approved_targets.txt b/config/approved_targets.txt new file mode 100644 index 0000000..c577a0e --- /dev/null +++ b/config/approved_targets.txt @@ -0,0 +1,14 @@ +# Approved targets for scan/capture scripts (nmap.sh, host.sh, scanPlus.sh, +# live_network_monitor.sh). One entry per line. +# +# - Exact hostnames, IPs, or network interface names are matched as-is. +# - Glob patterns are supported, e.g. 10.0.0.*, *.internal.example.com. +# - Lines starting with # are comments. +# +# Every scan/capture run also requires AUTHORIZED_TICKET to be set to a +# change/work-order reference — this file alone does not authorize a run. +# Keep this list under change control: additions should map to an actual +# approved engagement or maintenance window, not be added ad hoc. + +127.0.0.1 +localhost diff --git a/lib/authorization.sh b/lib/authorization.sh new file mode 100755 index 0000000..3e4708b --- /dev/null +++ b/lib/authorization.sh @@ -0,0 +1,48 @@ +#!/bin/bash +# Authorization gate for scan/capture tools. CJIS-adjacent environments +# require scanning and monitoring to be explicitly authorized, not just +# recorded after the fact — this checks a target against an approved- +# targets allowlist and requires a ticket/work-order reference before the +# caller is allowed to proceed. Depends on audit_log() (source +# lib/audit_log.sh first). + +AUTH_ALLOWLIST="${AUTH_ALLOWLIST:-$REPO_ROOT/config/approved_targets.txt}" + +# require_authorization +# Exits the calling script if AUTHORIZED_TICKET is unset or the target +# isn't on the allowlist. Every denial and grant is audit-logged. +require_authorization() { + local script_name="$1" target="$2" + + if [ -z "${AUTHORIZED_TICKET:-}" ]; then + echo "Refusing to run: set AUTHORIZED_TICKET to the change/work-order reference authorizing this action." >&2 + audit_log "$script_name" "authorization_denied" "$target" 1 + exit 1 + fi + + if [ ! -f "$AUTH_ALLOWLIST" ]; then + echo "Refusing to run: no approved-targets allowlist found at $AUTH_ALLOWLIST." >&2 + audit_log "$script_name" "authorization_denied" "$target" 1 + exit 1 + fi + + local pattern matched=0 + while IFS= read -r pattern; do + [ -z "$pattern" ] && continue + case "$pattern" in + \#*) continue ;; + esac + # shellcheck disable=SC2254 # unquoted on purpose: allowlist entries are globs + case "$target" in + $pattern) matched=1; break ;; + esac + done < "$AUTH_ALLOWLIST" + + if [ "$matched" -ne 1 ]; then + echo "Refusing to run: '$target' is not on the approved-targets allowlist ($AUTH_ALLOWLIST)." >&2 + audit_log "$script_name" "authorization_denied" "$target" 1 + exit 1 + fi + + audit_log "$script_name" "authorization_granted" "${target} (ticket=${AUTHORIZED_TICKET})" 0 +} diff --git a/scanPlus.sh b/scanPlus.sh index 9fdf65e..860877a 100755 --- a/scanPlus.sh +++ b/scanPlus.sh @@ -5,10 +5,14 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")" # shellcheck source=/dev/null source "$REPO_ROOT/lib/audit_log.sh" +# shellcheck source=/dev/null +source "$REPO_ROOT/lib/authorization.sh" echo "Enter the target IP address:" read -r target +require_authorization "scanPlus.sh" "$target" + echo "Enter the starting port number:" read -r start diff --git a/src/networking-tools/host_scan/host.sh b/src/networking-tools/host_scan/host.sh index fbb4747..befceb2 100755 --- a/src/networking-tools/host_scan/host.sh +++ b/src/networking-tools/host_scan/host.sh @@ -5,6 +5,8 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")" # shellcheck source=/dev/null source "$REPO_ROOT/lib/audit_log.sh" +# shellcheck source=/dev/null +source "$REPO_ROOT/lib/authorization.sh" # Scan output never lives inside the repo — it's reconnaissance data, not # source, and a repo directory risks it getting swept up by `git add -A`. @@ -22,6 +24,8 @@ if ! [[ "$HOST" =~ ^[a-zA-Z0-9._-]+$ ]]; then exit 1 fi +require_authorization "host.sh" "$HOST" + RAW_FILE="$OUTPUT_DIR/${HOST}.raw.txt" RESULT_FILE="$OUTPUT_DIR/${HOST}.subdomains.txt" diff --git a/src/networking-tools/live_network_monitor.sh b/src/networking-tools/live_network_monitor.sh index fa8997a..75f3268 100755 --- a/src/networking-tools/live_network_monitor.sh +++ b/src/networking-tools/live_network_monitor.sh @@ -4,10 +4,14 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")" # shellcheck source=/dev/null source "$REPO_ROOT/lib/audit_log.sh" +# shellcheck source=/dev/null +source "$REPO_ROOT/lib/authorization.sh" # Detect the active network interface automatically INTERFACE=$(ip route | grep default | awk '{print $5}') +require_authorization "live_network_monitor.sh" "$INTERFACE" + # Check if necessary tools are installed. This only warns — it does not # silently sudo-install packages, since unreviewed installs on a system # with audit/monitoring requirements need a change record, not a script. diff --git a/src/networking-tools/nmap/nmap.sh b/src/networking-tools/nmap/nmap.sh index 8908bf2..b9304b4 100755 --- a/src/networking-tools/nmap/nmap.sh +++ b/src/networking-tools/nmap/nmap.sh @@ -5,10 +5,14 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" REPO_ROOT="$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || echo "$SCRIPT_DIR")" # shellcheck source=/dev/null source "$REPO_ROOT/lib/audit_log.sh" +# shellcheck source=/dev/null +source "$REPO_ROOT/lib/authorization.sh" SERVER="${HOST:?Set HOST to the scan target, e.g. HOST=127.0.0.1 ./nmap.sh}" PORT_NUMBER=8080 # HTTPS port. +require_authorization "nmap.sh" "$SERVER" + set +e nmap "$SERVER" | grep -w "$PORT_NUMBER" # Is that particular port open? # grep -w matches whole words only,