2611.1
This commit is contained in:
1 parent
c929b299bf
commit
0fdc1facfa
2 files changed
+148
-11
No files matched your search
+131
-11
@@ -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 [ "$EUID" -ne 0 ]; then
|
||||
echo "This script must be run as root (use sudo)."
|
||||
exit 1
|
||||
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
|
||||
|
||||
main_menu
|
||||
Reference in new issue
Block a user