Files
arr-suite-template-bootstrap/templates/network-monitor.sh.j2
openhands 24f2cd64e9 Initial template repository
🎬 ARR Suite Template Bootstrap - Complete Media Automation Stack

Features:
- 16 production services (Prowlarr, Sonarr, Radarr, Plex, etc.)
- One-command Ansible deployment
- VPN-protected downloads via Gluetun
- Tailscale secure access
- Production-ready security (UFW, Fail2Ban)
- Automated backups and monitoring
- Comprehensive documentation

Ready for customization and deployment to any VPS.

Co-authored-by: openhands <openhands@all-hands.dev>
2025-11-28 04:26:12 +00:00

237 lines
8.9 KiB
Django/Jinja

#!/bin/bash
# Network monitoring script for Arrs Media Stack
# Generated by Ansible
LOG_DIR="{{ docker_root }}/logs/system"
TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')
NET_LOG="$LOG_DIR/network-monitor-$(date '+%Y%m%d').log"
# Ensure log directory exists
mkdir -p "$LOG_DIR"
# Function to log with timestamp
log_net() {
echo "[$TIMESTAMP] $1" >> "$NET_LOG"
}
log_net "=== NETWORK MONITORING ==="
# Network interface information
log_net "=== NETWORK INTERFACES ==="
ip addr show | grep -E "^[0-9]+:|inet " | while IFS= read -r line; do
log_net "INTERFACE $line"
done
# Default route
DEFAULT_ROUTE=$(ip route | grep default)
log_net "DEFAULT_ROUTE $DEFAULT_ROUTE"
# Network statistics
log_net "=== NETWORK STATISTICS ==="
MAIN_INTERFACE=$(ip route | grep default | awk '{print $5}' | head -1)
if [[ -n "$MAIN_INTERFACE" ]]; then
# Interface statistics
RX_BYTES=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/rx_bytes 2>/dev/null || echo "0")
TX_BYTES=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/tx_bytes 2>/dev/null || echo "0")
RX_PACKETS=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/rx_packets 2>/dev/null || echo "0")
TX_PACKETS=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/tx_packets 2>/dev/null || echo "0")
RX_ERRORS=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/rx_errors 2>/dev/null || echo "0")
TX_ERRORS=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/tx_errors 2>/dev/null || echo "0")
RX_DROPPED=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/rx_dropped 2>/dev/null || echo "0")
TX_DROPPED=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/tx_dropped 2>/dev/null || echo "0")
# Convert bytes to human readable
RX_MB=$((RX_BYTES / 1024 / 1024))
TX_MB=$((TX_BYTES / 1024 / 1024))
log_net "INTERFACE_STATS $MAIN_INTERFACE - RX: ${RX_MB}MB (${RX_PACKETS} packets, ${RX_ERRORS} errors, ${RX_DROPPED} dropped)"
log_net "INTERFACE_STATS $MAIN_INTERFACE - TX: ${TX_MB}MB (${TX_PACKETS} packets, ${TX_ERRORS} errors, ${TX_DROPPED} dropped)"
# Check for high error rates
if [[ $RX_ERRORS -gt 100 ]]; then
log_net "ALERT_NETWORK High RX errors on $MAIN_INTERFACE: $RX_ERRORS"
fi
if [[ $TX_ERRORS -gt 100 ]]; then
log_net "ALERT_NETWORK High TX errors on $MAIN_INTERFACE: $TX_ERRORS"
fi
fi
# Network connectivity tests
log_net "=== CONNECTIVITY TESTS ==="
# Test DNS resolution
if nslookup google.com >/dev/null 2>&1; then
log_net "DNS_TEST OK - DNS resolution working"
else
log_net "DNS_TEST FAILED - DNS resolution not working"
fi
# Test internet connectivity
if ping -c 1 8.8.8.8 >/dev/null 2>&1; then
log_net "INTERNET_TEST OK - Internet connectivity working"
else
log_net "INTERNET_TEST FAILED - No internet connectivity"
fi
# Test Tailscale connectivity (if configured)
if command -v tailscale >/dev/null 2>&1; then
TAILSCALE_STATUS=$(tailscale status --json 2>/dev/null | jq -r '.BackendState' 2>/dev/null || echo "unknown")
log_net "TAILSCALE_STATUS $TAILSCALE_STATUS"
if [[ "$TAILSCALE_STATUS" == "Running" ]]; then
TAILSCALE_IP=$(tailscale ip -4 2>/dev/null || echo "unknown")
log_net "TAILSCALE_IP $TAILSCALE_IP"
fi
fi
# Port connectivity tests for Arrs services
log_net "=== SERVICE PORT TESTS ==="
SERVICES_PORTS=(
"sonarr:{{ ports.sonarr }}"
"radarr:{{ ports.radarr }}"
"lidarr:{{ ports.lidarr }}"
"bazarr:{{ ports.bazarr }}"
"prowlarr:{{ ports.prowlarr }}"
)
for service_port in "${SERVICES_PORTS[@]}"; do
SERVICE=$(echo "$service_port" | cut -d: -f1)
PORT=$(echo "$service_port" | cut -d: -f2)
if nc -z localhost "$PORT" 2>/dev/null; then
log_net "PORT_TEST $SERVICE (port $PORT) - OK"
else
log_net "PORT_TEST $SERVICE (port $PORT) - FAILED"
fi
done
# Active network connections
log_net "=== ACTIVE CONNECTIONS ==="
ACTIVE_CONNECTIONS=$(netstat -tuln 2>/dev/null | grep LISTEN | wc -l)
log_net "LISTENING_PORTS Total listening ports: $ACTIVE_CONNECTIONS"
# Show listening ports for our services
netstat -tuln 2>/dev/null | grep -E ":{{ ports.sonarr }}|:{{ ports.radarr }}|:{{ ports.lidarr }}|:{{ ports.bazarr }}|:{{ ports.prowlarr }}" | while IFS= read -r line; do
log_net "SERVICE_PORT $line"
done
# Network load monitoring
log_net "=== NETWORK LOAD ==="
if command -v ss >/dev/null 2>&1; then
ESTABLISHED_CONNECTIONS=$(ss -t state established | wc -l)
TIME_WAIT_CONNECTIONS=$(ss -t state time-wait | wc -l)
log_net "CONNECTION_STATS Established: $ESTABLISHED_CONNECTIONS, Time-wait: $TIME_WAIT_CONNECTIONS"
# Check for high connection counts
if [[ $ESTABLISHED_CONNECTIONS -gt 1000 ]]; then
log_net "ALERT_NETWORK High number of established connections: $ESTABLISHED_CONNECTIONS"
fi
if [[ $TIME_WAIT_CONNECTIONS -gt 5000 ]]; then
log_net "ALERT_NETWORK High number of time-wait connections: $TIME_WAIT_CONNECTIONS"
fi
fi
# Docker network information
if command -v docker >/dev/null 2>&1; then
log_net "=== DOCKER NETWORK ==="
# Docker networks
DOCKER_NETWORKS=$(docker network ls --format "{{ '{{.Name}}' }}\t{{ '{{.Driver}}' }}\t{{ '{{.Scope}}' }}" 2>/dev/null)
if [[ -n "$DOCKER_NETWORKS" ]]; then
echo "$DOCKER_NETWORKS" | while IFS=$'\t' read -r name driver scope; do
log_net "DOCKER_NETWORK $name - Driver: $driver, Scope: $scope"
done
fi
# Container network stats
cd {{ docker_compose_dir }}
SERVICES=("sonarr" "radarr" "lidarr" "bazarr" "prowlarr" "watchtower")
for service in "${SERVICES[@]}"; do
CONTAINER_ID=$(docker-compose ps -q "$service" 2>/dev/null)
if [[ -n "$CONTAINER_ID" ]]; then
# Get container network stats
NET_STATS=$(docker stats --no-stream --format "{{ '{{.NetIO}}' }}" "$CONTAINER_ID" 2>/dev/null)
if [[ -n "$NET_STATS" ]]; then
log_net "CONTAINER_NETWORK $service - Network I/O: $NET_STATS"
fi
fi
done
fi
# Firewall status
log_net "=== FIREWALL STATUS ==="
if command -v ufw >/dev/null 2>&1; then
UFW_STATUS=$(ufw status 2>/dev/null | head -1)
log_net "UFW_STATUS $UFW_STATUS"
# Show rules for our ports
ufw status numbered 2>/dev/null | grep -E "{{ ports.sonarr }}|{{ ports.radarr }}|{{ ports.lidarr }}|{{ ports.bazarr }}|{{ ports.prowlarr }}" | while IFS= read -r line; do
log_net "UFW_RULE $line"
done
fi
# Network security checks
log_net "=== SECURITY CHECKS ==="
# Check for open ports that shouldn't be
UNEXPECTED_PORTS=$(netstat -tuln 2>/dev/null | grep LISTEN | grep -v -E ":22|:{{ ports.sonarr }}|:{{ ports.radarr }}|:{{ ports.lidarr }}|:{{ ports.bazarr }}|:{{ ports.prowlarr }}|:127.0.0.1" | wc -l)
if [[ $UNEXPECTED_PORTS -gt 0 ]]; then
log_net "SECURITY_ALERT $UNEXPECTED_PORTS unexpected open ports detected"
netstat -tuln 2>/dev/null | grep LISTEN | grep -v -E ":22|:{{ ports.sonarr }}|:{{ ports.radarr }}|:{{ ports.lidarr }}|:{{ ports.bazarr }}|:{{ ports.prowlarr }}|:127.0.0.1" | while IFS= read -r line; do
log_net "UNEXPECTED_PORT $line"
done
fi
# Check for failed connection attempts (from auth.log)
FAILED_CONNECTIONS=$(grep "Failed" /var/log/auth.log 2>/dev/null | grep "$(date '+%b %d')" | wc -l)
if [[ $FAILED_CONNECTIONS -gt 10 ]]; then
log_net "SECURITY_ALERT $FAILED_CONNECTIONS failed connection attempts today"
fi
# Bandwidth usage estimation
log_net "=== BANDWIDTH ESTIMATION ==="
if [[ -n "$MAIN_INTERFACE" ]]; then
# Read current stats
CURRENT_RX=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/rx_bytes 2>/dev/null || echo "0")
CURRENT_TX=$(cat /sys/class/net/$MAIN_INTERFACE/statistics/tx_bytes 2>/dev/null || echo "0")
# Read previous stats if available
STATS_FILE="/tmp/network_stats_$MAIN_INTERFACE"
if [[ -f "$STATS_FILE" ]]; then
PREV_TIMESTAMP=$(head -1 "$STATS_FILE")
PREV_RX=$(sed -n '2p' "$STATS_FILE")
PREV_TX=$(sed -n '3p' "$STATS_FILE")
# Calculate time difference (in seconds)
TIME_DIFF=$(($(date +%s) - PREV_TIMESTAMP))
if [[ $TIME_DIFF -gt 0 ]]; then
# Calculate bandwidth (bytes per second)
RX_RATE=$(((CURRENT_RX - PREV_RX) / TIME_DIFF))
TX_RATE=$(((CURRENT_TX - PREV_TX) / TIME_DIFF))
# Convert to human readable (Mbps)
RX_MBPS=$(echo "scale=2; $RX_RATE * 8 / 1024 / 1024" | bc -l 2>/dev/null || echo "0")
TX_MBPS=$(echo "scale=2; $TX_RATE * 8 / 1024 / 1024" | bc -l 2>/dev/null || echo "0")
log_net "BANDWIDTH_USAGE RX: ${RX_MBPS} Mbps, TX: ${TX_MBPS} Mbps (over ${TIME_DIFF}s)"
fi
fi
# Save current stats for next run
echo "$(date +%s)" > "$STATS_FILE"
echo "$CURRENT_RX" >> "$STATS_FILE"
echo "$CURRENT_TX" >> "$STATS_FILE"
fi
log_net "=== END NETWORK MONITORING ==="
# Cleanup old network logs (keep 7 days)
find "$LOG_DIR" -name "network-monitor-*.log" -mtime +7 -delete 2>/dev/null
exit 0