Files
HomeAudit/scripts/discover_all_backup_targets.sh
admin 705a2757c1 Major infrastructure migration and Vaultwarden PostgreSQL troubleshooting
COMPREHENSIVE CHANGES:

INFRASTRUCTURE MIGRATION:
- Migrated services to Docker Swarm on OMV800 (192.168.50.229)
- Deployed PostgreSQL database for Vaultwarden migration
- Updated all stack configurations for Docker Swarm compatibility
- Added comprehensive monitoring stack (Prometheus, Grafana, Blackbox)
- Implemented proper secret management for all services

VAULTWARDEN POSTGRESQL MIGRATION:
- Attempted migration from SQLite to PostgreSQL for NFS compatibility
- Created PostgreSQL stack with proper user/password configuration
- Built custom Vaultwarden image with PostgreSQL support
- Troubleshot persistent SQLite fallback issue despite PostgreSQL config
- Identified known issue where Vaultwarden silently falls back to SQLite
- Added ENABLE_DB_WAL=false to prevent filesystem compatibility issues
- Current status: Old Vaultwarden on lenovo410 still working, new one has config issues

PAPERLESS SERVICES:
- Successfully deployed Paperless-NGX and Paperless-AI on OMV800
- Both services running on ports 8000 and 3000 respectively
- Caddy configuration updated for external access
- Services accessible via paperless.pressmess.duckdns.org and paperless-ai.pressmess.duckdns.org

CADDY CONFIGURATION:
- Updated Caddyfile on Surface (192.168.50.254) for new service locations
- Fixed Vaultwarden reverse proxy to point to new Docker Swarm service
- Removed old notification hub reference that was causing conflicts
- All services properly configured for external access via DuckDNS

BACKUP AND DISCOVERY:
- Created comprehensive backup system for all hosts
- Generated detailed discovery reports for infrastructure analysis
- Implemented automated backup validation scripts
- Created migration progress tracking and verification reports

MONITORING STACK:
- Deployed Prometheus, Grafana, and Blackbox monitoring
- Created infrastructure and system overview dashboards
- Added proper service discovery and alerting configuration
- Implemented performance monitoring for all critical services

DOCUMENTATION:
- Reorganized documentation into logical structure
- Created comprehensive migration playbook and troubleshooting guides
- Added hardware specifications and optimization recommendations
- Documented all configuration changes and service dependencies

CURRENT STATUS:
- Paperless services:  Working and accessible externally
- Vaultwarden:  PostgreSQL configuration issues, old instance still working
- Monitoring:  Deployed and operational
- Caddy:  Updated and working for external access
- PostgreSQL:  Database running, connection issues with Vaultwarden

NEXT STEPS:
- Continue troubleshooting Vaultwarden PostgreSQL configuration
- Consider alternative approaches for Vaultwarden migration
- Validate all external service access
- Complete final migration validation

TECHNICAL NOTES:
- Used Docker Swarm for orchestration on OMV800
- Implemented proper secret management for sensitive data
- Added comprehensive logging and monitoring
- Created automated backup and validation scripts
2025-08-30 20:18:44 -04:00

480 lines
20 KiB
Bash
Executable File

