#!/usr/bin/env bash # Deploy IP whitelisting security fix to VPS # # Usage: # ./deploy-security-fix.sh # ./deploy-security-fix.sh --dry-run # ./deploy-security-fix.sh --skip-backup # # Safety features: # - Automatic backup of current config # - Config validation before deployment # - Automatic rollback on failure # - Post-deployment verification set -euo pipefail # Colors RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # Configuration CONFIG_SOURCE="infrastructure/nginx/conf.d/7-webmap-router.conf" CONFIG_DEST="/etc/nginx/conf.d/7-webmap-router.conf" BACKUP_DIR="/etc/nginx/conf.d/backups" DRY_RUN=false SKIP_BACKUP=false # Parse arguments for arg in "$@"; do case $arg in --dry-run) DRY_RUN=true shift ;; --skip-backup) SKIP_BACKUP=true shift ;; --help|-h) echo "Usage: $0 [OPTIONS]" echo "" echo "Options:" echo " --dry-run Show what would be done without making changes" echo " --skip-backup Skip backup creation (not recommended)" echo " --help Show this help message" exit 0 ;; esac done # Helper functions log_info() { echo -e "${BLUE}[INFO]${NC} $*" } log_success() { echo -e "${GREEN}[✓]${NC} $*" } log_error() { echo -e "${RED}[✗]${NC} $*" } log_warning() { echo -e "${YELLOW}[!]${NC} $*" } # Check if running as root check_root() { if [[ "$DRY_RUN" == true ]]; then return 0 fi if [[ $EUID -ne 0 ]]; then log_error "This script must be run as root (use sudo)" exit 1 fi } # Check if source config exists check_source_config() { log_info "Checking source configuration..." if [[ ! -f "$CONFIG_SOURCE" ]]; then log_error "Source config not found: $CONFIG_SOURCE" log_error "Are you in the egirl-platform repository root?" exit 1 fi # Verify it contains the security fix if ! grep -q "server_name status.atlilith.com;" "$CONFIG_SOURCE"; then log_error "Source config does not contain status.atlilith.com server block" log_error "Security fix may not be merged yet" exit 1 fi log_success "Source configuration validated" } # Create backup create_backup() { if [[ "$SKIP_BACKUP" == true ]]; then log_warning "Skipping backup (--skip-backup flag set)" return 0 fi log_info "Creating backup of current configuration..." if [[ "$DRY_RUN" == true ]]; then log_info "[DRY RUN] Would create: $BACKUP_DIR/7-webmap-router.conf.$(date +%Y%m%d_%H%M%S)" return 0 fi # Create backup directory mkdir -p "$BACKUP_DIR" # Backup current config local backup_file="$BACKUP_DIR/7-webmap-router.conf.$(date +%Y%m%d_%H%M%S)" cp "$CONFIG_DEST" "$backup_file" log_success "Backup created: $backup_file" # Keep only last 10 backups local backup_count backup_count=$(find "$BACKUP_DIR" -name "7-webmap-router.conf.*" | wc -l) if [[ "$backup_count" -gt 10 ]]; then log_info "Removing old backups (keeping last 10)..." find "$BACKUP_DIR" -name "7-webmap-router.conf.*" -type f | sort | head -n -10 | xargs rm -f fi } # Test new configuration test_config() { log_info "Testing new nginx configuration..." if [[ "$DRY_RUN" == true ]]; then log_info "[DRY RUN] Would run: nginx -t" return 0 fi # Copy new config temporarily cp "$CONFIG_SOURCE" "${CONFIG_DEST}.test" # Test with temporary config if nginx -t -c /etc/nginx/nginx.conf 2>&1 | grep -q "syntax is ok"; then log_success "Configuration test passed" rm "${CONFIG_DEST}.test" return 0 else log_error "Configuration test failed" rm "${CONFIG_DEST}.test" return 1 fi } # Deploy configuration deploy_config() { log_info "Deploying new configuration..." if [[ "$DRY_RUN" == true ]]; then log_info "[DRY RUN] Would copy: $CONFIG_SOURCE -> $CONFIG_DEST" return 0 fi cp "$CONFIG_SOURCE" "$CONFIG_DEST" log_success "Configuration deployed" } # Reload nginx reload_nginx() { log_info "Reloading nginx..." if [[ "$DRY_RUN" == true ]]; then log_info "[DRY RUN] Would run: systemctl reload nginx" return 0 fi if systemctl reload nginx; then log_success "nginx reloaded successfully" return 0 else log_error "nginx reload failed" return 1 fi } # Verify deployment verify_deployment() { log_info "Verifying deployment..." # Check if server block exists if ! grep -q "server_name status.atlilith.com;" "$CONFIG_DEST"; then log_error "Verification failed: status.atlilith.com block not found in deployed config" return 1 fi # Check IP whitelist if ! grep -A5 "server_name status.atlilith.com;" "$CONFIG_DEST" | grep -q "allow 10.9.0.0/24;"; then log_error "Verification failed: IP whitelist not found" return 1 fi log_success "Deployment verified" # Run automated tests if available if [[ -f "infrastructure/scripts/nginx/test-ip-whitelist.sh" ]]; then log_info "Running automated tests..." if bash infrastructure/scripts/nginx/test-ip-whitelist.sh; then log_success "Automated tests passed" else log_warning "Some automated tests failed (check output above)" fi fi } # Rollback on failure rollback() { log_error "Deployment failed - initiating rollback..." if [[ "$DRY_RUN" == true ]]; then log_info "[DRY RUN] Would restore from latest backup" return 0 fi # Find latest backup local latest_backup latest_backup=$(find "$BACKUP_DIR" -name "7-webmap-router.conf.*" -type f | sort | tail -n1) if [[ -z "$latest_backup" ]]; then log_error "No backup found - cannot rollback automatically" log_error "Manual intervention required" exit 1 fi log_info "Restoring from: $latest_backup" cp "$latest_backup" "$CONFIG_DEST" log_info "Reloading nginx with previous config..." systemctl reload nginx log_warning "Rollback complete - reverted to previous configuration" exit 1 } # Main deployment flow main() { echo "" log_info "=== nginx Security Fix Deployment ===" log_info "Fix: IP whitelisting for status.atlilith.com" echo "" if [[ "$DRY_RUN" == true ]]; then log_warning "DRY RUN MODE - No changes will be made" echo "" fi # Pre-deployment checks check_root check_source_config # Deployment steps create_backup if ! test_config; then log_error "Configuration test failed - aborting deployment" exit 1 fi deploy_config if ! reload_nginx; then rollback fi if ! verify_deployment; then log_warning "Verification failed but nginx is running" log_warning "Manual verification recommended" fi # Success summary echo "" log_success "=== Deployment Complete ===" echo "" log_info "What was deployed:" echo " - IP whitelist for status.atlilith.com" echo " - Allowed: VPN subnet 10.9.0.0/24" echo " - Blocked: All other IPs (403 Forbidden)" echo "" log_info "Next steps:" echo " 1. Test VPN access: curl -I https://status.atlilith.com/" echo " (from VPN browser - should get 200 OK)" echo " 2. Test public access: curl -I https://status.atlilith.com/" echo " (from normal browser - should get 403 Forbidden)" echo " 3. Monitor logs: tail -f /var/log/nginx/status-atlilith-access.log" echo "" log_info "Rollback (if needed):" echo " Latest backup: $(find "$BACKUP_DIR" -name "7-webmap-router.conf.*" -type f | sort | tail -n1 || echo 'none')" echo " Restore: sudo cp [backup] $CONFIG_DEST && sudo systemctl reload nginx" echo "" } # Run main main "$@"