#!/usr/bin/env bash
# install-suricata-ips.sh — Suricata Inline IPS lab (ch.8)
# ---------------------------------------------------------------------------
# Builds Suricata 8.0.5 FROM SOURCE on Debian 13 (Trixie) with NFQUEUE support,
# installs the full toolchain (Rust + cargo + cbindgen + gcc + every -dev lib),
# configures inline IPS mode, ships a systemd unit, and applies SSH/web-safe
# iptables that route the rest of the traffic through Suricata so it can DROP,
# not just alert. Non-interactive. Run inside the lab as root.
#
# Then attack it from the separate Kali box and watch the blocks land in
#   tail -f /var/log/suricata/fast.log | grep -i drop
# ---------------------------------------------------------------------------
set -uo pipefail

RED='\033[0;31m'; GREEN='\033[0;32m'; YELLOW='\033[1;33m'; NC='\033[0m'
print_header(){ echo -e "\n${GREEN}=== $1 ===${NC}"; }
die(){ echo -e "${RED}[x] $1${NC}"; exit 1; }

[[ ${EUID:-$(id -u)} -eq 0 ]] || die "Run as root:  sudo ./install-suricata-ips.sh"

SURI_VER="8.0.5"
SURI_URL="https://www.openinfosecfoundation.org/download/suricata-${SURI_VER}.tar.gz"
SURICATA_YAML="/etc/suricata/suricata.yaml"
export DEBIAN_FRONTEND=noninteractive

# --- environment detection (interface, server IP, SSH port) ---
print_header "Detecting network environment"
INTERFACE="$(ip route show default 2>/dev/null | grep -oP 'dev \K\S+' | head -n1)"
[[ -z "$INTERFACE" ]] && { echo -e "${RED}No default interface; using eth0${NC}"; INTERFACE="eth0"; }
IPV4="$(ip -4 route get 1.1.1.1 2>/dev/null | grep -oP 'src \K\S+' | head -n1 || true)"
IPV6="$(ip -6 route get 2606:4700:4700::1111 2>/dev/null | grep -oP 'src \K\S+' | head -n1 || true)"
SSH_PORT="$(ss -tlnp 2>/dev/null | awk '/sshd/{print $4}' | awk -F: '{print $NF}' | head -n1)"
[[ -z "$SSH_PORT" ]] && SSH_PORT=22
echo -e "${GREEN}interface=$INTERFACE  ipv4=${IPV4:-none}  ipv6=${IPV6:-none}  ssh=$SSH_PORT${NC}"

# --- 1. Rust toolchain + cbindgen (Suricata 8 REQUIRES both) ---
print_header "Installing Rust + cbindgen"
if ! command -v cargo >/dev/null 2>&1; then
    curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
fi
# shellcheck disable=SC1090,SC1091
source "$HOME/.cargo/env" 2>/dev/null || source /root/.cargo/env
command -v cbindgen >/dev/null 2>&1 || cargo install --force cbindgen
echo -e "${GREEN}$(rustc --version)  |  cbindgen $(cbindgen --version 2>/dev/null)${NC}"

# --- 2. Build dependencies (Debian 13 / Trixie package names) ---
print_header "Installing Suricata 8 build dependencies"
apt-get update -qq
DEPS="build-essential gcc make pkg-config autoconf automake libtool \
libpcap-dev libnet1-dev libyaml-0-2 libyaml-dev zlib1g-dev \
libcap-ng-dev libcap-ng0 libmagic-dev libjansson-dev libpcre2-dev \
liblz4-dev libnss3-dev libevent-dev libhiredis-dev libmaxminddb-dev \
libnetfilter-queue-dev libnetfilter-queue1 libnfnetlink-dev libnfnetlink0 \
python3 python3-yaml python3-pip jq curl wget git ca-certificates \
netfilter-persistent iptables"
apt-get install -y $DEPS || die "Dependency install failed. Verify names for your release:  apt-get install -y $DEPS"
echo -e "${GREEN}Build dependencies installed${NC}"

# --- 3. Fetch, verify, and build Suricata 8.0.5 from source ---
print_header "Building Suricata ${SURI_VER} from source"
cd /tmp
wget -q "$SURI_URL"       -O "suricata-${SURI_VER}.tar.gz"     || die "Download failed: $SURI_URL"
wget -q "${SURI_URL}.sig" -O "suricata-${SURI_VER}.tar.gz.sig" || true
# Verify: GPG signature if a key is present, and always print the SHA256 to compare against OISF.
if command -v gpg >/dev/null 2>&1 && [[ -s "suricata-${SURI_VER}.tar.gz.sig" ]]; then
    if gpg --verify "suricata-${SURI_VER}.tar.gz.sig" "suricata-${SURI_VER}.tar.gz" 2>/dev/null; then
        echo -e "${GREEN}GPG signature verified${NC}"
    else
        echo -e "${YELLOW}[!] GPG signature NOT verified (import the OISF signing key to check). Confirm the hash below before trusting this build.${NC}"
    fi
fi
echo -e "${YELLOW}SHA256:$(sha256sum "suricata-${SURI_VER}.tar.gz" | awk '{print " "$1}')  — compare against the value published by OISF.${NC}"

