This page documents the security measures, best practices, and incident response procedures for the Lucille4 homelab server.
The homelab implements defense-in-depth by using Tecnativa's docker-socket-proxy to prevent direct Docker socket access:
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:latest
environment:
CONTAINERS: 1 # Read-only container list
## All other permissions disabled by default
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- caddy
Security Benefits:
tcp://docker-socket-proxy:2375 instead of socketTwo isolated Docker networks provide defense in depth:
caddy)internal)
## Service with external access
myservice:
networks:
- caddy # External access via reverse proxy
- internal # Can communicate with databases
## Backend service
database:
networks:
- internal # Isolated from external access
.env file.env file encrypted with git-crypt
## Step 1: Create credential in 1Password
op item create --category=login \
--title="Service Name - Homelab" \
--vault="Private" \
username=admin \
generate-password=letters,digits,symbols,32 \
--tags=homelab
## Step 2: Add to .env for service use
echo "SERVICE_PASSWORD=generated-password" >> .env
## List all homelab credentials
op item list --vault Private --tags homelab
## Retrieve specific password
op item get "Service Name - Homelab" --fields password
## Core Authentication
AUTHENTIK_SECRET_KEY= # Generate: openssl rand -hex 64
AUTHENTIK_POSTGRES_PASSWORD= # Database password
AUTHENTIK_REDIS_PASSWORD= # Redis password
## Monitoring Stack
GF_SECURITY_ADMIN_PASSWORD= # Grafana admin
INFLUXDB_ADMIN_PASSWORD= # InfluxDB admin
SEQ_FIRSTRUN_ADMINPASSWORD= # Seq admin
## Backup Credentials
RESTIC_PASSWORD_PRIMARY= # Backblaze repository
RESTIC_PASSWORD_SECONDARY= # Hetzner repository
RESTIC_B2_ACCESS_KEY_ID= # Backblaze access
RESTIC_B2_SECRET_ACCESS_KEY= # Backblaze secret
## Service Secrets
PAPERLESS_SECRET_KEY= # Document management
N8N_ENCRYPTION_KEY= # Workflow encryption
METAMCP_API_KEY= # AI assistant API
MEILISEARCH_MASTER_KEY= # Search engine
CODE_SERVER_PASSWORD= # IDE password
Critical infrastructure services use specific versions to ensure stability:
| Service | Version | Update Frequency |
|---|---|---|
| Caddy | 2.10.0 |
Quarterly |
| PostgreSQL | 16 |
Major versions only |
| Redis | 7-alpine |
Security patches |
| Authentik | 2024.10 |
Monthly |
| Paperless | 2.17.1 |
After testing |
Prevent resource exhaustion attacks with container limits:
service:
mem_limit: 2g # Hard memory limit
mem_reservation: 1g # Soft reservation
cpus: '2.0' # CPU limit
restart: always # Auto-restart on failure
All external services include security headers via Caddy labels:
labels:
caddy.header.Strict-Transport-Security: "max-age=31536000; includeSubDomains"
caddy.header.X-Frame-Options: "SAMEORIGIN"
caddy.header.X-Content-Type-Options: "nosniff"
caddy.header.X-XSS-Protection: "1; mode=block"
caddy.header.Referrer-Policy: "strict-origin-when-cross-origin"
## Monthly security check
python3 check-updates.py
## Update priority (lowest to highest risk):
## 1. Utility containers (it-tools, cyberchef)
## 2. Monitoring stack (grafana, dozzle)
## 3. Workflow tools (n8n, jupyter)
## 4. Critical services (authentik, paperless)
## Always backup before critical updates
sudo -E ./backup.sh
.env fileIsolate immediately:
Investigate:
Remove and redeploy:
Post-incident:
docker stop [container]
docker network disconnect caddy [container]
docker logs [container] --since 24h > incident.log
docker inspect [container] > container-config.json
docker rm [container]
docker compose pull [service]
docker compose up -d [service]
Immediate rotation:
Service update:
Audit access:
Prevention:
.envdocker compose up -d [affected-service]
Check service logs for unauthorized use
Review Authentik authentication logs
Monitor for 24-48 hours
Enable 2FA where possible
Use service accounts
Regular rotation schedule
docker logs caddy --since 24h | grep -E "4[0-9]{2}|5[0-9]{2}"
docker logs auth-server --since 24h | grep -i "failed|error|denied"
1. **Network analysis**:
```bash
## Active connections
docker exec caddy netstat -an
## Traffic analysis
docker stats --no-stream
Isolation protocol:
The homelab uses Tecnativa's docker-socket-proxy to prevent direct Docker socket access:
docker-socket-proxy:
image: tecnativa/docker-socket-proxy:latest
environment:
CONTAINERS: 1 # Read-only container list
## All other permissions disabled by default
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
networks:
- caddy
Benefits:
tcp://docker-socket-proxy:2375Two isolated Docker networks provide defense in depth:
caddy Networkinternal Network
## Service with external access
myservice:
networks:
- caddy # External access
- internal # Database access
## Backend service
database:
networks:
- internal # No external access
.env file.env file encrypted with git-crypt
## Save all homelab credentials to 1Password
./scripts/save-to-1password.sh
## Retrieve credentials from 1Password
./scripts/get-from-1password.sh list # List all items
./scripts/get-from-1password.sh check # Verify .env matches 1Password
./scripts/get-from-1password.sh export # Export credentials
## Create new credential in 1Password
op item create --category=login \
--title="Service Name - Homelab" \
--vault="Private" \
username=admin \
generate-password=letters,digits,symbols,32 \
--tags=homelab
## Add to .env for service use
echo "SERVICE_PASSWORD=generated-password" >> .env
## List all homelab credentials
op item list --vault Private --tags homelab
## Authentication
AUTHENTIK_SECRET_KEY= # Generate with: openssl rand -hex 64
AUTHENTIK_POSTGRES_PASSWORD= # Database password
AUTHENTIK_REDIS_PASSWORD= # Redis password
## Monitoring
GF_SECURITY_ADMIN_PASSWORD= # Grafana admin password
INFLUXDB_ADMIN_PASSWORD= # InfluxDB admin password
## Backups
RESTIC_PASSWORD_PRIMARY= # Backblaze repository
RESTIC_PASSWORD_SECONDARY= # Hetzner repository
RESTIC_B2_ACCESS_KEY_ID= # Backblaze access key
RESTIC_B2_SECRET_ACCESS_KEY= # Backblaze secret
## Services
PAPERLESS_SECRET_KEY= # Document management
N8N_ENCRYPTION_KEY= # Workflow encryption
METAMCP_API_KEY= # AI assistant API
JUPYTER_TOKEN= # Jupyter notebook authentication
QDRANT_API_KEY= # Qdrant vector database API
no-new-privileges) on critical servicesinternal network onlyCritical infrastructure services use specific versions:
2.10.01672.17.1High-usage containers have memory limits:
service:
mem_limit: 2g # Hard limit
mem_reservation: 1g # Soft limit
All services include security headers via Caddy:
labels:
caddy.header.Strict-Transport-Security: "max-age=31536000"
caddy.header.X-Frame-Options: "SAMEORIGIN"
caddy.header.X-Content-Type-Options: "nosniff"
## Check for updates monthly
python3 scripts/check-updates.py
## Update order (lowest to highest risk):
## 1. Utilities
## 2. Monitoring
## 3. Workflow tools
## 4. Critical services (with backups!)
.envdocker stop [container]docker logs [container]docker rm [container]docker compose up -d [service].envdocker logs caddydocker logs auth-server