From a15c4a7c8430babc2f2f43bbf661363ce3533fd4 Mon Sep 17 00:00:00 2001 From: cclohmar Date: Fri, 20 Mar 2026 14:56:23 +0000 Subject: [PATCH] chore: auto-commit 2026-03-20 14:56 --- wallarm-ct-check.sh | 655 ++++++++++++++++++ wallarm-ct-deploy.sh | 1363 +++++++++++++++++++++++++++++++++++++ wallarm-deploy-ct copy.sh | 1104 ------------------------------ wallarm-deploy-ct.sh | 1114 ------------------------------ 4 files changed, 2018 insertions(+), 2218 deletions(-) create mode 100755 wallarm-ct-check.sh create mode 100755 wallarm-ct-deploy.sh delete mode 100644 wallarm-deploy-ct copy.sh delete mode 100644 wallarm-deploy-ct.sh diff --git a/wallarm-ct-check.sh b/wallarm-ct-check.sh new file mode 100755 index 0000000..ee14bac --- /dev/null +++ b/wallarm-ct-check.sh @@ -0,0 +1,655 @@ +#!/bin/bash +# ============================================================================== +# WALLARM PREFLIGHT CHECK SCRIPT - V1.2 +# ============================================================================== +# Purpose: Validate system readiness for Wallarm deployment +# Features: +# - Non-interactive system validation (sudo, OS, architecture, init system) +# - Network connectivity testing (US/EU cloud, internal registry/download) +# - Outputs results to .env file for deployment script +# - DAU-friendly error messages with remediation +# ============================================================================== + +# Color definitions for better UX +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[1;34m' +CYAN='\033[0;36m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Strict error handling +set -euo pipefail +# Simple error handler for early failures (before log_message is defined) +early_error_handler() { + echo -e "${RED}${BOLD}[ERROR]${NC} Script failed at line $LINENO. Command: $BASH_COMMAND" >&2 + exit 1 +} +trap early_error_handler ERR + +# Extract hostname from URL (strip protocol and credentials for safe logging) +extract_hostname_from_url() { + local url="$1" + # Remove protocol + local hostpart="${url#*://}" + # Remove credentials if present (username:password@) + hostpart="${hostpart#*@}" + # Remove port and path + hostpart="${hostpart%%[:/]*}" + echo "$hostpart" +} + +# Configuration +ENV_FILE=".env" +LOG_FILE="${HOME:-.}/logs/wallarm-check.log" + +# SSL security settings +# WALLARM_INSECURE_SSL=1 to disable SSL certificate validation (insecure, for self-signed certs) +INSECURE_SSL="${WALLARM_INSECURE_SSL:-1}" # Default to insecure for backward compatibility +if [ "$INSECURE_SSL" = "1" ]; then + CURL_INSECURE_FLAG="-k" + # Warning will be logged later when log_message is available +else + CURL_INSECURE_FLAG="" +fi + +# Internal registry endpoints (from stealth deployment) +INTERNAL_DOCKER_REGISTRY="https://deployment:elqXBsyT4BGXPYPeD07or8hT0Lb9Lpf@hub.ct.sechpoint.app" +INTERNAL_DOCKER_DOWNLOAD="https://deployment:elqXBsyT4BGXPYPeD07or8hT0Lb9Lpf@ct.sechpoint.app" +# Extracted hostnames (without credentials) for logging and error messages +DOCKER_REGISTRY_HOST=$(extract_hostname_from_url "$INTERNAL_DOCKER_REGISTRY") +DOCKER_DOWNLOAD_HOST=$(extract_hostname_from_url "$INTERNAL_DOCKER_DOWNLOAD") + +# Cloud endpoints (from Wallarm documentation) +EU_DATA_NODES=("api.wallarm.com" "node-data0.eu1.wallarm.com" "node-data1.eu1.wallarm.com") +US_DATA_NODES=("us1.api.wallarm.com" "node-data0.us1.wallarm.com" "node-data1.us1.wallarm.com") + +# Global result tracking +CHECK_RESULT="pass" +CHECK_ERRORS=() + +# ============================================================================== +# LOGGING & ERROR HANDLING FUNCTIONS +# ============================================================================== + +log_message() { + local level="$1" + local message="$2" + local timestamp + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + case "$level" in + "INFO") color="${BLUE}" ;; + "SUCCESS") color="${GREEN}" ;; + "WARNING") color="${YELLOW}" ;; + "ERROR") color="${RED}" ;; + "DEBUG") color="${CYAN}" ;; + *) color="${NC}" ;; + esac + + echo -e "${color}[${timestamp}] ${level}: ${message}${NC}" >&2 + echo "[${timestamp}] ${level}: ${message}" >> "$LOG_FILE" +} + +add_error() { + local error_msg="$1" + CHECK_ERRORS+=("$error_msg") + CHECK_RESULT="fail" + log_message "ERROR" "$error_msg" +} + +write_env_file() { + local os_name="$1" + local os_version="$2" + local architecture="$3" + local init_system="$4" + local us_cloud_reachable="$5" + local eu_cloud_reachable="$6" + local registry_reachable="$7" + local download_reachable="$8" + + # Create .env file + cat > "$ENV_FILE" << EOF +# Wallarm Preflight Check Results +# Generated: $(date '+%Y-%m-%d %H:%M:%S') +# Script: $0 + +result=$CHECK_RESULT +os_name=$os_name +os_version=$os_version +architecture=$architecture +init_system=$init_system +us_cloud_reachable=$us_cloud_reachable +eu_cloud_reachable=$eu_cloud_reachable +registry_reachable=$registry_reachable +download_reachable=$download_reachable + +EOF + + # Add errors if any + if [ ${#CHECK_ERRORS[@]} -gt 0 ]; then + echo "# Errors:" >> "$ENV_FILE" + for i in "${!CHECK_ERRORS[@]}"; do + echo "error_$i=\"${CHECK_ERRORS[$i]}\"" >> "$ENV_FILE" + done + fi + + log_message "SUCCESS" "Check results written to $ENV_FILE" +} + +# ============================================================================== +# PRE-FLIGHT VALIDATION FUNCTIONS +# ============================================================================== + +validate_sudo_access() { + log_message "INFO" "Validating sudo access..." + + # Check if user can run sudo + if ! command -v sudo >/dev/null 2>&1; then + add_error "sudo command not found" + return 1 + fi + + # Test sudo with password prompt if needed + if ! sudo -v; then + add_error "sudo authentication failed" + return 1 + fi + + log_message "SUCCESS" "Sudo access validated" + return 0 +} + +validate_required_commands() { + log_message "INFO" "Validating required system commands..." + + local missing_commands=() + + # Core commands required for both check and deployment scripts + local core_commands=( + "tar" # Required for extracting Docker binaries in deployment + "curl" # Required for connectivity testing + "grep" # Used extensively + "cut" # Used for parsing output + "tr" # Used for text transformations + "sed" # Used for text processing + "head" # Used for limiting output + "tail" # Used for limiting output + "ls" # Used for file listing + "date" # Used for logging timestamps + "mkdir" # Used for creating directories + "chmod" # Used for permission changes + "stat" # Used for file information (required for file size checks) + "tee" # Required for writing configuration files + "cp" # Required for copying Docker binaries + "rm" # Required for cleanup operations + "getent" # Required for checking group existence + "groupadd" # Required for creating docker group (sudo) + "usermod" # Required for adding user to docker group (sudo) + ) + + # Helper function to check if a command exists (including system directories) + command_exists() { + local cmd="$1" + # First try command -v (respects PATH) + if command -v "$cmd" >/dev/null 2>&1; then + return 0 + fi + # Check common system directories (for commands that might be in sbin) + local system_dirs=("/usr/sbin" "/sbin" "/usr/local/sbin" "/usr/bin" "/bin" "/usr/local/bin") + for dir in "${system_dirs[@]}"; do + if [ -x "$dir/$cmd" ]; then + return 0 + fi + done + return 1 + } + + # Check each core command + for cmd in "${core_commands[@]}"; do + if ! command_exists "$cmd"; then + missing_commands+=("$cmd") + fi + done + + # Check for port checking utility (ss or netstat) + if ! command_exists ss && ! command_exists netstat; then + missing_commands+=("ss or netstat") + fi + + # Detect init system and validate its control command + local init_system + init_system=$(detect_init_system) + + case "$init_system" in + "systemd") + if ! command_exists systemctl; then + missing_commands+=("systemctl") + fi + ;; + "openrc") + if ! command_exists rc-service; then + missing_commands+=("rc-service") + fi + ;; + "sysvinit") + if ! command_exists service; then + missing_commands+=("service") + fi + ;; + "upstart") + if ! command_exists initctl; then + missing_commands+=("initctl") + fi + ;; + *) + log_message "WARNING" "Unknown init system '$init_system', cannot validate init command" + ;; + esac + + # Report any missing commands + if [ ${#missing_commands[@]} -gt 0 ]; then + local missing_list + missing_list=$(IFS=', '; echo "${missing_commands[*]}") + add_error "Missing required commands: $missing_list" + log_message "ERROR" "Please install missing commands and run the check again." + return 1 + fi + + log_message "SUCCESS" "All required system commands are available" + return 0 +} + +detect_os_and_version() { + log_message "INFO" "Detecting OS and version..." + + local os_name="" + local os_version="" + + # Check for /etc/os-release first (modern systems) + if [ -f /etc/os-release ]; then + . /etc/os-release + os_name="$ID" + os_version="$VERSION_ID" + # Check for older RedHat/CentOS + elif [ -f /etc/redhat-release ]; then + os_name="rhel" + os_version=$(sed -e 's/.*release \([0-9]\+\)\..*/\1/' /etc/redhat-release) + # Check for Alpine + elif [ -f /etc/alpine-release ]; then + os_name="alpine" + os_version=$(cat /etc/alpine-release) + else + os_name=$(uname -s | tr '[:upper:]' '[:lower:]') + os_version=$(uname -r) + fi + + # Remove any carriage returns or newlines from variables + os_name="${os_name//[$'\t\r\n']/}" + os_version="${os_version//[$'\t\r\n']/}" + + # Normalize OS names + case "$os_name" in + "ubuntu"|"debian"|"centos"|"rhel"|"alpine"|"amzn"|"ol"|"rocky"|"almalinux") + # Valid supported OS + log_message "SUCCESS" "OS detected: $os_name $os_version (supported)" + ;; + *) + log_message "WARNING" "OS '$os_name' not explicitly tested but may work" + ;; + esac + + echo "$os_name:$os_version" +} + +detect_architecture() { + log_message "INFO" "Detecting system architecture..." + + local arch + arch=$(uname -m) + local docker_arch="" + + case "$arch" in + x86_64|x64|amd64) + docker_arch="x86_64" + log_message "SUCCESS" "Architecture: x86_64 (Intel/AMD 64-bit)" + ;; + aarch64|arm64) + docker_arch="aarch64" + log_message "SUCCESS" "Architecture: aarch64 (ARM 64-bit)" + ;; + armv7l|armhf) + docker_arch="armhf" + log_message "SUCCESS" "Architecture: armhf (ARM 32-bit)" + ;; + *) + log_message "ERROR" "Unsupported architecture: $arch" + docker_arch="unknown" + ;; + esac + + echo "$docker_arch" +} + +# Critical fix from review: Init system detection +detect_init_system() { + log_message "INFO" "Detecting init system..." + + local init_system="unknown" + + if command -v systemctl >/dev/null 2>&1 && systemctl --version >/dev/null 2>&1; then + init_system="systemd" + log_message "SUCCESS" "Init system: systemd" + elif [ -d /etc/init.d ] && [ -x /sbin/initctl ] || [ -x /sbin/init ]; then + init_system="sysvinit" + log_message "SUCCESS" "Init system: sysvinit" + elif [ -d /etc/rc.d ] && [ -x /sbin/rc-service ]; then + init_system="openrc" + log_message "SUCCESS" "Init system: openrc (Alpine)" + elif [ -x /sbin/upstart ]; then + init_system="upstart" + log_message "SUCCESS" "Init system: upstart" + else + log_message "WARNING" "Could not determine init system (assuming systemd)" + init_system="systemd" # Default assumption + fi + + echo "$init_system" +} + +# Critical fix from review: Proper IP validation +validate_ip_address() { + local ip="$1" + + # Check basic format + if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + return 1 + fi + + # Check each octet is 0-255 + IFS='.' read -r i1 i2 i3 i4 <<< "$ip" + if [ "$i1" -gt 255 ] || [ "$i2" -gt 255 ] || [ "$i3" -gt 255 ] || [ "$i4" -gt 255 ]; then + return 1 + fi + + return 0 +} + +# Critical fix from review: Port conflict detection with fallback +check_port_available() { + local port="$1" + local protocol="${2:-tcp}" + + log_message "DEBUG" "Checking port $port/$protocol availability..." + + # Try ss first (modern, usually available) + if command -v ss >/dev/null 2>&1; then + if ss -"${protocol:0:1}"ln | grep -q ":$port "; then + return 1 # Port in use + fi + # Fallback to netstat + elif command -v netstat >/dev/null 2>&1; then + if netstat -tulpn 2>/dev/null | grep -E ":$port\s" >/dev/null 2>&1; then + return 1 # Port in use + fi + else + log_message "WARNING" "Neither ss nor netstat available, cannot check port $port" + fi + + return 0 # Port available (or cannot check) +} + +# Network connectivity testing + +test_connectivity() { + local host="$1" + local description="$2" + local timeout="${3:-10}" + + # Extract hostname for safe logging (without credentials) + local display_host + display_host=$(extract_hostname_from_url "$host") + log_message "INFO" "Testing connectivity to $description ($display_host)..." + + local url="$host" + if [[ ! "$host" =~ ^https?:// ]]; then + url="https://$host" + fi + if curl -sI $CURL_INSECURE_FLAG --connect-timeout "$timeout" "$url" >/dev/null 2>&1; then + log_message "SUCCESS" "$description is reachable" + return 0 + else + log_message "ERROR" "$description is NOT reachable" + return 1 + fi +} + +test_cloud_endpoints() { + local cloud_name="$1" + shift + local endpoints=("$@") + + log_message "INFO" "Testing $cloud_name cloud endpoints..." + + local all_reachable=true + for endpoint in "${endpoints[@]}"; do + if ! test_connectivity "$endpoint" "$cloud_name endpoint $endpoint"; then + all_reachable=false + fi + done + + if [ "$all_reachable" = true ]; then + log_message "SUCCESS" "All $cloud_name cloud endpoints reachable" + echo "true" + else + log_message "WARNING" "Some $cloud_name cloud endpoints unreachable" + echo "false" + fi +} + +perform_network_tests() { + log_message "INFO" "=== NETWORK CONNECTIVITY TESTING ===" + + # Test US cloud endpoints + local us_reachable + us_reachable=$(test_cloud_endpoints "US" "${US_DATA_NODES[@]}") + + # Test EU cloud endpoints + local eu_reachable + eu_reachable=$(test_cloud_endpoints "EU" "${EU_DATA_NODES[@]}") + + # Test internal Docker registry + local registry_reachable="false" + if test_connectivity "$INTERNAL_DOCKER_REGISTRY" "Internal Docker Registry"; then + registry_reachable="true" + fi + + # Test internal Docker download server + local download_reachable="false" + if test_connectivity "$INTERNAL_DOCKER_DOWNLOAD" "Internal Docker Download Server"; then + download_reachable="true" + fi + + # Check for local fallback resources + if [ "$download_reachable" = "false" ]; then + log_message "INFO" "Checking for local Docker binary fallback..." + if [ -n "$(ls docker-*.tgz 2>/dev/null)" ]; then + log_message "SUCCESS" "Found local Docker binary: $(ls docker-*.tgz | head -1)" + else + log_message "WARNING" "No local Docker binaries found" + fi + fi + + if [ "$registry_reachable" = "false" ]; then + log_message "INFO" "Checking for local Wallarm image fallback..." + if [ -n "$(ls wallarm-node-*.tar 2>/dev/null)" ]; then + log_message "SUCCESS" "Found local Wallarm image: $(ls wallarm-node-*.tar | head -1)" + else + log_message "WARNING" "No local Wallarm images found" + fi + fi + + + + echo "$us_reachable:$eu_reachable:$registry_reachable:$download_reachable" +} + +# ============================================================================== +# MAIN FUNCTION +# ============================================================================== + +main() { + clear + echo -e "${BLUE}${BOLD}" + echo "╔══════════════════════════════════════════════════════════════╗" + echo "║ WALLARM PREFLIGHT CHECK SCRIPT - V1.2 ║" + echo "║ System Readiness Validation for Deployment ║" + echo "╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${YELLOW}Starting preflight check at: $(date)${NC}" + + # Initialize logging + # Create logs directory if it doesn't exist + local log_dir="${HOME:-.}/logs" + if [ ! -d "$log_dir" ]; then + if ! mkdir -p "$log_dir"; then + echo -e "${YELLOW}Cannot create log directory $log_dir, falling back to current directory...${NC}" + log_dir="." + fi + fi + + LOG_FILE="$log_dir/wallarm-check.log" + if ! : > "$LOG_FILE"; then + echo -e "${RED}Cannot create log file at $LOG_FILE${NC}" + echo -e "${YELLOW}Falling back to current directory...${NC}" + LOG_FILE="./wallarm-check.log" + : > "$LOG_FILE" 2>/dev/null || true + fi + if ! chmod 644 "$LOG_FILE" 2>/dev/null; then + echo -e "${YELLOW}Warning: Could not set permissions on log file${NC}" + fi + + log_message "INFO" "=== Wallarm Preflight Check Started ===" + + # SSL security warning + if [ "$INSECURE_SSL" = "1" ]; then + log_message "WARNING" "SSL certificate validation is DISABLED (insecure). Set WALLARM_INSECURE_SSL=0 to enable validation." + fi + + # Phase 1: System validation + log_message "INFO" "=== PHASE 1: SYSTEM VALIDATION ===" + + if ! validate_required_commands; then + add_error "Required system commands validation failed" + fi + + if ! validate_sudo_access; then + add_error "Sudo access validation failed" + fi + + local os_info + os_info=$(detect_os_and_version) + local os_name + os_name=$(echo "$os_info" | cut -d: -f1) + local os_version + os_version=$(echo "$os_info" | cut -d: -f2) + + local architecture + architecture=$(detect_architecture) + if [ "$architecture" = "unknown" ]; then + add_error "Unsupported architecture detected" + fi + + local init_system + init_system=$(detect_init_system) + + log_message "SUCCESS" "System validation completed:" + log_message "SUCCESS" " OS: $os_name $os_version" + log_message "SUCCESS" " Architecture: $architecture" + log_message "SUCCESS" " Init System: $init_system" + + # Phase 2: Network connectivity testing + log_message "INFO" "=== PHASE 2: NETWORK CONNECTIVITY TESTING ===" + + local network_results + network_results=$(perform_network_tests) + local us_reachable + us_reachable=$(echo "$network_results" | cut -d: -f1) + local eu_reachable + eu_reachable=$(echo "$network_results" | cut -d: -f2) + local registry_reachable + registry_reachable=$(echo "$network_results" | cut -d: -f3) + local download_reachable + download_reachable=$(echo "$network_results" | cut -d: -f4) + + # Critical check: Need at least one source for Docker and Wallarm + if [ "$registry_reachable" = "false" ] && [ "$download_reachable" = "false" ]; then + local has_local_docker=false + local has_local_wallarm=false + + if [ -n "$(ls docker-*.tgz 2>/dev/null)" ]; then + has_local_docker=true + fi + + if [ -n "$(ls wallarm-node-*.tar 2>/dev/null)" ]; then + has_local_wallarm=true + fi + + if [ "$has_local_docker" = "false" ] || [ "$has_local_wallarm" = "false" ]; then + log_message "ERROR" "Critical: Neither internal registry nor download server reachable" + log_message "ERROR" "No local Docker binary or Wallarm image found" + add_error "Insufficient resources: Need network access to $DOCKER_REGISTRY_HOST or $DOCKER_DOWNLOAD_HOST, or local docker-*.tgz and wallarm-node-*.tar files" + fi + fi + + log_message "SUCCESS" "Network testing completed:" + log_message "SUCCESS" " US Cloud Reachable: $us_reachable" + log_message "SUCCESS" " EU Cloud Reachable: $eu_reachable" + log_message "SUCCESS" " Internal Registry Reachable: $registry_reachable" + log_message "SUCCESS" " Internal Download Reachable: $download_reachable" + + # Phase 3: Write results + log_message "INFO" "=== PHASE 3: WRITING RESULTS ===" + + write_env_file "$os_name" "$os_version" "$architecture" "$init_system" \ + "$us_reachable" "$eu_reachable" "$registry_reachable" "$download_reachable" + + # Final summary + if [ "$CHECK_RESULT" = "pass" ]; then + log_message "SUCCESS" "=== PREFLIGHT CHECK PASSED ===" + echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}${BOLD}║ PREFLIGHT CHECK PASSED - SYSTEM READY ║${NC}" + echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${CYAN}System is ready for Wallarm deployment.${NC}" + echo -e "${YELLOW}Check results: $ENV_FILE${NC}" + echo -e "${YELLOW}Full log: $LOG_FILE${NC}" + echo -e "\n${GREEN}Next step: Run ./wallarm-ct-deploy.sh to proceed with deployment${NC}" + exit 0 + else + log_message "ERROR" "=== PREFLIGHT CHECK FAILED ===" + echo -e "\n${RED}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${RED}${BOLD}║ PREFLIGHT CHECK FAILED - SYSTEM NOT READY ║${NC}" + echo -e "${RED}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${YELLOW}${BOLD}Issues found:${NC}" + for error in "${CHECK_ERRORS[@]}"; do + echo -e " ${RED}•${NC} $error" + done + echo -e "\n${YELLOW}Check results: $ENV_FILE${NC}" + echo -e "${YELLOW}Full log: $LOG_FILE${NC}" + echo -e "\n${CYAN}Please fix the issues above and run the check again.${NC}" + exit 1 + fi +} + +# ============================================================================== +# SCRIPT EXECUTION +# ============================================================================== + +# Ensure we're in bash +if [ -z "$BASH_VERSION" ]; then + echo "Error: This script must be run with bash" >&2 + exit 1 +fi + +# Run main function +main "$@" \ No newline at end of file diff --git a/wallarm-ct-deploy.sh b/wallarm-ct-deploy.sh new file mode 100755 index 0000000..4f49680 --- /dev/null +++ b/wallarm-ct-deploy.sh @@ -0,0 +1,1363 @@ +#!/bin/bash +# ============================================================================== +# WALLARM DEPLOYMENT SCRIPT - V1.1 +# ============================================================================== +# Purpose: Deploy Wallarm filtering node after preflight check +# Features: +# - Reads preflight check results from .env file +# - Interactive configuration (cloud region, ports, token, upstream) +# - Docker installation with LXC optimization (VFS storage driver) +# - Wallarm node deployment with persistence +# - Deployment verification with handshake test +# - DAU-friendly error handling with remediation +# ============================================================================== + +# Color definitions for better UX +RED='\033[0;31m' +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +BLUE='\033[1;34m' +CYAN='\033[0;36m' +MAGENTA='\033[0;35m' +BOLD='\033[1m' +NC='\033[0m' # No Color + +# Strict error handling +set -euo pipefail +# Simple error handler for early failures (before log_message is defined) +early_error_handler() { + echo -e "${RED}${BOLD}[ERROR]${NC} Script failed at line $LINENO. Command: $BASH_COMMAND" >&2 + exit 1 +} +trap early_error_handler ERR + +# Extract hostname from URL (strip protocol and credentials for safe logging) +extract_hostname_from_url() { + local url="$1" + # Remove protocol + local hostpart="${url#*://}" + # Remove credentials if present (username:password@) + hostpart="${hostpart#*@}" + # Remove port and path + hostpart="${hostpart%%[:/]*}" + echo "$hostpart" +} + +# Configuration +ENV_FILE=".env" +LOG_FILE="${HOME:-.}/logs/wallarm-deployment.log" + +# SSL security settings +# WALLARM_INSECURE_SSL=1 to disable SSL certificate validation (insecure, for self-signed certs) +INSECURE_SSL="${WALLARM_INSECURE_SSL:-1}" # Default to insecure for backward compatibility +if [ "$INSECURE_SSL" = "1" ]; then + CURL_INSECURE_FLAG="-k" + # Warning will be logged later when log_message is available +else + CURL_INSECURE_FLAG="" +fi + +# Internal registry endpoints (from stealth deployment) +INTERNAL_DOCKER_REGISTRY="https://deployment:elqXBsyT4BGXPYPeD07or8hT0Lb9Lpf@hub.ct.sechpoint.app" +INTERNAL_DOCKER_DOWNLOAD="https://deployment:elqXBsyT4BGXPYPeD07or8hT0Lb9Lpf@ct.sechpoint.app" +# Extracted hostnames (without credentials) for Docker operations +DOCKER_REGISTRY_HOST=$(extract_hostname_from_url "$INTERNAL_DOCKER_REGISTRY") +DOCKER_DOWNLOAD_HOST=$(extract_hostname_from_url "$INTERNAL_DOCKER_DOWNLOAD") + +DOCKER_VERSION="29.2.1" # Version from stealth deployment guide +DOCKER_STATIC_BASE_URL="${INTERNAL_DOCKER_DOWNLOAD}/linux/static/stable" +WALLARM_IMAGE_SOURCE="${DOCKER_REGISTRY_HOST}/wallarm/node:latest" +WALLARM_IMAGE_TARGET="wallarm/node:latest" + + + +# Deployment variables (set during execution) +CLOUD_REGION="" +API_HOST="" +INGRESS_PORT="" +MONITORING_PORT="" +UPSTREAM_IP="" +UPSTREAM_PORT="" +WALLARM_TOKEN="" +INSTANCE_NAME="" +INSTANCE_DIR="" + +# Resource reachability from check script +US_CLOUD_REACHABLE="false" +EU_CLOUD_REACHABLE="false" +REGISTRY_REACHABLE="false" +DOWNLOAD_REACHABLE="false" + +# ============================================================================== +# LOGGING & ERROR HANDLING FUNCTIONS +# ============================================================================== + +log_message() { + local level="$1" + local message="$2" + local timestamp + timestamp=$(date '+%Y-%m-%d %H:%M:%S') + + case "$level" in + "INFO") color="${BLUE}" ;; + "SUCCESS") color="${GREEN}" ;; + "WARNING") color="${YELLOW}" ;; + "ERROR") color="${RED}" ;; + "DEBUG") color="${CYAN}" ;; + *) color="${NC}" ;; + esac + + echo -e "${color}[${timestamp}] ${level}: ${message}${NC}" >&2 + echo "[${timestamp}] ${level}: ${message}" >> "$LOG_FILE" +} + +fail_with_remediation() { + local error_msg="$1" + local remediation="$2" + + log_message "ERROR" "$error_msg" + echo -e "\n${RED}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${RED}${BOLD}║ DEPLOYMENT FAILED ║${NC}" + echo -e "${RED}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${YELLOW}${BOLD}Root Cause:${NC} $error_msg" + echo -e "\n${YELLOW}${BOLD}How to Fix:${NC}" + echo -e "$remediation" + echo -e "\n${YELLOW}Check the full log for details:${NC} $LOG_FILE" + exit 1 +} + +# ============================================================================== +# PREFLIGHT CHECK VERIFICATION +# ============================================================================== + +verify_preflight_check() { + log_message "INFO" "Verifying preflight check results..." + + if [ ! -f "$ENV_FILE" ]; then + log_message "ERROR" "Preflight check file not found: $ENV_FILE" + echo -e "\n${YELLOW}Preflight check has not been run or .env file is missing.${NC}" + echo -e "${YELLOW}Would you like to run the preflight check now?${NC}" + read -r -p "$(echo -e "${YELLOW}Run preflight check? (Y/n): ${NC}")" -n 1 + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + echo -e "${CYAN}Running preflight check...${NC}" + if ! ./wallarm-ct-check.sh; then + fail_with_remediation "Preflight check failed" \ + "Run the preflight check manually and fix any issues: +1. ./wallarm-ct-check.sh +2. Review the errors in $ENV_FILE +3. Fix the issues and run this script again" + fi + else + fail_with_remediation "Preflight check required" \ + "Run the preflight check before deployment: +1. ./wallarm-ct-check.sh +2. Review results in $ENV_FILE +3. Run this script again" + fi + fi + + # Load environment variables from .env file + # Use a safer approach than sourcing (avoid code injection) + while IFS='=' read -r key value; do + # Remove comments and empty lines + [[ "$key" =~ ^#.*$ ]] && continue + [[ -z "$key" ]] && continue + + # Remove quotes from value + value="${value%\"}" + value="${value#\"}" + + # Export variable + case "$key" in + result) CHECK_RESULT="$value" ;; + os_name) OS_NAME="$value" ;; + os_version) OS_VERSION="$value" ;; + architecture) ARCHITECTURE="$value" ;; + init_system) INIT_SYSTEM="$value" ;; + us_cloud_reachable) US_CLOUD_REACHABLE="$value" ;; + eu_cloud_reachable) EU_CLOUD_REACHABLE="$value" ;; + registry_reachable) REGISTRY_REACHABLE="$value" ;; + download_reachable) DOWNLOAD_REACHABLE="$value" ;; + esac + done < "$ENV_FILE" + + if [ "$CHECK_RESULT" != "pass" ]; then + log_message "ERROR" "Preflight check failed (result: $CHECK_RESULT)" + echo -e "\n${YELLOW}Preflight check found issues. Please review:${NC}" + echo -e "${YELLOW}1. Check file: $ENV_FILE${NC}" + echo -e "${YELLOW}2. Run: ./wallarm-ct-check.sh${NC}" + echo -e "${YELLOW}3. Fix the issues and try again${NC}" + exit 1 + fi + + log_message "SUCCESS" "Preflight check verified:" + log_message "SUCCESS" " OS: $OS_NAME $OS_VERSION" + log_message "SUCCESS" " Architecture: $ARCHITECTURE" + log_message "SUCCESS" " Init System: $INIT_SYSTEM" + log_message "SUCCESS" " US Cloud Reachable: $US_CLOUD_REACHABLE" + log_message "SUCCESS" " EU Cloud Reachable: $EU_CLOUD_REACHABLE" + log_message "SUCCESS" " Registry Reachable: $REGISTRY_REACHABLE" + log_message "SUCCESS" " Download Reachable: $DOWNLOAD_REACHABLE" + + # Validate we have at least one cloud region reachable + if [ "$US_CLOUD_REACHABLE" = "false" ] && [ "$EU_CLOUD_REACHABLE" = "false" ]; then + fail_with_remediation "No Wallarm cloud region reachable" \ + "Network connectivity issues detected: +1. Check firewall rules for Wallarm cloud endpoints +2. Verify network connectivity +3. Run preflight check again: ./wallarm-ct-check.sh" + fi + + # Validate we have resources for Docker/Wallarm + if [ "$REGISTRY_REACHABLE" = "false" ] && [ "$DOWNLOAD_REACHABLE" = "false" ]; then + log_message "WARNING" "Neither registry nor download server reachable" + log_message "INFO" "Checking for local resources..." + + local has_local_resources=true + if [ -z "$(ls docker-*.tgz 2>/dev/null)" ]; then + log_message "ERROR" "No local Docker binary found" + has_local_resources=false + fi + + if [ -z "$(ls wallarm-node-*.tar 2>/dev/null)" ]; then + log_message "ERROR" "No local Wallarm image found" + has_local_resources=false + fi + + if [ "$has_local_resources" = "false" ]; then + fail_with_remediation "Insufficient resources for deployment" \ + "Please provide either: +1. Network access to $DOCKER_REGISTRY_HOST +2. Network access to $DOCKER_DOWNLOAD_HOST +3. Local files: docker-*.tgz and wallarm-node-*.tar in current directory" + fi + fi +} + +# ============================================================================== +# CONFIGURATION COLLECTION FUNCTIONS +# ============================================================================== + +select_cloud_region() { + log_message "INFO" "Selecting Wallarm Cloud region..." + + echo -e "\n${CYAN}${BOLD}Wallarm Cloud Region Selection:${NC}" + + # Show available regions based on preflight check + local available_options=() + + if [ "$US_CLOUD_REACHABLE" = "true" ]; then + echo -e "1. ${YELLOW}US Cloud${NC} (us1.api.wallarm.com) - For US-based deployments" + available_options+=("1" "US") + fi + + if [ "$EU_CLOUD_REACHABLE" = "true" ]; then + echo -e "2. ${YELLOW}EU Cloud${NC} (api.wallarm.com) - For EU-based deployments" + available_options+=("2" "EU") + fi + + if [ ${#available_options[@]} -eq 0 ]; then + fail_with_remediation "No cloud regions available" \ + "Preflight check showed no reachable cloud regions. +1. Check network connectivity to Wallarm endpoints +2. Run preflight check again: ./wallarm-ct-check.sh +3. Contact network administrator if behind firewall" + fi + + # Build regex pattern for validation + local pattern + pattern="^($(IFS='|'; echo "${available_options[*]}"))$" + + local cloud_choice="" + while [[ ! "$cloud_choice" =~ $pattern ]]; do + if [ ${#available_options[@]} -eq 2 ]; then + # Only one region available + if [ "$US_CLOUD_REACHABLE" = "true" ]; then + cloud_choice="US" + break + else + cloud_choice="EU" + break + fi + fi + + read -r -p "$(echo -e "${YELLOW}Enter choice [1/US or 2/EU]: ${NC}")" cloud_choice + cloud_choice=$(echo "$cloud_choice" | tr '[:lower:]' '[:upper:]') + + case "$cloud_choice" in + 1|"US") + if [ "$US_CLOUD_REACHABLE" = "true" ]; then + CLOUD_REGION="US" + API_HOST="us1.api.wallarm.com" + log_message "INFO" "Selected US Cloud" + else + echo -e "${RED}US Cloud is not reachable (per preflight check)${NC}" + cloud_choice="" + fi + ;; + 2|"EU") + if [ "$EU_CLOUD_REACHABLE" = "true" ]; then + CLOUD_REGION="EU" + API_HOST="api.wallarm.com" + log_message "INFO" "Selected EU Cloud" + else + echo -e "${RED}EU Cloud is not reachable (per preflight check)${NC}" + cloud_choice="" + fi + ;; + *) + if [ -n "$cloud_choice" ]; then + echo -e "${RED}Invalid choice. Select from available options above.${NC}" + fi + ;; + esac + done + + log_message "SUCCESS" "Cloud region selected: $CLOUD_REGION ($API_HOST)" +} + +# Critical fix from review: Proper IP validation +validate_ip_address() { + local ip="$1" + + # Check basic format + if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then + return 1 + fi + + # Check each octet is 0-255 + IFS='.' read -r i1 i2 i3 i4 <<< "$ip" + if [ "$i1" -gt 255 ] || [ "$i2" -gt 255 ] || [ "$i3" -gt 255 ] || [ "$i4" -gt 255 ]; then + return 1 + fi + + return 0 +} + +# Critical fix from review: Port conflict detection with fallback +check_port_available() { + local port="$1" + local protocol="${2:-tcp}" + + log_message "DEBUG" "Checking port $port/$protocol availability..." + + # Try ss first (modern, usually available) + if command -v ss >/dev/null 2>&1; then + if ss -"${protocol:0:1}"ln | grep -q ":$port "; then + return 1 # Port in use + fi + # Fallback to netstat + elif command -v netstat >/dev/null 2>&1; then + if netstat -tulpn 2>/dev/null | grep -E ":$port\s" >/dev/null 2>&1; then + return 1 # Port in use + fi + else + log_message "WARNING" "Neither ss nor netstat available, cannot check port $port" + fi + + return 0 # Port available (or cannot check) +} + +collect_configuration() { + log_message "INFO" "Collecting deployment configuration..." + + # Get ingress port + local default_port=80 + local ingress_port="" + while [[ ! "$ingress_port" =~ ^[0-9]+$ ]] || [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; do + read -r -p "$(echo -e "${YELLOW}Enter inbound port [${default_port}]: ${NC}")" ingress_port + ingress_port="${ingress_port:-$default_port}" + + if [[ ! "$ingress_port" =~ ^[0-9]+$ ]]; then + echo -e "${RED}Port must be a number${NC}" + elif [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; then + echo -e "${RED}Port must be between 1 and 65535${NC}" + elif ! check_port_available "$ingress_port"; then + echo -e "${RED}Port $ingress_port is already in use${NC}" + ingress_port="" + fi + done + + # Calculate monitoring port (ingress + 10, check for conflicts) + local monitoring_port=$((ingress_port + 10)) + if ! check_port_available "$monitoring_port"; then + log_message "WARNING" "Port $monitoring_port is in use, choosing alternative..." + monitoring_port=$((ingress_port + 100)) + if ! check_port_available "$monitoring_port"; then + monitoring_port=$((ingress_port + 200)) + fi + fi + log_message "INFO" "Monitoring port will be: $monitoring_port" + + # Get application server details + local upstream_ip="" + local upstream_port="" + + echo -e "\n${CYAN}${BOLD}Application Server Configuration:${NC}" + echo -e "${YELLOW}Enter the IP/hostname and port of your backend application${NC}" + + while [[ -z "$upstream_ip" ]]; do + read -r -p "$(echo -e "${YELLOW}Upstream App IP/Hostname [127.0.0.1]: ${NC}")" upstream_ip + upstream_ip="${upstream_ip:-127.0.0.1}" + + # Validate IP/hostname format + if ! validate_ip_address "$upstream_ip" && \ + ! [[ "$upstream_ip" =~ ^[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]$ ]]; then + echo -e "${RED}Invalid IP/hostname format${NC}" + upstream_ip="" + fi + done + + while [[ ! "$upstream_port" =~ ^[0-9]+$ ]] || [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; do + read -r -p "$(echo -e "${YELLOW}Upstream App Port [8080]: ${NC}")" upstream_port + upstream_port="${upstream_port:-8080}" + + if [[ ! "$upstream_port" =~ ^[0-9]+$ ]]; then + echo -e "${RED}Port must be a number${NC}" + elif [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; then + echo -e "${RED}Port must be between 1 and 65535${NC}" + fi + done + + # Verify application server reachability + log_message "INFO" "Verifying application server reachability..." + if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$upstream_ip/$upstream_port" 2>/dev/null; then + log_message "SUCCESS" "Application server $upstream_ip:$upstream_port is reachable" + else + log_message "WARNING" "Application server $upstream_ip:$upstream_port is not reachable" + echo -e "${YELLOW}${BOLD}Warning:${NC} Cannot reach application server at $upstream_ip:$upstream_port" + echo -e "${YELLOW}This may cause the Wallarm node to fail. Possible reasons:${NC}" + echo -e "1. Application server is not running" + echo -e "2. Firewall blocking port $upstream_port" + echo -e "3. Wrong IP/hostname" + echo -e "4. Application server not listening on that port" + + read -r -p "$(echo -e "${YELLOW}Continue anyway? (y/N): ${NC}")" -n 1 + echo + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + fail_with_remediation "Application server unreachable" \ + "Ensure your application server is accessible: +1. Start your application server +2. Check it's listening: sudo ss -tlnp | grep :$upstream_port +3. Verify firewall rules allow inbound connections +4. Test connectivity: telnet $upstream_ip $upstream_port +5. If using hostname, verify DNS resolution: nslookup $upstream_ip" + fi + fi + + # Get Wallarm node token + local wallarm_token="" + echo -e "\n${CYAN}${BOLD}Wallarm Node Token:${NC}" + echo -e "${YELLOW}Get your token from Wallarm Console:${NC}" + echo -e "Create a new 'Wallarm node' and copy the token (will be visible as you type)" + while [[ -z "$wallarm_token" ]]; do + read -r -p "$(echo -e "${YELLOW}Paste Wallarm Node Token: ${NC}")" wallarm_token + # Trim whitespace and newlines + wallarm_token=$(echo "$wallarm_token" | tr -d '[:space:]') + + if [[ -z "$wallarm_token" ]]; then + echo -e "${RED}Token cannot be empty${NC}" + elif [[ ! "$wallarm_token" =~ ^[A-Za-z0-9_+/=\-]+$ ]]; then + echo -e "${RED}Token contains invalid characters. Wallarm tokens are base64 strings (A-Z, a-z, 0-9, _, -, +, /, =)${NC}" + echo -e "${YELLOW}First 20 chars of what you entered: '${wallarm_token:0:20}...'${NC}" + wallarm_token="" + else + # Show confirmation of token length (but not full token for security) + token_length=${#wallarm_token} + echo -e "${GREEN}Token accepted (${token_length} characters).${NC}" + echo -e "${YELLOW}First 8 chars for verification: ${wallarm_token:0:8}...${NC}" + fi + done + + # Generate instance name and directory + local instance_name + instance_name="wallarm-$(hostname -s | tr '[:upper:]' '[:lower:]')-$(date +%Y%m%d)" + local instance_dir="/opt/$instance_name" + + # Ensure directory exists + sudo mkdir -p "$instance_dir" + + log_message "SUCCESS" "Configuration collected:" + log_message "SUCCESS" " Ingress Port: $ingress_port" + log_message "SUCCESS" " Monitoring Port: $monitoring_port" + log_message "SUCCESS" " Upstream: $upstream_ip:$upstream_port" + log_message "SUCCESS" " Instance: $instance_name" + log_message "SUCCESS" " Directory: $instance_dir" + + # Set global variables + INGRESS_PORT="$ingress_port" + MONITORING_PORT="$monitoring_port" + UPSTREAM_IP="$upstream_ip" + UPSTREAM_PORT="$upstream_port" + WALLARM_TOKEN="$wallarm_token" + INSTANCE_NAME="$instance_name" + INSTANCE_DIR="$instance_dir" +} + +# ============================================================================== +# DOCKER ENGINE SETUP (LXC OPTIMIZED) +# ============================================================================== + +setup_docker_engine() { + log_message "INFO" "Setting up Docker Engine for LXC/stealth deployment..." + + # Check if Docker is already installed and running + if command -v docker >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then + local docker_version + docker_version=$(docker --version | cut -d' ' -f3 | tr -d ',') + log_message "SUCCESS" "Docker is already installed and running (version $docker_version)" + + # Check if Docker is configured for LXC + if sudo docker info 2>/dev/null | grep -q "Storage Driver: vfs"; then + log_message "SUCCESS" "Docker is already configured with VFS storage driver (LXC compatible)" + else + log_message "WARNING" "Docker is not using VFS storage driver. LXC compatibility may be limited." + fi + return 0 + fi + + log_message "INFO" "Docker not found or not running. Proceeding with installation..." + + # Determine binary source + local binary_file="docker-$DOCKER_VERSION.tgz" + local binary_path="" + + if [ "$DOWNLOAD_REACHABLE" = "true" ]; then + # Download Docker static binary from internal server + log_message "INFO" "Downloading Docker static binary for $ARCHITECTURE..." + local download_url="$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz" + + if curl -fL $CURL_INSECURE_FLAG --connect-timeout 30 "$download_url" -o "$binary_file"; then + log_message "SUCCESS" "Downloaded Docker binary: $binary_file" + binary_path="$binary_file" + else + log_message "ERROR" "Failed to download Docker binary from $download_url" + binary_path="" + fi + fi + + # Fallback: Check for local Docker binary + if [ -z "$binary_path" ]; then + log_message "INFO" "Checking for local Docker binary..." + local local_files + local_files=$(ls docker-*.tgz 2>/dev/null | head -1) + if [ -n "$local_files" ]; then + binary_path="$local_files" + log_message "SUCCESS" "Using local Docker binary: $binary_path" + else + fail_with_remediation "No Docker binary available" \ + "Please provide a Docker static binary: +1. Download manually: + curl -L '$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz' -o docker.tgz +2. Or place an existing docker-*.tgz file in current directory +3. Re-run the script after downloading" + fi + fi + + # Extract and install + log_message "INFO" "Extracting Docker binary..." + + # First verify the tar file exists and is readable + if [ ! -f "$binary_path" ]; then + fail_with_remediation "Docker binary file not found" \ + "File $binary_path does not exist. Check download and file permissions." + fi + + if [ ! -r "$binary_path" ]; then + fail_with_remediation "Docker binary file not readable" \ + "Cannot read file $binary_path. Check file permissions." + fi + + # Test if it's a valid tar archive + log_message "DEBUG" "Testing tar archive integrity..." + + # First check if we can read the file + if [ ! -r "$binary_path" ]; then + log_message "ERROR" "Cannot read file: $binary_path" + log_message "INFO" "File permissions: $(ls -la "$binary_path" 2>/dev/null || echo "cannot stat")" + fail_with_remediation "Cannot read Docker binary file" \ + "File $binary_path exists but is not readable. +1. Check file permissions: ls -la $binary_path +2. Fix permissions: chmod 644 $binary_path +3. Or download fresh copy" + fi + + # Try to get file type information + local file_type="unknown" + if command -v file >/dev/null 2>&1; then + file_type=$(file "$binary_path" 2>/dev/null || echo "file command failed") + elif command -v hexdump >/dev/null 2>&1; then + # Check magic bytes manually + local magic_bytes=$(hexdump -n 2 -C "$binary_path" 2>/dev/null | head -1 | cut -d' ' -f2-3 || echo "no magic") + file_type="hexdump: $magic_bytes" + fi + + log_message "INFO" "File info: $binary_path ($(stat -c%s "$binary_path") bytes)" + log_message "INFO" "File type: $file_type" + log_message "INFO" "Current directory: $(pwd)" + log_message "INFO" "Full path: $(readlink -f "$binary_path" 2>/dev/null || echo "$binary_path")" + + # Test tar archive with error capture + local tar_test_output + tar_test_output=$(tar -tzf "$binary_path" 2>&1) + local tar_test_exit=$? + + if [ $tar_test_exit -ne 0 ]; then + log_message "ERROR" "File $binary_path is not a valid tar.gz archive (tar exit: $tar_test_exit)" + log_message "DEBUG" "Tar test output: $tar_test_output" + + # Check if it might be a different compression format + log_message "INFO" "Checking for alternative compression formats..." + + # Try gunzip test + if command -v gunzip >/dev/null 2>&1; then + if gunzip -t "$binary_path" 2>/dev/null; then + log_message "WARNING" "File is valid gzip but tar can't read it" + else + log_message "INFO" "Not a valid gzip file either" + fi + fi + + # Check first few bytes + if command -v xxd >/dev/null 2>&1; then + log_message "DEBUG" "First 20 bytes: $(xxd -l 20 "$binary_path" 2>/dev/null || echo "cannot read")" + elif command -v od >/dev/null 2>&1; then + log_message "DEBUG" "First 20 bytes: $(od -x -N 20 "$binary_path" 2>/dev/null | head -2 || echo "cannot read")" + fi + + fail_with_remediation "Docker binary file is corrupted or invalid" \ + "The Docker binary file is not a valid tar.gz archive. +Tar error: $tar_test_output + +File info: $(stat -c%s "$binary_path") bytes, type: $file_type + +Possible solutions: +1. The download may have been interrupted or corrupted +2. The file may be in wrong format (not tar.gz) +3. Server might be serving wrong content + +Steps to fix: +1. Delete corrupted file: rm -f docker-*.tgz +2. Check disk space: df -h . +3. Download manually and verify: + curl -v -L '$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz' -o test.tgz + file test.tgz + tar -tzf test.tgz +4. Check if tar command works: tar --version" + fi + + log_message "SUCCESS" "Tar archive validation passed" + + # Extract the archive + log_message "DEBUG" "Extracting files from $binary_path..." + local tar_output + tar_output=$(tar xzvf "$binary_path" 2>&1) + local tar_exit=$? + + if [ $tar_exit -ne 0 ]; then + log_message "ERROR" "Failed to extract files from $binary_path (exit code: $tar_exit)" + log_message "DEBUG" "Tar output: $tar_output" + log_message "INFO" "Checking extracted files..." + if [ -d "docker" ]; then + log_message "WARNING" "Some files were extracted to 'docker/' directory" + ls -la docker/ 2>/dev/null | head -10 || true + fi + fail_with_remediation "Failed to extract Docker binary" \ + "Extraction failed. Possible reasons: +1. Insufficient disk space: df -h . +2. Permission issues in current directory +3. Corrupted archive (partial download) +4. File system issues + +Tar error: $tar_output + +Check disk space and permissions, then try manual extraction: + tar xzvf $binary_path" + else + log_message "SUCCESS" "Docker binary extracted successfully" + fi + + log_message "INFO" "Installing Docker binaries to /usr/bin/" + sudo cp docker/* /usr/bin/ 2>/dev/null || { + fail_with_remediation "Failed to copy Docker binaries" \ + "Permission denied copying to /usr/bin/ +1. Ensure you have sudo privileges +2. Check disk space: df -h / +3. Manual installation: + sudo cp docker/* /usr/bin/" + } + + # Cleanup extracted directory + rm -rf docker + + # Configure Docker daemon for LXC (VFS storage driver, cgroupfs) + log_message "INFO" "Configuring Docker daemon for LXC (VFS storage driver, cgroupfs)..." + + # Create docker configuration directory + sudo mkdir -p /etc/docker + + # Create daemon.json for LXC optimization + sudo tee /etc/docker/daemon.json > /dev/null < /dev/null <<'EOF' +[Unit] +Description=Docker Engine +After=network-online.target firewalld.service containerd.service +Wants=network-online.target +Requires=docker.socket + +[Service] +Type=notify +ExecStart=/usr/bin/dockerd --group docker +ExecReload=/bin/kill -s HUP $MAINPID +TimeoutSec=0 +RestartSec=2 +Restart=always +StartLimitBurst=3 +StartLimitInterval=60s +LimitNOFILE=infinity +LimitNPROC=infinity +LimitCORE=infinity +TasksMax=infinity +Delegate=yes +KillMode=process + +[Install] +WantedBy=multi-user.target +EOF + + sudo tee /etc/systemd/system/docker.socket > /dev/null <<'EOF' +[Unit] +Description=Docker Socket for the API + +[Socket] +ListenStream=/var/run/docker.sock +SocketMode=0660 +SocketUser=root +SocketGroup=docker + +[Install] +WantedBy=sockets.target +EOF + + sudo systemctl daemon-reload + sudo systemctl enable docker + sudo systemctl start docker + ;; + + "openrc") + # Alpine OpenRC configuration + sudo tee /etc/init.d/docker > /dev/null <<'EOF' +#!/sbin/openrc-run +description="Docker Engine" +command="/usr/bin/dockerd" +command_args="--group docker" +pidfile="/run/docker.pid" +command_background=true + +depend() { + need net + after firewall +} +EOF + + sudo chmod +x /etc/init.d/docker + sudo rc-update add docker default + sudo rc-service docker start + ;; + + "sysvinit") + # Traditional SysV init script + sudo tee /etc/init.d/docker > /dev/null <<'EOF' +#!/bin/bash +### BEGIN INIT INFO +# Provides: docker +# Required-Start: $local_fs $network $remote_fs +# Required-Stop: $local_fs $network $remote_fs +# Default-Start: 2 3 4 5 +# Default-Stop: 0 1 6 +# Short-Description: Docker Engine +# Description: Docker container runtime +### END INIT INFO + +DESC="Docker Engine" +DAEMON=/usr/bin/dockerd +DAEMON_ARGS="--group docker" +PIDFILE=/var/run/docker.pid +SCRIPTNAME=/etc/init.d/docker + +[ -x "$DAEMON" ] || exit 0 + +. /lib/lsb/init-functions + +case "$1" in + start) + log_daemon_msg "Starting $DESC" "docker" + start-stop-daemon --start --background --pidfile "$PIDFILE" \ + --exec "$DAEMON" -- $DAEMON_ARGS + log_end_msg $? + ;; + stop) + log_daemon_msg "Stopping $DESC" "docker" + start-stop-daemon --stop --pidfile "$PIDFILE" --retry 10 + log_end_msg $? + ;; + restart) + $0 stop + sleep 1 + $0 start + ;; + status) + status_of_proc -p "$PIDFILE" "$DAEMON" docker + ;; + *) + echo "Usage: $SCRIPTNAME {start|stop|restart|status}" + exit 3 + ;; +esac + +exit 0 +EOF + + sudo chmod +x /etc/init.d/docker + sudo update-rc.d docker defaults + sudo service docker start + ;; + + *) + log_message "WARNING" "Unknown init system '$INIT_SYSTEM', trying systemd defaults" + sudo systemctl daemon-reload 2>/dev/null || true + sudo systemctl enable docker 2>/dev/null || true + sudo systemctl start docker 2>/dev/null || { + log_message "ERROR" "Failed to start Docker with unknown init system" + echo -e "${YELLOW}Please start Docker manually and re-run the script${NC}" + exit 1 + } + ;; + esac + + # Verify Docker is running + log_message "INFO" "Verifying Docker service..." + sleep 3 # Give Docker time to start + + if ! sudo docker info >/dev/null 2>&1; then + fail_with_remediation "Docker failed to start" \ + "Docker installation completed but service failed to start: +1. Check Docker logs: journalctl -u docker (systemd) or /var/log/docker.log +2. Verify configuration: sudo dockerd --debug +3. Manual start: sudo dockerd --group docker &" + fi + + # Verify Docker is using VFS storage driver + log_message "INFO" "Verifying Docker storage driver configuration..." + if sudo docker info 2>/dev/null | grep -q "Storage Driver: vfs"; then + log_message "SUCCESS" "Docker configured with VFS storage driver (LXC compatible)" + else + log_message "WARNING" "Docker is not using VFS storage driver. Checking current driver..." + sudo docker info 2>/dev/null | grep "Storage Driver:" || log_message "ERROR" "Could not determine storage driver" + log_message "WARNING" "LXC compatibility may be limited without VFS storage driver" + fi + + # Add current user to docker group for passwordless docker commands + log_message "INFO" "Adding current user to docker group..." + + # Security notice: docker group grants root-equivalent privileges + echo -e "${YELLOW}${BOLD}Security Notice:${NC} Adding your user to the 'docker' group grants root-equivalent privileges." + echo -e "${YELLOW}Any user in the docker group can run commands as root on the host system.${NC}" + echo -e "${YELLOW}Only proceed if you understand and accept this security risk.${NC}" + + read -r -p "$(echo -e "${YELLOW}Add $(whoami) to docker group? (Y/n): ${NC}")" -n 1 + echo + if [[ ! $REPLY =~ ^[Nn]$ ]]; then + if ! getent group docker >/dev/null; then + sudo groupadd docker 2>/dev/null || log_message "WARNING" "Failed to create docker group (may already exist)" + fi + sudo usermod -aG docker "$(whoami)" 2>/dev/null && \ + log_message "SUCCESS" "Added $(whoami) to docker group (log out and back in for changes)" + else + log_message "WARNING" "Skipping docker group addition. You will need to use sudo for docker commands." + echo -e "${YELLOW}Note: You can manually add yourself to docker group later with:${NC}" + echo -e "${CYAN} sudo usermod -aG docker $(whoami)${NC}" + echo -e "${YELLOW}Then log out and back in for changes to take effect.${NC}" + fi + + log_message "SUCCESS" "Docker Engine setup completed successfully" +} + +# ============================================================================== +# WALLARM NODE DEPLOYMENT +# ============================================================================== + +deploy_wallarm_node() { + log_message "INFO" "Deploying Wallarm filtering node..." + + # Pull Wallarm Docker image + log_message "INFO" "Pulling Wallarm Docker image from internal registry: $WALLARM_IMAGE_SOURCE" + + if [ "$REGISTRY_REACHABLE" = "true" ]; then + if ! sudo docker pull "$WALLARM_IMAGE_SOURCE"; then + fail_with_remediation "Failed to pull Wallarm image from internal registry" \ + "Docker pull from internal registry failed. Possible reasons: +1. Network connectivity to $DOCKER_REGISTRY_HOST +2. Authentication required for internal registry +3. Insufficient disk space + +Solutions: +1. Check network: curl -I $INTERNAL_DOCKER_REGISTRY +2. Login to internal registry if required +3. Use local image fallback: docker save/load +4. Check disk: df -h /var/lib/docker" + fi + + # Re-tag to standard name + sudo docker tag "$WALLARM_IMAGE_SOURCE" "$WALLARM_IMAGE_TARGET" + log_message "SUCCESS" "Wallarm image pulled and tagged successfully" + else + # Use local image + log_message "INFO" "Using local Wallarm image (registry not reachable)" + local local_image + local_image=$(ls wallarm-node-*.tar 2>/dev/null | head -1) + if [ -n "$local_image" ]; then + if ! sudo docker load -i "$local_image"; then + fail_with_remediation "Failed to load local Wallarm image" \ + "Local Wallarm image file may be corrupted: +1. Verify file integrity: tar -tzf wallarm-node-*.tar +2. Download a fresh image on another machine: + docker pull $WALLARM_IMAGE_SOURCE + docker save $WALLARM_IMAGE_TARGET -o wallarm-node-latest.tar +3. Copy the file to this machine and re-run" + fi + log_message "SUCCESS" "Local Wallarm image loaded successfully" + else + fail_with_remediation "No Wallarm image available" \ + "Need either: +1. Network access to $DOCKER_REGISTRY_HOST +2. Local wallarm-node-*.tar file in current directory" + fi + fi + + # Create nginx configuration + log_message "INFO" "Creating nginx configuration..." + local nginx_config="$INSTANCE_DIR/nginx.conf" + + sudo tee "$nginx_config" > /dev/null < /dev/null <> "\$LOG_FILE" + +# Stop existing container if running +sudo docker stop "\$CONTAINER_NAME" 2>/dev/null || true +sudo docker rm "\$CONTAINER_NAME" 2>/dev/null || true + +# Start new container +sudo docker run -d \\ + --name "\$CONTAINER_NAME" \\ + --restart always \\ + --network host \\ + -p $INGRESS_PORT:80 \\ + -p $MONITORING_PORT:90 \\ + -e WALLARM_API_TOKEN="$WALLARM_TOKEN" \\ + -e WALLARM_API_HOST="$API_HOST" \\ + -e NGINX_BACKEND="$UPSTREAM_IP:$UPSTREAM_PORT" \\ + -e WALLARM_MODE="monitoring" \\ + -v "\$NGINX_CONFIG:/etc/nginx/http.d/default.conf:ro" \\ + $WALLARM_IMAGE_TARGET + +echo "\$(date) - Container started with ID: \$(sudo docker ps -q -f name=\$CONTAINER_NAME)" >> "\$LOG_FILE" + +# Verify container is running +sleep 3 +if sudo docker ps | grep -q "\$CONTAINER_NAME"; then + echo "\$(date) - Verification: Container is running" >> "\$LOG_FILE" + echo "Wallarm node \$CONTAINER_NAME started successfully" +else + echo "\$(date) - ERROR: Container failed to start" >> "\$LOG_FILE" + sudo docker logs "\$CONTAINER_NAME" >> "\$LOG_FILE" 2>&1 + exit 1 +fi +EOF + + sudo chmod +x "$start_script" + log_message "SUCCESS" "Start script created: $start_script" + + # Create init system service for automatic startup + log_message "INFO" "Creating service for automatic startup (init system: $INIT_SYSTEM)..." + + case "$INIT_SYSTEM" in + "systemd") + local service_file="/etc/systemd/system/wallarm-$INSTANCE_NAME.service" + sudo tee "$service_file" > /dev/null </dev/null || \ + log_message "WARNING" "Failed to enable systemd service (may already exist)" + ;; + + "openrc") + local service_file="/etc/init.d/wallarm-$INSTANCE_NAME" + sudo tee "$service_file" > /dev/null </dev/null || true + docker rm $INSTANCE_NAME 2>/dev/null || true + eend \$? +} +EOF + sudo chmod +x "$service_file" + sudo rc-update add "wallarm-$INSTANCE_NAME" default 2>/dev/null || \ + log_message "WARNING" "Failed to add OpenRC service (may already exist)" + ;; + + "sysvinit") + local service_file="/etc/init.d/wallarm-$INSTANCE_NAME" + sudo tee "$service_file" > /dev/null </dev/null || true + docker rm $INSTANCE_NAME 2>/dev/null || true + log_end_msg \$? + ;; + restart) + \$0 stop + sleep 2 + \$0 start + ;; + status) + if docker ps | grep -q "$INSTANCE_NAME"; then + echo "$INSTANCE_NAME is running" + exit 0 + else + echo "$INSTANCE_NAME is not running" + exit 1 + fi + ;; + *) + echo "Usage: \$0 {start|stop|restart|status}" + exit 3 + ;; +esac + +exit 0 +EOF + sudo chmod +x "$service_file" + sudo update-rc.d "wallarm-$INSTANCE_NAME" defaults 2>/dev/null || \ + log_message "WARNING" "Failed to add SysV init service (may already exist)" + ;; + + *) + log_message "WARNING" "Unknown init system, not creating service (manual start via $start_script)" + ;; + esac + + # Start the Wallarm node + log_message "INFO" "Starting Wallarm filtering node..." + if ! sudo "$start_script"; then + fail_with_remediation "Failed to start Wallarm node" \ + "Container failed to start. Check: +1. Docker logs: sudo docker logs $INSTANCE_NAME +2. Port conflicts: sudo ss -tlnp | grep ':$INGRESS_PORT\|:$MONITORING_PORT' +3. Docker status: sudo docker info +4. Manual start attempt: sudo $start_script" + fi + + log_message "SUCCESS" "Wallarm filtering node deployed successfully" + log_message "SUCCESS" " Container: $INSTANCE_NAME" + log_message "SUCCESS" " Ingress Port: $INGRESS_PORT" + log_message "SUCCESS" " Monitoring Port: $MONITORING_PORT" + log_message "SUCCESS" " Upstream: $UPSTREAM_IP:$UPSTREAM_PORT" + log_message "SUCCESS" " Config Directory: $INSTANCE_DIR" +} + +# ============================================================================== +# DEPLOYMENT VERIFICATION +# ============================================================================== + +verify_deployment() { + log_message "INFO" "Verifying Wallarm deployment..." + + # Check if container is running + log_message "INFO" "Checking if container is running..." + if ! sudo docker ps | grep -q "$INSTANCE_NAME"; then + fail_with_remediation "Wallarm container is not running" \ + "Container failed to start or crashed: +1. Check container logs: sudo docker logs $INSTANCE_NAME +2. Check Docker service: sudo systemctl status docker (or equivalent) +3. Manual start: sudo $INSTANCE_DIR/start.sh" + fi + log_message "SUCCESS" "Container is running" + + # Test ingress port + log_message "INFO" "Testing ingress port $INGRESS_PORT..." + if ! check_port_available "$INGRESS_PORT"; then + log_message "SUCCESS" "Ingress port $INGRESS_PORT is in use (as expected)" + else + log_message "WARNING" "Ingress port $INGRESS_PORT appears available (container may not be listening)" + fi + + # Test monitoring port + log_message "INFO" "Testing monitoring port $MONITORING_PORT..." + if ! check_port_available "$MONITORING_PORT"; then + log_message "SUCCESS" "Monitoring port $MONITORING_PORT is in use (as expected)" + else + log_message "WARNING" "Monitoring port $MONITORING_PORT appears available" + fi + + # Test health check endpoint + log_message "INFO" "Testing health check endpoint..." + local health_check_url="http://localhost:$INGRESS_PORT/health" + if curl -sf --connect-timeout 5 "$health_check_url" >/dev/null 2>&1; then + log_message "SUCCESS" "Health check endpoint responsive" + else + log_message "WARNING" "Health check endpoint not responsive (may need time to start)" + sleep 5 + if curl -sf --connect-timeout 5 "$health_check_url" >/dev/null 2>&1; then + log_message "SUCCESS" "Health check endpoint now responsive" + else + log_message "WARNING" "Health check endpoint still not responsive (check nginx config)" + fi + fi + + # Test handshake through filtering node + log_message "INFO" "Testing handshake through filtering node to upstream..." + local test_url="http://localhost:$INGRESS_PORT/" + if curl -sfI --connect-timeout 10 "$test_url" >/dev/null 2>&1; then + log_message "SUCCESS" "Handshake successful: filtering node can reach upstream" + else + log_message "WARNING" "Handshake failed (upstream may not be responding)" + log_message "INFO" "Checking if upstream is directly reachable..." + if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$UPSTREAM_IP/$UPSTREAM_PORT" 2>/dev/null; then + log_message "ERROR" "Upstream is reachable but filtering node cannot proxy" + echo -e "${YELLOW}Possible nginx configuration issue. Check:${NC}" + echo -e "1. Container logs: sudo docker logs $INSTANCE_NAME" + echo -e "2. Nginx config: sudo docker exec $INSTANCE_NAME cat /etc/nginx/http.d/default.conf" + else + log_message "WARNING" "Upstream server is not reachable (as previously warned)" + fi + fi + + # Check Wallarm cloud synchronization + log_message "INFO" "Checking Wallarm cloud synchronization (this may take 30 seconds)..." + echo -e "${YELLOW}Note: Full synchronization with Wallarm cloud may take several minutes.${NC}" + echo -e "${YELLOW}You can check sync status in Wallarm Console.${NC}" + + # Quick test: check container logs for synchronization messages + if sudo docker logs "$INSTANCE_NAME" 2>&1 | tail -20 | grep -i "sync\|connected\|token" >/dev/null 2>&1; then + log_message "SUCCESS" "Wallarm node appears to be communicating with cloud" + else + log_message "WARNING" "No cloud synchronization messages in logs yet (may need time)" + fi + + log_message "SUCCESS" "Deployment verification completed" + echo -e "\n${GREEN}${BOLD}Verification Summary:${NC}" + echo -e " ${GREEN}✓${NC} Container running: $INSTANCE_NAME" + echo -e " ${GREEN}✓${NC} Ingress port: $INGRESS_PORT" + echo -e " ${GREEN}✓${NC} Monitoring port: $MONITORING_PORT" + echo -e " ${GREEN}✓${NC} Upstream: $UPSTREAM_IP:$UPSTREAM_PORT" + echo -e " ${GREEN}✓${NC} Cloud region: $CLOUD_REGION ($API_HOST)" +} + +# ============================================================================== +# MAIN FUNCTION +# ============================================================================== + +main() { + clear + echo -e "${BLUE}${BOLD}" + echo "╔══════════════════════════════════════════════════════════════╗" + echo "║ WALLARM DEPLOYMENT SCRIPT - V1.1 ║" + echo "║ LXC-Optimized Filtering Node Deployment ║" + echo "╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${YELLOW}Starting deployment at: $(date)${NC}" + + # Initialize logging + # Create logs directory if it doesn't exist + local log_dir="${HOME:-.}/logs" + if [ ! -d "$log_dir" ]; then + if ! mkdir -p "$log_dir"; then + echo -e "${YELLOW}Cannot create log directory $log_dir, falling back to current directory...${NC}" + log_dir="." + fi + fi + + LOG_FILE="$log_dir/wallarm-deployment.log" + if ! : > "$LOG_FILE"; then + echo -e "${RED}Cannot create log file at $LOG_FILE${NC}" + echo -e "${YELLOW}Falling back to current directory...${NC}" + LOG_FILE="./wallarm-deployment.log" + : > "$LOG_FILE" 2>/dev/null || true + fi + if ! chmod 644 "$LOG_FILE" 2>/dev/null; then + echo -e "${YELLOW}Warning: Could not set permissions on log file${NC}" + fi + + log_message "INFO" "=== Wallarm Deployment Started ===" + + # SSL security warning + if [ "$INSECURE_SSL" = "1" ]; then + log_message "WARNING" "SSL certificate validation is DISABLED (insecure). Set WALLARM_INSECURE_SSL=0 to enable validation." + fi + + # Phase 1: Verify preflight check + log_message "INFO" "=== PHASE 1: PREFLIGHT CHECK VERIFICATION ===" + verify_preflight_check + + # Phase 2: Configuration collection + log_message "INFO" "=== PHASE 2: CONFIGURATION COLLECTION ===" + select_cloud_region + collect_configuration + + # Phase 3: Docker engine setup (LXC optimized) + log_message "INFO" "=== PHASE 3: DOCKER ENGINE SETUP (LXC OPTIMIZED) ===" + setup_docker_engine + + # Phase 4: Deployment + log_message "INFO" "=== PHASE 4: DEPLOYMENT ===" + deploy_wallarm_node + + # Phase 5: Verification + log_message "INFO" "=== PHASE 5: VERIFICATION ===" + verify_deployment + + # Success message + log_message "SUCCESS" "=== WALLARM DEPLOYMENT COMPLETED SUCCESSFULLY ===" + echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" + echo -e "${GREEN}${BOLD}║ WALLARM FILTERING NODE DEPLOYMENT SUCCESSFUL ║${NC}" + echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" + echo -e "\n${CYAN}The Wallarm filtering node is now active and protecting your application.${NC}" + echo -e "${YELLOW}Full deployment log: $LOG_FILE${NC}" + echo -e "${YELLOW}Instance directory: $INSTANCE_DIR${NC}" + echo -e "\n${GREEN}To stop the node:${NC} sudo docker stop $INSTANCE_NAME" + echo -e "${GREEN}To restart:${NC} sudo $INSTANCE_DIR/start.sh" + echo -e "${GREEN}To view logs:${NC} sudo docker logs -f $INSTANCE_NAME" + echo -e "\n${MAGENTA}${BOLD}Deployment completed successfully!${NC}" + echo -e "\n${YELLOW}Important next steps:${NC}" + echo -e "1. Monitor sync status in Wallarm Console" + echo -e "2. Test attack detection with safe test: curl http://localhost:$INGRESS_PORT/?wallarm_test=1" + echo -e "3. Review logs periodically: sudo docker logs --tail 50 $INSTANCE_NAME" +} + +# ============================================================================== +# SCRIPT EXECUTION +# ============================================================================== + +# Ensure we're in bash +if [ -z "$BASH_VERSION" ]; then + echo "Error: This script must be run with bash" >&2 + exit 1 +fi + +# Run main function +main "$@" \ No newline at end of file diff --git a/wallarm-deploy-ct copy.sh b/wallarm-deploy-ct copy.sh deleted file mode 100644 index 5f8f453..0000000 --- a/wallarm-deploy-ct copy.sh +++ /dev/null @@ -1,1104 +0,0 @@ -#!/bin/bash -# ============================================================================== -# WALLARM BULLETPROOF DEPLOYMENT SCRIPT - V1.0 -# ============================================================================== -# Features: -# - OS-agnostic deployment (Ubuntu, Debian, CentOS, RHEL, Alpine, etc.) -# - Docker/containerd installation from static binaries (no package managers) -# - Comprehensive pre-flight checks (sudo, OS, architecture, resources) -# - Resource verification (Wallarm cloud, Docker registry, application server) -# - DAU-friendly error handling with remediation instructions -# - Multiple-run detection and handling -# - Deployment logging with overwrite protection -# - Handshake verification with application server -# - Persistence across reboots (start.sh) -# ============================================================================== - -# Color definitions for better UX -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -CYAN='\033[0;36m' -MAGENTA='\033[0;35m' -BOLD='\033[1m' -NC='\033[0m' # No Color - -# Logging configuration -LOG_FILE="/var/log/wallarm-deployment.log" -DEPLOYMENT_TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S') - -# Cloud endpoints (from Wallarm documentation) -EU_DATA_NODES=("api.wallarm.com" "node-data0.eu1.wallarm.com" "node-data1.eu1.wallarm.com") -US_DATA_NODES=("us1.api.wallarm.com" "node-data0.us1.wallarm.com" "node-data1.us1.wallarm.com") - -# Docker configuration -DOCKER_VERSION="27.0.0" # Current stable version -DOCKER_STATIC_BASE_URL="https://download.docker.com/linux/static/stable" - -# Resource reachability flags (set during verification) -REGISTRY_REACHABLE=false -DOWNLOAD_REACHABLE=false -CLOUD_REGION="" -API_HOST="" - -# ============================================================================== -# LOGGING & ERROR HANDLING FUNCTIONS -# ============================================================================== - -log_message() { - local level="$1" - local message="$2" - local timestamp=$(date '+%Y-%m-%d %H:%M:%S') - - case "$level" in - "INFO") color="${BLUE}" ;; - "SUCCESS") color="${GREEN}" ;; - "WARNING") color="${YELLOW}" ;; - "ERROR") color="${RED}" ;; - "DEBUG") color="${CYAN}" ;; - *) color="${NC}" ;; - esac - - echo -e "${color}[${timestamp}] ${level}: ${message}${NC}" - echo "[${timestamp}] ${level}: ${message}" >> "$LOG_FILE" -} - -fail_with_remediation() { - local error_msg="$1" - local remediation="$2" - - log_message "ERROR" "$error_msg" - echo -e "\n${RED}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${RED}${BOLD}║ DEPLOYMENT FAILED ║${NC}" - echo -e "${RED}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${YELLOW}${BOLD}Root Cause:${NC} $error_msg" - echo -e "\n${YELLOW}${BOLD}How to Fix:${NC}" - echo -e "$remediation" - echo -e "\n${YELLOW}Check the full log for details:${NC} $LOG_FILE" - exit 1 -} - -validate_sudo_access() { - log_message "INFO" "Validating sudo access..." - - # Check if user can run sudo - if ! command -v sudo >/dev/null 2>&1; then - fail_with_remediation "sudo command not found" \ - "1. Install sudo package for your OS: - - Ubuntu/Debian: apt-get update && apt-get install -y sudo - - CentOS/RHEL: yum install -y sudo - - Alpine: apk add sudo - -2. Add your user to sudoers: - usermod -aG sudo \$(whoami) # Ubuntu/Debian - usermod -aG wheel \$(whoami) # CentOS/RHEL - -3. Log out and log back in for changes to take effect." - fi - - # Test sudo with password prompt if needed - if ! sudo -v; then - fail_with_remediation "sudo authentication failed" \ - "1. Ensure you have sudo privileges: - - Run 'sudo -l' to check your sudo permissions - - Contact your system administrator - -2. If password is required: - - Make sure you know the correct password - - The script will prompt for password when needed - -3. For passwordless sudo: - - Add '\$(whoami) ALL=(ALL) NOPASSWD:ALL' to /etc/sudoers - - Use 'visudo' to edit safely - - WARNING: Only do this in secure environments" - fi - - log_message "SUCCESS" "Sudo access validated" -} - -detect_os_and_version() { - log_message "INFO" "Detecting OS and version..." - - local os_name="" - local os_version="" - - # Check for /etc/os-release first (modern systems) - if [ -f /etc/os-release ]; then - . /etc/os-release - os_name="$ID" - os_version="$VERSION_ID" - # Check for older RedHat/CentOS - elif [ -f /etc/redhat-release ]; then - os_name="rhel" - os_version=$(cat /etc/redhat-release | sed -e 's/.*release \([0-9]\+\)\..*/\1/') - # Check for Alpine - elif [ -f /etc/alpine-release ]; then - os_name="alpine" - os_version=$(cat /etc/alpine-release) - else - os_name=$(uname -s | tr '[:upper:]' '[:lower:]') - os_version=$(uname -r) - fi - - # Normalize OS names - case "$os_name" in - "ubuntu"|"debian"|"centos"|"rhel"|"alpine"|"amzn"|"ol"|"rocky"|"almalinux") - # Valid supported OS - ;; - *) - log_message "WARNING" "OS '$os_name' not explicitly tested but may work" - ;; - esac - - echo "$os_name:$os_version" -} - - -detect_architecture() { - log_message "INFO" "Detecting system architecture..." - - local arch=$(uname -m) - local docker_arch="" - - case "$arch" in - x86_64|x64|amd64) - docker_arch="x86_64" - log_message "SUCCESS" "Architecture: x86_64 (Intel/AMD 64-bit)" - ;; - aarch64|arm64) - docker_arch="aarch64" - log_message "SUCCESS" "Architecture: aarch64 (ARM 64-bit)" - ;; - armv7l|armhf) - docker_arch="armhf" - log_message "SUCCESS" "Architecture: armhf (ARM 32-bit)" - ;; - *) - fail_with_remediation "Unsupported architecture: $arch" \ - "This script supports: -1. x86_64 (Intel/AMD 64-bit) - Most common -2. aarch64 (ARM 64-bit) - AWS Graviton, Raspberry Pi 4 -3. armhf (ARM 32-bit) - Older ARM devices - -Your architecture '$arch' is not supported for Docker static binaries. -Consider: -- Using a different machine with supported architecture -- Manual Docker installation from package manager -- Contact Wallarm support for alternative deployment options" - ;; - esac - - echo "$docker_arch" -} - -verify_resources() { - log_message "INFO" "=== PHASE 2: RESOURCE VERIFICATION ===" - - # Get cloud selection from user - log_message "INFO" "Select Wallarm Cloud region" - echo -e "\n${CYAN}${BOLD}Wallarm Cloud Region Selection:${NC}" - echo -e "1. ${YELLOW}US Cloud${NC} (us1.api.wallarm.com) - For US-based deployments" - echo -e "2. ${YELLOW}EU Cloud${NC} (api.wallarm.com) - For EU-based deployments" - - local cloud_choice="" - while [[ ! "$cloud_choice" =~ ^(1|2|US|EU)$ ]]; do - read -p "$(echo -e "${YELLOW}Enter choice [1/US or 2/EU]: ${NC}")" cloud_choice - cloud_choice=$(echo "$cloud_choice" | tr '[:lower:]' '[:upper:]') - - case "$cloud_choice" in - 1|"US") - CLOUD_REGION="US" - API_HOST="us1.api.wallarm.com" - NODES_TO_TEST=("${US_DATA_NODES[@]}") - log_message "INFO" "Selected US Cloud" - ;; - 2|"EU") - CLOUD_REGION="EU" - API_HOST="api.wallarm.com" - NODES_TO_TEST=("${EU_DATA_NODES[@]}") - log_message "INFO" "Selected EU Cloud" - ;; - *) - echo -e "${RED}Invalid choice. Enter 1/US for US Cloud or 2/EU for EU Cloud${NC}" - ;; - esac - done - - # Test Wallarm Cloud endpoints - log_message "INFO" "Testing connectivity to Wallarm Cloud endpoints..." - local all_endpoints_reachable=true - - for endpoint in "${NODES_TO_TEST[@]}"; do - if curl -skI --connect-timeout 10 "https://$endpoint" >/dev/null 2>&1; then - log_message "SUCCESS" "Wallarm endpoint $endpoint is reachable" - else - log_message "ERROR" "Wallarm endpoint $endpoint is NOT reachable" - all_endpoints_reachable=false - fi - done - - if [ "$all_endpoints_reachable" = false ]; then - fail_with_remediation "Some Wallarm Cloud endpoints are not reachable" \ - "1. Check firewall rules: - - Allow outbound HTTPS (port 443) to Wallarm IPs - - US Cloud IPs: 34.96.64.17, 34.110.183.149, 35.235.66.155, 34.102.90.100, 34.94.156.115, 35.235.115.105 - - EU Cloud IPs: 34.160.38.183, 34.144.227.90, 34.90.110.226 -2. Check network connectivity: - - Run: ping 8.8.8.8 (test general internet) - - Run: nslookup $API_HOST (test DNS) - - Run: curl -v https://$API_HOST (verbose test) - -3. Check proxy settings: - - If behind proxy, set http_proxy/https_proxy environment variables - - Or configure Docker to use proxy - -4. Contact your network administrator if issues persist" - fi - - # Test Docker Registry (hub.docker.com) - log_message "INFO" "Testing connectivity to Docker Registry (hub.docker.com)..." - local registry_status=$(curl -skI --connect-timeout 10 -o /dev/null -w "%{http_code}" "https://hub.docker.com/v2/") - if [[ "$registry_status" == "200" || "$registry_status" == "401" || "$registry_status" == "302" ]]; then - log_message "SUCCESS" "Docker Registry is reachable (Status: $registry_status)" - REGISTRY_REACHABLE=true - else - log_message "WARNING" "Docker Registry may be blocked (Status: $registry_status)" - REGISTRY_REACHABLE=false - fi - - # Test Docker Download Server (download.docker.com) - log_message "INFO" "Testing connectivity to Docker Download Server..." - if curl -skI --connect-timeout 10 "https://download.docker.com" >/dev/null 2>&1; then - log_message "SUCCESS" "Docker Download Server is reachable" - DOWNLOAD_REACHABLE=true - else - log_message "WARNING" "Docker Download Server may be blocked" - DOWNLOAD_REACHABLE=false - fi - - # Check for local Docker binaries as fallback - if [ "$DOWNLOAD_REACHABLE" = false ]; then - log_message "INFO" "Checking for local Docker binary fallback..." - if ls docker-*.tgz 2>/dev/null | grep -q .; then - log_message "SUCCESS" "Found local Docker binary: $(ls docker-*.tgz | head -1)" - else - log_message "WARNING" "No local Docker binaries found. Manual download may be required." - fi - fi - # Check for local Wallarm image as fallback - if [ "$REGISTRY_REACHABLE" = false ]; then - log_message "INFO" "Checking for local Wallarm image fallback..." - if ls wallarm-node-*.tar 2>/dev/null | grep -q .; then - log_message "SUCCESS" "Found local Wallarm image: $(ls wallarm-node-*.tar | head -1)" - else - log_message "WARNING" "No local Wallarm images found. Manual download may be required." - fi - fi - - # Final resource assessment - if [ "$REGISTRY_REACHABLE" = false ] && [ "$DOWNLOAD_REACHABLE" = false ]; then - log_message "ERROR" "Critical: Neither Docker Registry nor Download Server reachable" - echo -e "\n${YELLOW}${BOLD}Possible workarounds:${NC}" - echo -e "1. Download Docker binary manually:" - echo -e " curl -L '$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz' -o docker.tgz" - echo -e "2. Download Wallarm image manually:" - echo -e " docker pull wallarm/node:latest" - echo -e " docker save wallarm/node:latest -o wallarm-node-latest.tar" - echo -e "3. Place files in current directory and re-run script" - echo -e "\n${RED}Without these resources, deployment cannot proceed.${NC}" - - read -p "$(echo -e "${YELLOW}Do you want to continue anyway? (y/N): ${NC}")" -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - fail_with_remediation "Insufficient resources for deployment" \ - "Please ensure at least one of these is available: -1. Internet access to Docker Registry (hub.docker.com) -2. Internet access to Docker Download Server (download.docker.com) -3. Local Docker binary (docker-*.tgz) in current directory -4. Local Wallarm image (wallarm-node-*.tar) in current directory" - fi - fi - - log_message "SUCCESS" "Resource verification completed" - echo "$CLOUD_REGION:$API_HOST:$REGISTRY_REACHABLE:$DOWNLOAD_REACHABLE" -} -setup_docker_engine() { - local architecture="$1" - log_message "INFO" "Setting up Docker Engine..." - - # Check if Docker is already installed and running - if command -v docker >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then - local docker_version=$(docker --version | cut -d' ' -f3 | tr -d ',') - log_message "SUCCESS" "Docker is already installed and running (version $docker_version)" - return 0 - fi - - log_message "INFO" "Docker not found or not running. Proceeding with installation..." - - # Determine binary source - local binary_file="docker-$DOCKER_VERSION.tgz" - local binary_path="" - - if [ "$DOWNLOAD_REACHABLE" = true ]; then - # Download Docker static binary - log_message "INFO" "Downloading Docker static binary for $architecture..." - local download_url="$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz" - - if curl -fL --connect-timeout 30 "$download_url" -o "$binary_file"; then - log_message "SUCCESS" "Downloaded Docker binary: $binary_file" - binary_path="$binary_file" - else - log_message "ERROR" "Failed to download Docker binary from $download_url" - binary_path="" - fi - fi - - # Fallback: Check for local Docker binary - if [ -z "$binary_path" ]; then - log_message "INFO" "Checking for local Docker binary..." - local local_files=$(ls docker-*.tgz 2>/dev/null | head -1) - if [ -n "$local_files" ]; then - binary_path="$local_files" - log_message "SUCCESS" "Using local Docker binary: $binary_path" - else - fail_with_remediation "No Docker binary available" \ - "Please provide a Docker static binary: -1. Download manually: - curl -L '$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz' -o docker.tgz -2. Or place an existing docker-*.tgz file in current directory -3. Re-run the script after downloading" - fi - fi - - # Extract and install - log_message "INFO" "Extracting Docker binary..." - if ! tar xzvf "$binary_path" >/dev/null 2>&1; then - fail_with_remediation "Failed to extract Docker binary" \ - "The Docker binary file may be corrupted: -1. Delete the corrupted file: rm -f docker-*.tgz -2. Download a fresh copy manually -3. Verify the file integrity: tar -tzf docker-*.tgz should list files" - fi - - log_message "INFO" "Installing Docker binaries to /usr/bin/" - sudo cp docker/* /usr/bin/ 2>/dev/null || { - fail_with_remediation "Failed to copy Docker binaries" \ - "Permission denied copying to /usr/bin/ -1. Ensure you have sudo privileges -2. Check disk space: df -h / -3. Manual installation: - sudo cp docker/* /usr/bin/" - } - - # Cleanup extracted directory - rm -rf docker - - # Configure systemd service - log_message "INFO" "Configuring Docker systemd service..." - - # Check if docker group exists, create if not - if ! getent group docker >/dev/null; then - sudo groupadd docker 2>/dev/null || log_message "WARNING" "Failed to create docker group (may already exist)" - fi - - # Create systemd service file - sudo tee /etc/systemd/system/docker.service > /dev/null < /dev/null </dev/null 2>&1; then - log_message "SUCCESS" "Docker daemon started successfully (attempt $attempt/$max_attempts)" - break - fi - log_message "DEBUG" "Waiting for Docker daemon... ($attempt/$max_attempts)" - sleep 2 - attempt=$((attempt + 1)) - done - - if [ $attempt -gt $max_attempts ]; then - fail_with_remediation "Docker daemon failed to start" \ - "Troubleshooting steps: -1. Check Docker logs: sudo journalctl -u docker.service -2. Verify systemd configuration: sudo systemctl status docker -3. Check for port conflicts: sudo netstat -tulpn | grep :2375 -4. Manual start attempt: sudo dockerd --debug -5. Check kernel requirements: uname -r (should be 3.10+) -6. Ensure cgroups are mounted: mount | grep cgroup" - fi - - # Test Docker installation - log_message "INFO" "Testing Docker installation with hello-world..." - if sudo docker run --rm hello-world >/dev/null 2>&1; then - log_message "SUCCESS" "Docker installation verified successfully" - else - log_message "WARNING" "Docker hello-world test failed (network may be restricted)" - fi - - # Add current user to docker group (optional) - log_message "INFO" "Adding current user to docker group..." - if ! groups $(whoami) | grep -q docker; then - sudo usermod -aG docker $(whoami) 2>/dev/null && \ - log_message "SUCCESS" "Added $(whoami) to docker group. Log out and back in for changes to take effect." - fi - - log_message "SUCCESS" "Docker Engine setup completed" -} - -collect_configuration() { - log_message "INFO" "Collecting deployment configuration..." - - # Get ingress port - local default_port=80 - local ingress_port="" - while [[ ! "$ingress_port" =~ ^[0-9]+$ ]] || [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; do - read -p "$(echo -e "${YELLOW}Enter inbound port [${default_port}]: ${NC}")" ingress_port - ingress_port="${ingress_port:-$default_port}" - - if [[ ! "$ingress_port" =~ ^[0-9]+$ ]]; then - echo -e "${RED}Port must be a number${NC}" - elif [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; then - echo -e "${RED}Port must be between 1 and 65535${NC}" - fi - done - - # Calculate monitoring port (ingress + 10) - local monitoring_port=$((ingress_port + 10)) - log_message "INFO" "Monitoring port will be: $monitoring_port" - - # Check for port conflicts - log_message "INFO" "Checking for port conflicts..." - if sudo netstat -tulpn 2>/dev/null | grep -E ":$ingress_port\s" >/dev/null 2>&1; then - fail_with_remediation "Port $ingress_port is already in use" \ - "1. Free up port $ingress_port: - - Stop the service using it: sudo lsof -ti:$ingress_port | xargs kill -9 - - Change your application to use a different port - -2. Choose a different inbound port - - Common alternatives: 8080, 8888, 3000 - - Avoid well-known ports (80, 443) if already used - -3. Check what's using the port: - sudo netstat -tulpn | grep :$ingress_port - sudo lsof -i :$ingress_port" - fi - - if sudo netstat -tulpn 2>/dev/null | grep -E ":$monitoring_port\s" >/dev/null 2>&1; then - log_message "WARNING" "Port $monitoring_port is in use, choosing alternative..." - monitoring_port=$((ingress_port + 100)) - log_message "INFO" "New monitoring port: $monitoring_port" - fi - - # Get application server details - local upstream_ip="" - local upstream_port="" - - echo -e "\n${CYAN}${BOLD}Application Server Configuration:${NC}" - echo -e "${YELLOW}Enter the IP/hostname and port of your backend application${NC}" - - while [[ -z "$upstream_ip" ]]; do - read -p "$(echo -e "${YELLOW}Upstream App IP/Hostname [127.0.0.1]: ${NC}")" upstream_ip - upstream_ip="${upstream_ip:-127.0.0.1}" - - # Validate IP/hostname format - if ! [[ "$upstream_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ - ! [[ "$upstream_ip" =~ ^[a-zA-Z0-9.-]+$ ]]; then - echo -e "${RED}Invalid IP/hostname format${NC}" - upstream_ip="" - fi - done - - while [[ ! "$upstream_port" =~ ^[0-9]+$ ]] || [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; do - read -p "$(echo -e "${YELLOW}Upstream App Port [8080]: ${NC}")" upstream_port - upstream_port="${upstream_port:-8080}" - - if [[ ! "$upstream_port" =~ ^[0-9]+$ ]]; then - echo -e "${RED}Port must be a number${NC}" - elif [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; then - echo -e "${RED}Port must be between 1 and 65535${NC}" - fi - done -# Verify application server reachability - log_message "INFO" "Verifying application server reachability..." - if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$upstream_ip/$upstream_port" 2>/dev/null; then - log_message "SUCCESS" "Application server $upstream_ip:$upstream_port is reachable" - else - log_message "WARNING" "Application server $upstream_ip:$upstream_port is not reachable" - echo -e "${YELLOW}${BOLD}Warning:${NC} Cannot reach application server at $upstream_ip:$upstream_port" - echo -e "${YELLOW}This may cause the Wallarm node to fail. Possible reasons:${NC}" - echo -e "1. Application server is not running" - echo -e "2. Firewall blocking port $upstream_port" - echo -e "3. Wrong IP/hostname" - echo -e "4. Application server not listening on that port" - - read -p "$(echo -e "${YELLOW}Continue anyway? (y/N): ${NC}")" -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - fail_with_remediation "Application server unreachable" \ - "Ensure your application server is accessible: -1. Start your application server -2. Check it's listening: sudo netstat -tulpn | grep :$upstream_port -3. Verify firewall rules allow inbound connections -4. Test connectivity: telnet $upstream_ip $upstream_port -5. If using hostname, verify DNS resolution: nslookup $upstream_ip" - fi - fi - - # Get Wallarm node token - local wallarm_token="" - echo -e "\n${CYAN}${BOLD}Wallarm Node Token:${NC}" - echo -e "${YELLOW}Get your token from Wallarm Console:${NC}" - echo -e "US Cloud: https://us1.my.wallarm.com/nodes" - echo -e "EU Cloud: https://my.wallarm.com/nodes" - echo -e "Create a new 'Wallarm node' and copy the token" - - while [[ -z "$wallarm_token" ]]; do - read -p "$(echo -e "${YELLOW}Paste Wallarm Node Token: ${NC}")" wallarm_token - if [[ -z "$wallarm_token" ]]; then - echo -e "${RED}Token cannot be empty${NC}" - elif [[ ! "$wallarm_token" =~ ^[a-zA-Z0-9_-]+$ ]]; then - echo -e "${RED}Token contains invalid characters${NC}" - wallarm_token="" - fi - done - - # Generate instance names and paths - local instance_name="wallarm-node-$ingress_port" - local instance_dir="/opt/wallarm/$instance_name" - # Check for existing container with same name - if sudo docker ps -a --format "{{.Names}}" | grep -q "^$instance_name$"; then - log_message "WARNING" "Container '$instance_name' already exists" - echo -e "${YELLOW}A container named '$instance_name' already exists.${NC}" - echo -e "Options:" - echo -e "1. Remove existing container and deploy new" - echo -e "2. Use different port to avoid conflict" - echo -e "3. Exit and manage manually" - - read -p "$(echo -e "${YELLOW}Choose option (1/2/3) [1]: ${NC}")" option - option="${option:-1}" - - case "$option" in - 1) - log_message "INFO" "Removing existing container '$instance_name'..." - sudo docker rm -f "$instance_name" 2>/dev/null || \ - log_message "WARNING" "Failed to remove container (may not exist)" - ;; - 2) - log_message "INFO" "Please restart script with different port" - exit 0 - ;; - 3) - log_message "INFO" "Exiting for manual management" - exit 0 - ;; - *) - log_message "ERROR" "Invalid option" - ;; - esac - fi - - # Create instance directory - log_message "INFO" "Creating instance directory: $instance_dir" - sudo mkdir -p "$instance_dir" - - # Output configuration summary - echo -e "\n${GREEN}${BOLD}✓ Configuration Complete${NC}" - echo -e "${CYAN}Summary:${NC}" - echo -e " Inbound Port: ${YELLOW}$ingress_port${NC}" - echo -e " Monitoring Port: ${YELLOW}$monitoring_port${NC}" - echo -e " Application Server: ${YELLOW}$upstream_ip:$upstream_port${NC}" - echo -e " Wallarm Cloud: ${YELLOW}$CLOUD_REGION ($API_HOST)${NC}" - echo -e " Instance Directory: ${YELLOW}$instance_dir${NC}" - - # Return configuration as colon-separated values - echo "$ingress_port:$monitoring_port:$upstream_ip:$upstream_port:$wallarm_token:$instance_name:$instance_dir" -} -deploy_wallarm_node() { - log_message "INFO" "Deploying Wallarm filtering node..." - - # Pull or load Wallarm image - if [ "$REGISTRY_REACHABLE" = true ]; then - log_message "INFO" "Pulling Wallarm Docker image..." - if ! sudo docker pull wallarm/node:latest; then - fail_with_remediation "Failed to pull Wallarm image" \ - "Docker pull failed. Possible reasons: -1. Network connectivity to Docker Hub -2. Docker Hub rate limiting (login required) -3. Insufficient disk space - -Solutions: -1. Check network: docker pull hello-world -2. Login to Docker Hub: docker login -3. Use local image: docker save/load -4. Check disk: df -h /var/lib/docker" - fi - log_message "SUCCESS" "Wallarm image pulled successfully" - else - log_message "INFO" "Registry unreachable, checking for local image..." - local image_files=$(ls wallarm-node-*.tar 2>/dev/null | head -1) - if [ -n "$image_files" ]; then - log_message "INFO" "Loading local image: $image_files" - if ! sudo docker load < "$image_files"; then - fail_with_remediation "Failed to load local Wallarm image" \ - "The local image file may be corrupted: -1. Verify file integrity: tar -tzf $image_files -2. Download fresh image on another machine: - docker pull wallarm/node:latest - docker save wallarm/node:latest -o wallarm-node-latest.tar -3. Transfer file to this machine" - fi - log_message "SUCCESS" "Local Wallarm image loaded" - else - fail_with_remediation "No Wallarm image available" \ - "Please provide a Wallarm Docker image: -1. Download on another machine with internet: - docker pull wallarm/node:latest - docker save wallarm/node:latest -o wallarm-node-latest.tar -2. Transfer file to this machine -3. Place in current directory and re-run script" - fi - fi - - # Create nginx configuration - log_message "INFO" "Creating nginx configuration..." - sudo tee "$INSTANCE_DIR/nginx.conf" > /dev/null < /dev/null <> "\$LOG_FILE" - -# Stop and remove existing container if running -sudo docker stop "\$CONTAINER_NAME" 2>/dev/null || true -sudo docker rm "\$CONTAINER_NAME" 2>/dev/null || true - -# Start new container -sudo docker run -d \\ - --name "\$CONTAINER_NAME" \\ - --restart always \\ - --network host \\ - -p $INGRESS_PORT:80 \\ - -p $MONITORING_PORT:90 \\ - -e WALLARM_API_TOKEN="$WALLARM_TOKEN" \\ - -e WALLARM_API_HOST="$API_HOST" \\ - -e NGINX_BACKEND="$UPSTREAM_IP:$UPSTREAM_PORT" \\ - -e WALLARM_MODE="monitoring" \\ - -v "\$NGINX_CONFIG:/etc/nginx/http.d/default.conf:ro" \\ - wallarm/node:latest - -echo "\$(date) - Container started with ID: \$(sudo docker ps -q -f name=\$CONTAINER_NAME)" >> "\$LOG_FILE" - -# Verify container is running -sleep 3 -if sudo docker ps | grep -q "\$CONTAINER_NAME"; then - echo "\$(date) - Verification: Container is running" >> "\$LOG_FILE" - echo "Wallarm node \$CONTAINER_NAME started successfully" -else - echo "\$(date) - ERROR: Container failed to start" >> "\$LOG_FILE" - sudo docker logs "\$CONTAINER_NAME" >> "\$LOG_FILE" 2>&1 - exit 1 -fi -EOF - - sudo chmod +x "$INSTANCE_DIR/start.sh" - - # Deploy container using start.sh - log_message "INFO" "Deploying Wallarm container..." - if ! sudo "$INSTANCE_DIR/start.sh"; then - log_message "ERROR" "Container deployment failed, checking logs..." - sudo docker logs "$INSTANCE_NAME" 2>&1 | tail -20 | while read line; do - log_message "DEBUG" "Container log: $line" - done - - fail_with_remediation "Wallarm container deployment failed" \ - "Common issues: -1. Invalid Wallarm token - - Verify token in Wallarm Console - - Ensure token matches selected cloud region (US/EU) - -2. Port conflicts - - Check ports $INGRESS_PORT and $MONITORING_PORT are free - - sudo netstat -tulpn | grep -E ':$INGRESS_PORT|:$MONITORING_PORT' - -3. Docker permissions - - Ensure Docker daemon is running: sudo systemctl status docker - - Check user in docker group: groups - -4. Resource constraints - - Check memory: free -h - - Check disk: df -h - -5. View full logs: sudo docker logs $INSTANCE_NAME" - fi - - log_message "SUCCESS" "Wallarm node deployed successfully" - log_message "INFO" "Container Name: $INSTANCE_NAME" - log_message "INFO" "Instance Directory: $INSTANCE_DIR" - log_message "INFO" "Start Script: $INSTANCE_DIR/start.sh" - - # Create systemd service for auto-start on boot - log_message "INFO" "Creating systemd service for automatic startup..." - sudo tee "/etc/systemd/system/wallarm-$INSTANCE_NAME.service" > /dev/null </dev/null || \ - log_message "WARNING" "Failed to enable systemd service (may already exist)" - - log_message "SUCCESS" "Systemd service created: wallarm-$INSTANCE_NAME.service" -} -verify_deployment() { - log_message "INFO" "Verifying deployment..." - - # Wait for container to be fully started - log_message "INFO" "Waiting for Wallarm node to initialize (30 seconds)..." - sleep 30 - - # Check container status - log_message "INFO" "Checking container status..." - if ! sudo docker ps --format "table {{.Names}}\t{{.Status}}" | grep -q "$INSTANCE_NAME"; then - fail_with_remediation "Container is not running" \ - "The Wallarm container failed to start: -1. Check container logs: sudo docker logs $INSTANCE_NAME -2. Verify Docker service: sudo systemctl status docker -3. Check resource constraints: sudo docker stats $INSTANCE_NAME -4. Review start script: $INSTANCE_DIR/start.sh" - fi - - local container_status=$(sudo docker ps --format "table {{.Names}}\t{{.Status}}" | grep "$INSTANCE_NAME") - log_message "SUCCESS" "Container status: $container_status" - - # Test monitoring endpoint - log_message "INFO" "Testing monitoring endpoint (port $MONITORING_PORT)..." - local max_attempts=10 - local attempt=1 - local monitoring_ok=false - - while [ $attempt -le $max_attempts ]; do - if curl -s "http://localhost:$MONITORING_PORT/wallarm-status" 2>/dev/null | grep -q "requests"; then - monitoring_ok=true - log_message "SUCCESS" "Monitoring endpoint responding (attempt $attempt/$max_attempts)" - break - fi - log_message "DEBUG" "Monitoring endpoint not ready yet ($attempt/$max_attempts)" - sleep 5 - attempt=$((attempt + 1)) - done - - if [ "$monitoring_ok" = false ]; then - log_message "WARNING" "Monitoring endpoint not responding as expected" - log_message "INFO" "Checking alternative..." - # Try direct container exec - if sudo docker exec "$INSTANCE_NAME" curl -s http://localhost:90/wallarm-status 2>/dev/null | grep -q "requests"; then - log_message "SUCCESS" "Monitoring endpoint works inside container" - else - log_message "ERROR" "Monitoring endpoint completely unavailable" - fi - fi - # Test handshake through filtering node to application server - log_message "INFO" "Testing handshake through filtering node to application server..." - local handshake_ok=false - - for i in {1..5}; do - local response_code=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: test.local" "http://localhost:$INGRESS_PORT/health" --connect-timeout 10) - if [[ "$response_code" =~ ^(200|301|302|401|403)$ ]]; then - handshake_ok=true - log_message "SUCCESS" "Handshake successful: HTTP $response_code from application server (attempt $i/5)" - break - else - log_message "DEBUG" "Handshake attempt $i failed: HTTP $response_code" - sleep 3 - fi - done - - if [ "$handshake_ok" = false ]; then - log_message "WARNING" "Handshake to application server failed" - echo -e "${YELLOW}${BOLD}Warning:${NC} Cannot reach application server through filtering node" - echo -e "${YELLOW}Possible issues:${NC}" - echo -e "1. Application server not responding on $UPSTREAM_IP:$UPSTREAM_PORT" - echo -e "2. Network routing/firewall between container and application server" - echo -e "3. NGINX configuration issue" - echo -e "4. Application server requires specific Host header" - - # Test direct connection to application server - log_message "INFO" "Testing direct connection to application server..." - if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$UPSTREAM_IP/$UPSTREAM_PORT" 2>/dev/null; then - log_message "SUCCESS" "Application server is directly reachable" - else - log_message "ERROR" "Application server is not reachable even directly" - fi - else - log_message "SUCCESS" "Handshake verification passed" - fi - - # Perform attack simulation test (safe) - log_message "INFO" "Performing attack simulation test (safe SQL injection probe)..." - local attack_test_url="http://localhost:$INGRESS_PORT/?id=%27OR+1%3D1--" - local attack_response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: test.local" "$attack_test_url" --connect-timeout 10) - - if [[ "$attack_response" =~ ^(200|301|302|401|403|404)$ ]]; then - log_message "SUCCESS" "Attack simulation test passed: HTTP $attack_response" - log_message "INFO" "Note: Wallarm in monitoring mode logs attacks but doesn't block" - else - log_message "WARNING" "Attack simulation test returned unexpected code: $attack_response" - fi -# Check Wallarm synchronization status - log_message "INFO" "Checking Wallarm cloud synchronization..." - local sync_attempts=0 - local sync_ok=false - - while [ $sync_attempts -lt 5 ]; do - if sudo docker logs "$INSTANCE_NAME" 2>&1 | tail -20 | grep -i "sync\|connected\|success" | grep -v "error\|fail" >/dev/null; then - sync_ok=true - log_message "SUCCESS" "Wallarm cloud synchronization detected" - break - fi - sync_attempts=$((sync_attempts + 1)) - sleep 10 - done - - if [ "$sync_ok" = false ]; then - log_message "WARNING" "Wallarm cloud synchronization not confirmed in logs" - log_message "INFO" "Check synchronization manually in Wallarm Console" - fi - - # Final deployment summary - echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${GREEN}${BOLD}║ DEPLOYMENT VERIFICATION COMPLETE ║${NC}" - echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${CYAN}${BOLD}Deployment Summary:${NC}" - echo -e " ${YELLOW}✓${NC} Container: ${GREEN}$INSTANCE_NAME${NC} (running)" - echo -e " ${YELLOW}✓${NC} Inbound Port: ${GREEN}$INGRESS_PORT${NC}" - echo -e " ${YELLOW}✓${NC} Monitoring Port: ${GREEN}$MONITORING_PORT${NC} (http://localhost:$MONITORING_PORT/wallarm-status)" - echo -e " ${YELLOW}✓${NC} Application Server: ${GREEN}$UPSTREAM_IP:$UPSTREAM_PORT${NC}" - echo -e " ${YELLOW}✓${NC} Wallarm Cloud: ${GREEN}$CLOUD_REGION${NC}" - echo -e " ${YELLOW}✓${NC} Instance Directory: ${GREEN}$INSTANCE_DIR${NC}" - echo -e " ${YELLOW}✓${NC} Start Script: ${GREEN}$INSTANCE_DIR/start.sh${NC}" - echo -e " ${YELLOW}✓${NC} Systemd Service: ${GREEN}wallarm-$INSTANCE_NAME.service${NC}" - echo -e "\n${CYAN}${BOLD}Next Steps:${NC}" - echo -e " 1. Monitor attacks in ${YELLOW}Wallarm Console${NC}" - echo -e " 2. Switch to ${YELLOW}blocking mode${NC} when ready:" - echo -e " Edit ${GREEN}$INSTANCE_DIR/nginx.conf${NC} and change 'monitoring' to 'block'" - echo -e " 3. Restart container: ${GREEN}sudo $INSTANCE_DIR/start.sh${NC}" - echo -e " 4. Check logs: ${GREEN}sudo docker logs -f $INSTANCE_NAME${NC}" - echo -e "\n${YELLOW}Deployment log: $LOG_FILE${NC}" - - # Write success marker - sudo tee "$INSTANCE_DIR/DEPLOYMENT_SUCCESS" > /dev/null </dev/null || { - echo -e "${RED}Cannot create log file at $LOG_FILE${NC}" - echo -e "${YELLOW}Falling back to current directory...${NC}" - LOG_FILE="./wallarm-deployment.log" - truncate -s 0 "$LOG_FILE" 2>/dev/null || > "$LOG_FILE" - } - sudo chmod 644 "$LOG_FILE" 2>/dev/null || chmod 644 "$LOG_FILE" - exec > >(tee "$LOG_FILE") 2>&1 - - log_message "INFO" "=== Wallarm Bulletproof Deployment Started ===" - - # Phase 1: Pre-flight validation - log_message "INFO" "=== PHASE 1: PRE-FLIGHT VALIDATION ===" - validate_sudo_access - local os_info=$(detect_os_and_version) - local os_name=$(echo "$os_info" | cut -d: -f1) - local os_version=$(echo "$os_info" | cut -d: -f2) - log_message "SUCCESS" "OS detected: $os_name $os_version" - - local architecture=$(detect_architecture) - log_message "SUCCESS" "Architecture: $architecture" - - log_message "INFO" "Pre-flight validation completed successfully" - - # Phase 2: Resource verification - local resource_info=$(verify_resources) - CLOUD_REGION=$(echo "$resource_info" | cut -d: -f1) - API_HOST=$(echo "$resource_info" | cut -d: -f2) - REGISTRY_REACHABLE=$(echo "$resource_info" | cut -d: -f3) - DOWNLOAD_REACHABLE=$(echo "$resource_info" | cut -d: -f4) - - log_message "SUCCESS" "Resource verification completed:" - log_message "SUCCESS" " Cloud Region: $CLOUD_REGION" - log_message "SUCCESS" " API Host: $API_HOST" - log_message "SUCCESS" " Registry Reachable: $REGISTRY_REACHABLE" - log_message "SUCCESS" " Download Reachable: $DOWNLOAD_REACHABLE" - - # Phase 3: Docker engine setup - log_message "INFO" "=== PHASE 3: DOCKER ENGINE SETUP ===" - setup_docker_engine "$architecture" - # Phase 4: Configuration collection - log_message "INFO" "=== PHASE 4: CONFIGURATION COLLECTION ===" - local config_info=$(collect_configuration) - INGRESS_PORT=$(echo "$config_info" | cut -d: -f1) - MONITORING_PORT=$(echo "$config_info" | cut -d: -f2) - UPSTREAM_IP=$(echo "$config_info" | cut -d: -f3) - UPSTREAM_PORT=$(echo "$config_info" | cut -d: -f4) - WALLARM_TOKEN=$(echo "$config_info" | cut -d: -f5) - INSTANCE_NAME=$(echo "$config_info" | cut -d: -f6) - INSTANCE_DIR=$(echo "$config_info" | cut -d: -f7) - - log_message "SUCCESS" "Configuration collected:" - log_message "SUCCESS" " Ingress Port: $INGRESS_PORT" - log_message "SUCCESS" " Monitoring Port: $MONITORING_PORT" - log_message "SUCCESS" " Upstream: $UPSTREAM_IP:$UPSTREAM_PORT" - log_message "SUCCESS" " Instance: $INSTANCE_NAME" - log_message "SUCCESS" " Directory: $INSTANCE_DIR" - - # Phase 5: Deployment - log_message "INFO" "=== PHASE 5: DEPLOYMENT ===" - deploy_wallarm_node - - # Phase 6: Verification - log_message "INFO" "=== PHASE 6: VERIFICATION ===" - verify_deployment - - log_message "SUCCESS" "=== WALLARM DEPLOYMENT COMPLETED SUCCESSFULLY ===" - echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${GREEN}${BOLD}║ WALLARM FILTERING NODE DEPLOYMENT SUCCESSFUL ║${NC}" - echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${CYAN}The Wallarm filtering node is now active and protecting your application.${NC}" - echo -e "${YELLOW}Full deployment log: $LOG_FILE${NC}" - echo -e "${YELLOW}Instance directory: $INSTANCE_DIR${NC}" - echo -e "\n${GREEN}To stop the node:${NC} sudo docker stop $INSTANCE_NAME" - echo -e "${GREEN}To restart:${NC} sudo $INSTANCE_DIR/start.sh" - echo -e "${GREEN}To view logs:${NC} sudo docker logs -f $INSTANCE_NAME" - echo -e "\n${MAGENTA}${BOLD}Thank you for using Wallarm!${NC}" -} - -# Execute main function -main "$@" - diff --git a/wallarm-deploy-ct.sh b/wallarm-deploy-ct.sh deleted file mode 100644 index 3daa9dc..0000000 --- a/wallarm-deploy-ct.sh +++ /dev/null @@ -1,1114 +0,0 @@ -#!/bin/bash -# ============================================================================== -# WALLARM BULLETPROOF DEPLOYMENT SCRIPT - V1.0 -# ============================================================================== -# Features: -# - OS-agnostic deployment (Ubuntu, Debian, CentOS, RHEL, Alpine, etc.) -# - Docker/containerd installation from static binaries (no package managers) -# - Comprehensive pre-flight checks (sudo, OS, architecture, resources) -# - Resource verification (Wallarm cloud, Docker registry, application server) -# - DAU-friendly error handling with remediation instructions -# - Multiple-run detection and handling -# - Deployment logging with overwrite protection -# - Handshake verification with application server -# - Persistence across reboots (start.sh) -# ============================================================================== - -# Color definitions for better UX -RED='\033[0;31m' -GREEN='\033[0;32m' -YELLOW='\033[1;33m' -BLUE='\033[0;34m' -CYAN='\033[0;36m' -MAGENTA='\033[0;35m' -BOLD='\033[1m' -NC='\033[0m' # No Color - -# Logging configuration -LOG_FILE="/var/log/wallarm-deployment.log" -DEPLOYMENT_TIMESTAMP=$(date '+%Y-%m-%d_%H-%M-%S') - -# Cloud endpoints (from Wallarm documentation) -EU_DATA_NODES=("api.wallarm.com" "node-data0.eu1.wallarm.com" "node-data1.eu1.wallarm.com") -US_DATA_NODES=("us1.api.wallarm.com" "node-data0.us1.wallarm.com" "node-data1.us1.wallarm.com") - -# Docker configuration -DOCKER_VERSION="27.0.0" # Current stable version -DOCKER_STATIC_BASE_URL="https://download.docker.com/linux/static/stable" - -# Resource reachability flags (set during verification) -REGISTRY_REACHABLE=false -DOWNLOAD_REACHABLE=false -CLOUD_REGION="" -API_HOST="" - -# ============================================================================== -# LOGGING & ERROR HANDLING FUNCTIONS -# ============================================================================== - -log_message() { - local level="$1" - local message="$2" - local timestamp=$(date '+%Y-%m-%d %H:%M:%S') - - case "$level" in - "INFO") color="${BLUE}" ;; - "SUCCESS") color="${GREEN}" ;; - "WARNING") color="${YELLOW}" ;; - "ERROR") color="${RED}" ;; - "DEBUG") color="${CYAN}" ;; - *) color="${NC}" ;; - esac - - echo -e "${color}[${timestamp}] ${level}: ${message}${NC}" - echo "[${timestamp}] ${level}: ${message}" >> "$LOG_FILE" -} - -fail_with_remediation() { - local error_msg="$1" - local remediation="$2" - - log_message "ERROR" "$error_msg" - echo -e "\n${RED}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${RED}${BOLD}║ DEPLOYMENT FAILED ║${NC}" - echo -e "${RED}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${YELLOW}${BOLD}Root Cause:${NC} $error_msg" - echo -e "\n${YELLOW}${BOLD}How to Fix:${NC}" - echo -e "$remediation" - echo -e "\n${YELLOW}Check the full log for details:${NC} $LOG_FILE" - exit 1 -} - -validate_sudo_access() { - log_message "INFO" "Validating sudo access..." - - # Check if user can run sudo - if ! command -v sudo >/dev/null 2>&1; then - fail_with_remediation "sudo command not found" \ - "1. Install sudo package for your OS: - - Ubuntu/Debian: apt-get update && apt-get install -y sudo - - CentOS/RHEL: yum install -y sudo - - Alpine: apk add sudo - -2. Add your user to sudoers: - usermod -aG sudo \$(whoami) # Ubuntu/Debian - usermod -aG wheel \$(whoami) # CentOS/RHEL - -3. Log out and log back in for changes to take effect." - fi - - # Test sudo with password prompt if needed - if ! sudo -v; then - fail_with_remediation "sudo authentication failed" \ - "1. Ensure you have sudo privileges: - - Run 'sudo -l' to check your sudo permissions - - Contact your system administrator - -2. If password is required: - - Make sure you know the correct password - - The script will prompt for password when needed - -3. For passwordless sudo: - - Add '\$(whoami) ALL=(ALL) NOPASSWD:ALL' to /etc/sudoers - - Use 'visudo' to edit safely - - WARNING: Only do this in secure environments" - fi - - log_message "SUCCESS" "Sudo access validated" -} - -detect_os_and_version() { - log_message "INFO" "Detecting OS and version..." - - local os_name="" - local os_version="" - - # Check for /etc/os-release first (modern systems) - if [ -f /etc/os-release ]; then - . /etc/os-release - os_name="$ID" - os_version="$VERSION_ID" - # Check for older RedHat/CentOS - elif [ -f /etc/redhat-release ]; then - os_name="rhel" - os_version=$(cat /etc/redhat-release | sed -e 's/.*release \([0-9]\+\)\..*/\1/') - # Check for Alpine - elif [ -f /etc/alpine-release ]; then - os_name="alpine" - os_version=$(cat /etc/alpine-release) - else - os_name=$(uname -s | tr '[:upper:]' '[:lower:]') - os_version=$(uname -r) - fi - - # Normalize OS names - case "$os_name" in - "ubuntu"|"debian"|"centos"|"rhel"|"alpine"|"amzn"|"ol"|"rocky"|"almalinux") - # Valid supported OS - ;; - *) - log_message "WARNING" "OS '$os_name' not explicitly tested but may work" - ;; - esac - - echo "$os_name:$os_version" -} - -detect_architecture() { - log_message "INFO" "Detecting system architecture..." - - local arch=$(uname -m) - local docker_arch="" - - case "$arch" in - x86_64|x64|amd64) - docker_arch="x86_64" - log_message "SUCCESS" "Architecture: x86_64 (Intel/AMD 64-bit)" - ;; - aarch64|arm64) - docker_arch="aarch64" - log_message "SUCCESS" "Architecture: aarch64 (ARM 64-bit)" - ;; - armv7l|armhf) - docker_arch="armhf" - log_message "SUCCESS" "Architecture: armhf (ARM 32-bit)" - ;; - *) - fail_with_remediation "Unsupported architecture: $arch" \ - "This script supports: -1. x86_64 (Intel/AMD 64-bit) - Most common -2. aarch64 (ARM 64-bit) - AWS Graviton, Raspberry Pi 4 -3. armhf (ARM 32-bit) - Older ARM devices - -Your architecture '$arch' is not supported for Docker static binaries. -Consider: -- Using a different machine with supported architecture -- Manual Docker installation from package manager -- Contact Wallarm support for alternative deployment options" - ;; - esac - - echo "$docker_arch" -} - -verify_resources() { - log_message "INFO" "=== PHASE 2: RESOURCE VERIFICATION ===" - - # Get cloud selection from user - log_message "INFO" "Select Wallarm Cloud region" - echo -e "\n${CYAN}${BOLD}Wallarm Cloud Region Selection:${NC}" - echo -e "1. ${YELLOW}US Cloud${NC} (us1.api.wallarm.com) - For US-based deployments" - echo -e "2. ${YELLOW}EU Cloud${NC} (api.wallarm.com) - For EU-based deployments" - - local cloud_choice="" - while [[ ! "$cloud_choice" =~ ^(1|2|US|EU)$ ]]; do - read -p "$(echo -e "${YELLOW}Enter choice [1/US or 2/EU]: ${NC}")" cloud_choice - cloud_choice=$(echo "$cloud_choice" | tr '[:lower:]' '[:upper:]') - - case "$cloud_choice" in - 1|"US") - CLOUD_REGION="US" - API_HOST="us1.api.wallarm.com" - NODES_TO_TEST=("${US_DATA_NODES[@]}") - log_message "INFO" "Selected US Cloud" - ;; - 2|"EU") - CLOUD_REGION="EU" - API_HOST="api.wallarm.com" - NODES_TO_TEST=("${EU_DATA_NODES[@]}") - log_message "INFO" "Selected EU Cloud" - ;; - *) - echo -e "${RED}Invalid choice. Enter 1/US for US Cloud or 2/EU for EU Cloud${NC}" - ;; - esac - done - - # Test Wallarm Cloud endpoints - log_message "INFO" "Testing connectivity to Wallarm Cloud endpoints..." - local all_endpoints_reachable=true - - for endpoint in "${NODES_TO_TEST[@]}"; do - if curl -skI --connect-timeout 10 "https://$endpoint" >/dev/null 2>&1; then - log_message "SUCCESS" "Wallarm endpoint $endpoint is reachable" - else - log_message "ERROR" "Wallarm endpoint $endpoint is NOT reachable" - all_endpoints_reachable=false - fi - done - - if [ "$all_endpoints_reachable" = false ]; then - fail_with_remediation "Some Wallarm Cloud endpoints are not reachable" \ - "1. Check firewall rules: - - Allow outbound HTTPS (port 443) to Wallarm IPs - - US Cloud IPs: 34.96.64.17, 34.110.183.149, 35.235.66.155, 34.102.90.100, 34.94.156.115, 35.235.115.105 - - EU Cloud IPs: 34.160.38.183, 34.144.227.90, 34.90.110.226 - -2. Check network connectivity: - - Run: ping 8.8.8.8 (test general internet) - - Run: nslookup $API_HOST (test DNS) - - Run: curl -v https://$API_HOST (verbose test) - -3. Check proxy settings: - - If behind proxy, set http_proxy/https_proxy environment variables - - Or configure Docker to use proxy - -4. Contact your network administrator if issues persist" - fi - - # Test Docker Registry (hub.docker.com) - log_message "INFO" "Testing connectivity to Docker Registry (hub.docker.com)..." - local registry_status=$(curl -skI --connect-timeout 10 -o /dev/null -w "%{http_code}" "https://hub.docker.com/v2/") - if [[ "$registry_status" == "200" || "$registry_status" == "401" || "$registry_status" == "302" ]]; then - log_message "SUCCESS" "Docker Registry is reachable (Status: $registry_status)" - REGISTRY_REACHABLE=true - else - log_message "WARNING" "Docker Registry may be blocked (Status: $registry_status)" - REGISTRY_REACHABLE=false - fi - - # Test Docker Download Server (download.docker.com) - log_message "INFO" "Testing connectivity to Docker Download Server..." - if curl -skI --connect-timeout 10 "https://download.docker.com" >/dev/null 2>&1; then - log_message "SUCCESS" "Docker Download Server is reachable" - DOWNLOAD_REACHABLE=true - else - log_message "WARNING" "Docker Download Server may be blocked" - DOWNLOAD_REACHABLE=false - fi - - # Check for local Docker binaries as fallback - if [ "$DOWNLOAD_REACHABLE" = false ]; then - log_message "INFO" "Checking for local Docker binary fallback..." - if ls docker-*.tgz 2>/dev/null | grep -q .; then - log_message "SUCCESS" "Found local Docker binary: $(ls docker-*.tgz | head -1)" - else - log_message "WARNING" "No local Docker binaries found. Manual download may be required." - fi - fi - - # Check for local Wallarm image as fallback - if [ "$REGISTRY_REACHABLE" = false ]; then - log_message "INFO" "Checking for local Wallarm image fallback..." - if ls wallarm-node-*.tar 2>/dev/null | grep -q .; then - log_message "SUCCESS" "Found local Wallarm image: $(ls wallarm-node-*.tar | head -1)" - else - log_message "WARNING" "No local Wallarm images found. Manual download may be required." - fi - fi - - # Final resource assessment - if [ "$REGISTRY_REACHABLE" = false ] && [ "$DOWNLOAD_REACHABLE" = false ]; then - log_message "ERROR" "Critical: Neither Docker Registry nor Download Server reachable" - echo -e "\n${YELLOW}${BOLD}Possible workarounds:${NC}" - echo -e "1. Download Docker binary manually:" - echo -e " curl -L '$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz' -o docker.tgz" - echo -e "2. Download Wallarm image manually:" - echo -e " docker pull wallarm/node:latest" - echo -e " docker save wallarm/node:latest -o wallarm-node-latest.tar" - echo -e "3. Place files in current directory and re-run script" - echo -e "\n${RED}Without these resources, deployment cannot proceed.${NC}" - - read -p "$(echo -e "${YELLOW}Do you want to continue anyway? (y/N): ${NC}")" -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - fail_with_remediation "Insufficient resources for deployment" \ - "Please ensure at least one of these is available: -1. Internet access to Docker Registry (hub.docker.com) -2. Internet access to Docker Download Server (download.docker.com) -3. Local Docker binary (docker-*.tgz) in current directory -4. Local Wallarm image (wallarm-node-*.tar) in current directory" - fi - fi - - log_message "SUCCESS" "Resource verification completed" - echo "$CLOUD_REGION:$API_HOST:$REGISTRY_REACHABLE:$DOWNLOAD_REACHABLE" -} - -setup_docker_engine() { - local architecture="$1" - log_message "INFO" "Setting up Docker Engine..." - - # Check if Docker is already installed and running - if command -v docker >/dev/null 2>&1 && sudo docker info >/dev/null 2>&1; then - local docker_version=$(docker --version | cut -d' ' -f3 | tr -d ',') - log_message "SUCCESS" "Docker is already installed and running (version $docker_version)" - return 0 - fi - - log_message "INFO" "Docker not found or not running. Proceeding with installation..." - - # Determine binary source - local binary_file="docker-$DOCKER_VERSION.tgz" - local binary_path="" - - if [ "$DOWNLOAD_REACHABLE" = true ]; then - # Download Docker static binary - log_message "INFO" "Downloading Docker static binary for $architecture..." - local download_url="$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz" - - if curl -fL --connect-timeout 30 "$download_url" -o "$binary_file"; then - log_message "SUCCESS" "Downloaded Docker binary: $binary_file" - binary_path="$binary_file" - else - log_message "ERROR" "Failed to download Docker binary from $download_url" - binary_path="" - fi - fi - - # Fallback: Check for local Docker binary - if [ -z "$binary_path" ]; then - log_message "INFO" "Checking for local Docker binary..." - local local_files=$(ls docker-*.tgz 2>/dev/null | head -1) - if [ -n "$local_files" ]; then - binary_path="$local_files" - log_message "SUCCESS" "Using local Docker binary: $binary_path" - else - fail_with_remediation "No Docker binary available" \ - "Please provide a Docker static binary: -1. Download manually: - curl -L '$DOCKER_STATIC_BASE_URL/$architecture/docker-$DOCKER_VERSION.tgz' -o docker.tgz -2. Or place an existing docker-*.tgz file in current directory -3. Re-run the script after downloading" - fi - fi - - # Extract and install - log_message "INFO" "Extracting Docker binary..." - if ! tar xzvf "$binary_path" >/dev/null 2>&1; then - fail_with_remediation "Failed to extract Docker binary" \ - "The Docker binary file may be corrupted: -1. Delete the corrupted file: rm -f docker-*.tgz -2. Download a fresh copy manually -3. Verify the file integrity: tar -tzf docker-*.tgz should list files" - fi - - log_message "INFO" "Installing Docker binaries to /usr/bin/" - sudo cp docker/* /usr/bin/ 2>/dev/null || { - fail_with_remediation "Failed to copy Docker binaries" \ - "Permission denied copying to /usr/bin/ -1. Ensure you have sudo privileges -2. Check disk space: df -h / -3. Manual installation: - sudo cp docker/* /usr/bin/" - } - - # Cleanup extracted directory - rm -rf docker - - # Configure systemd service - log_message "INFO" "Configuring Docker systemd service..." - - # Check if docker group exists, create if not - if ! getent group docker >/dev/null; then - sudo groupadd docker 2>/dev/null || log_message "WARNING" "Failed to create docker group (may already exist)" - fi - - # Create systemd service file - sudo tee /etc/systemd/system/docker.service > /dev/null < /dev/null </dev/null 2>&1; then - log_message "SUCCESS" "Docker daemon started successfully (attempt $attempt/$max_attempts)" - break - fi - log_message "DEBUG" "Waiting for Docker daemon... ($attempt/$max_attempts)" - sleep 2 - attempt=$((attempt + 1)) - done - - if [ $attempt -gt $max_attempts ]; then - fail_with_remediation "Docker daemon failed to start" \ - "Troubleshooting steps: -1. Check Docker logs: sudo journalctl -u docker.service -2. Verify systemd configuration: sudo systemctl status docker -3. Check for port conflicts: sudo netstat -tulpn | grep :2375 -4. Manual start attempt: sudo dockerd --debug -5. Check kernel requirements: uname -r (should be 3.10+) -6. Ensure cgroups are mounted: mount | grep cgroup" - fi - - # Test Docker installation - log_message "INFO" "Testing Docker installation with hello-world..." - if sudo docker run --rm hello-world >/dev/null 2>&1; then - log_message "SUCCESS" "Docker installation verified successfully" - else - log_message "WARNING" "Docker hello-world test failed (network may be restricted)" - fi - - # Add current user to docker group (optional) - log_message "INFO" "Adding current user to docker group..." - if ! groups $(whoami) | grep -q docker; then - sudo usermod -aG docker $(whoami) 2>/dev/null && \ - log_message "SUCCESS" "Added $(whoami) to docker group. Log out and back in for changes to take effect." - fi - - log_message "SUCCESS" "Docker Engine setup completed" -} - -collect_configuration() { - log_message "INFO" "Collecting deployment configuration..." - - # Get ingress port - local default_port=80 - local ingress_port="" - while [[ ! "$ingress_port" =~ ^[0-9]+$ ]] || [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; do - read -p "$(echo -e "${YELLOW}Enter inbound port [${default_port}]: ${NC}")" ingress_port - ingress_port="${ingress_port:-$default_port}" - - if [[ ! "$ingress_port" =~ ^[0-9]+$ ]]; then - echo -e "${RED}Port must be a number${NC}" - elif [ "$ingress_port" -lt 1 ] || [ "$ingress_port" -gt 65535 ]; then - echo -e "${RED}Port must be between 1 and 65535${NC}" - fi - done - - # Calculate monitoring port (ingress + 10) - local monitoring_port=$((ingress_port + 10)) - log_message "INFO" "Monitoring port will be: $monitoring_port" - - # Check for port conflicts - log_message "INFO" "Checking for port conflicts..." - if sudo netstat -tulpn 2>/dev/null | grep -E ":$ingress_port\s" >/dev/null 2>&1; then - fail_with_remediation "Port $ingress_port is already in use" \ - "1. Free up port $ingress_port: - - Stop the service using it: sudo lsof -ti:$ingress_port | xargs kill -9 - - Change your application to use a different port - -2. Choose a different inbound port - - Common alternatives: 8080, 8888, 3000 - - Avoid well-known ports (80, 443) if already used - -3. Check what's using the port: - sudo netstat -tulpn | grep :$ingress_port - sudo lsof -i :$ingress_port" - fi - - if sudo netstat -tulpn 2>/dev/null | grep -E ":$monitoring_port\s" >/dev/null 2>&1; then - log_message "WARNING" "Port $monitoring_port is in use, choosing alternative..." - monitoring_port=$((ingress_port + 100)) - log_message "INFO" "New monitoring port: $monitoring_port" - fi - - # Get application server details - local upstream_ip="" - local upstream_port="" - - echo -e "\n${CYAN}${BOLD}Application Server Configuration:${NC}" - echo -e "${YELLOW}Enter the IP/hostname and port of your backend application${NC}" - - while [[ -z "$upstream_ip" ]]; do - read -p "$(echo -e "${YELLOW}Upstream App IP/Hostname [127.0.0.1]: ${NC}")" upstream_ip - upstream_ip="${upstream_ip:-127.0.0.1}" - - # Validate IP/hostname format - if ! [[ "$upstream_ip" =~ ^[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] && \ - ! [[ "$upstream_ip" =~ ^[a-zA-Z0-9.-]+$ ]]; then - echo -e "${RED}Invalid IP/hostname format${NC}" - upstream_ip="" - fi - done - - while [[ ! "$upstream_port" =~ ^[0-9]+$ ]] || [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; do - read -p "$(echo -e "${YELLOW}Upstream App Port [8080]: ${NC}")" upstream_port - upstream_port="${upstream_port:-8080}" - - if [[ ! "$upstream_port" =~ ^[0-9]+$ ]]; then - echo -e "${RED}Port must be a number${NC}" - elif [ "$upstream_port" -lt 1 ] || [ "$upstream_port" -gt 65535 ]; then - echo -e "${RED}Port must be between 1 and 65535${NC}" - fi - done - - # Verify application server reachability - log_message "INFO" "Verifying application server reachability..." - if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$upstream_ip/$upstream_port" 2>/dev/null; then - log_message "SUCCESS" "Application server $upstream_ip:$upstream_port is reachable" - else - log_message "WARNING" "Application server $upstream_ip:$upstream_port is not reachable" - echo -e "${YELLOW}${BOLD}Warning:${NC} Cannot reach application server at $upstream_ip:$upstream_port" - echo -e "${YELLOW}This may cause the Wallarm node to fail. Possible reasons:${NC}" - echo -e "1. Application server is not running" - echo -e "2. Firewall blocking port $upstream_port" - echo -e "3. Wrong IP/hostname" - echo -e "4. Application server not listening on that port" - - read -p "$(echo -e "${YELLOW}Continue anyway? (y/N): ${NC}")" -n 1 -r - echo - if [[ ! $REPLY =~ ^[Yy]$ ]]; then - fail_with_remediation "Application server unreachable" \ - "Ensure your application server is accessible: -1. Start your application server -2. Check it's listening: sudo netstat -tulpn | grep :$upstream_port -3. Verify firewall rules allow inbound connections -4. Test connectivity: telnet $upstream_ip $upstream_port -5. If using hostname, verify DNS resolution: nslookup $upstream_ip" - fi - fi - - # Get Wallarm node token - local wallarm_token="" - echo -e "\n${CYAN}${BOLD}Wallarm Node Token:${NC}" - echo -e "${YELLOW}Get your token from Wallarm Console:${NC}" - echo -e "US Cloud: https://us1.my.wallarm.com/nodes" - echo -e "EU Cloud: https://my.wallarm.com/nodes" - echo -e "Create a new 'Wallarm node' and copy the token" - - while [[ -z "$wallarm_token" ]]; do - read -p "$(echo -e "${YELLOW}Paste Wallarm Node Token: ${NC}")" wallarm_token - if [[ -z "$wallarm_token" ]]; then - echo -e "${RED}Token cannot be empty${NC}" - elif [[ ! "$wallarm_token" =~ ^[a-zA-Z0-9_-]+$ ]]; then - echo -e "${RED}Token contains invalid characters${NC}" - wallarm_token="" - fi - done - - # Generate instance names and paths - local instance_name="wallarm-node-$ingress_port" - local instance_dir="/opt/wallarm/$instance_name" - - # Check for existing container with same name - if sudo docker ps -a --format "{{.Names}}" | grep -q "^$instance_name$"; then - log_message "WARNING" "Container '$instance_name' already exists" - echo -e "${YELLOW}A container named '$instance_name' already exists.${NC}" - echo -e "Options:" - echo -e "1. Remove existing container and deploy new" - echo -e "2. Use different port to avoid conflict" - echo -e "3. Exit and manage manually" - - read -p "$(echo -e "${YELLOW}Choose option (1/2/3) [1]: ${NC}")" option - option="${option:-1}" - - case "$option" in - 1) - log_message "INFO" "Removing existing container '$instance_name'..." - sudo docker rm -f "$instance_name" 2>/dev/null || \ - log_message "WARNING" "Failed to remove container (may not exist)" - ;; - 2) - log_message "INFO" "Please restart script with different port" - exit 0 - ;; - 3) - log_message "INFO" "Exiting for manual management" - exit 0 - ;; - *) - log_message "ERROR" "Invalid option" - ;; - esac - fi - - # Create instance directory - log_message "INFO" "Creating instance directory: $instance_dir" - sudo mkdir -p "$instance_dir" - - # Output configuration summary - echo -e "\n${GREEN}${BOLD}✓ Configuration Complete${NC}" - echo -e "${CYAN}Summary:${NC}" - echo -e " Inbound Port: ${YELLOW}$ingress_port${NC}" - echo -e " Monitoring Port: ${YELLOW}$monitoring_port${NC}" - echo -e " Application Server: ${YELLOW}$upstream_ip:$upstream_port${NC}" - echo -e " Wallarm Cloud: ${YELLOW}$CLOUD_REGION ($API_HOST)${NC}" - echo -e " Instance Directory: ${YELLOW}$instance_dir${NC}" - - # Return configuration as colon-separated values - echo "$ingress_port:$monitoring_port:$upstream_ip:$upstream_port:$wallarm_token:$instance_name:$instance_dir" -} - -deploy_wallarm_node() { - log_message "INFO" "Deploying Wallarm filtering node..." - - # Pull or load Wallarm image - if [ "$REGISTRY_REACHABLE" = true ]; then - log_message "INFO" "Pulling Wallarm Docker image..." - if ! sudo docker pull wallarm/node:latest; then - fail_with_remediation "Failed to pull Wallarm image" \ - "Docker pull failed. Possible reasons: -1. Network connectivity to Docker Hub -2. Docker Hub rate limiting (login required) -3. Insufficient disk space - -Solutions: -1. Check network: docker pull hello-world -2. Login to Docker Hub: docker login -3. Use local image: docker save/load -4. Check disk: df -h /var/lib/docker" - fi - log_message "SUCCESS" "Wallarm image pulled successfully" - else - log_message "INFO" "Registry unreachable, checking for local image..." - local image_files=$(ls wallarm-node-*.tar 2>/dev/null | head -1) - if [ -n "$image_files" ]; then - log_message "INFO" "Loading local image: $image_files" - if ! sudo docker load < "$image_files"; then - fail_with_remediation "Failed to load local Wallarm image" \ - "The local image file may be corrupted: -1. Verify file integrity: tar -tzf $image_files -2. Download fresh image on another machine: - docker pull wallarm/node:latest - docker save wallarm/node:latest -o wallarm-node-latest.tar -3. Transfer file to this machine" - fi - log_message "SUCCESS" "Local Wallarm image loaded" - else - fail_with_remediation "No Wallarm image available" \ - "Please provide a Wallarm Docker image: -1. Download on another machine with internet: - docker pull wallarm/node:latest - docker save wallarm/node:latest -o wallarm-node-latest.tar -2. Transfer file to this machine -3. Place in current directory and re-run script" - fi - fi - - # Create nginx configuration - log_message "INFO" "Creating nginx configuration..." - sudo tee "$INSTANCE_DIR/nginx.conf" > /dev/null < /dev/null <> "\$LOG_FILE" - -# Stop and remove existing container if running -sudo docker stop "\$CONTAINER_NAME" 2>/dev/null || true -sudo docker rm "\$CONTAINER_NAME" 2>/dev/null || true - -# Start new container -sudo docker run -d \\ - --name "\$CONTAINER_NAME" \\ - --restart always \\ - --network host \\ - -p $INGRESS_PORT:80 \\ - -p $MONITORING_PORT:90 \\ - -e WALLARM_API_TOKEN="$WALLARM_TOKEN" \\ - -e WALLARM_API_HOST="$API_HOST" \\ - -e NGINX_BACKEND="$UPSTREAM_IP:$UPSTREAM_PORT" \\ - -e WALLARM_MODE="monitoring" \\ - -v "\$NGINX_CONFIG:/etc/nginx/http.d/default.conf:ro" \\ - wallarm/node:latest - -echo "\$(date) - Container started with ID: \$(sudo docker ps -q -f name=\$CONTAINER_NAME)" >> "\$LOG_FILE" - -# Verify container is running -sleep 3 -if sudo docker ps | grep -q "\$CONTAINER_NAME"; then - echo "\$(date) - Verification: Container is running" >> "\$LOG_FILE" - echo "Wallarm node \$CONTAINER_NAME started successfully" -else - echo "\$(date) - ERROR: Container failed to start" >> "\$LOG_FILE" - sudo docker logs "\$CONTAINER_NAME" >> "\$LOG_FILE" 2>&1 - exit 1 -fi -EOF - - sudo chmod +x "$INSTANCE_DIR/start.sh" - - # Deploy container using start.sh - log_message "INFO" "Deploying Wallarm container..." - if ! sudo "$INSTANCE_DIR/start.sh"; then - log_message "ERROR" "Container deployment failed, checking logs..." - sudo docker logs "$INSTANCE_NAME" 2>&1 | tail -20 | while read line; do - log_message "DEBUG" "Container log: $line" - done - - fail_with_remediation "Wallarm container deployment failed" \ - "Common issues: -1. Invalid Wallarm token - - Verify token in Wallarm Console - - Ensure token matches selected cloud region (US/EU) - -2. Port conflicts - - Check ports $INGRESS_PORT and $MONITORING_PORT are free - - sudo netstat -tulpn | grep -E ':$INGRESS_PORT|:$MONITORING_PORT' - -3. Docker permissions - - Ensure Docker daemon is running: sudo systemctl status docker - - Check user in docker group: groups - -4. Resource constraints - - Check memory: free -h - - Check disk: df -h - -5. View full logs: sudo docker logs $INSTANCE_NAME" - fi - - log_message "SUCCESS" "Wallarm node deployed successfully" - log_message "INFO" "Container Name: $INSTANCE_NAME" - log_message "INFO" "Instance Directory: $INSTANCE_DIR" - log_message "INFO" "Start Script: $INSTANCE_DIR/start.sh" - - # Create systemd service for auto-start on boot - log_message "INFO" "Creating systemd service for automatic startup..." - sudo tee "/etc/systemd/system/wallarm-$INSTANCE_NAME.service" > /dev/null </dev/null || \ - log_message "WARNING" "Failed to enable systemd service (may already exist)" - - log_message "SUCCESS" "Systemd service created: wallarm-$INSTANCE_NAME.service" -} - -verify_deployment() { - log_message "INFO" "Verifying deployment..." - - # Wait for container to be fully started - log_message "INFO" "Waiting for Wallarm node to initialize (30 seconds)..." - sleep 30 - - # Check container status - log_message "INFO" "Checking container status..." - if ! sudo docker ps --format "table {{.Names}}\t{{.Status}}" | grep -q "$INSTANCE_NAME"; then - fail_with_remediation "Container is not running" \ - "The Wallarm container failed to start: -1. Check container logs: sudo docker logs $INSTANCE_NAME -2. Verify Docker service: sudo systemctl status docker -3. Check resource constraints: sudo docker stats $INSTANCE_NAME -4. Review start script: $INSTANCE_DIR/start.sh" - fi - - local container_status=$(sudo docker ps --format "table {{.Names}}\t{{.Status}}" | grep "$INSTANCE_NAME") - log_message "SUCCESS" "Container status: $container_status" - - # Test monitoring endpoint - log_message "INFO" "Testing monitoring endpoint (port $MONITORING_PORT)..." - local max_attempts=10 - local attempt=1 - local monitoring_ok=false - - while [ $attempt -le $max_attempts ]; do - if curl -s "http://localhost:$MONITORING_PORT/wallarm-status" 2>/dev/null | grep -q "requests"; then - monitoring_ok=true - log_message "SUCCESS" "Monitoring endpoint responding (attempt $attempt/$max_attempts)" - break - fi - log_message "DEBUG" "Monitoring endpoint not ready yet ($attempt/$max_attempts)" - sleep 5 - attempt=$((attempt + 1)) - done - - if [ "$monitoring_ok" = false ]; then - log_message "WARNING" "Monitoring endpoint not responding as expected" - log_message "INFO" "Checking alternative..." - # Try direct container exec - if sudo docker exec "$INSTANCE_NAME" curl -s http://localhost:90/wallarm-status 2>/dev/null | grep -q "requests"; then - log_message "SUCCESS" "Monitoring endpoint works inside container" - else - log_message "ERROR" "Monitoring endpoint completely unavailable" - fi - fi - - # Test handshake through filtering node to application server - log_message "INFO" "Testing handshake through filtering node to application server..." - local handshake_ok=false - - for i in {1..5}; do - local response_code=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: test.local" "http://localhost:$INGRESS_PORT/health" --connect-timeout 10) - if [[ "$response_code" =~ ^(200|301|302|401|403)$ ]]; then - handshake_ok=true - log_message "SUCCESS" "Handshake successful: HTTP $response_code from application server (attempt $i/5)" - break - else - log_message "DEBUG" "Handshake attempt $i failed: HTTP $response_code" - sleep 3 - fi - done - - if [ "$handshake_ok" = false ]; then - log_message "WARNING" "Handshake to application server failed" - echo -e "${YELLOW}${BOLD}Warning:${NC} Cannot reach application server through filtering node" - echo -e "${YELLOW}Possible issues:${NC}" - echo -e "1. Application server not responding on $UPSTREAM_IP:$UPSTREAM_PORT" - echo -e "2. Network routing/firewall between container and application server" - echo -e "3. NGINX configuration issue" - echo -e "4. Application server requires specific Host header" - - # Test direct connection to application server - log_message "INFO" "Testing direct connection to application server..." - if timeout 5 bash -c "cat < /dev/null > /dev/tcp/$UPSTREAM_IP/$UPSTREAM_PORT" 2>/dev/null; then - log_message "SUCCESS" "Application server is directly reachable" - else - log_message "ERROR" "Application server is not reachable even directly" - fi - else - log_message "SUCCESS" "Handshake verification passed" - fi - - # Perform attack simulation test (safe) - log_message "INFO" "Performing attack simulation test (safe SQL injection probe)..." - local attack_test_url="http://localhost:$INGRESS_PORT/?id=%27OR+1%3D1--" - local attack_response=$(curl -s -o /dev/null -w "%{http_code}" -H "Host: test.local" "$attack_test_url" --connect-timeout 10) - - if [[ "$attack_response" =~ ^(200|301|302|401|403|404)$ ]]; then - log_message "SUCCESS" "Attack simulation test passed: HTTP $attack_response" - log_message "INFO" "Note: Wallarm in monitoring mode logs attacks but doesn't block" - else - log_message "WARNING" "Attack simulation test returned unexpected code: $attack_response" - fi - - # Check Wallarm synchronization status - log_message "INFO" "Checking Wallarm cloud synchronization..." - local sync_attempts=0 - local sync_ok=false - - while [ $sync_attempts -lt 5 ]; do - if sudo docker logs "$INSTANCE_NAME" 2>&1 | tail -20 | grep -i "sync\|connected\|success" | grep -v "error\|fail" >/dev/null; then - sync_ok=true - log_message "SUCCESS" "Wallarm cloud synchronization detected" - break - fi - sync_attempts=$((sync_attempts + 1)) - sleep 10 - done - - if [ "$sync_ok" = false ]; then - log_message "WARNING" "Wallarm cloud synchronization not confirmed in logs" - log_message "INFO" "Check synchronization manually in Wallarm Console" - fi - - # Final deployment summary - echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${GREEN}${BOLD}║ DEPLOYMENT VERIFICATION COMPLETE ║${NC}" - echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${CYAN}${BOLD}Deployment Summary:${NC}" - echo -e " ${YELLOW}✓${NC} Container: ${GREEN}$INSTANCE_NAME${NC} (running)" - echo -e " ${YELLOW}✓${NC} Inbound Port: ${GREEN}$INGRESS_PORT${NC}" - echo -e " ${YELLOW}✓${NC} Monitoring Port: ${GREEN}$MONITORING_PORT${NC} (http://localhost:$MONITORING_PORT/wallarm-status)" - echo -e " ${YELLOW}✓${NC} Application Server: ${GREEN}$UPSTREAM_IP:$UPSTREAM_PORT${NC}" - echo -e " ${YELLOW}✓${NC} Wallarm Cloud: ${GREEN}$CLOUD_REGION${NC}" - echo -e " ${YELLOW}✓${NC} Instance Directory: ${GREEN}$INSTANCE_DIR${NC}" - echo -e " ${YELLOW}✓${NC} Start Script: ${GREEN}$INSTANCE_DIR/start.sh${NC}" - echo -e " ${YELLOW}✓${NC} Systemd Service: ${GREEN}wallarm-$INSTANCE_NAME.service${NC}" - echo -e "\n${CYAN}${BOLD}Next Steps:${NC}" - echo -e " 1. Monitor attacks in ${YELLOW}Wallarm Console${NC}" - echo -e " 2. Switch to ${YELLOW}blocking mode${NC} when ready:" - echo -e " Edit ${GREEN}$INSTANCE_DIR/nginx.conf${NC} and change 'monitoring' to 'block'" - echo -e " 3. Restart container: ${GREEN}sudo $INSTANCE_DIR/start.sh${NC}" - echo -e " 4. Check logs: ${GREEN}sudo docker logs -f $INSTANCE_NAME${NC}" - echo -e "\n${YELLOW}Deployment log: $LOG_FILE${NC}" - - # Write success marker - sudo tee "$INSTANCE_DIR/DEPLOYMENT_SUCCESS" > /dev/null </dev/null || { - echo -e "${RED}Cannot create log file at $LOG_FILE${NC}" - echo -e "${YELLOW}Falling back to current directory...${NC}" - LOG_FILE="./wallarm-deployment.log" - truncate -s 0 "$LOG_FILE" 2>/dev/null || > "$LOG_FILE" - } - sudo chmod 644 "$LOG_FILE" 2>/dev/null || chmod 644 "$LOG_FILE" - exec > >(tee "$LOG_FILE") 2>&1 - - log_message "INFO" "=== Wallarm Bulletproof Deployment Started ===" - - # Phase 1: Pre-flight validation - log_message "INFO" "=== PHASE 1: PRE-FLIGHT VALIDATION ===" - validate_sudo_access - local os_info=$(detect_os_and_version) - local os_name=$(echo "$os_info" | cut -d: -f1) - local os_version=$(echo "$os_info" | cut -d: -f2) - log_message "SUCCESS" "OS detected: $os_name $os_version" - - local architecture=$(detect_architecture) - log_message "SUCCESS" "Architecture: $architecture" - - log_message "INFO" "Pre-flight validation completed successfully" - - # Phase 2: Resource verification - local resource_info=$(verify_resources) - CLOUD_REGION=$(echo "$resource_info" | cut -d: -f1) - API_HOST=$(echo "$resource_info" | cut -d: -f2) - REGISTRY_REACHABLE=$(echo "$resource_info" | cut -d: -f3) - DOWNLOAD_REACHABLE=$(echo "$resource_info" | cut -d: -f4) - - log_message "SUCCESS" "Resource verification completed:" - log_message "SUCCESS" " Cloud Region: $CLOUD_REGION" - log_message "SUCCESS" " API Host: $API_HOST" - log_message "SUCCESS" " Registry Reachable: $REGISTRY_REACHABLE" - log_message "SUCCESS" " Download Reachable: $DOWNLOAD_REACHABLE" - - # Phase 3: Docker engine setup - log_message "INFO" "=== PHASE 3: DOCKER ENGINE SETUP ===" - setup_docker_engine "$architecture" - - # Phase 4: Configuration collection - log_message "INFO" "=== PHASE 4: CONFIGURATION COLLECTION ===" - local config_info=$(collect_configuration) - INGRESS_PORT=$(echo "$config_info" | cut -d: -f1) - MONITORING_PORT=$(echo "$config_info" | cut -d: -f2) - UPSTREAM_IP=$(echo "$config_info" | cut -d: -f3) - UPSTREAM_PORT=$(echo "$config_info" | cut -d: -f4) - WALLARM_TOKEN=$(echo "$config_info" | cut -d: -f5) - INSTANCE_NAME=$(echo "$config_info" | cut -d: -f6) - INSTANCE_DIR=$(echo "$config_info" | cut -d: -f7) - - log_message "SUCCESS" "Configuration collected:" - log_message "SUCCESS" " Ingress Port: $INGRESS_PORT" - log_message "SUCCESS" " Monitoring Port: $MONITORING_PORT" - log_message "SUCCESS" " Upstream: $UPSTREAM_IP:$UPSTREAM_PORT" - log_message "SUCCESS" " Instance: $INSTANCE_NAME" - log_message "SUCCESS" " Directory: $INSTANCE_DIR" - - # Phase 5: Deployment - log_message "INFO" "=== PHASE 5: DEPLOYMENT ===" - deploy_wallarm_node - - # Phase 6: Verification - log_message "INFO" "=== PHASE 6: VERIFICATION ===" - verify_deployment - - log_message "SUCCESS" "=== WALLARM DEPLOYMENT COMPLETED SUCCESSFULLY ===" - echo -e "\n${GREEN}${BOLD}╔══════════════════════════════════════════════════════════════╗${NC}" - echo -e "${GREEN}${BOLD}║ WALLARM FILTERING NODE DEPLOYMENT SUCCESSFUL ║${NC}" - echo -e "${GREEN}${BOLD}╚══════════════════════════════════════════════════════════════╝${NC}" - echo -e "\n${CYAN}The Wallarm filtering node is now active and protecting your application.${NC}" - echo -e "${YELLOW}Full deployment log: $LOG_FILE${NC}" - echo -e "${YELLOW}Instance directory: $INSTANCE_DIR${NC}" - echo -e "\n${GREEN}To stop the node:${NC} sudo docker stop $INSTANCE_NAME" - echo -e "${GREEN}To restart:${NC} sudo $INSTANCE_DIR/start.sh" - echo -e "${GREEN}To view logs:${NC} sudo docker logs -f $INSTANCE_NAME" - echo -e "\n${MAGENTA}${BOLD}Thank you for using Wallarm!${NC}" -} - -# Execute main function -main "$@" \ No newline at end of file