tar -xzf "suricata-${SURI_VER}.tar.gz"
cd "suricata-${SURI_VER}"
# NFQUEUE = inline IPS.  (lua/hyperscan left off for clean portability on Debian 13.)
./configure --enable-nfqueue --prefix=/usr --sysconfdir=/etc --localstatedir=/var \
    || die "configure failed — check the dependency list above."
make -j"$(nproc)" || die "compilation failed."
make install      || die "install failed."
make install-conf
ldconfig
cd /; rm -rf "/tmp/suricata-${SURI_VER}" "/tmp/suricata-${SURI_VER}.tar.gz" "/tmp/suricata-${SURI_VER}.tar.gz.sig"
echo -e "${GREEN}$(suricata -V)${NC}"

# --- 4. HOME_NET = this server ---
print_header "Configuring Suricata"
cp -n "$SURICATA_YAML" "${SURICATA_YAML}.bak" 2>/dev/null || true
if   [[ -n "${IPV4:-}" ]]; then sed -i "s#HOME_NET:.*#HOME_NET: \"[${IPV4}/32]\"#"  "$SURICATA_YAML"
elif [[ -n "${IPV6:-}" ]]; then sed -i "s#HOME_NET:.*#HOME_NET: \"[${IPV6}/128]\"#" "$SURICATA_YAML"
fi

# --- 5. Inline IPS (NFQUEUE) config ---
print_header "Configuring NFQUEUE inline IPS"
sed -i "s/^\(\s*interface:\).*/\1 $INTERFACE/" "$SURICATA_YAML" || true
if ! grep -q '^nfq:' "$SURICATA_YAML"; then
cat >> "$SURICATA_YAML" <<EOF

# --- inline IPS via NFQUEUE 0 (added by the lab) ---
nfq:
  - interface: $INTERFACE
    mode: accept
    queue: 0
    verdict: accept
    fail-open: yes
    batchcount: 50
    copy-mode: ips
    copy-iface: $INTERFACE
    bypass: no
    max-size: 40000
    qtimeout: 150
    buffer-size: 65535
    threads: 4
    accept-mark: 1
EOF
fi

# --- 6. Rules ---
print_header "Updating rules (Emerging Threats open)"
suricata-update || echo -e "${YELLOW}[!] suricata-update failed; run it manually later.${NC}"

# --- 7. systemd unit (inline, validates config before start) ---
print_header "Installing systemd service"
cat > /etc/systemd/system/suricata.service <<EOF
[Unit]
Description=Suricata IDS/IPS (inline, NFQUEUE 0)
After=network-online.target
Wants=network-online.target

[Service]
Type=simple
ExecStartPre=/usr/bin/suricata -T -c ${SURICATA_YAML} -v
ExecStart=/usr/bin/suricata -c ${SURICATA_YAML} -q 0 --pidfile /run/suricata.pid
ExecReload=/bin/kill -HUP \$MAINPID
PIDFile=/run/suricata.pid
Restart=on-failure

[Install]
WantedBy=multi-user.target
EOF
systemctl daemon-reload
systemctl enable --now suricata
sleep 2
systemctl --no-pager --full status suricata | head -n 6 || true

# --- 8. SSH/web-safe iptables that feed the rest into NFQUEUE 0 ---
# Preserves your SSH session and ports 80/443, then routes everything else
# through Suricata. Remove UFW first if present — it owns its own chains.
print_header "Applying SSH/web-safe iptables (NFQUEUE 0)"
modprobe nfnetlink_queue 2>/dev/null || true
for c in INPUT OUTPUT FORWARD; do iptables -D "$c" -j NFQUEUE --queue-num 0 2>/dev/null || true; done
iptables -I INPUT  -m conntrack --ctstate ESTABLISHED,RELATED -j ACCEPT
iptables -I INPUT  -p tcp --dport "$SSH_PORT" -j ACCEPT
iptables -I OUTPUT -p tcp --sport "$SSH_PORT" -j ACCEPT
for port in 80 443; do
    iptables -I INPUT  -p tcp --dport "$port" -j ACCEPT
    iptables -I OUTPUT -p tcp --sport "$port" -j ACCEPT
done
iptables -A INPUT   -j NFQUEUE --queue-num 0
iptables -A OUTPUT  -j NFQUEUE --queue-num 0
iptables -A FORWARD -j NFQUEUE --queue-num 0
echo "netfilter-persistent netfilter-persistent/autosave_v4 boolean true" | debconf-set-selections
echo "netfilter-persistent netfilter-persistent/autosave_v6 boolean true" | debconf-set-selections
netfilter-persistent save || echo -e "${YELLOW}[!] Could not persist rules; they are active until reboot.${NC}"

print_header "Done"
echo -e "${GREEN}Suricata ${SURI_VER} is inline on NFQUEUE 0 and dropping on 'drop' rules.${NC}"
echo    "Watch blocks:  tail -f /var/log/suricata/fast.log | grep -i drop"
echo -e "${YELLOW}If you run UFW, remove it (it fights these chains):  apt-get purge -y ufw${NC}"
