security-secrets-remediation.prompt.md - Phase 1 (CRITICAL) Eliminates hardcoded secrets (Docker Registry, Komodo, Plex) Creates .env templates and migration workflow Priority: Immediate (This Week) security-container-hardening.prompt.md - Phase 2 (HIGH) Removes privileged containers Converts root users to non-root (PUID/PGID) Secures Docker socket access patterns Priority: Short Term (This Month) security-ansible-hardening.prompt.md - Phase 3 (MEDIUM) Enables SSH host key checking Implements restricted sudo rules Deploys UFW firewalls and fail2ban Priority: Medium Term (Next Month) security-network-access.prompt.md - Phase 4 (MEDIUM) Restricts port exposure (0.0.0.0 → 127.0.0.1) Implements network segmentation Adds authentication middleware Priority: Ongoing (Next Quarter) Each prompt follows your existing format with: ✅ Gated workflows with confirmation checkpoints ✅ Rollback procedures for safety ✅ Testing and validation steps ✅ Incremental deployment strategies ✅ Clear success criteria
455 lines
14 KiB
Markdown
455 lines
14 KiB
Markdown
---
|
||
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: <count> 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
|