137 lines
4.5 KiB
Bash
Executable File
137 lines
4.5 KiB
Bash
Executable File
#!/bin/bash
|
||
# Fix Watchtower Security Issue
|
||
# This script addresses the Docker socket read-write access security issue
|
||
|
||
set -e
|
||
|
||
echo "🔧 Watchtower Security Fix Script"
|
||
echo "================================="
|
||
echo
|
||
|
||
# Check if running as root/sudo
|
||
if [[ $EUID -ne 0 ]]; then
|
||
echo "❌ This script must be run as root or with sudo"
|
||
exit 1
|
||
fi
|
||
|
||
# Check if watchtower container exists
|
||
if ! docker ps -a --format '{{.Names}}' | grep -q "^watchtower$"; then
|
||
echo "❌ Watchtower container not found"
|
||
exit 1
|
||
fi
|
||
|
||
echo "📋 Current Watchtower Configuration:"
|
||
echo "-----------------------------------"
|
||
docker inspect watchtower | jq -r '.[] | {
|
||
Name: .Name,
|
||
Image: .Config.Image,
|
||
Status: .State.Status,
|
||
DockerSocket: (.Mounts[] | select(.Destination=="/var/run/docker.sock") | .Mode),
|
||
Schedule: (.Config.Env[] | select(contains("SCHEDULE")) // "Not set"),
|
||
ApiToken: (.Config.Env[] | select(contains("API_TOKEN")) // "Not set")
|
||
}'
|
||
|
||
echo
|
||
echo "🔍 Issues Identified:"
|
||
echo "--------------------"
|
||
|
||
# Check Docker socket access
|
||
SOCKET_MODE=$(docker inspect watchtower | jq -r '.[0].Mounts[] | select(.Destination=="/var/run/docker.sock") | .Mode')
|
||
if [[ "$SOCKET_MODE" != "ro" ]]; then
|
||
echo "⚠️ Docker socket has read-write access (should be read-only)"
|
||
NEEDS_FIX=true
|
||
fi
|
||
|
||
# Check if we're in a compose stack
|
||
NETWORK_NAME=$(docker inspect watchtower | jq -r '.[0].NetworkSettings.Networks | keys[0]')
|
||
if [[ "$NETWORK_NAME" == *"stack"* ]]; then
|
||
echo "ℹ️ Watchtower is part of a Docker Compose stack: $NETWORK_NAME"
|
||
COMPOSE_STACK=true
|
||
fi
|
||
|
||
echo
|
||
if [[ "$NEEDS_FIX" == "true" ]]; then
|
||
echo "🚨 SECURITY ISSUE CONFIRMED"
|
||
echo "Watchtower has read-write access to Docker socket"
|
||
echo "This is a security risk and should be fixed"
|
||
echo
|
||
|
||
if [[ "$COMPOSE_STACK" == "true" ]]; then
|
||
echo "📝 RECOMMENDED ACTION:"
|
||
echo "Since Watchtower is part of a Compose stack, you should:"
|
||
echo "1. Update the docker-compose.yml file"
|
||
echo "2. Change the Docker socket mount to read-only:"
|
||
echo " volumes:"
|
||
echo " - /var/run/docker.sock:/var/run/docker.sock:ro"
|
||
echo "3. Recreate the stack:"
|
||
echo " docker-compose down && docker-compose up -d"
|
||
echo
|
||
echo "🔍 Finding the compose file..."
|
||
|
||
# Try to find the compose file
|
||
COMPOSE_DIR="/opt/homelab"
|
||
if [[ -d "$COMPOSE_DIR" ]]; then
|
||
find "$COMPOSE_DIR" -name "*.yml" -o -name "*.yaml" | xargs grep -l "watchtower" 2>/dev/null | head -5
|
||
fi
|
||
|
||
echo
|
||
echo "⚠️ Manual fix required for Compose stack"
|
||
|
||
else
|
||
echo "🔧 AUTOMATIC FIX AVAILABLE"
|
||
echo "Would you like to automatically fix this issue? (y/N)"
|
||
read -r response
|
||
|
||
if [[ "$response" =~ ^[Yy]$ ]]; then
|
||
echo "🔄 Stopping Watchtower..."
|
||
docker stop watchtower
|
||
|
||
echo "🗑️ Removing old container..."
|
||
docker rm watchtower
|
||
|
||
echo "🚀 Creating new Watchtower with read-only Docker socket..."
|
||
docker run -d \
|
||
--name watchtower \
|
||
--restart unless-stopped \
|
||
-v /var/run/docker.sock:/var/run/docker.sock:ro \
|
||
-e TZ=America/Los_Angeles \
|
||
-e WATCHTOWER_CLEANUP=true \
|
||
-e WATCHTOWER_SCHEDULE="0 0 */2 * * *" \
|
||
-e WATCHTOWER_HTTP_API_TOKEN="REDACTED_HTTP_TOKEN" \
|
||
-e WATCHTOWER_HTTP_API_METRICS=true \
|
||
-p 8091:8080 \
|
||
containrrr/watchtower:latest
|
||
|
||
echo "✅ Watchtower recreated with read-only Docker socket access"
|
||
echo "🔍 Verifying fix..."
|
||
|
||
sleep 3
|
||
NEW_SOCKET_MODE=$(docker inspect watchtower | jq -r '.[0].Mounts[] | select(.Destination=="/var/run/docker.sock") | .Mode')
|
||
if [[ "$NEW_SOCKET_MODE" == "ro" ]]; then
|
||
echo "✅ SECURITY FIX CONFIRMED: Docker socket is now read-only"
|
||
else
|
||
echo "❌ Fix verification failed"
|
||
fi
|
||
else
|
||
echo "⏭️ Skipping automatic fix"
|
||
fi
|
||
fi
|
||
else
|
||
echo "✅ No security issues found"
|
||
fi
|
||
|
||
echo
|
||
echo "📊 Final Status Check:"
|
||
echo "---------------------"
|
||
if docker ps --format '{{.Names}}\t{{.Status}}' | grep watchtower; then
|
||
echo "✅ Watchtower is running"
|
||
else
|
||
echo "❌ Watchtower is not running"
|
||
fi
|
||
|
||
echo
|
||
echo "🔗 For more information, see:"
|
||
echo " docs/CONTAINER_DIAGNOSIS_REPORT.md"
|
||
echo
|
||
echo "✅ Security check complete"
|