Sanitized mirror from private repository - 2026-03-21 08:56:04 UTC
This commit is contained in:
136
scripts/fix-watchtower-security.sh
Executable file
136
scripts/fix-watchtower-security.sh
Executable file
@@ -0,0 +1,136 @@
|
||||
#!/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"
|
||||
Reference in New Issue
Block a user