OpenWRT Router Security Audit Collector

───────────────────────────────────

OpenWRT is the router software of my choice — powerful, simple, lightweight.

Below is a copy-and-paste script that you can use on your OpenWRT box to collect telemetry information about the system, and perform offline checks on the security and health of your box.

The script contains comments explaining all the steps. I tried to be concise — the script has a job to do, not to be an essay.

What you need to do on the OpenWRT box:

  • step 1: create a file in the /root directory with a name like security_audit_script.shvim /root/security_audit_script.sh
  • step 2: copy the script from this post and paste it into the newly created file (still open in vim)
  • step 3: make sure you have bash and ip-full installed (along with any other packages used in the script like banIP, ss). On OpenWRT < 25.X run opkg update && opkg install bash banip ip-full openssl, and on OpenWRT >= 25.X run apk update && apk add bash banip ip-full openssl. I added openssl on purpose — you should not be using dropbear any more. Bear in mind that the script is designed for OpenWRT >= 25.X, so it uses apk rather than opkg.
  • step 4: add the executable bit to the file — chmod +x security_audit_script.sh
  • step 5: execute the script — sh /root/security_audit_script.sh

The output file will be saved in the /tmp directory under the name security-audit-${TIMESTAMP}.tar.gz (TIMESTAMP will be the current date and time).

Any error during execution most likely means you have a package missing.

Steps you can ignore:

  • 12 (I use banIP for traffic control — remove this section if you don’t)
  • 15 (I use an IPv6 HE tunnel — remove this section if you don’t; if you run a different IPv6 tunnel, fill in the missing bits)
  • 17 (I run a tcpdump script that collects all packets for inspection — same as 15, remove if you don’t)

Script:

#!/bin/sh
# ============================================================
# OpenWrt Edge Router Security Audit Collector
# OpenWrt 25.x version (apk + openssh + IPv6 dual-stack)
#
# Usage:  sh security_audit_script.sh
# Output: /tmp/security-audit-<timestamp>.tar.gz
# ============================================================

AUDIT_DIR="/tmp/security-audit"
TIMESTAMP=$(date +"%Y%m%d_%H%M%S")
OUT_FILE="/tmp/security-audit-${TIMESTAMP}.tar.gz"

echo "=== OpenWrt Security Audit Collector ==="
echo "=== Timestamp: $TIMESTAMP ==="
echo ""

rm -rf "$AUDIT_DIR"
mkdir -p "$AUDIT_DIR/uci-config"

step() { echo "[$1/18] $2..."; }

# --- 1: System info ---
step 1 "System information"
{
    uname -a
    cat /etc/openwrt_release 2>/dev/null
    echo "---"
    cat /etc/os-release 2>/dev/null
    echo "---"
    uptime
    echo "---"
    cat /proc/cmdline
} > "$AUDIT_DIR/system-info.txt"

