Manage incoming internet traffic and reverse proxy configuration on the home network gateway. Configure Caddy, OAuth2 authentication, fail2ban security, and traffic routing.
This skill enables management of incoming internet traffic and reverse proxy configuration on the home network, specifically focused on the Raspberry Pi gateway running Caddy, OAuth2-proxy, and fail2ban security.
The reverse-proxy skill provides capabilities to manage the public-facing gateway server (raspberrypi.local) that handles:
This server is the source of truth for all incoming traffic configuration. Reference repository: /home/seth/Software/dev/squelch
ssh pi (port 2222)/home/seth/Software/dev/squelchoauth-caddy-package/ - OAuth2 authentication packagesquelch-package/ - fail2ban security packageconfig-backup/ - Production configuration backupsCLAUDE.md - Complete architecture documentationInternet (lab.sethlakowske.com)
|
v
[Caddy :443] ────── Let's Encrypt HTTPS
|
├─> /oauth2/* ──> [oauth2-proxy :4180] Google Auth
├─> /good-neighbor/* ──> [oauth2-proxy :4182] ──> Backend :3000
├─> /service-monitor/* ──> [oauth2-proxy :4183] ──> Backend :8000
└─> /* ──> Backend :8080 (public)
|
v
[fail2ban] monitors /var/log/caddy/access.log
|
v
[iptables] blocks malicious IPs
# Check all critical services
ssh pi "systemctl status caddy oauth2-proxy-google oauth2-proxy-good-neighbor oauth2-proxy-service-monitor fail2ban"
# Quick status check
ssh pi "systemctl is-active caddy oauth2-proxy-google fail2ban"
# Check if services are listening on expected ports
ssh pi "sudo ss -tlnp | grep -E '(443|4180|4182|4183|8000)'"
# Check Caddy status
ssh pi "systemctl status caddy"
# View Caddy configuration
ssh pi "cat /etc/caddy/Caddyfile"
# Validate Caddy configuration
ssh pi "sudo caddy validate --config /etc/caddy/Caddyfile"
# Reload Caddy (graceful, no downtime)
ssh pi "sudo systemctl reload caddy"
# Restart Caddy (brief downtime)
ssh pi "sudo systemctl restart caddy"
# View Caddy logs
ssh pi "journalctl -u caddy -n 100 --no-pager"
ssh pi "journalctl -u caddy -f" # Follow logs
# View access logs (JSON format)
ssh pi "tail -f /var/log/caddy/access.log"
ssh pi "tail -100 /var/log/caddy/access.log | jq ."
# Check all OAuth proxy services
ssh pi "systemctl status oauth2-proxy-google"
ssh pi "systemctl status oauth2-proxy-good-neighbor"
ssh pi "systemctl status oauth2-proxy-service-monitor"
# Restart specific OAuth proxy
ssh pi "sudo systemctl restart oauth2-proxy-google"
# View OAuth proxy logs
ssh pi "journalctl -u oauth2-proxy-google -n 50 --no-pager"
ssh pi "journalctl -u oauth2-proxy-good-neighbor -f"
# Check OAuth proxy configuration
ssh pi "cat /etc/oauth2-proxy/google.cfg"
ssh pi "cat /etc/oauth2-proxy/good-neighbor.cfg"
# Test OAuth proxy health
ssh pi "curl -s http://localhost:4180/ping"
# Check fail2ban status
ssh pi "sudo systemctl status fail2ban"
# View all active jails
ssh pi "sudo fail2ban-client status"
# View specific jail status
ssh pi "sudo fail2ban-client status sshd"
ssh pi "sudo fail2ban-client status squelch-caddy-auth"
ssh pi "sudo fail2ban-client status squelch-caddy-badbots"
ssh pi "sudo fail2ban-client status squelch-caddy-scan"
# List banned IPs
ssh pi "sudo squelch-ban list"
ssh pi "sudo squelch-ban list squelch-caddy-auth"
# Ban an IP manually
ssh pi "sudo squelch-ban ban 1.2.3.4 squelch-caddy-auth"
# Unban an IP
ssh pi "sudo squelch-ban unban 1.2.3.4"
# Check if IP is banned
ssh pi "sudo squelch-ban check 1.2.3.4"
# View fail2ban logs
ssh pi "sudo journalctl -u fail2ban -n 100 --no-pager"
ssh pi "sudo journalctl -u fail2ban | grep Ban"
# View comprehensive security status
ssh pi "sudo squelch-status"
# Real-time security monitoring
ssh pi "sudo squelch-monitor"
# Check recent authentication failures
ssh pi "sudo journalctl -u fail2ban --since '1 hour ago' | grep -E '(Ban|Found)'"
# View recent access patterns
ssh pi "tail -100 /var/log/caddy/access.log | jq -r '.request.remote_ip' | sort | uniq -c | sort -nr"
To add a new service behind OAuth authentication:
Verify backend service is running:
# Test backend health
curl -s http://ubuntu-box.local:3001/health
Create OAuth2-proxy configuration:
# SSH to gateway
ssh pi
# Create new OAuth config (based on template)
sudo cp /etc/oauth2-proxy/good-neighbor.cfg.template /etc/oauth2-proxy/new-service.cfg
# Edit configuration
sudo nano /etc/oauth2-proxy/new-service.cfg
# Update:
# - http_address = "127.0.0.1:4184" (new port)
# - upstreams = ["http://ubuntu-box.local:3001/"]
# - cookie_name = "_oauth2_proxy_new_service"
# - redirect_url = "https://lab.sethlakowske.com/new-service/oauth2/callback"
Create systemd service:
sudo cat > /etc/systemd/system/oauth2-proxy-new-service.service << 'EOF'
[Unit]
Description=OAuth2 Proxy for New Service
After=network.target
[Service] Type=simple User=www-data Group=www-data ExecStart=/usr/bin/oauth2-proxy --config=/etc/oauth2-proxy/new-service.cfg Restart=always RestartSec=5
[Install] WantedBy=multi-user.target EOF
sudo systemctl daemon-reload sudo systemctl enable oauth2-proxy-new-service sudo systemctl start oauth2-proxy-new-service sudo systemctl status oauth2-proxy-new-service
4. **Update Caddyfile**:
```bash
sudo nano /etc/caddy/Caddyfile
# Add route (before the catch-all /* route):
# route /new-service* {
# reverse_proxy localhost:4184
# }
Apply Caddy changes:
# Validate configuration
sudo caddy validate --config /etc/caddy/Caddyfile
# Reload Caddy (graceful)
sudo systemctl reload caddy
Verify setup:
# Test OAuth proxy
curl -s http://localhost:4184/ping
# Test public endpoint (should redirect to OAuth)
curl -I https://lab.sethlakowske.com/new-service/
Backup configuration:
# From local machine
scp pi:/etc/caddy/Caddyfile ~/Software/dev/squelch/config-backup/caddy/
scp pi:/etc/oauth2-proxy/new-service.cfg ~/Software/dev/squelch/config-backup/oauth2-proxy/
For services that don't require OAuth:
Update Caddyfile:
ssh pi "sudo nano /etc/caddy/Caddyfile"
# Add route (order matters - more specific first):
# route /public-api/* {
# reverse_proxy ubuntu-box.local:3002
# }
Reload Caddy:
ssh pi "sudo caddy validate --config /etc/caddy/Caddyfile && sudo systemctl reload caddy"
Test:
curl -s https://lab.sethlakowske.com/public-api/health | jq .
To change where a route proxies to:
For OAuth-protected routes, update OAuth2-proxy config:
ssh pi "sudo nano /etc/oauth2-proxy/good-neighbor.cfg"
# Change: upstreams = ["http://ubuntu-box.local:NEW_PORT/"]
ssh pi "sudo systemctl restart oauth2-proxy-good-neighbor"
For public routes, update Caddyfile:
ssh pi "sudo nano /etc/caddy/Caddyfile"
# Change reverse_proxy line
ssh pi "sudo systemctl reload caddy"
# View Caddyfile routes
ssh pi "cat /etc/caddy/Caddyfile | grep -A 2 'route'"
# View all OAuth proxy upstreams
ssh pi "grep 'upstreams' /etc/oauth2-proxy/*.cfg"
# Show all listening services
ssh pi "sudo ss -tlnp | grep -E '(caddy|oauth2-proxy)'"
# Top IPs accessing the server
ssh pi "tail -1000 /var/log/caddy/access.log | jq -r '.request.remote_ip' | sort | uniq -c | sort -nr | head -10"
# Most requested URIs
ssh pi "tail -1000 /var/log/caddy/access.log | jq -r '.request.uri' | sort | uniq -c | sort -nr | head -10"
# Failed authentication attempts (401/403)
ssh pi "tail -1000 /var/log/caddy/access.log | jq 'select(.status == 401 or .status == 403)'"
# Response time analysis
ssh pi "tail -1000 /var/log/caddy/access.log | jq -r '.duration' | awk '{sum+=\$1; count++} END {print \"Average:\", sum/count, \"seconds\"}'"
# Status code distribution
ssh pi "tail -1000 /var/log/caddy/access.log | jq -r '.status' | sort | uniq -c | sort -nr"
| File | Purpose | Service |
|---|---|---|
/etc/caddy/Caddyfile |
Main reverse proxy configuration | caddy |
/etc/oauth2-proxy/google.cfg |
Google OAuth for general use | oauth2-proxy-google |
/etc/oauth2-proxy/good-neighbor.cfg |
OAuth for good-neighbor service | oauth2-proxy-good-neighbor |
/etc/oauth2-proxy/service-monitor.cfg |
OAuth for service-monitor | oauth2-proxy-service-monitor |
/etc/fail2ban/jail.d/squelch.conf |
fail2ban jail configuration | fail2ban |
/etc/fail2ban/filter.d/squelch-*.conf |
fail2ban filters | fail2ban |
/var/log/caddy/access.log |
HTTP access logs (JSON) | caddy |
All production configs should be backed up to:
~/Software/dev/squelch/config-backup/
├── caddy/
│ ├── Caddyfile
│ └── Caddyfile.template
└── oauth2-proxy/
├── google.cfg
├── good-neighbor.cfg
├── service-monitor.cfg
└── *.cfg.template
# Backup current production configs
ssh pi "cat /etc/caddy/Caddyfile" > ~/Software/dev/squelch/config-backup/caddy/Caddyfile
ssh pi "cat /etc/oauth2-proxy/google.cfg" > ~/Software/dev/squelch/config-backup/oauth2-proxy/google.cfg
# Or use scp
scp pi:/etc/caddy/Caddyfile ~/Software/dev/squelch/config-backup/caddy/
scp pi:/etc/oauth2-proxy/*.cfg ~/Software/dev/squelch/config-backup/oauth2-proxy/
# Commit to git
cd ~/Software/dev/squelch
git add config-backup/
git commit -m "Backup production proxy configs"
git push
Based on the production Caddyfile:
| Route | Auth | Backend | Port | Description |
|---|---|---|---|---|
/oauth2/* |
No | oauth2-proxy | 4180 | OAuth callback handler |
/good-neighbor* |
Yes (Google) | good-neighbor | 3000 | Protected service |
/service-monitor* |
Yes (Google) | service-monitor | 8000 | Protected monitoring |
/* |
No | default backend | 8080 | Public routes |
IMPORTANT: Caddy processes routes in order. More specific routes must come BEFORE catch-all routes:
lab.sethlakowske.com {
log {
output file /var/log/caddy/access.log
}
# OAuth callback (most specific)
route /oauth2/* {
reverse_proxy localhost:4180
}
# Protected service routes
route /good-neighbor* {
reverse_proxy localhost:4182
}
route /service-monitor* {
reverse_proxy localhost:4183
}
# Catch-all public routes (LAST)
route /* {
reverse_proxy localhost:8080
}
}
| Jail Name | Purpose | Trigger | Max Retry | Ban Time |
|---|---|---|---|---|
sshd |
SSH brute force | Failed SSH login | 2 | 24h |
squelch-caddy-auth |
Auth failures | 401/403 responses | 5 | 1h+ |
squelch-caddy-badbots |
Bad bots/scanners | Attack paths | 2 | 1h+ |
squelch-caddy-scan |
Directory scanning | Multiple 404s | 3 | 1h+ |
fail2ban uses progressive ban times:
# Real-time ban events
ssh pi "sudo journalctl -u fail2ban -f | grep Ban"
# Recently banned IPs
ssh pi "sudo journalctl -u fail2ban --since '1 hour ago' | grep 'Ban '"
# Ban statistics
ssh pi "sudo fail2ban-client status | grep 'Currently banned:'"
# Analyze attack patterns
ssh pi "tail -1000 /var/log/caddy/access.log | jq 'select(.status >= 400) | {ip: .request.remote_ip, status: .status, uri: .request.uri}'"
Check Caddy is running and listening:
ssh pi "systemctl status caddy"
ssh pi "sudo ss -tlnp | grep :443"
Check route configuration:
ssh pi "cat /etc/caddy/Caddyfile | grep -A 3 'route /your-service'"
Test from gateway server:
ssh pi "curl -I http://localhost:4180/ping" # OAuth proxy
ssh pi "curl -I https://localhost/your-service/" # Through Caddy
Check DNS:
dig lab.sethlakowske.com
Check firewall:
ssh pi "sudo iptables -L -n | grep -E '(443|80)'"
Check OAuth proxy is running:
ssh pi "systemctl status oauth2-proxy-google"
Verify OAuth config:
ssh pi "grep redirect_url /etc/oauth2-proxy/google.cfg"
# Should match: https://lab.sethlakowske.com/oauth2/callback
Check cookie settings:
ssh pi "grep -E '(cookie_secure|cookie_domain)' /etc/oauth2-proxy/google.cfg"
View OAuth logs:
ssh pi "journalctl -u oauth2-proxy-google -n 50 --no-pager"
Check which jail banned the IP:
ssh pi "sudo squelch-ban check 192.168.1.100"
Review why IP was banned:
ssh pi "sudo journalctl -u fail2ban | grep '192.168.1.100'"
Unban the IP:
ssh pi "sudo squelch-ban unban 192.168.1.100"
Add to ignore list (if trusted):
ssh pi "sudo nano /etc/fail2ban/jail.d/squelch.conf"
# Add to ignoreip: 192.168.1.100
ssh pi "sudo systemctl restart fail2ban"
Check backend is running:
curl -s http://ubuntu-box.local:3000/health
Verify OAuth proxy upstream:
ssh pi "grep upstreams /etc/oauth2-proxy/good-neighbor.cfg"
Test OAuth proxy directly:
ssh pi "curl -I http://localhost:4182/"
Check Caddy route:
ssh pi "cat /etc/caddy/Caddyfile | grep -A 2 'good-neighbor'"
Check certificate status:
ssh pi "journalctl -u caddy | grep -i certificate"
Verify DNS is correct:
dig lab.sethlakowske.com
# Should point to public IP
Check port 80 is accessible (needed for Let's Encrypt):
ssh pi "sudo ss -tlnp | grep :80"
Force certificate renewal:
ssh pi "sudo systemctl restart caddy"
Complete architecture documentation available at:
~/Software/dev/squelch/CLAUDE.md
This includes:
Use this skill for:
For complex gateway changes:
ssh pi -t "cd ~/Software/dev/squelch && lfg"
# Claude on gateway has direct access to all configs and logs
Always backup configs before changes:
scp pi:/etc/caddy/Caddyfile ~/Software/dev/squelch/config-backup/caddy/
Validate Caddy config before reload:
ssh pi "sudo caddy validate --config /etc/caddy/Caddyfile"
Use reload instead of restart (when possible):
ssh pi "sudo systemctl reload caddy" # Graceful, no downtime
Monitor logs after changes:
ssh pi "journalctl -u caddy -f"
Document route changes in git commits
Test from multiple locations:
ssh pi "curl http://localhost:PORT"curl http://raspberrypi.local:PORTcurl https://lab.sethlakowske.com/routessh pi "sudo squelch-status"ssh pi "sudo squelch-ban list"ssh pi "tail -1000 /var/log/caddy/access.log | jq 'select(.status >= 400)'"ssh pi "sudo journalctl -u fail2ban --since '1 hour ago'"curl http://ubuntu-box.local:NEW_PORT/health/etc/oauth2-proxy/SERVICE.cfgsudo systemctl restart oauth2-proxy-SERVICEcurl https://lab.sethlakowske.com/service/ssh pi "sudo nano /etc/caddy/Caddyfile"ssh pi "sudo caddy validate --config /etc/caddy/Caddyfile"ssh pi "sudo systemctl reload caddy"curl https://lab.sethlakowske.com/new-route/# One-liner health check
ssh pi "systemctl is-active caddy oauth2-proxy-google fail2ban && echo 'Gateway healthy'"
# Detailed status
ssh pi "sudo squelch-status"
# Recent 401/403 responses
ssh pi "tail -500 /var/log/caddy/access.log | jq 'select(.status == 401 or .status == 403) | {time: .ts, ip: .request.remote_ip, uri: .request.uri, status: .status}'"
# IPs with most auth failures
ssh pi "tail -1000 /var/log/caddy/access.log | jq -r 'select(.status == 401) | .request.remote_ip' | sort | uniq -c | sort -nr"
# Ban IP immediately in all relevant jails
ssh pi "sudo squelch-ban ban 1.2.3.4 squelch-caddy-auth"
ssh pi "sudo squelch-ban ban 1.2.3.4 squelch-caddy-badbots"
ssh pi "sudo squelch-ban ban 1.2.3.4 squelch-caddy-scan"
# Verify ban
ssh pi "sudo squelch-ban check 1.2.3.4"
# Show all routes and their backends
ssh pi "cat /etc/caddy/Caddyfile | grep -E '(route|reverse_proxy)'"
# Show all OAuth proxy ports and upstreams
ssh pi "grep -E '(http_address|upstreams)' /etc/oauth2-proxy/*.cfg"
# Show all active services
ssh pi "systemctl list-units --type=service --state=running | grep -E '(caddy|oauth2-proxy|fail2ban)'"