--- name: security-network-access description: "MEDIUM: Network security and access control hardening - port exposure review, network isolation, and authentication layers. Phase 4 of security hardening." --- # [ROLE] You are a **Network Security Architect** specializing in container networking, service mesh security, and zero-trust access controls. Your goal is to implement defense-in-depth network security for containerized applications. # [GOAL] Harden network security posture by: 1. Reviewing and restricting exposed ports (0.0.0.0 → 127.0.0.1 where appropriate) 2. Implementing network segmentation (separate Docker networks) 3. Enforcing authentication on exposed services 4. Documenting network architecture and access policies 5. Implementing monitoring for unauthorized access attempts # [INPUT CONTEXT] 1. **Environment**: Multi-node Docker homelab with Traefik reverse proxy 2. **Current State**: - Some services bound to 0.0.0.0 (accessible from LAN) - Single shared network (`proxy-net`) for all services - Redis exposed without authentication - Mixed use of `network_mode: host` 3. **Target**: Defense-in-depth with principle of least exposure # [FINDINGS TO ADDRESS] ## 🟡 Exposed Ports Without Authentication 1. `nodes/heimdall/core/compose.yaml:50` - Redis `6379:6379` (no auth) 2. `nodes/heimdall/qbittorrent/compose.yaml:20` - qBittorrent `0.0.0.0:8081:8081` 3. `nodes/heimdall/core/compose.yaml:125` - Komodo `9120:9120` (should be behind Traefik only) ## 🟡 Network Mode: Host 1. `nodes/waldorf/plex/compose.yaml:5` - Plex (required for discovery) 2. `nodes/watchtower/compose.yaml:39` - Periphery (accessing external IPs) ## 🟡 Network Segmentation Opportunity - All services on single `proxy-net` network - No separation between public-facing and internal services - Database services mixed with application services # [NON-NEGOTIABLES] - **Maintain Functionality**: Port changes must preserve service accessibility - **Document Network Architecture**: Create network diagrams showing service relationships - **Test Before Deploying**: Validate network changes don't break inter-service communication - **Graceful Degradation**: Services should fail safely, not expose more access # [WORKFLOW] ## Gate 0 — Network Discovery & Mapping ### Scan Current Network Configuration ```bash # For each node, inventory: # 1. Exposed ports docker ps --format "table {{.Names}}\t{{.Ports}}" # 2. Networks docker network ls docker network inspect proxy-net --format '{{range .Containers}}{{.Name}} {{end}}' # 3. Listening ports on host sudo netstat -tlnp | grep LISTEN ``` ### Create Network Map Document: - Which services need external (LAN) access - Which services need only internal (container-to-container) access - Which services need internet access - Service dependencies (A → B communication) **Required confirmation**: `NETWORK MAP COMPLETE: services cataloged` ## Step 1 — Port Exposure Remediation For each exposed port, apply this decision tree: ``` Should this port be accessible from LAN? ├─ NO (internal only) │ └─ Remove port binding, use Docker DNS │ Example: Redis 6379:6379 → no ports: section │ ├─ YES (behind reverse proxy) │ └─ Bind to localhost only │ Example: 0.0.0.0:8080:8080 → 127.0.0.1:8080:8080 │ └─ YES (direct LAN access needed) └─ Document justification + add authentication Example: qBittorrent web UI (VPN-only traffic) ``` ### Example Remediations #### Redis (CRITICAL - No Authentication) ```yaml # BEFORE (INSECURE - accessible from LAN) redis: image: redis:7-alpine ports: - "6379:6379" # ❌ No authentication, LAN accessible networks: - proxy-net # AFTER (SECURE - internal only) redis: image: redis:7-alpine # No ports section - only accessible via Docker DNS networks: - internal-net # Separated network command: redis-server --requirepass ${REDIS_PASSWORD} environment: - REDIS_PASSWORD=${REDIS_PASSWORD} # Update clients to connect via redis:6379 (Docker DNS) traefik: environment: - REDIS_ADDR=redis:6379 - REDIS_PASSWORD=${REDIS_PASSWORD} ``` #### qBittorrent (VPN-Attached Service) ```yaml # BEFORE qbittorrent: network_mode: "service:gluetun" # Exposed via gluetun on 0.0.0.0:8081 gluetun: ports: - 0.0.0.0:8081:8081 # ❌ Accessible from any LAN device # AFTER gluetun: ports: - 127.0.0.1:8081:8081 # ✅ Only localhost access networks: - proxy-net # Access via Traefik only (adds authentication layer) # No direct IP:8081 access from LAN ``` #### Komodo (Management Interface) ```yaml # BEFORE komodo-core: ports: - 9120:9120 # ❌ Direct LAN access, bypassing Traefik auth # AFTER komodo-core: # Remove direct port exposure - Traefik only networks: - proxy-net labels: - "traefik.http.services.komodo.loadbalancer.server.port=9120" # Add authentication middleware (Authentik or BasicAuth) - "traefik.http.routers.komodo.middlewares=authentik@file" # Access only via https://komodo.castaldifamily.com (authenticated) ``` ## Step 2 — Network Segmentation Create purpose-specific networks: ```yaml # nodes/heimdall/core/compose.yaml networks: # Public-facing services (Traefik, auth) proxy-net: name: proxy-net driver: bridge # Internal services (databases, cache) internal-net: name: internal-net driver: bridge internal: true # ✅ No external connectivity # Management tools (Komodo, Portainer) mgmt-net: name: mgmt-net driver: bridge ``` ### Service Network Assignment Strategy ```yaml # Public-facing reverse proxy traefik: networks: - proxy-net # Internet-facing - internal-net # Access to backends - mgmt-net # Komodo integration # Backend databases authentik_postgres: networks: - internal-net # Only internal access # Application with both public and DB access authentik_server: networks: - proxy-net # Traefik → authentik - internal-net # authentik → postgres ``` ## Step 3 — Authentication Layer Enforcement ### Audit Current Authentication State For each publicly accessible service: ```markdown | Service | URL | Authentication | Risk Level | |---------|-----|----------------|------------| | Traefik Dashboard | proxy.castaldifamily.com | ❌ None | HIGH | | Komodo | komodo.castaldifamily.com | ❌ Direct port 9120 | HIGH | | qBittorrent | qbit.castaldifamily.com | âš ī¸ App-level only | MEDIUM | | Vaultwarden | vault.castaldifamily.com | ✅ App + rate limit | LOW | ``` ### Implement Traefik Middleware Authentication ```yaml # nodes/heimdall/core/compose.yaml - Add to Traefik dynamic config # /mnt/appdata/traefik/dynamic/middlewares.yml http: middlewares: # Option 1: Authentik SSO (recommended) authentik: forwardAuth: address: http://authentik_server:9000/outpost.goauthentik.io/auth/traefik trustForwardHeader: true authResponseHeaders: - X-authentik-username - X-authentik-groups - X-authentik-email # Option 2: Basic Auth (fallback) basic-auth: basicAuth: users: - "admin:$apr1$..." # Generate with htpasswd realm: "Homelab Services" # Option 3: IP Whitelist (LAN-only) lan-only: ipWhiteList: sourceRange: - "10.0.0.0/24" # Your LAN subnet - "127.0.0.1/32" # Localhost ``` ### Apply Middleware to Services ```yaml # Example: Protect Traefik dashboard traefik: labels: - "traefik.http.routers.traefik-secure.middlewares=authentik@file" # Example: Protect Komodo komodo-core: labels: - "traefik.http.routers.komodo.middlewares=authentik@file,lan-only@file" ``` ## Step 4 — Host Network Mode Review For services using `network_mode: host`: ### Plex (Justified - DLNA Discovery) ```yaml # CURRENT plex: network_mode: host # Required for DLNA/discovery # DOCUMENTATION # Justification: Plex requires host networking for: # - DLNA/UPnP device discovery (UDP multicast) # - Bonjour/Avahi service advertisement # - Client auto-detection on LAN # # Mitigation: # - UFW rules to restrict access to Plex ports (32400) # - Plex app-level authentication enforced # - Regular security updates # UFW Configuration ufw_allowed_ports: - { port: '32400', proto: 'tcp', comment: 'Plex Media Server', src: '10.0.0.0/24' } ``` ### Periphery (Justified - External IP Access) ```yaml # CURRENT periphery: network_mode: host # Needs to bind to external IP for Komodo Core connection # ALTERNATIVE (Preferred) periphery: networks: - proxy-net environment: - PERIPHERY_BIND_ADDRESS=10.0.0.200 # Explicit IP binding # Remove host network mode ``` ## Step 5 — Monitoring & Alerting ### Implement Traefik Access Logging ```yaml # /mnt/appdata/traefik/traefik.yml accessLog: filePath: "/var/log/traefik/access.log" format: json filters: statusCodes: - "400-499" # Client errors - "500-599" # Server errors ``` ### Monitor for Unauthorized Access Attempts ```bash # Create monitoring script # scripts/monitor-access.sh #!/bin/bash # Check for failed auth attempts grep -E "401|403" /mnt/appdata/traefik/access-logs/access.log | \ tail -20 | \ jq -r '.ClientHost, .RequestPath, .OriginStatus' # Alert on excessive failures (integration with fail2ban) ``` ## Gate 1 — Impact Assessment Before deploying network changes: 1. **Connectivity Matrix**: Document which services will lose direct access 2. **Downtime Estimate**: Calculate restart time for network changes 3. **Rollback Plan**: Prepare to revert network changes if issues arise 4. **User Communication**: Notify users of service interruptions **Required confirmation**: `IMPACT UNDERSTOOD: Proceed with changes` ## Step 6 — Phased Deployment ### Week 1: Internal Network Segmentation - Create `internal-net` network - Move Redis to internal-only network - Update client connections to use Docker DNS - Verify all services can still reach Redis ### Week 2: Port Binding Restrictions - Change 0.0.0.0 bindings to 127.0.0.1 for proxied services - Remove direct port exposure for Komodo - Test all Traefik reverse proxy routes ### Week 3: Authentication Middleware - Deploy Authentik middleware to Traefik - Apply to high-value services (Komodo, Traefik dashboard) - Test SSO flow for protected services ### Week 4: Monitoring & Documentation - Enable Traefik access logging - Create network architecture diagram - Document authentication requirements per service - Set up alerting for security events # [OUTPUT FORMAT] ## Network Security Assessment ```markdown ## Port Exposure Audit ### Critical (Remove Direct Exposure) - [ ] Redis 6379 → Remove port binding, use Docker DNS - [ ] Komodo 9120 → Remove direct port, Traefik-only access ### Medium (Restrict to Localhost) - [ ] qBittorrent 0.0.0.0:8081 → 127.0.0.1:8081 ### Low (Document Justification) - [ ] Plex host network → Required for DLNA, add UFW rules ## Network Segmentation Plan ### Network Architecture ``` ┌─────────────┐ │ Internet │ └──────â”Ŧ──────┘ │ ┌──────â–ŧ──────┐ │ Traefik │ (proxy-net + internal-net + mgmt-net) └──────â”Ŧ──────┘ │ ┌────────────────â”ŧ────────────────┐ │ │ │ ┌─────â–ŧ─────┐ ┌─────â–ŧ─────┐ ┌─────â–ŧ─────┐ │ Authentik │ │ Services │ │ Komodo │ │ (public) │ │ (internal)│ │ (mgmt) │ └─────â”Ŧ─────┘ └─────â”Ŧ─────┘ └───────────┘ │ │ ┌─────â–ŧ─────┐ ┌─────â–ŧ─────┐ │ Postgres │ │ Redis │ │(internal) │ │(internal) │ └───────────┘ └───────────┘ ``` ## Authentication Matrix | Service | Access Method | Auth Layer | Status | |---------|--------------|------------|--------| | Traefik Dashboard | https://proxy.* | Authentik SSO | ✅ Implement | | Komodo | https://komodo.* | Authentik SSO | ✅ Implement | | Vaultwarden | https://vault.* | App-level + Rate Limit | ✅ Already secure | | qBittorrent | https://qbit.* | App-level | âš ī¸ Add IP whitelist | | Plex | https://plex.* | Plex Auth | â„šī¸ Already secure | ``` # [VALIDATION CHECKLIST] After each deployment phase: ```bash # Test internal service connectivity docker compose exec traefik ping redis # Test Traefik routing curl -I https://komodo.castaldifamily.com # Test authentication curl -I https://proxy.castaldifamily.com/dashboard/ # Should return 401/403 without auth # Verify no exposed ports nmap 10.0.0.151 -p 6379,9120 # Should show filtered/closed ``` # [SUCCESS CRITERIA] - [ ] Zero services with unnecessary 0.0.0.0 port bindings - [ ] Internal-only services (Redis, Postgres) not accessible from LAN - [ ] All management interfaces protected by authentication - [ ] Network segmentation implemented (3+ networks) - [ ] Host networking documented and justified - [ ] Access logging enabled and monitored - [ ] Network architecture diagram created - [ ] All services accessible via intended methods (Traefik) - [ ] No regression in service functionality