dmesg > "$AUDIT_DIR/dmesg.txt"
for f in /sys/devices/system/cpu/vulnerabilities/*; do
	printf '%-25s %s\n' "$(basename "$f")" "$(cat "$f")"
done > "$AUDIT_DIR/cpu-vulnerabilities.txt"

cat /tmp/dhcp.leases > "$AUDIT_DIR/dhcp-leases.txt"
ip neigh show > "$AUDIT_DIR/arp-nd-table.txt"
cat /etc/init.d/sshd > "$AUDIT_DIR/init-sshd.txt"

# --- 2: UCI configs ---
step 2 "UCI configuration files"
for f in /etc/config/*; do
    [ -f "$f" ] && cp "$f" "$AUDIT_DIR/uci-config/" 2>/dev/null
done

# --- 3: SSH config (openssh, dropbear removed) ---
step 3 "SSH configuration"
cp /etc/ssh/sshd_config "$AUDIT_DIR/sshd_config" 2>/dev/null
ls -la /etc/ssh/ > "$AUDIT_DIR/ssh-dir-listing.txt" 2>/dev/null
[ -f /root/.ssh/authorized_keys ] && \
    cp /root/.ssh/authorized_keys "$AUDIT_DIR/root-authorized_keys" 2>/dev/null

# --- 4: Firewall / nftables ---
step 4 "Firewall rules"
nft list ruleset > "$AUDIT_DIR/nftables-ruleset.txt" 2>/dev/null
fw4 print > "$AUDIT_DIR/fw4-print.txt" 2>&1

# --- 5: Network state ---
step 5 "Network state"
{
    echo "=== ip -br link ==="
    ip -br link
    echo
    echo "=== ip -br addr ==="
    ip -br addr
    echo
    echo "=== ip -4 route ==="
    ip -4 route
    echo
    echo "=== ip -6 route ==="
    ip -6 route
    echo
    echo "=== ip -6 neigh ==="
    ip -6 neigh
    echo
    echo "=== ip -6 tunnel show ==="
    ip -6 tunnel show
    echo
    echo "=== resolv.conf ==="
    cat /etc/resolv.conf 2>/dev/null
    cat /tmp/resolv.conf.d/resolv.conf.auto 2>/dev/null
} > "$AUDIT_DIR/network.txt"

# --- 6: Listening services ---
step 6 "Listening services"
if command -v ss >/dev/null 2>&1; then
    {
        echo "=== TCP v4 ==="
        ss -tlnp -4
        echo
        echo "=== TCP v6 ==="
        ss -tlnp -6
        echo
        echo "=== UDP v4 ==="
        ss -ulnp -4
        echo
        echo "=== UDP v6 ==="
        ss -ulnp -6
    } > "$AUDIT_DIR/listening-services.txt" 2>/dev/null
elif command -v netstat >/dev/null 2>&1; then
    netstat -tlnp  > "$AUDIT_DIR/listening-services.txt" 2>/dev/null
    netstat -ulnp >> "$AUDIT_DIR/listening-services.txt" 2>/dev/null
fi

# --- 7: Processes ---
step 7 "Running processes"
ps w > "$AUDIT_DIR/processes.txt" 2>/dev/null

# --- 8: Installed and upgradable packages (apk, not opkg) ---
step 8 "Installed packages"
apk list --installed > "$AUDIT_DIR/installed-packages.txt" 2>/dev/null
apk list --upgradable > "$AUDIT_DIR/apk-upgradable.txt" 2>/dev/null

# --- 9: Cron ---
step 9 "Cron jobs"
crontab -l > "$AUDIT_DIR/crontab.txt" 2>/dev/null

# --- 10: Sysctl ---
step 10 "Kernel parameters"
sysctl -a > "$AUDIT_DIR/sysctl.txt" 2>/dev/null

# --- 11: HTTPS certs (LuCI if present) ---
step 11 "HTTPS certificate"
for cert in /etc/uhttpd.crt /etc/luci-uploads/*.crt /etc/nginx/cert.pem; do
    [ -f "$cert" ] || continue
    {
        echo "=== $cert ==="
        openssl x509 -in "$cert" -noout -dates -subject -issuer 2>/dev/null
        echo
    } >> "$AUDIT_DIR/cert-info.txt"
done

# --- 12: BanIP status ---
step 12 "BanIP status"
if [ -x /etc/init.d/banip ]; then
    /etc/init.d/banip status > "$AUDIT_DIR/banip-status.txt" 2>/dev/null
    ls -la /etc/banip/ > "$AUDIT_DIR/banip-dir.txt" 2>/dev/null
else
    echo "banip not installed" > "$AUDIT_DIR/banip-status.txt"
fi

# --- 13: Memory / disk ---
step 13 "Memory and disk usage"
{
    echo "=== free ==="
    free
    echo
    echo "=== df -h ==="
    df -h
    echo
    echo "=== /overlay/ usage ==="
    du -sh /overlay/* 2>/dev/null
} > "$AUDIT_DIR/memory-disk.txt"

# --- 14: File permissions ---
step 14 "Sensitive file permissions"
{
    ls -la /etc/shadow /etc/passwd 2>/dev/null
    echo
    ls -la /etc/ssh/ 2>/dev/null
    echo
    ls -la /root/ 2>/dev/null
    [ -d /root/.ssh ] && ls -la /root/.ssh/ 2>/dev/null
    ls -la /etc/config/network /etc/config/banip
    cat /etc/sysupgrade.conf
} > "$AUDIT_DIR/file-permissions.txt"

# --- 15: HE 6in4 tunnel state ---
step 15 "HE 6in4 tunnel state"
{
    echo "=== ip -6 tunnel show ==="
    ip -6 tunnel show
    echo
    echo "=== Tunnel interface ==="
    ip -d link show 6in4-henet 2>/dev/null
    echo
    echo "=== Tunnel route via tunnel ==="
    # Replace <YOUR_IPV6> with any reachable public IPv6 address (e.g. a known public DNS)
    ip -6 route get <YOUR_IPV6> 2>/dev/null
    echo
    echo "=== Public DNS sees us correctly? ==="
    # Replace <YOUR_HOSTNAME> with the public DNS name of your router
    echo "<YOUR_HOSTNAME> A:"
    nslookup <YOUR_HOSTNAME> 2>/dev/null | grep -A1 Name
    echo "<YOUR_HOSTNAME> AAAA:"
    nslookup -type=AAAA <YOUR_HOSTNAME> 2>/dev/null | grep -A1 Name
} > "$AUDIT_DIR/ipv6-tunnel.txt"

# --- 16: Logs (last 200 lines) ---
step 16 "Recent logs"
logread | tail -200 > "$AUDIT_DIR/logread-tail.txt" 2>/dev/null

# --- 17: Check of tcpdump ---
step 17 "tcpdump-ring health check"
{
    /etc/init.d/tcpdump-ring enabled && echo "auto-start ON" || echo "auto-start OFF"
    /etc/init.d/tcpdump-ring restart
    sleep 3 && logread -e tcpdump-ring.sh | tail -n 10
} > "$AUDIT_DIR/tcpdump-ring-health.txt"

# --- 18: Check directories for sysupgrade and uci-defaults ---
step 18 "sysupgrade and uci-defaults directories"
cat /etc/sysupgrade.conf > "$AUDIT_DIR/sysupgrade-conf.txt"
ls -la /etc/uci-defaults/ > "$AUDIT_DIR/uci-defaults-dir.txt"
sysupgrade -l 2>/dev/null | sort > "$AUDIT_DIR/sysupgrade-keep-list.txt"


# --- Package it ---
echo ""
echo "=== Compressing ==="
cd /tmp && tar czf "$OUT_FILE" "$(basename "$AUDIT_DIR")"

rm -rf "$AUDIT_DIR"

SIZE=$(ls -lh "$OUT_FILE" | awk '{print $5}')
echo ""
echo "=== Done ==="
echo "=== Output: $OUT_FILE ($SIZE) ==="
echo "=== Copy off device: scp root@<ip>:$OUT_FILE . ==="

Peace!