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)¶
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)¶
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.caddycon ingress y se sirven vΓa CF Tunnel + Caddy. El modelo de acceso actual es Caddy HA en LXC 270/271 (generado porhomelab-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:
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)¶
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¶
- Go to Grafana (http://192.168.0.208:3030)
- Create New Dashboard
- Add panels with these queries:
Panel: SSH Failed Logins
Panel: Firewall Blocks
Panel: Container Auth Errors
Panel: External Connections (Table)
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:
-
Immediate Actions:
-
Investigation:
-
Recovery:
- Change passwords (Jellyfin, *arr stack, Grafana)
- Rotate API keys
- Check backup integrity
- 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