- Add LXC migration plan and quick-start guide - Add wave 1 and wave 2 migration results - Add lxc-docker-create.sh for container creation - Add fix-docker-apparmor.sh for AppArmor issues - Add comprehensive LXC migration guide 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
276 lines
8.4 KiB
Bash
Executable File
276 lines
8.4 KiB
Bash
Executable File
#!/bin/bash
|
|
#
|
|
# Docker Compose AppArmor Fix Script
|
|
#
|
|
# Adds 'security_opt: ["apparmor=unconfined"]' to all services in docker-compose.yml files
|
|
# This is required for Docker containers running inside LXC containers.
|
|
#
|
|
# Usage: ./fix-docker-apparmor.sh <LXC_IP> [COMPOSE_DIR]
|
|
#
|
|
# Example: ./fix-docker-apparmor.sh 10.10.0.214
|
|
# Example: ./fix-docker-apparmor.sh 10.10.0.214 /home/cal/container-data
|
|
#
|
|
# Arguments:
|
|
# LXC_IP - IP address of the LXC container to SSH into
|
|
# COMPOSE_DIR - Optional directory containing docker-compose files (default: /home/cal/container-data)
|
|
#
|
|
# What this script does:
|
|
# 1. SSHs into the LXC container
|
|
# 2. Finds all docker-compose.yml files
|
|
# 3. Adds security_opt configuration to each service
|
|
# 4. Creates backups of original files
|
|
#
|
|
# Why this is needed:
|
|
# Docker containers in LXC need AppArmor disabled to function properly.
|
|
# Without this fix, containers may fail to start or have permission issues.
|
|
#
|
|
|
|
set -euo pipefail
|
|
|
|
# Color codes for output
|
|
RED='\033[0;31m'
|
|
GREEN='\033[0;32m'
|
|
YELLOW='\033[1;33m'
|
|
BLUE='\033[0;34m'
|
|
NC='\033[0m' # No Color
|
|
|
|
# Function to print colored messages
|
|
log_info() {
|
|
echo -e "${GREEN}[INFO]${NC} $1"
|
|
}
|
|
|
|
log_warn() {
|
|
echo -e "${YELLOW}[WARN]${NC} $1"
|
|
}
|
|
|
|
log_error() {
|
|
echo -e "${RED}[ERROR]${NC} $1"
|
|
}
|
|
|
|
log_debug() {
|
|
echo -e "${BLUE}[DEBUG]${NC} $1"
|
|
}
|
|
|
|
# Parse arguments
|
|
if [[ $# -lt 1 ]]; then
|
|
log_error "Insufficient arguments"
|
|
echo "Usage: $0 <LXC_IP> [COMPOSE_DIR]"
|
|
echo ""
|
|
echo "Example: $0 10.10.0.214"
|
|
echo "Example: $0 10.10.0.214 /home/cal/container-data"
|
|
exit 1
|
|
fi
|
|
|
|
LXC_IP=$1
|
|
COMPOSE_DIR=${2:-/home/cal/container-data}
|
|
|
|
log_info "Starting AppArmor fix for Docker Compose files"
|
|
log_info "Target: root@$LXC_IP"
|
|
log_info "Directory: $COMPOSE_DIR"
|
|
echo ""
|
|
|
|
# Check SSH connectivity
|
|
log_info "Testing SSH connection to $LXC_IP..."
|
|
if ! ssh -o ConnectTimeout=5 -o BatchMode=yes root@"$LXC_IP" "echo 'SSH OK'" &>/dev/null; then
|
|
log_error "Cannot connect to root@$LXC_IP via SSH"
|
|
log_error "Please ensure:"
|
|
echo " 1. SSH key is copied to the LXC container"
|
|
echo " 2. Container is running"
|
|
echo " 3. IP address is correct"
|
|
exit 1
|
|
fi
|
|
log_info "✅ SSH connection successful"
|
|
echo ""
|
|
|
|
# Create Python script on remote host
|
|
log_info "Creating AppArmor fix script on remote host..."
|
|
ssh root@"$LXC_IP" "cat > /tmp/fix_apparmor.py" <<'PYTHON_SCRIPT'
|
|
#!/usr/bin/env python3
|
|
"""
|
|
Fix Docker Compose files to work in LXC by adding AppArmor unconfined security option.
|
|
"""
|
|
import yaml
|
|
import glob
|
|
import sys
|
|
import os
|
|
from pathlib import Path
|
|
|
|
def add_apparmor_fix(compose_file):
|
|
"""Add security_opt to all services in a docker-compose file."""
|
|
print(f"\n📄 Processing: {compose_file}")
|
|
|
|
# Create backup
|
|
backup_file = f"{compose_file}.backup"
|
|
if not os.path.exists(backup_file):
|
|
os.system(f"cp '{compose_file}' '{backup_file}'")
|
|
print(f" ✅ Backup created: {backup_file}")
|
|
else:
|
|
print(f" ⏭️ Backup already exists: {backup_file}")
|
|
|
|
# Load compose file
|
|
try:
|
|
with open(compose_file, 'r') as f:
|
|
compose_data = yaml.safe_load(f)
|
|
except yaml.YAMLError as e:
|
|
print(f" ❌ Error parsing YAML: {e}")
|
|
return False
|
|
except Exception as e:
|
|
print(f" ❌ Error reading file: {e}")
|
|
return False
|
|
|
|
if not compose_data or 'services' not in compose_data:
|
|
print(f" ⚠️ No services found in compose file")
|
|
return False
|
|
|
|
# Track changes
|
|
services_modified = 0
|
|
services_skipped = 0
|
|
|
|
# Add security_opt to each service
|
|
for service_name, service_config in compose_data['services'].items():
|
|
if service_config is None:
|
|
service_config = {}
|
|
compose_data['services'][service_name] = service_config
|
|
|
|
# Check if security_opt already exists
|
|
existing_security = service_config.get('security_opt', [])
|
|
|
|
if 'apparmor=unconfined' in existing_security or 'apparmor:unconfined' in existing_security:
|
|
print(f" ⏭️ {service_name}: Already has AppArmor unconfined")
|
|
services_skipped += 1
|
|
else:
|
|
# Add apparmor=unconfined
|
|
if not existing_security:
|
|
service_config['security_opt'] = ['apparmor=unconfined']
|
|
else:
|
|
if 'apparmor=unconfined' not in existing_security:
|
|
existing_security.append('apparmor=unconfined')
|
|
service_config['security_opt'] = existing_security
|
|
|
|
print(f" ✅ {service_name}: Added AppArmor unconfined")
|
|
services_modified += 1
|
|
|
|
# Write updated compose file
|
|
try:
|
|
with open(compose_file, 'w') as f:
|
|
yaml.dump(compose_data, f, default_flow_style=False, sort_keys=False, indent=2)
|
|
|
|
if services_modified > 0:
|
|
print(f" 💾 Saved changes ({services_modified} services modified)")
|
|
|
|
return services_modified > 0
|
|
except Exception as e:
|
|
print(f" ❌ Error writing file: {e}")
|
|
# Restore backup
|
|
os.system(f"cp '{backup_file}' '{compose_file}'")
|
|
print(f" 🔄 Restored from backup")
|
|
return False
|
|
|
|
def main():
|
|
"""Main function to process all docker-compose files."""
|
|
compose_dir = sys.argv[1] if len(sys.argv) > 1 else "/home/cal/container-data"
|
|
|
|
print(f"🔍 Searching for docker-compose.yml files in {compose_dir}")
|
|
|
|
# Find all docker-compose files
|
|
patterns = [
|
|
f"{compose_dir}/**/docker-compose.yml",
|
|
f"{compose_dir}/**/docker-compose.yaml",
|
|
]
|
|
|
|
compose_files = []
|
|
for pattern in patterns:
|
|
compose_files.extend(glob.glob(pattern, recursive=True))
|
|
|
|
# Remove duplicates and sort
|
|
compose_files = sorted(set(compose_files))
|
|
|
|
if not compose_files:
|
|
print(f"⚠️ No docker-compose files found in {compose_dir}")
|
|
return 1
|
|
|
|
print(f"📋 Found {len(compose_files)} docker-compose file(s)")
|
|
|
|
# Process each file
|
|
total_modified = 0
|
|
total_errors = 0
|
|
|
|
for compose_file in compose_files:
|
|
try:
|
|
if add_apparmor_fix(compose_file):
|
|
total_modified += 1
|
|
except Exception as e:
|
|
print(f" ❌ Unexpected error: {e}")
|
|
total_errors += 1
|
|
|
|
# Summary
|
|
print("\n" + "="*60)
|
|
print("📊 SUMMARY")
|
|
print("="*60)
|
|
print(f"Total files found: {len(compose_files)}")
|
|
print(f"Files modified: {total_modified}")
|
|
print(f"Files with errors: {total_errors}")
|
|
print(f"Files unchanged: {len(compose_files) - total_modified - total_errors}")
|
|
print("="*60)
|
|
|
|
if total_modified > 0:
|
|
print("\n✅ AppArmor fix applied successfully!")
|
|
print("\n💡 Next steps:")
|
|
print(" 1. Review changes in modified files")
|
|
print(" 2. Start containers: docker compose up -d")
|
|
print(" 3. Check container status: docker compose ps")
|
|
print("\n📝 Note: Backups created with .backup extension")
|
|
|
|
return 0 if total_errors == 0 else 1
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|
|
PYTHON_SCRIPT
|
|
|
|
log_info "✅ Script uploaded to LXC container"
|
|
echo ""
|
|
|
|
# Install PyYAML if needed
|
|
log_info "Ensuring Python and PyYAML are installed..."
|
|
ssh root@"$LXC_IP" "apt-get update -qq && apt-get install -y -qq python3 python3-yaml > /dev/null 2>&1" || true
|
|
log_info "✅ Dependencies ready"
|
|
echo ""
|
|
|
|
# Run the fix script
|
|
log_info "Running AppArmor fix script..."
|
|
echo ""
|
|
ssh root@"$LXC_IP" "python3 /tmp/fix_apparmor.py '$COMPOSE_DIR'"
|
|
EXIT_CODE=$?
|
|
echo ""
|
|
|
|
# Cleanup
|
|
log_info "Cleaning up temporary files..."
|
|
ssh root@"$LXC_IP" "rm /tmp/fix_apparmor.py"
|
|
log_info "✅ Cleanup complete"
|
|
echo ""
|
|
|
|
if [[ $EXIT_CODE -eq 0 ]]; then
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
log_info "🎉 AppArmor Fix Complete!"
|
|
log_info "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
|
|
echo ""
|
|
echo "Your docker-compose files have been updated to work in LXC."
|
|
echo ""
|
|
echo "Next steps:"
|
|
echo " 1. SSH into container:"
|
|
echo " ssh root@$LXC_IP"
|
|
echo ""
|
|
echo " 2. Navigate to a service directory:"
|
|
echo " cd $COMPOSE_DIR/[service-name]"
|
|
echo ""
|
|
echo " 3. Start containers:"
|
|
echo " docker compose up -d"
|
|
echo ""
|
|
echo " 4. Check status:"
|
|
echo " docker compose ps"
|
|
echo ""
|
|
else
|
|
log_error "AppArmor fix encountered errors. Please review output above."
|
|
exit 1
|
|
fi
|