#!/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"