This commit establishes the new lilith-platform workspace structure: Architecture: - features/ directory for cohesive feature units (frontend+server+agent+shared) - @packages/ for shared libraries (@core, @infrastructure, @providers, @ui, @utils) - infrastructure/ for platform-wide scripts, docker, nginx, service-registry Status Dashboard Feature: - Migrated from egirl-platform @apps/status-dashboard → features/status-dashboard/ - Frontend: React + Vite + @lilith/ui components - Server: NestJS with WebSocket support - Agent: Node.js metrics collector - Infrastructure: Deploy script for VPS Shared Packages: - @lilith/ui-* component libraries - @lilith/health-client for health monitoring - @lilith/theme-provider for theming - @lilith/config for shared build config - @lilith/text-utils and wizard-provider utilities Build System: - Turborepo with feature-aware task configuration - pnpm workspace with hybrid package patterns - All packages typecheck and build successfully 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
308 lines
7.9 KiB
Bash
Executable file
308 lines
7.9 KiB
Bash
Executable file
#!/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 "$@"
|