Skip to content

Security Monitoring Guide

Monitor and detect suspicious access to your media server

Overview

Your server is behind Cloudflare Tunnels (*.monxas.casa) with UFW firewall enabled. This guide covers how to monitor for suspicious activity.

Current Security Posture

Firewall (UFW)

sudo ufw status verbose

Current rules: - βœ… Default DENY incoming (and DENY routed) - βœ… Allow local network (192.168.0.0/24) - βœ… SSH with rate limiting (22/tcp LIMIT) - βœ… Port 8082 (ntfy) - for notifications - βœ… Docker/overlay range (172.16.0.0/12)

Fail2Ban (Active)

sudo fail2ban-client status sshd

Protection: - βœ… SSH brute-force detection and IP banning - βœ… Automatic blocking after 5 failed attempts - βœ… 10-minute ban duration (default) - βœ… Monitoring /var/log/auth.log

Commands:

# Check status
sudo fail2ban-client status sshd

# View banned IPs
sudo fail2ban-client get sshd banip

# Unban an IP
sudo fail2ban-client set sshd unbanip <IP>

All services are accessed via: 1. Local network (192.168.0.x) 2. Cloudflare Tunnels (external β†’ *.monxas.casa β†’ Cloudflare β†’ your server)

Exposed Services

Service Port Access Method
Homepage 80 Local + Cloudflare Tunnel
Jellyfin 8096 Local + Cloudflare Tunnel
Sonarr 8989 Local + Cloudflare Tunnel + Caddy
Radarr 7878 Local + Cloudflare Tunnel + Caddy
Prowlarr 9696 Local + Cloudflare Tunnel + Caddy
Jellyseerr 5055 Local + Cloudflare Tunnel
ntfy 8082 Public (notifications)

Nota (2026-07-12): Sonarr/Radarr/Prowlarr ya no son Β«local onlyΒ» β€” estΓ‘n declarados en managed.caddy con ingress y se sirven vΓ­a CF Tunnel + Caddy. El modelo de acceso actual es Caddy HA en LXC 270/271 (generado por homelab-ctl) + PocketID para SSO, no el reverse-proxy pre-F4 en la propia VM 208.

Monitoring Tools

1. Security Monitor Script

Location: /home/monxas/scripts/security-monitor.sh

What it checks: - βœ… SSH failed login attempts - βœ… SSH logins from non-local IPs - βœ… UFW firewall blocks - βœ… Jellyfin failed authentication - βœ… *arr stack unauthorized API calls - βœ… External network connections - βœ… New system users created - βœ… Unexpected sudo usage - βœ… Cloudflare tunnel status

Usage:

# Run manually
/home/monxas/scripts/security-monitor.sh

# Check last run
tail -f /var/log/security-monitor.log

# Run every hour via cron
crontab -e
# Add: 0 * * * * /home/monxas/scripts/security-monitor.sh