#!/bin/bash
# Comprehensive Backup Target Discovery Script
# Discovers 100% of what needs to be backed up across the entire infrastructure
set -euo pipefail
# Configuration
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(dirname "$SCRIPT_DIR")"
DISCOVERY_TIMESTAMP=$(date +%Y%m%d_%H%M%S)
DISCOVERY_DIR="$PROJECT_ROOT/comprehensive_discovery_results"
LOG_FILE="$PROJECT_ROOT/logs/discovery_${DISCOVERY_TIMESTAMP}.log"
# Create directories
mkdir -p "$DISCOVERY_DIR" "$(dirname "$LOG_FILE")"
# Logging function
log() {
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" | tee -a "$LOG_FILE"
}
# Error handling
cleanup() {
log "Cleaning up temporary files..."
rm -f /tmp/discovery_*.txt /tmp/docker_*.json /tmp/volume_*.txt 2>/dev/null || true
}
trap cleanup EXIT
# Main discovery function
main() {
log "=== COMPREHENSIVE BACKUP TARGET DISCOVERY STARTED ==="
log "Timestamp: $DISCOVERY_TIMESTAMP"
log "Discovery directory: $DISCOVERY_DIR"
# Discover all hosts in the infrastructure
discover_hosts
# Discover all Docker environments
discover_docker_environments
# Discover all systemd services (native services)
discover_systemd_services
# Discover all databases
discover_databases
# Discover all volumes and persistent data
discover_volumes
# Discover all configuration files
discover_configurations
# Discover all secrets and sensitive data
discover_secrets
# Discover all network configurations
discover_network_configs
# Discover all user data and applications
discover_user_data
# Discover all application-specific data
discover_application_data
# Generate comprehensive summary
generate_discovery_summary
log "=== DISCOVERY COMPLETE ==="
log "Results saved to: $DISCOVERY_DIR"
}
# Discover all hosts in the infrastructure
discover_hosts() {
log "=== DISCOVERING ALL HOSTS ==="
# Create a list of known hosts with their correct usernames from inventory
cat > "$DISCOVERY_DIR/all_hosts.txt" << 'EOF'
fedora:jonathan
omvbackup:jon
lenovo:jonathan
lenovo420:jon
omv800:root
surface:jon
audrey:jon
raspberrypi:jon
EOF
# Check connectivity to each host
while IFS=: read -r host user; do
if [[ -n "$host" && -n "$user" ]]; then
log "Checking connectivity to $host (user: $user)..."
if ping -c 1 -W 2 "$host" >/dev/null 2>&1; then
echo "$host:$user:ONLINE" >> "$DISCOVERY_DIR/host_status.txt"
else
echo "$host:$user:OFFLINE" >> "$DISCOVERY_DIR/host_status.txt"
fi
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
# Also backup the inventory file
if [[ -f "$PROJECT_ROOT/inventory.ini" ]]; then
cp "$PROJECT_ROOT/inventory.ini" "$DISCOVERY_DIR/inventory_backup.txt"
fi
}
# Discover all Docker environments
discover_docker_environments() {
log "=== DISCOVERING DOCKER ENVIRONMENTS ==="
# Check each host for Docker
while IFS=: read -r host user; do
if [[ -n "$host" && -n "$user" ]]; then
log "Checking Docker on $host (user: $user)..."
# Check if Docker is running
if ssh -o ConnectTimeout=5 -o StrictHostKeyChecking=no "$user@$host" "docker --version" 2>/dev/null; then
echo "$host:$user:DOCKER_AVAILABLE" >> "$DISCOVERY_DIR/docker_hosts.txt"
# Get Docker info
ssh "$user@$host" "docker info" > "$DISCOVERY_DIR/docker_info_${host}.txt" 2>/dev/null || true
# Get all containers
ssh "$user@$host" "docker ps -a --format 'table {{.Names}}\t{{.Image}}\t{{.Status}}\t{{.Ports}}'" > "$DISCOVERY_DIR/containers_${host}.txt" 2>/dev/null || true
# Get all images
ssh "$user@$host" "docker images --format 'table {{.Repository}}\t{{.Tag}}\t{{.Size}}'" > "$DISCOVERY_DIR/images_${host}.txt" 2>/dev/null || true
# Get all networks
ssh "$user@$host" "docker network ls" > "$DISCOVERY_DIR/networks_${host}.txt" 2>/dev/null || true
# Get all volumes
ssh "$user@$host" "docker volume ls" > "$DISCOVERY_DIR/volumes_${host}.txt" 2>/dev/null || true
else
echo "$host:$user:NO_DOCKER" >> "$DISCOVERY_DIR/docker_hosts.txt"
fi
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
}
# Discover all systemd services (native services)
discover_systemd_services() {
log "=== DISCOVERING SYSTEMD SERVICES ==="
# Check each host for systemd services
while IFS=: read -r host user; do
if [[ -n "$host" && -n "$user" ]]; then
log "Checking systemd services on $host (user: $user)..."
# Get active services
ssh "$user@$host" "systemctl list-units --type=service --state=running --full --no-pager" > "$DISCOVERY_DIR/active_services_${host}.txt" 2>/dev/null || true
# Get service descriptions
ssh "$user@$host" "systemctl list-units --type=service --full --no-pager" > "$DISCOVERY_DIR/service_descriptions_${host}.txt" 2>/dev/null || true
# Get service dependencies
ssh "$user@$host" "systemctl list-dependencies --type=service --full --no-pager" > "$DISCOVERY_DIR/service_dependencies_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/all_hosts.txt"
}
# Discover all databases
discover_databases() {
log "=== DISCOVERING ALL DATABASES ==="
# Check each Docker host for databases
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering databases on $host (user: $user)..."
# Get containers that might be databases
ssh "$user@$host" "docker ps --format '{{.Names}} {{.Image}}' | grep -iE '(postgres|mysql|mariadb|redis|mongodb|sqlite)'" > "$DISCOVERY_DIR/databases_${host}.txt" 2>/dev/null || true
# For each database container, get detailed info
while IFS= read -r db_line; do
if [[ -n "$db_line" ]]; then
container_name=$(echo "$db_line" | awk '{print $1}')
image=$(echo "$db_line" | awk '{print $2}')
log "Analyzing database container: $container_name ($image) on $host"
# Get environment variables
ssh "$user@$host" "docker inspect $container_name | jq '.[0].Config.Env[]' -r" > "$DISCOVERY_DIR/db_env_${host}_${container_name}.txt" 2>/dev/null || true
# Get volume mounts
ssh "$user@$host" "docker inspect $container_name | jq '.[0].Mounts[] | {Source: .Source, Destination: .Destination, Type: .Type}'" > "$DISCOVERY_DIR/db_mounts_${host}_${container_name}.json" 2>/dev/null || true
# Get database type and version
echo "Container: $container_name" > "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
echo "Image: $image" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
echo "Host: $host" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt"
# Try to get database version
if [[ "$image" == *"postgres"* ]]; then
ssh "$user@$host" "docker exec $container_name psql --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
elif [[ "$image" == *"mysql"* ]] || [[ "$image" == *"mariadb"* ]]; then
ssh "$user@$host" "docker exec $container_name mysql --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
elif [[ "$image" == *"redis"* ]]; then
ssh "$user@$host" "docker exec $container_name redis-server --version" >> "$DISCOVERY_DIR/db_details_${host}_${container_name}.txt" 2>/dev/null || true
fi
fi
done < "$DISCOVERY_DIR/databases_${host}.txt"
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all volumes and persistent data
discover_volumes() {
log "=== DISCOVERING ALL VOLUMES AND PERSISTENT DATA ==="
# Check each Docker host for volumes
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering volumes on $host (user: $user)..."
# Get all Docker volumes with details
ssh "$user@$host" "docker volume ls -q | xargs -I {} docker volume inspect {}" > "$DISCOVERY_DIR/volume_details_${host}.json" 2>/dev/null || true
# Get bind mounts from all containers
ssh "$user@$host" "docker ps -q | xargs -I {} docker inspect {} | jq '.[] | {Name: .Name, Mounts: .Mounts}'" > "$DISCOVERY_DIR/bind_mounts_${host}.json" 2>/dev/null || true
# Check for important directories that might contain data
ssh "$user@$host" "find /opt /var/lib /home /root -name '*.db' -o -name '*.sqlite' -o -name 'data' -o -name 'config' 2>/dev/null | head -50" > "$DISCOVERY_DIR/important_dirs_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all configuration files
discover_configurations() {
log "=== DISCOVERING ALL CONFIGURATIONS ==="
# Local configurations
log "Discovering local configurations..."
# Docker Compose files
find "$PROJECT_ROOT" -name "*.yml" -o -name "*.yaml" -o -name "docker-compose*" > "$DISCOVERY_DIR/local_configs.txt"
# Environment files
find "$PROJECT_ROOT" -name "*.env" -o -name ".env*" >> "$DISCOVERY_DIR/local_configs.txt"
# Configuration directories
find "$PROJECT_ROOT" -type d -name "config*" -o -name "conf*" -o -name "etc*" >> "$DISCOVERY_DIR/local_configs.txt"
# Check each host for configurations
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering configurations on $host (user: $user)..."
# Find configuration files
ssh "$user@$host" "find /etc /opt /var/lib -name '*.conf' -o -name '*.yml' -o -name '*.yaml' -o -name '*.env' 2>/dev/null | head -100" > "$DISCOVERY_DIR/configs_${host}.txt" 2>/dev/null || true
# Get Docker Compose files
ssh "$user@$host" "find /opt /root /home -name 'docker-compose*.yml' -o -name '*.stack.yml' 2>/dev/null" > "$DISCOVERY_DIR/compose_files_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all secrets and sensitive data
discover_secrets() {
log "=== DISCOVERING ALL SECRETS AND SENSITIVE DATA ==="
# Local secrets
if [[ -d "$PROJECT_ROOT/secrets" ]]; then
log "Discovering local secrets..."
find "$PROJECT_ROOT/secrets" -type f > "$DISCOVERY_DIR/local_secrets.txt"
# Get secrets mapping
if [[ -f "$PROJECT_ROOT/secrets/docker-secrets-mapping.yaml" ]]; then
cp "$PROJECT_ROOT/secrets/docker-secrets-mapping.yaml" "$DISCOVERY_DIR/"
fi
fi
# Check each host for secrets
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering secrets on $host (user: $user)..."
# Check for Docker secrets
ssh "$user@$host" "docker secret ls" > "$DISCOVERY_DIR/secrets_${host}.txt" 2>/dev/null || true
# Check for environment files with secrets
ssh "$user@$host" "find /opt /root /home -name '.env*' -o -name '*secret*' -o -name '*password*' 2>/dev/null" > "$DISCOVERY_DIR/secret_files_${host}.txt" 2>/dev/null || true
# Check for SSL certificates
ssh "$user@$host" "find /etc /opt -name '*.crt' -o -name '*.key' -o -name '*.pem' 2>/dev/null" > "$DISCOVERY_DIR/ssl_files_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all network configurations
discover_network_configs() {
log "=== DISCOVERING ALL NETWORK CONFIGURATIONS ==="
# Local network config
log "Discovering local network configuration..."
ip route > "$DISCOVERY_DIR/local_routes.txt"
ip addr > "$DISCOVERY_DIR/local_interfaces.txt"
cat /etc/hosts > "$DISCOVERY_DIR/local_hosts.txt"
# Check each host for network config
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering network configuration on $host (user: $user)..."
# Network interfaces
ssh "$user@$host" "ip addr" > "$DISCOVERY_DIR/interfaces_${host}.txt" 2>/dev/null || true
# Routing table
ssh "$user@$host" "ip route" > "$DISCOVERY_DIR/routes_${host}.txt" 2>/dev/null || true
# Hosts file
ssh "$user@$host" "cat /etc/hosts" > "$DISCOVERY_DIR/hosts_${host}.txt" 2>/dev/null || true
# Docker networks
ssh "$user@$host" "docker network ls" > "$DISCOVERY_DIR/docker_networks_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all user data and applications
discover_user_data() {
log "=== DISCOVERING ALL USER DATA AND APPLICATIONS ==="
# Check each host for user data
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering user data on $host (user: $user)..."
# Check for common application data directories
ssh "$user@$host" "find /opt /var/lib /home -type d -name '*data*' -o -name '*app*' -o -name '*user*' 2>/dev/null | head -50" > "$DISCOVERY_DIR/app_dirs_${host}.txt" 2>/dev/null || true
# Check for specific application directories
ssh "$user@$host" "find /opt /var/lib -name '*nextcloud*' -o -name '*immich*' -o -name '*joplin*' -o -name '*photoprism*' 2>/dev/null" > "$DISCOVERY_DIR/specific_apps_${host}.txt" 2>/dev/null || true
# Check for media files
ssh "$user@$host" "find /opt /var/lib -type d -name '*media*' -o -name '*photos*' -o -name '*videos*' -o -name '*music*' 2>/dev/null" > "$DISCOVERY_DIR/media_dirs_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Discover all application-specific data
discover_application_data() {
log "=== DISCOVERING ALL APPLICATION-SPECIFIC DATA ==="
# Check each host for application-specific data
while IFS=: read -r host user status; do
if [[ "$status" == *"DOCKER_AVAILABLE"* ]]; then
log "Discovering application-specific data on $host (user: $user)..."
# Check for Nextcloud data
ssh "$user@$host" "find /opt /var/lib -name 'nextcloud' -type d -o -name 'nextcloud.db' 2>/dev/null" > "$DISCOVERY_DIR/nextcloud_data_${host}.txt" 2>/dev/null || true
# Check for Immich data
ssh "$user@$host" "find /opt /var/lib -name 'immich' -type d -o -name 'immich.db' 2>/dev/null" > "$DISCOVERY_DIR/immich_data_${host}.txt" 2>/dev/null || true
# Check for Joplin data
ssh "$user@$host" "find /opt /var/lib -name 'joplin' -type d -o -name 'joplin.db' 2>/dev/null" > "$DISCOVERY_DIR/joplin_data_${host}.txt" 2>/dev/null || true
# Check for PhotoPrism data
ssh "$user@$host" "find /opt /var/lib -name 'photoprism' -type d -o -name 'photoprism.db' 2>/dev/null" > "$DISCOVERY_DIR/photoprism_data_${host}.txt" 2>/dev/null || true
# Check for specific application data directories
ssh "$user@$host" "find /opt /var/lib -name '*nextcloud*' -o -name '*immich*' -o -name '*joplin*' -o -name '*photoprism*' 2>/dev/null" > "$DISCOVERY_DIR/specific_apps_${host}.txt" 2>/dev/null || true
# Check for media files
ssh "$user@$host" "find /opt /var/lib -type d -name '*media*' -o -name '*photos*' -o -name '*videos*' -o -name '*music*' 2>/dev/null" > "$DISCOVERY_DIR/media_dirs_${host}.txt" 2>/dev/null || true
fi
done < "$DISCOVERY_DIR/docker_hosts.txt"
}
# Generate comprehensive summary
generate_discovery_summary() {
log "=== GENERATING DISCOVERY SUMMARY ==="
cat > "$DISCOVERY_DIR/DISCOVERY_SUMMARY.md" << EOF
# Comprehensive Backup Target Discovery Summary
**Discovery Timestamp:** $DISCOVERY_TIMESTAMP
**Discovery Directory:** $DISCOVERY_DIR
## Hosts Discovered
$(cat "$DISCOVERY_DIR/host_status.txt" 2>/dev/null || echo "No host status found")
## Docker Environments
$(cat "$DISCOVERY_DIR/docker_hosts.txt" 2>/dev/null || echo "No Docker hosts found")
## Systemd Services
$(for file in "$DISCOVERY_DIR"/active_services_*.txt; do
if [[ -f "$file" ]]; then
host=$(basename "$file" | sed 's/active_services_//;s/.txt//')
echo "### $host"
cat "$file" | sed 's/^/ - /'
echo
fi
done)
## Databases Found
$(for file in "$DISCOVERY_DIR"/databases_*.txt; do
if [[ -f "$file" ]]; then
host=$(basename "$file" | sed 's/databases_//;s/.txt//')
echo "### $host"
cat "$file" | sed 's/^/ - /'
echo
fi
done)
## Volumes and Persistent Data
$(for file in "$DISCOVERY_DIR"/volumes_*.txt; do
if [[ -f "$file" ]]; then
host=$(basename "$file" | sed 's/volumes_//;s/.txt//')
echo "### $host"
cat "$file" | sed 's/^/ - /'
echo
fi
done)
## Configuration Files
- Local configurations: $(wc -l < "$DISCOVERY_DIR/local_configs.txt" 2>/dev/null || echo "0")
- Environment files: $(grep -c "\.env" "$DISCOVERY_DIR/local_configs.txt" 2>/dev/null || echo "0")
## Secrets and SSL Certificates
- Local secrets: $(wc -l < "$DISCOVERY_DIR/local_secrets.txt" 2>/dev/null || echo "0")
- SSL files across hosts: $(find "$DISCOVERY_DIR" -name "*ssl_files*.txt" | wc -l)
## Network Configurations
- Local network config captured
- Network configs for $(find "$DISCOVERY_DIR" -name "*interfaces*.txt" | wc -l) hosts
## User Data and Applications
$(for file in "$DISCOVERY_DIR"/specific_apps_*.txt; do
if [[ -f "$file" ]]; then
host=$(basename "$file" | sed 's/specific_apps_//;s/.txt//')
echo "### $host"
cat "$file" | sed 's/^/ - /'
echo
fi
done)
## Application-Specific Data
$(for file in "$DISCOVERY_DIR"/nextcloud_data_*.txt "$DISCOVERY_DIR"/immich_data_*.txt "$DISCOVERY_DIR"/joplin_data_*.txt "$DISCOVERY_DIR"/photoprism_data_*.txt; do
if [[ -f "$file" ]]; then
host=$(basename "$file" | sed 's/nextcloud_data_//;s/immich_data_//;s/joplin_data_//;s/photoprism_data_//;s/.txt//')
echo "### $host"
cat "$file" | sed 's/^/ - /'
echo
fi
done)
## Backup Requirements Summary
### Critical Data to Backup:
1. **Databases**: All PostgreSQL, MariaDB, Redis instances
2. **Volumes**: All Docker volumes and bind mounts
3. **Configurations**: All .env files, docker-compose files, config directories
4. **Secrets**: All SSL certificates, API keys, passwords
5. **User Data**: Nextcloud, Immich, Joplin, PhotoPrism data
6. **Network Configs**: Routing, interfaces, Docker networks
7. **Documentation**: All infrastructure documentation and scripts
### Estimated Backup Size:
- Configuration files: ~10-50MB
- Database dumps: ~100MB-1GB (depending on data)
- User data: ~1-10GB (depending on media)
- Total estimated: ~1-15GB
## Next Steps:
1. Review this discovery summary
2. Create comprehensive backup script based on discovered targets
3. Test backup process on non-critical data first
4. Execute full backup before migration
EOF
log "Discovery summary generated: $DISCOVERY_DIR/DISCOVERY_SUMMARY.md"
}
# Execute main function
main "$@"