docs(security): comprehensive security audit and remediation documentation
- Add SECURITY.md policy with credential management, Docker security, SSL/TLS guidance - Add security audit report (2025-12-20) with 31 findings across 4 severity levels - Add pre-deployment security checklist template - Update CLAUDE_STATUS.md with security audit initiative - Expand services/README.md with comprehensive security sections - Add script validation report and container name fix guide Audit identified 6 CRITICAL, 3 HIGH, 2 MEDIUM findings 4-phase remediation roadmap created (estimated 6-13 min downtime) All security scripts validated and ready for execution Related: Security Audit Q4 2025, CRITICAL-001 through CRITICAL-006 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
621
scripts/security/CONTAINER_NAME_FIXES.md
Normal file
621
scripts/security/CONTAINER_NAME_FIXES.md
Normal file
@@ -0,0 +1,621 @@
|
||||
# Container Name Standardization
|
||||
|
||||
**Issue**: MED-010 from Security Audit 2025-12-20
|
||||
**Severity**: Medium (Low priority, continuous improvement)
|
||||
**Impact**: Inconsistent container naming makes monitoring and automation difficult
|
||||
|
||||
---
|
||||
|
||||
## Current State
|
||||
|
||||
Docker Compose automatically generates container names using the format:
|
||||
```
|
||||
<directory>-<service>-<instance>
|
||||
```
|
||||
|
||||
This results in inconsistent and unclear names:
|
||||
|
||||
| Current Name | Service | Issue |
|
||||
|--------------|---------|-------|
|
||||
| `paperless-ngx-webserver-1` | Paperless webserver | Redundant "ngx" and unclear purpose |
|
||||
| `paperless-ngx-db-1` | PostgreSQL | Unclear it's Paperless database |
|
||||
| `speedtest-tracker-app-1` | Speedtest main service | Generic "app" name |
|
||||
| `tinyauth-tinyauth-1` | TinyAuth | Duplicate service name |
|
||||
| `monitoring-grafana-1` | Grafana | Directory name included |
|
||||
| `monitoring-prometheus-1` | Prometheus | Directory name included |
|
||||
|
||||
---
|
||||
|
||||
## Desired State
|
||||
|
||||
Use explicit `container_name` directive for clarity:
|
||||
|
||||
| Desired Name | Service | Benefit |
|
||||
|--------------|---------|---------|
|
||||
| `paperless-webserver` | Paperless webserver | Clear, no instance suffix |
|
||||
| `paperless-db` | Paperless PostgreSQL | Obviously Paperless database |
|
||||
| `paperless-redis` | Paperless Redis | Clear purpose |
|
||||
| `speedtest-tracker` | Speedtest service | Concise, descriptive |
|
||||
| `tinyauth` | TinyAuth | Simple, no duplication |
|
||||
| `grafana` | Grafana | Short, clear |
|
||||
| `prometheus` | Prometheus | Short, clear |
|
||||
|
||||
---
|
||||
|
||||
## Naming Convention Standard
|
||||
|
||||
### Format
|
||||
```
|
||||
<service>[-<component>]
|
||||
```
|
||||
|
||||
### Examples
|
||||
|
||||
**Single-container services**:
|
||||
```yaml
|
||||
services:
|
||||
tinyauth:
|
||||
container_name: tinyauth
|
||||
# ...
|
||||
```
|
||||
|
||||
**Multi-container services**:
|
||||
```yaml
|
||||
services:
|
||||
webserver:
|
||||
container_name: paperless-webserver
|
||||
# ...
|
||||
|
||||
db:
|
||||
container_name: paperless-db
|
||||
# ...
|
||||
|
||||
redis:
|
||||
container_name: paperless-redis
|
||||
# ...
|
||||
```
|
||||
|
||||
### Rules
|
||||
|
||||
1. **Use lowercase** - All container names lowercase
|
||||
2. **Use hyphens** - Separate words with hyphens (not underscores)
|
||||
3. **Be descriptive** - Name should indicate purpose
|
||||
4. **Be concise** - Avoid redundancy (no "paperless-ngx-paperless-1")
|
||||
5. **No instance numbers** - Use `container_name` to remove `-1`, `-2` suffixes
|
||||
6. **Service prefix for multi-container** - e.g., `paperless-db`, `paperless-redis`
|
||||
7. **No directory names** - Avoid `monitoring-grafana`, just use `grafana`
|
||||
|
||||
---
|
||||
|
||||
## Implementation
|
||||
|
||||
### Step 1: Update docker-compose.yaml Files
|
||||
|
||||
For each service, add `container_name` directive.
|
||||
|
||||
#### ByteStash
|
||||
|
||||
**File**: `/home/jramos/homelab/services/bytestash/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
bytestash:
|
||||
container_name: bytestash # Add this line
|
||||
image: ghcr.io/jordan-dalby/bytestash:latest
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
#### FileBrowser
|
||||
|
||||
**File**: `/home/jramos/homelab/services/filebrowser/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
filebrowser:
|
||||
container_name: filebrowser # Add this line
|
||||
image: filebrowser/filebrowser:latest
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
#### Paperless-ngx
|
||||
|
||||
**File**: `/home/jramos/homelab/services/paperless-ngx/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
broker:
|
||||
container_name: paperless-redis # Add this line
|
||||
image: redis:8
|
||||
# ...
|
||||
|
||||
db:
|
||||
container_name: paperless-db # Add this line
|
||||
image: postgres:17
|
||||
# ...
|
||||
|
||||
webserver:
|
||||
container_name: paperless-webserver # Add this line
|
||||
image: ghcr.io/paperless-ngx/paperless-ngx:latest
|
||||
# ...
|
||||
|
||||
gotenberg:
|
||||
container_name: paperless-gotenberg # Add this line
|
||||
image: gotenberg:8.20
|
||||
# ...
|
||||
|
||||
tika:
|
||||
container_name: paperless-tika # Add this line
|
||||
image: apache/tika:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
#### Portainer
|
||||
|
||||
**File**: `/home/jramos/homelab/services/portainer/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
portainer:
|
||||
container_name: portainer # Add this line
|
||||
image: portainer/portainer-ce:latest
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
#### Speedtest Tracker
|
||||
|
||||
**File**: `/home/jramos/homelab/services/speedtest-tracker/docker-compose.yaml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
app:
|
||||
container_name: speedtest-tracker # Add this line
|
||||
image: lscr.io/linuxserver/speedtest-tracker:latest
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
#### TinyAuth
|
||||
|
||||
**File**: `/home/jramos/homelab/services/tinyauth/docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
tinyauth:
|
||||
container_name: tinyauth # Add this line
|
||||
image: ghcr.io/steveiliop56/tinyauth:v4
|
||||
# ... rest of configuration
|
||||
```
|
||||
|
||||
#### Monitoring Stack
|
||||
|
||||
**Grafana** - `/home/jramos/homelab/monitoring/grafana/docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
grafana:
|
||||
container_name: grafana # Add this line
|
||||
image: grafana/grafana:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
**Prometheus** - `/home/jramos/homelab/monitoring/prometheus/docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
prometheus:
|
||||
container_name: prometheus # Add this line
|
||||
image: prom/prometheus:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
**PVE Exporter** - `/home/jramos/homelab/monitoring/pve-exporter/docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
pve-exporter:
|
||||
container_name: pve-exporter # Add this line
|
||||
image: prompve/prometheus-pve-exporter:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
**Loki** - `/home/jramos/homelab/monitoring/loki/docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
loki:
|
||||
container_name: loki # Add this line
|
||||
image: grafana/loki:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
**Promtail** - `/home/jramos/homelab/monitoring/promtail/docker-compose.yml`:
|
||||
```yaml
|
||||
services:
|
||||
promtail:
|
||||
container_name: promtail # Add this line
|
||||
image: grafana/promtail:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
#### n8n
|
||||
|
||||
**File**: `/home/jramos/homelab/services/n8n/docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
n8n:
|
||||
container_name: n8n # Add this line
|
||||
image: n8nio/n8n:latest
|
||||
# ...
|
||||
|
||||
postgres:
|
||||
container_name: n8n-db # Add this line
|
||||
image: postgres:15
|
||||
# ...
|
||||
```
|
||||
|
||||
#### Docker Socket Proxy
|
||||
|
||||
**File**: `/home/jramos/homelab/services/docker-socket-proxy/docker-compose.yml`
|
||||
|
||||
```yaml
|
||||
services:
|
||||
socket-proxy:
|
||||
container_name: socket-proxy # Add this line
|
||||
image: tecnativa/docker-socket-proxy:latest
|
||||
# ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 2: Apply Changes
|
||||
|
||||
For each service, recreate containers with new names:
|
||||
|
||||
```bash
|
||||
cd /home/jramos/homelab/services/<service-name>
|
||||
|
||||
# Stop existing containers
|
||||
docker compose down
|
||||
|
||||
# Start with new container names
|
||||
docker compose up -d
|
||||
|
||||
# Verify new container names
|
||||
docker compose ps
|
||||
```
|
||||
|
||||
**Important**: This will recreate containers but preserve data in volumes.
|
||||
|
||||
---
|
||||
|
||||
### Step 3: Update Monitoring
|
||||
|
||||
After renaming containers, update Prometheus scrape configs if using container discovery:
|
||||
|
||||
**File**: `/home/jramos/homelab/monitoring/prometheus/prometheus.yml`
|
||||
|
||||
```yaml
|
||||
scrape_configs:
|
||||
- job_name: 'grafana'
|
||||
static_configs:
|
||||
- targets: ['grafana:3000'] # Use new container name
|
||||
|
||||
- job_name: 'prometheus'
|
||||
static_configs:
|
||||
- targets: ['prometheus:9090'] # Use new container name
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### Step 4: Update Documentation
|
||||
|
||||
Update references to container names in:
|
||||
- `/home/jramos/homelab/services/README.md`
|
||||
- `/home/jramos/homelab/monitoring/README.md`
|
||||
- Any troubleshooting guides
|
||||
- Any automation scripts
|
||||
|
||||
---
|
||||
|
||||
## Automated Fix Script
|
||||
|
||||
To automate the container name standardization:
|
||||
|
||||
**File**: `/home/jramos/homelab/scripts/security/fix-container-names.sh`
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
# Standardize container names across all Docker Compose services
|
||||
# Addresses MED-010: Container Name Inconsistency
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
SERVICES_DIR="/home/jramos/homelab/services"
|
||||
MONITORING_DIR="/home/jramos/homelab/monitoring"
|
||||
TIMESTAMP=$(date +%Y%m%d-%H%M%S)
|
||||
DRY_RUN=false
|
||||
|
||||
if [[ "${1:-}" == "--dry-run" ]]; then
|
||||
DRY_RUN=true
|
||||
echo "DRY RUN MODE - No changes will be made"
|
||||
fi
|
||||
|
||||
# Container name mappings
|
||||
declare -A CONTAINER_NAMES=(
|
||||
# Services
|
||||
["bytestash"]="bytestash"
|
||||
["filebrowser"]="filebrowser"
|
||||
["paperless-ngx/broker"]="paperless-redis"
|
||||
["paperless-ngx/db"]="paperless-db"
|
||||
["paperless-ngx/webserver"]="paperless-webserver"
|
||||
["paperless-ngx/gotenberg"]="paperless-gotenberg"
|
||||
["paperless-ngx/tika"]="paperless-tika"
|
||||
["portainer"]="portainer"
|
||||
["speedtest-tracker/app"]="speedtest-tracker"
|
||||
["tinyauth"]="tinyauth"
|
||||
["n8n/n8n"]="n8n"
|
||||
["n8n/postgres"]="n8n-db"
|
||||
["docker-socket-proxy/socket-proxy"]="socket-proxy"
|
||||
|
||||
# Monitoring
|
||||
["monitoring/grafana"]="grafana"
|
||||
["monitoring/prometheus"]="prometheus"
|
||||
["monitoring/pve-exporter"]="pve-exporter"
|
||||
["monitoring/loki"]="loki"
|
||||
["monitoring/promtail"]="promtail"
|
||||
)
|
||||
|
||||
add_container_name() {
|
||||
local COMPOSE_FILE=$1
|
||||
local SERVICE=$2
|
||||
local CONTAINER_NAME=$3
|
||||
|
||||
echo "Processing $COMPOSE_FILE (service: $SERVICE)"
|
||||
|
||||
if [[ ! -f "$COMPOSE_FILE" ]]; then
|
||||
echo " ⚠️ File not found: $COMPOSE_FILE"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Backup original file
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
cp "$COMPOSE_FILE" "$COMPOSE_FILE.backup-$TIMESTAMP"
|
||||
echo " ✓ Backup created"
|
||||
fi
|
||||
|
||||
# Check if container_name already exists for this service
|
||||
if grep -A 5 "^[[:space:]]*$SERVICE:" "$COMPOSE_FILE" | grep -q "container_name:"; then
|
||||
echo " ℹ️ container_name already set"
|
||||
return 0
|
||||
fi
|
||||
|
||||
# Add container_name directive
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
# Find the service block and add container_name after service name
|
||||
awk -v service="$SERVICE" -v name="$CONTAINER_NAME" '
|
||||
/^[[:space:]]*'"$SERVICE"':/ {
|
||||
print
|
||||
print " container_name: " name
|
||||
next
|
||||
}
|
||||
{print}
|
||||
' "$COMPOSE_FILE" > "$COMPOSE_FILE.tmp"
|
||||
|
||||
mv "$COMPOSE_FILE.tmp" "$COMPOSE_FILE"
|
||||
echo " ✓ Added container_name: $CONTAINER_NAME"
|
||||
else
|
||||
echo " [DRY RUN] Would add container_name: $CONTAINER_NAME"
|
||||
fi
|
||||
|
||||
# Validate compose file syntax
|
||||
if [[ "$DRY_RUN" == false ]]; then
|
||||
if docker compose -f "$COMPOSE_FILE" config > /dev/null 2>&1; then
|
||||
echo " ✓ Compose file syntax valid"
|
||||
else
|
||||
echo " ✗ ERROR: Compose file syntax invalid"
|
||||
echo " Restoring backup..."
|
||||
mv "$COMPOSE_FILE.backup-$TIMESTAMP" "$COMPOSE_FILE"
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
main() {
|
||||
echo "=== Container Name Standardization ==="
|
||||
echo ""
|
||||
|
||||
# Process all container name mappings
|
||||
for KEY in "${!CONTAINER_NAMES[@]}"; do
|
||||
# Parse key: "service" or "service/container"
|
||||
if [[ "$KEY" == *"/"* ]]; then
|
||||
# Multi-container service
|
||||
DIR=$(echo "$KEY" | cut -d'/' -f1)
|
||||
SERVICE=$(echo "$KEY" | cut -d'/' -f2)
|
||||
|
||||
if [[ "$DIR" == "monitoring" ]]; then
|
||||
COMPOSE_FILE="$MONITORING_DIR/$SERVICE/docker-compose.yml"
|
||||
else
|
||||
COMPOSE_FILE="$SERVICES_DIR/$DIR/docker-compose.yaml"
|
||||
fi
|
||||
else
|
||||
# Single-container service
|
||||
DIR="$KEY"
|
||||
SERVICE="$KEY"
|
||||
COMPOSE_FILE="$SERVICES_DIR/$DIR/docker-compose.yaml"
|
||||
fi
|
||||
|
||||
CONTAINER_NAME="${CONTAINER_NAMES[$KEY]}"
|
||||
|
||||
add_container_name "$COMPOSE_FILE" "$SERVICE" "$CONTAINER_NAME"
|
||||
echo ""
|
||||
done
|
||||
|
||||
echo "=== Summary ==="
|
||||
echo "Services processed: ${#CONTAINER_NAMES[@]}"
|
||||
if [[ "$DRY_RUN" == true ]]; then
|
||||
echo "Mode: DRY RUN (no changes made)"
|
||||
echo "Run without --dry-run to apply changes"
|
||||
else
|
||||
echo "Mode: LIVE (changes applied)"
|
||||
echo ""
|
||||
echo "⚠️ IMPORTANT: Restart services to use new container names"
|
||||
echo "Example:"
|
||||
echo " cd $SERVICES_DIR/paperless-ngx"
|
||||
echo " docker compose down"
|
||||
echo " docker compose up -d"
|
||||
fi
|
||||
}
|
||||
|
||||
main "$@"
|
||||
```
|
||||
|
||||
**Usage**:
|
||||
```bash
|
||||
# Test in dry-run mode
|
||||
./fix-container-names.sh --dry-run
|
||||
|
||||
# Apply changes
|
||||
./fix-container-names.sh
|
||||
|
||||
# Restart all services (optional script)
|
||||
cd /home/jramos/homelab
|
||||
find services monitoring -name "docker-compose.y*ml" -execdir bash -c 'docker compose down && docker compose up -d' \;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Verification
|
||||
|
||||
After applying changes, verify new container names:
|
||||
|
||||
```bash
|
||||
# List all containers with new names
|
||||
docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"
|
||||
|
||||
# Expected output:
|
||||
# NAMES IMAGE STATUS
|
||||
# bytestash ghcr.io/jordan-dalby/bytestash:latest Up 5 minutes
|
||||
# filebrowser filebrowser/filebrowser:latest Up 5 minutes
|
||||
# paperless-webserver ghcr.io/paperless-ngx/paperless-ngx Up 5 minutes
|
||||
# paperless-db postgres:17 Up 5 minutes
|
||||
# paperless-redis redis:8 Up 5 minutes
|
||||
# grafana grafana/grafana:latest Up 5 minutes
|
||||
# prometheus prom/prometheus:latest Up 5 minutes
|
||||
# tinyauth ghcr.io/steveiliop56/tinyauth:v4 Up 5 minutes
|
||||
```
|
||||
|
||||
### Monitoring Dashboard Update
|
||||
|
||||
If using Grafana dashboards that reference container names, update queries:
|
||||
|
||||
**Before**:
|
||||
```promql
|
||||
rate(container_cpu_usage_seconds_total{name="paperless-ngx-webserver-1"}[5m])
|
||||
```
|
||||
|
||||
**After**:
|
||||
```promql
|
||||
rate(container_cpu_usage_seconds_total{name="paperless-webserver"}[5m])
|
||||
```
|
||||
|
||||
### Log Aggregation Update
|
||||
|
||||
If using Loki/Promtail with container name labels, update label matchers:
|
||||
|
||||
**Before**:
|
||||
```logql
|
||||
{container_name="paperless-ngx-webserver-1"}
|
||||
```
|
||||
|
||||
**After**:
|
||||
```logql
|
||||
{container_name="paperless-webserver"}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Benefits
|
||||
|
||||
After standardization:
|
||||
|
||||
1. **Clarity**: Container names clearly indicate purpose
|
||||
2. **Consistency**: All containers follow same naming pattern
|
||||
3. **Automation**: Easier to write scripts targeting specific containers
|
||||
4. **Monitoring**: Cleaner metrics and log labels
|
||||
5. **Documentation**: Less confusion in guides and troubleshooting docs
|
||||
6. **Maintainability**: Easier for new team members to understand infrastructure
|
||||
|
||||
---
|
||||
|
||||
## Rollback
|
||||
|
||||
If issues occur after renaming:
|
||||
|
||||
```bash
|
||||
# Restore original docker-compose.yaml
|
||||
cd /home/jramos/homelab/services/<service>
|
||||
mv docker-compose.yaml.backup-<timestamp> docker-compose.yaml
|
||||
|
||||
# Recreate containers with original names
|
||||
docker compose down
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Future Considerations
|
||||
|
||||
### Docker Compose Project Names
|
||||
|
||||
Consider also standardizing Docker Compose project names using:
|
||||
|
||||
```yaml
|
||||
name: paperless # Add to top of docker-compose.yaml
|
||||
services:
|
||||
# ...
|
||||
```
|
||||
|
||||
This controls the prefix used in network and volume names.
|
||||
|
||||
### Container Labels
|
||||
|
||||
Add labels for better organization:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
paperless-webserver:
|
||||
container_name: paperless-webserver
|
||||
labels:
|
||||
- "com.homelab.service=paperless"
|
||||
- "com.homelab.component=webserver"
|
||||
- "com.homelab.tier=application"
|
||||
- "com.homelab.environment=production"
|
||||
```
|
||||
|
||||
Labels enable advanced filtering and automation.
|
||||
|
||||
---
|
||||
|
||||
## Completion Checklist
|
||||
|
||||
- [ ] Review current container names
|
||||
- [ ] Update all docker-compose.yaml files with `container_name`
|
||||
- [ ] Validate compose file syntax
|
||||
- [ ] Stop and restart all services
|
||||
- [ ] Verify new container names
|
||||
- [ ] Update Prometheus configs (if using container discovery)
|
||||
- [ ] Update Grafana dashboards
|
||||
- [ ] Update Loki/Promtail configs
|
||||
- [ ] Update documentation
|
||||
- [ ] Update automation scripts
|
||||
- [ ] Test monitoring and logging
|
||||
- [ ] Commit changes to git
|
||||
|
||||
---
|
||||
|
||||
**Issue**: MED-010
|
||||
**Priority**: Low (Continuous Improvement)
|
||||
**Estimated Effort**: 2-3 hours
|
||||
**Status**: Documentation Complete - Ready for Implementation
|
||||
|
||||
---
|
||||
|
||||
**Document Version**: 1.0
|
||||
**Last Updated**: 2025-12-20
|
||||
**Author**: Claude Code (Scribe Agent)
|
||||
2092
scripts/security/VALIDATION_REPORT.md
Normal file
2092
scripts/security/VALIDATION_REPORT.md
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user