This commit is contained in:
DAProgs committed 2026-11-05 18:08:22 -05:00
1 parent c929b299bf
commit 0fdc1facfa
2 files changed
+144 -7

No files matched your search

+127 -7
View File
@@ -5,6 +5,9 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOG_FILE="/var/log/portspoof.log"
EVENTS_WINDOW_FILE="$SCRIPT_DIR/.events_window"
EVENTS_HOURS=1
WEBHOOK_FILE="$SCRIPT_DIR/.discord_webhook"
WEBHOOK_OFFSET_FILE="$SCRIPT_DIR/.webhook_offset"
WEBHOOK_PID_FILE="$SCRIPT_DIR/.webhook.pid"
if [ -f "$EVENTS_WINDOW_FILE" ]; then
read -r _loaded < "$EVENTS_WINDOW_FILE" 2>/dev/null
case "$_loaded" in
@@ -102,7 +105,7 @@ status_box() {
echo " ╠══════════════════════════════════════════════════════════════════════════╣"
echo " ║ Network Interface Status ║"
echo " ╠══════════════════════════════════════════════════════════════════════════╣"
echo " ║ v0.18 ║"
echo " ║ v0.19 ║"
local ifaces
mapfile -t ifaces < <(get_interfaces)
@@ -415,6 +418,118 @@ shell_access() {
read -rp "Press Enter to continue..."
}
# ── discord webhook ───────────────────────────────────────────────────────────
send_discord() {
local webhook
webhook=$(cat "$WEBHOOK_FILE" 2>/dev/null)
[ -n "$webhook" ] || return 1
curl -s -o /dev/null --max-time 10 \
-H "Content-Type: application/json" \
-d "$(printf '{"content":"%s"}' "$1")" "$webhook"
}
# Builds the Discord message from one raw log line; exits non-zero if unparseable.
webhook_msg() {
python3 -c '
import sys, json
from datetime import datetime
try:
e = json.loads(sys.argv[1])
except ValueError:
sys.exit(1)
raw = str(e.get("timestamp", ""))
if raw.endswith("Z"):
raw = raw[:-1] + "+00:00"
try:
ts = datetime.fromisoformat(raw).astimezone().strftime("%Y-%m-%d %H:%M:%S")
except ValueError:
ts = "?"
src = str(e.get("src_ip", "?")).replace("\"", "").replace("\\", "")
dst = str(e.get("dst_port", "?"))
print(f"⚠️ **PortSpoof alert** — {src} → port {dst} at {ts}")
' "$1"
}
# Forwards new log lines to the webhook. Runs in the background; survives the menu.
start_webhook_watcher() {
[ -f "$WEBHOOK_FILE" ] || return 0
if [ -f "$WEBHOOK_PID_FILE" ] && kill -0 "$(cat "$WEBHOOK_PID_FILE")" 2>/dev/null; then
return 0
fi
# Seed offset so old events are not replayed.
echo "$(wc -l < "$LOG_FILE" 2>/dev/null || echo 0)" > "$WEBHOOK_OFFSET_FILE"
(
trap 'rm -f "$WEBHOOK_PID_FILE"' EXIT
while true; do
sleep 2
[ -f "$LOG_FILE" ] || continue
total=$(wc -l < "$LOG_FILE")
offset=$(cat "$WEBHOOK_OFFSET_FILE" 2>/dev/null)
[ -n "$offset" ] || offset=0
if [ "$total" -le "$offset" ]; then
[ "$total" -lt "$offset" ] && echo "$total" > "$WEBHOOK_OFFSET_FILE"
continue
fi
tail -n +"$((offset + 1))" "$LOG_FILE" | while IFS= read -r line; do
msg=$(webhook_msg "$line") || continue
send_discord "$msg"
done
echo "$total" > "$WEBHOOK_OFFSET_FILE"
done
) &
echo $! > "$WEBHOOK_PID_FILE"
}
stop_webhook_watcher() {
if [ -f "$WEBHOOK_PID_FILE" ]; then
kill "$(cat "$WEBHOOK_PID_FILE")" 2>/dev/null
rm -f "$WEBHOOK_PID_FILE"
fi
}
set_webhook() {
clear
echo ""
echo "╔══════════════════════════════════════════════════════════════════════════╗"
echo "║ Discord Webhook ║"
echo "╚══════════════════════════════════════════════════════════════════════════╝"
echo ""
local current
current=$(cat "$WEBHOOK_FILE" 2>/dev/null)
echo " Current: ${current:-none}"
echo ""
while true; do
read -rp " Webhook URL (leave empty to disable): " url
if [ -z "$url" ]; then
stop_webhook_watcher
rm -f "$WEBHOOK_FILE" "$WEBHOOK_OFFSET_FILE"
echo ""
echo " Webhook disabled."
read -rp "Press Enter to continue..."
return
fi
case "$url" in
https://discord.com/api/webhooks/*) ;;
*) echo " Invalid: must start with https://discord.com/api/webhooks/"; continue ;;
esac
if curl -s -o /dev/null --max-time 10 -X POST \
-H "Content-Type: application/json" \
-d '{"content":"PortSpoof webhook test — configured from menu"}' "$url"; then
echo "$url" > "$WEBHOOK_FILE"
start_webhook_watcher
echo ""
echo " Webhook saved — test message sent, watcher started."
break
else
echo " Test failed — check the URL and try again."
fi
done
echo ""
read -rp "Press Enter to continue..."
}
# ── log helpers ───────────────────────────────────────────────────────────────
# Prints the last 5 events within the last $EVENTS_HOURS hours
# (always exactly 5 lines): timestamp (local time), src_ip, dst_port.
@@ -584,7 +699,7 @@ main_screen() {
# ── main menu ─────────────────────────────────────────────────────────────────
MENU_ITEMS=("Refresh" "Timeframe: ${EVENTS_HOURS}h" "Set Static IP" "Set DHCP" "Shell" "Reboot" "Update")
MENU_ITEMS=("Refresh" "Timeframe: ${EVENTS_HOURS}h" "Discord Webhook" "Set Static IP" "Set DHCP" "Shell" "Reboot" "Update")
toggle_timeframe() {
if [ "$EVENTS_HOURS" = "1" ]; then
@@ -609,26 +724,29 @@ main_menu() {
toggle_timeframe
;;
2)
set_webhook
;;
3)
if select_interface; then
set_static "$SELECTED_IFACE"
fi
echo ""
read -rp "Press Enter to continue..."
;;
3)
4)
if select_interface; then
set_dhcp "$SELECTED_IFACE"
fi
echo ""
read -rp "Press Enter to continue..."
;;
4)
5)
shell_access
;;
5)
6)
reboot_confirm
;;
6)
7)
run_update
;;
esac
@@ -637,9 +755,11 @@ main_menu() {
# ── entry point ───────────────────────────────────────────────────────────────
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
if [ "$EUID" -ne 0 ]; then
echo "This script must be run as root (use sudo)."
exit 1
fi
start_webhook_watcher
main_menu
fi
+17
View File
@@ -0,0 +1,17 @@
#!/bin/bash
# Self-check for the Discord webhook helpers: bash test_webhook.sh
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# shellcheck source=network-config.sh
source "$SCRIPT_DIR/network-config.sh"
line='{"timestamp":"2026-11-05T12:00:00Z","src_ip":"1.2.3.4","dst_port":8080}'
msg=$(webhook_msg "$line") || { echo "FAIL: valid line rejected"; exit 1; }
echo "$msg" | grep -q "1.2.3.4" || { echo "FAIL: src_ip missing from: $msg"; exit 1; }
echo "$msg" | grep -q "8080" || { echo "FAIL: dst_port missing from: $msg"; exit 1; }
if webhook_msg 'not json' >/dev/null; then
echo "FAIL: invalid line accepted"
exit 1
fi
echo "OK: $msg"