Notifications: Alerts are sent to ntfy topic security (http://localhost:8082/security)

2. Real-time Log Monitoring

Using journalctl:

# Watch SSH attempts live
sudo journalctl -u ssh -f

# Watch UFW blocks
sudo journalctl -u ufw -f

# Watch all security events
sudo journalctl -p warning..emerg -f

Using Loki/Grafana: - Grafana: http://192.168.0.208:3030 - Loki aggregates all container logs via Promtail

Useful Loki queries:

# Failed SSH attempts
{job="systemd-journal"} |= "Failed password"

# UFW blocks
{job="systemd-journal"} |= "UFW BLOCK"

# Jellyfin auth failures
{container_name="jellyfin"} |~ "(?i)(auth.*failed|invalid.*password)"

# All authentication errors across containers
{job="docker"} |~ "(?i)(unauthorized|forbidden|401|403)"

# External IP connections
{job="systemd-journal"} |~ "SRC=(?!192.168.0)"

3. Uptime Kuma

URL: http://192.168.0.208:3001

What to monitor: - Add HTTP checks for Cloudflare Tunnel endpoints (*.monxas.casa) - Add port checks for critical local services - Set up notifications to ntfy

Example monitors: - Jellyfin: https://jellyfin.monxas.casa/health - Homepage: https://home.monxas.casa - SSH: TCP check on 192.168.0.208:22

4. Docker Container Logs

Check specific service logs:

# Jellyfin auth attempts
sudo docker logs jellyfin --since 1h | grep -i "auth\|login"

# Sonarr/Radarr API access
sudo docker logs sonarr --since 1h | grep -i "401\|403\|unauthorized"

# All containers errors
sudo docker ps -q | xargs -I {} sudo docker logs {} --since 1h 2>&1 | grep -i "error\|warning"

Manual Security Checks

Check Active Connections

# All established connections
sudo ss -tnp state established

# External connections only
sudo ss -tnp state established | grep -v "192.168.0"

# Listening services
sudo ss -tulnp | grep LISTEN

Check Authentication Logs

# Recent SSH logins
sudo lastlog

# Recent login attempts
sudo last -20

# Failed login attempts
sudo grep "Failed password" /var/log/auth.log | tail -20

# Successful logins
sudo grep "Accepted password" /var/log/auth.log | tail -20

Check Firewall Activity

# Recent UFW blocks
sudo grep "UFW BLOCK" /var/log/syslog | tail -50

# Top blocked IPs
sudo grep "UFW BLOCK" /var/log/syslog | \
  awk '{for(i=1;i<=NF;i++) if($i~/SRC=/) print $i}' | \
  sort | uniq -c | sort -rn | head -10

Check Cloudflare Access

If you have Cloudflare tunnels set up: - Go to Cloudflare Dashboard β†’ Zero Trust β†’ Access β†’ Audit Logs - Check for access attempts to your tunnels - Review active sessions

What to Look For (Red Flags)

🚨 Critical Alerts

  • SSH login from non-192.168.0.x IP
  • New system user created
  • Unexpected sudo usage
  • Cloudflare tunnel down
  • Multiple failed Jellyfin logins from same IP

⚠️ Warning Signs

  • High number of UFW blocks (>100/hour)
  • Failed SSH attempts (>5/day)
  • Multiple 401/403 errors in *arr stack
  • External connections to unexpected ports

ℹ️ Normal Activity

  • UFW blocks from internet scanners
  • Occasional failed Jellyfin login (typo)
  • Internal network connections
  • Watchtower update notifications

Hardening Recommendations

1. ~~Enable Fail2Ban~~ βœ… Already Configured

Fail2ban is active and protecting SSH. To add more jails (Jellyfin, *arr stack):

# Edit local config
sudo nano /etc/fail2ban/jail.local

# Restart after changes
sudo systemctl restart fail2ban

2. Use SSH Keys Only

# Disable password auth in SSH
sudo nano /etc/ssh/sshd_config
# Set: PasswordAuthentication no

sudo systemctl restart sshd

3. Add Cloudflare Access Policies

  • Require authentication for external access
  • Restrict by geographic location
  • Enable WAF rules

4. Container Access Logging

Add to compose files:

logging:
  driver: "json-file"
  options:
    max-size: "10m"
    max-file: "3"
    tag: "{{.Name}}"

5. Enable Promtail Journal Scraping

Ensure /home/monxas/appdata/promtail/config.yml includes:

scrape_configs:
  - job_name: systemd-journal
    journal:
      path: /var/log/journal
      max_age: 12h
      labels:
        job: systemd-journal
    relabel_configs:
      - source_labels: ['__journal__systemd_unit']
        target_label: 'unit'

Automated Monitoring Setup

Option 1: Cron (Simple)

crontab -e
# Add:
0 * * * * /home/monxas/scripts/security-monitor.sh  # Every hour

Option 2: Systemd Timer (Better)

# Create timer
sudo nano /etc/systemd/system/security-monitor.timer

[Unit]
Description=Security Monitor Timer
Requires=security-monitor.service

[Timer]
OnBootSec=5min
OnUnitActiveSec=1h

[Install]
WantedBy=timers.target
# Create service
sudo nano /etc/systemd/system/security-monitor.service

[Unit]
Description=Security Monitor Service

[Service]
Type=oneshot
User=monxas
ExecStart=/home/monxas/scripts/security-monitor.sh
# Enable and start
sudo systemctl daemon-reload
sudo systemctl enable security-monitor.timer
sudo systemctl start security-monitor.timer

Grafana Dashboard Setup

Create Security Dashboard

  1. Go to Grafana (http://192.168.0.208:3030)
  2. Create New Dashboard
  3. Add panels with these queries:

Panel: SSH Failed Logins

rate({job="systemd-journal"} |= "Failed password" [5m])

Panel: Firewall Blocks

rate({job="systemd-journal"} |= "UFW BLOCK" [5m])

Panel: Container Auth Errors

sum by (container_name) (rate({job="docker"} |~ "(?i)(401|403|unauthorized)" [5m]))

Panel: External Connections (Table)

{job="systemd-journal"} |= "UFW BLOCK" | logfmt | line_format "{{.SRC}}"

Testing Your Security

Simulate Attacks (Safe)

# Test UFW (will be blocked)
nmap -p 1-1000 192.168.0.208

# Test SSH rate limit (from different machine)
for i in {1..10}; do ssh [email protected]; done

# Check monitoring detected it
/home/monxas/scripts/security-monitor.sh

Incident Response

If you detect suspicious activity:

  1. Immediate Actions:

    # Block specific IP
    sudo ufw insert 1 deny from <IP_ADDRESS>
    
    # Kill suspicious sessions
    sudo pkill -u <username>
    
    # Check what was accessed
    sudo lastlog
    sudo history | grep -i "rm\|delete\|modify"
    

  2. Investigation:

    # Full auth log review
    sudo cat /var/log/auth.log | grep <IP_ADDRESS>
    
    # Check container logs
    sudo docker logs --since 24h <container>
    
    # Check file modifications
    find /home/monxas -type f -mtime -1
    

  3. Recovery:

  4. Change passwords (Jellyfin, *arr stack, Grafana)
  5. Rotate API keys
  6. Check backup integrity
  7. Review Cloudflare access logs

Regular Maintenance

Daily: - Check ntfy security notifications

Weekly: - Run security monitor manually: /home/monxas/scripts/security-monitor.sh - Review Grafana security dashboard - Check Uptime Kuma alerts

Monthly: - Review Cloudflare audit logs - Update UFW rules if needed - Review and update monitoring queries

Resources