#!/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