🔒 security(daarion-web): Hardening after crypto-mining incidents
## Root Cause Analysis - Found CRITICAL RCE vulnerability in Next.js 15.0.3 (GHSA-9qr9-h5gf-34mp) - 10 vulnerabilities total including SSRF, DoS, Auth Bypass - Attack vector: exposed port 3000 + vulnerable Next.js → remote code execution ## Security Fixes - Upgraded Next.js: 15.0.3 → 15.5.9 (0 vulnerabilities) - Upgraded eslint-config-next: 15.0.3 → 15.5.9 ## Hardening (New Files) - apps/web/Dockerfile.secure: Multi-stage build, read-only FS, no shell - docker-compose.web.secure.yml: Resource limits, cap_drop ALL, localhost bind - scripts/rebuild-daarion-web-secure.sh: Local secure rebuild with Trivy scan - scripts/deploy-daarion-web-node1.sh: Production deployment to NODE1 - SECURITY-REBUILD-REPORT.md: Full incident analysis and remediation report ## Key Security Measures - restart: "no" (until verified) - ports: 127.0.0.1:3000 (localhost only, use Nginx reverse proxy) - read_only: true - cap_drop: ALL - resources.limits: 1 CPU, 512M RAM - no-new-privileges: true ## Related Incidents - Incident #1 (Jan 8): catcal, G4NQXBp miners - Incident #2 (Jan 9): softirq, vrarhpb miners - Hetzner AbuseID: 10F3971:2A Co-authored-by: Cursor Agent <agent@cursor.sh>
This commit is contained in:
175
scripts/deploy-daarion-web-node1.sh
Executable file
175
scripts/deploy-daarion-web-node1.sh
Executable file
@@ -0,0 +1,175 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# DAARION Web - NODE1 Production Deployment
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-01-09
|
||||
# Purpose: Deploy secure daarion-web to NODE1
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m'
|
||||
|
||||
# Configuration
|
||||
NODE1_IP="144.76.224.179"
|
||||
NODE1_USER="root"
|
||||
PROJECT_DIR="/opt/microdao-daarion"
|
||||
LOCAL_DIR="/Users/apple/github-projects/microdao-daarion"
|
||||
IMAGE_NAME="daarion-web"
|
||||
IMAGE_TAG="secure-$(date +%Y%m%d)"
|
||||
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${BLUE} DAARION Web - NODE1 Production Deployment${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}⚠️ TARGET: ${NODE1_USER}@${NODE1_IP}${NC}"
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# Step 1: Pre-deployment checks
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[1/6] Pre-deployment checks...${NC}"
|
||||
|
||||
# Check SSH connectivity
|
||||
if ! ssh -o ConnectTimeout=5 ${NODE1_USER}@${NODE1_IP} "echo 'SSH OK'" 2>/dev/null; then
|
||||
echo -e "${RED}ERROR: Cannot connect to NODE1 via SSH${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ SSH connection OK${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 2: Sync updated files to NODE1
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[2/6] Syncing files to NODE1...${NC}"
|
||||
|
||||
# Sync web app directory
|
||||
rsync -avz --delete \
|
||||
"${LOCAL_DIR}/apps/web/" \
|
||||
"${NODE1_USER}@${NODE1_IP}:${PROJECT_DIR}/apps/web/" \
|
||||
--exclude 'node_modules' \
|
||||
--exclude '.next'
|
||||
|
||||
# Sync docker-compose
|
||||
scp "${LOCAL_DIR}/docker-compose.web.secure.yml" \
|
||||
"${NODE1_USER}@${NODE1_IP}:${PROJECT_DIR}/"
|
||||
|
||||
# Sync scripts
|
||||
scp "${LOCAL_DIR}/scripts/rebuild-daarion-web-secure.sh" \
|
||||
"${NODE1_USER}@${NODE1_IP}:${PROJECT_DIR}/scripts/"
|
||||
|
||||
echo -e "${GREEN}✓ Files synced${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 3: Install dependencies on NODE1
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[3/6] Installing dependencies on NODE1...${NC}"
|
||||
|
||||
ssh ${NODE1_USER}@${NODE1_IP} << 'ENDSSH'
|
||||
cd /opt/microdao-daarion/apps/web
|
||||
npm ci --only=production --ignore-scripts 2>/dev/null || npm install --only=production --ignore-scripts
|
||||
npm audit || true
|
||||
ENDSSH
|
||||
|
||||
echo -e "${GREEN}✓ Dependencies installed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 4: Build secure image on NODE1
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[4/6] Building secure Docker image on NODE1...${NC}"
|
||||
|
||||
ssh ${NODE1_USER}@${NODE1_IP} << ENDSSH
|
||||
cd /opt/microdao-daarion
|
||||
|
||||
# Stop old container
|
||||
docker stop daarion-web 2>/dev/null || true
|
||||
docker rm daarion-web 2>/dev/null || true
|
||||
|
||||
# Remove old images (CRITICAL for security)
|
||||
docker images | grep daarion-web | awk '{print \$3}' | xargs -r docker rmi -f 2>/dev/null || true
|
||||
|
||||
# Clear build cache
|
||||
docker builder prune -f 2>/dev/null || true
|
||||
|
||||
# Build new secure image
|
||||
docker build \
|
||||
--no-cache \
|
||||
--pull \
|
||||
-t ${IMAGE_NAME}:${IMAGE_TAG} \
|
||||
-t ${IMAGE_NAME}:latest \
|
||||
-f apps/web/Dockerfile.secure \
|
||||
apps/web/
|
||||
ENDSSH
|
||||
|
||||
echo -e "${GREEN}✓ Image built on NODE1${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 5: Security scan on NODE1
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[5/6] Running security scan on NODE1...${NC}"
|
||||
|
||||
ssh ${NODE1_USER}@${NODE1_IP} << ENDSSH
|
||||
# Install Trivy if not present
|
||||
if ! command -v trivy &> /dev/null; then
|
||||
echo "Installing Trivy..."
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||
fi
|
||||
|
||||
# Run security scan
|
||||
trivy image --severity HIGH,CRITICAL ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
ENDSSH
|
||||
|
||||
echo -e "${GREEN}✓ Security scan completed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 6: Deploy container
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[6/6] Deploying secure container...${NC}"
|
||||
|
||||
ssh ${NODE1_USER}@${NODE1_IP} << 'ENDSSH'
|
||||
cd /opt/microdao-daarion
|
||||
|
||||
# Create network if not exists
|
||||
docker network create dagi-network 2>/dev/null || true
|
||||
|
||||
# Deploy with secure compose file
|
||||
docker compose -f docker-compose.web.secure.yml up -d
|
||||
|
||||
# Wait for startup
|
||||
sleep 10
|
||||
|
||||
# Check status
|
||||
docker ps | grep daarion-web
|
||||
docker logs --tail 20 daarion-web
|
||||
ENDSSH
|
||||
|
||||
echo -e "${GREEN}✓ Container deployed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Summary
|
||||
# ============================================
|
||||
echo ""
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${GREEN} DEPLOYMENT COMPLETED${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
echo -e "Image: ${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
echo -e "Node: ${NODE1_IP}"
|
||||
echo ""
|
||||
echo -e "${YELLOW}MONITORING COMMANDS:${NC}"
|
||||
echo " ssh ${NODE1_USER}@${NODE1_IP} docker logs -f daarion-web"
|
||||
echo " ssh ${NODE1_USER}@${NODE1_IP} docker stats daarion-web"
|
||||
echo " ssh ${NODE1_USER}@${NODE1_IP} 'docker exec daarion-web ps aux'"
|
||||
echo ""
|
||||
echo -e "${RED}⚠️ MONITOR FOR 30+ MINUTES BEFORE ENABLING AUTO-RESTART!${NC}"
|
||||
echo ""
|
||||
echo "To enable auto-restart after verification:"
|
||||
echo " ssh ${NODE1_USER}@${NODE1_IP}"
|
||||
echo " vim /opt/microdao-daarion/docker-compose.web.secure.yml"
|
||||
echo " # Change: restart: \"no\" → restart: \"unless-stopped\""
|
||||
echo " docker compose -f docker-compose.web.secure.yml up -d"
|
||||
201
scripts/rebuild-daarion-web-secure.sh
Executable file
201
scripts/rebuild-daarion-web-secure.sh
Executable file
@@ -0,0 +1,201 @@
|
||||
#!/bin/bash
|
||||
# ============================================
|
||||
# DAARION Web - Secure Rebuild Script
|
||||
# Version: 1.0.0
|
||||
# Created: 2026-01-09
|
||||
# Purpose: Rebuild daarion-web with security hardening
|
||||
# ============================================
|
||||
|
||||
set -e
|
||||
|
||||
# Colors
|
||||
RED='\033[0;31m'
|
||||
GREEN='\033[0;32m'
|
||||
YELLOW='\033[1;33m'
|
||||
BLUE='\033[0;34m'
|
||||
NC='\033[0m' # No Color
|
||||
|
||||
# Configuration
|
||||
PROJECT_DIR="/Users/apple/github-projects/microdao-daarion"
|
||||
WEB_DIR="${PROJECT_DIR}/apps/web"
|
||||
IMAGE_NAME="daarion-web"
|
||||
IMAGE_TAG="secure-$(date +%Y%m%d-%H%M%S)"
|
||||
COMPOSE_FILE="${PROJECT_DIR}/docker-compose.web.secure.yml"
|
||||
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${BLUE} DAARION Web - Secure Rebuild Script${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
|
||||
# ============================================
|
||||
# Step 1: Pre-flight checks
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[1/7] Pre-flight checks...${NC}"
|
||||
|
||||
# Check Docker
|
||||
if ! command -v docker &> /dev/null; then
|
||||
echo -e "${RED}ERROR: Docker is not installed${NC}"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check Trivy
|
||||
if ! command -v trivy &> /dev/null; then
|
||||
echo -e "${YELLOW}WARNING: Trivy not found. Installing...${NC}"
|
||||
brew install trivy 2>/dev/null || {
|
||||
echo -e "${YELLOW}Installing Trivy via script...${NC}"
|
||||
curl -sfL https://raw.githubusercontent.com/aquasecurity/trivy/main/contrib/install.sh | sh -s -- -b /usr/local/bin
|
||||
}
|
||||
fi
|
||||
|
||||
echo -e "${GREEN}✓ Pre-flight checks passed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 2: Clean old images and containers
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[2/7] Cleaning old images and containers...${NC}"
|
||||
|
||||
# Stop and remove existing container
|
||||
docker stop ${IMAGE_NAME} 2>/dev/null || true
|
||||
docker rm ${IMAGE_NAME} 2>/dev/null || true
|
||||
|
||||
# Remove old images (keep none to prevent cache poisoning)
|
||||
docker images | grep ${IMAGE_NAME} | awk '{print $3}' | xargs -r docker rmi -f 2>/dev/null || true
|
||||
|
||||
# Clear build cache
|
||||
docker builder prune -f 2>/dev/null || true
|
||||
|
||||
echo -e "${GREEN}✓ Cleanup completed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 3: Audit npm dependencies
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[3/7] Auditing npm dependencies...${NC}"
|
||||
|
||||
cd "${WEB_DIR}"
|
||||
|
||||
# Run npm audit
|
||||
echo "Running npm audit..."
|
||||
npm audit --audit-level=high 2>&1 || {
|
||||
echo -e "${YELLOW}WARNING: npm audit found issues. Review above.${NC}"
|
||||
read -p "Continue anyway? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Aborted by user${NC}"
|
||||
exit 1
|
||||
fi
|
||||
}
|
||||
|
||||
echo -e "${GREEN}✓ npm audit completed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 4: Build secure image
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[4/7] Building secure Docker image...${NC}"
|
||||
|
||||
cd "${PROJECT_DIR}"
|
||||
|
||||
# Build with no-cache to prevent layer poisoning
|
||||
docker build \
|
||||
--no-cache \
|
||||
--pull \
|
||||
-t ${IMAGE_NAME}:${IMAGE_TAG} \
|
||||
-t ${IMAGE_NAME}:latest \
|
||||
-f "${WEB_DIR}/Dockerfile.secure" \
|
||||
"${WEB_DIR}"
|
||||
|
||||
echo -e "${GREEN}✓ Image built: ${IMAGE_NAME}:${IMAGE_TAG}${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 5: Security scanning with Trivy
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[5/7] Running Trivy security scan...${NC}"
|
||||
|
||||
echo "Scanning for vulnerabilities..."
|
||||
trivy image --severity HIGH,CRITICAL ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
|
||||
SCAN_EXIT=$?
|
||||
if [ $SCAN_EXIT -ne 0 ]; then
|
||||
echo -e "${RED}WARNING: Trivy found HIGH/CRITICAL vulnerabilities!${NC}"
|
||||
read -p "Continue deployment? (y/N) " -n 1 -r
|
||||
echo
|
||||
if [[ ! $REPLY =~ ^[Yy]$ ]]; then
|
||||
echo -e "${RED}Aborted by user${NC}"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# Full scan report
|
||||
echo "Generating full scan report..."
|
||||
trivy image --format json -o "${PROJECT_DIR}/logs/trivy-scan-${IMAGE_TAG}.json" ${IMAGE_NAME}:${IMAGE_TAG}
|
||||
|
||||
echo -e "${GREEN}✓ Security scan completed${NC}"
|
||||
echo -e " Report: ${PROJECT_DIR}/logs/trivy-scan-${IMAGE_TAG}.json"
|
||||
|
||||
# ============================================
|
||||
# Step 6: Test container locally
|
||||
# ============================================
|
||||
echo -e "${YELLOW}[6/7] Testing container locally...${NC}"
|
||||
|
||||
# Start container
|
||||
docker compose -f "${COMPOSE_FILE}" up -d
|
||||
|
||||
echo "Waiting for container to start..."
|
||||
sleep 10
|
||||
|
||||
# Check container is running
|
||||
if ! docker ps | grep -q ${IMAGE_NAME}; then
|
||||
echo -e "${RED}ERROR: Container failed to start${NC}"
|
||||
docker logs ${IMAGE_NAME}
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Check health
|
||||
echo "Checking container health..."
|
||||
for i in {1..5}; do
|
||||
HEALTH=$(docker inspect --format='{{.State.Health.Status}}' ${IMAGE_NAME} 2>/dev/null || echo "unknown")
|
||||
echo " Health status: $HEALTH"
|
||||
if [ "$HEALTH" == "healthy" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 5
|
||||
done
|
||||
|
||||
# Check for suspicious processes
|
||||
echo "Checking for suspicious processes..."
|
||||
docker exec ${IMAGE_NAME} ps aux 2>/dev/null || echo "(ps not available - expected in hardened container)"
|
||||
|
||||
# Check CPU usage (should be low)
|
||||
echo "Monitoring CPU usage for 30 seconds..."
|
||||
for i in {1..6}; do
|
||||
docker stats --no-stream --format "{{.Name}}: CPU {{.CPUPerc}}, MEM {{.MemUsage}}" ${IMAGE_NAME}
|
||||
sleep 5
|
||||
done
|
||||
|
||||
echo -e "${GREEN}✓ Container test passed${NC}"
|
||||
|
||||
# ============================================
|
||||
# Step 7: Summary
|
||||
# ============================================
|
||||
echo ""
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo -e "${GREEN} BUILD COMPLETED SUCCESSFULLY${NC}"
|
||||
echo -e "${BLUE}============================================${NC}"
|
||||
echo ""
|
||||
echo -e "Image: ${IMAGE_NAME}:${IMAGE_TAG}"
|
||||
echo -e "Container: ${IMAGE_NAME}"
|
||||
echo -e "Status: Running"
|
||||
echo ""
|
||||
echo -e "${YELLOW}NEXT STEPS:${NC}"
|
||||
echo "1. Monitor container for 15+ minutes:"
|
||||
echo " docker logs -f ${IMAGE_NAME}"
|
||||
echo " docker stats ${IMAGE_NAME}"
|
||||
echo ""
|
||||
echo "2. If stable, deploy to production:"
|
||||
echo " - Copy image to NODE1: docker save ${IMAGE_NAME}:${IMAGE_TAG} | ssh root@144.76.224.179 docker load"
|
||||
echo " - Or rebuild on NODE1 using Dockerfile.secure"
|
||||
echo ""
|
||||
echo "3. Enable auto-restart after verification:"
|
||||
echo " - Edit docker-compose.web.secure.yml"
|
||||
echo " - Change restart: \"no\" to restart: \"unless-stopped\""
|
||||
echo ""
|
||||
echo -e "${RED}⚠️ DO NOT deploy to production without 15+ min monitoring!${NC}"
|
||||
Reference in New Issue
Block a user