wallarm/wallarm-ct-deploy.sh
2026-03-30 06:33:38 +01:00

1972 lines
78 KiB
Bash
Executable file

#!/bin/bash
# ==============================================================================
# WALLARM DEPLOYMENT SCRIPT - V1.2
# ==============================================================================
# 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
# GitLab artifact URLs (primary source)
GITLAB_BASE_URL="https://git.sechpoint.app/customer-engineering/wallarm"
GITLAB_RAW_URL="https://git.sechpoint.app/customer-engineering/wallarm/-/raw/main"
GITLAB_DOCKER_BINARY_URL="${GITLAB_RAW_URL}/binaries/docker-29.2.1.tgz"
GITLAB_DOCKER_CHECKSUM_URL="${GITLAB_RAW_URL}/binaries/docker-29.2.1.tgz.sha256"
GITLAB_WALLARM_IMAGE_URL="${GITLAB_RAW_URL}/images/wallarm-node-6.11.0-rc1.tar.gz"
GITLAB_WALLARM_CHECKSUM_URL="${GITLAB_RAW_URL}/images/wallarm-node-6.11.0-rc1.tar.gz.sha256"
# Local artifact directories (relative to script location)
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
LOCAL_BINARY_DIR="${SCRIPT_DIR}/binaries"
LOCAL_IMAGE_DIR="${SCRIPT_DIR}/images"
# Internal registry endpoints (from stealth deployment) - fallback source
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:6.11.0-rc1"
WALLARM_IMAGE_TARGET="wallarm/node:6.11.0-rc1"
# 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
}
# ==============================================================================
# GITLAB ARTIFACT FUNCTIONS
# ==============================================================================
download_from_gitlab() {
local url="$1"
local output_path="$2"
local description="$3"
log_message "INFO" "Attempting to download $description from GitLab..."
log_message "DEBUG" "URL: $url"
log_message "DEBUG" "Output path: $output_path"
# Use curl with follow redirects, fail on HTTP error, timeout settings
if curl -fL "$CURL_INSECURE_FLAG" --connect-timeout 30 --max-time 300 --progress-bar "$url" -o "$output_path"; then
log_message "SUCCESS" "Downloaded $description to $output_path"
return 0
else
local curl_exit=$?
log_message "ERROR" "Failed to download $description from GitLab (curl exit: $curl_exit)"
# Clean up partial download if it exists
if [ -f "$output_path" ]; then
rm -f "$output_path"
log_message "DEBUG" "Removed partial download: $output_path"
fi
return 1
fi
}
verify_checksum() {
local file_path="$1"
local checksum_file_or_url="$2"
local description="$3"
log_message "INFO" "Verifying $description checksum..."
local checksum_file=""
# If checksum is a URL, download it first
if [[ "$checksum_file_or_url" =~ ^https?:// ]]; then
checksum_file="/tmp/$(basename "$checksum_file_or_url")"
log_message "DEBUG" "Downloading checksum from URL: $checksum_file_or_url"
if ! curl -fL "$CURL_INSECURE_FLAG" --connect-timeout 10 --max-time 30 -s "$checksum_file_or_url" -o "$checksum_file"; then
log_message "WARNING" "Could not download checksum file, skipping verification"
return 0 # Skip verification if checksum can't be downloaded
fi
else
checksum_file="$checksum_file_or_url"
fi
# Verify checksum file exists
if [ ! -f "$checksum_file" ]; then
log_message "WARNING" "Checksum file not found: $checksum_file, skipping verification"
return 0
fi
# Get expected checksum (first field from checksum file)
local expected_checksum
expected_checksum=$(awk '{print $1}' "$checksum_file" 2>/dev/null)
if [ -z "$expected_checksum" ]; then
log_message "WARNING" "Could not read checksum from $checksum_file, skipping verification"
return 0
fi
# Compute actual checksum
log_message "DEBUG" "Computing SHA256 checksum of $file_path..."
local actual_checksum
if command -v sha256sum >/dev/null 2>&1; then
actual_checksum=$(sha256sum "$file_path" | awk '{print $1}')
elif command -v shasum >/dev/null 2>&1; then
actual_checksum=$(shasum -a 256 "$file_path" | awk '{print $1}')
else
log_message "WARNING" "sha256sum or shasum not available, skipping checksum verification"
return 0
fi
# Compare checksums
if [ "$expected_checksum" = "$actual_checksum" ]; then
log_message "SUCCESS" "$description checksum verified successfully"
return 0
else
log_message "ERROR" "$description checksum verification FAILED"
log_message "DEBUG" "Expected: $expected_checksum"
log_message "DEBUG" "Actual: $actual_checksum"
# Clean up corrupted file
rm -f "$file_path"
log_message "INFO" "Removed corrupted file: $file_path"
return 1
fi
}
# ==============================================================================
# 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"
# Check for local artifact directories
if [ -d "$LOCAL_BINARY_DIR" ]; then
log_message "INFO" " Local binaries directory: $LOCAL_BINARY_DIR (exists)"
local binary_count=$(ls "$LOCAL_BINARY_DIR"/*.tgz 2>/dev/null | wc -l)
if [ "$binary_count" -gt 0 ]; then
log_message "INFO" " Found $binary_count Docker binary file(s)"
fi
else
log_message "INFO" " Local binaries directory: $LOCAL_BINARY_DIR (not found)"
fi
if [ -d "$LOCAL_IMAGE_DIR" ]; then
log_message "INFO" " Local images directory: $LOCAL_IMAGE_DIR (exists)"
local image_count=$(ls "$LOCAL_IMAGE_DIR"/*.tar.gz 2>/dev/null | wc -l)
if [ "$image_count" -gt 0 ]; then
log_message "INFO" " Found $image_count Wallarm image file(s)"
fi
else
log_message "INFO" " Local images directory: $LOCAL_IMAGE_DIR (not found)"
fi
# 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
# Get trusted proxy IPs for real IP configuration
local trusted_proxies=""
echo -e "\n${CYAN}${BOLD}Real Client IP Configuration:${NC}"
echo -e "${YELLOW}For Wallarm to see the real client IP, specify the IP address(es) of trusted proxies"
echo -e "(e.g., load balancers, firewalls, CDNs) that forward traffic to this node.${NC}"
echo -e "${YELLOW}You can enter:${NC}"
echo -e " - Single IP: 10.0.0.10"
echo -e " - CIDR range: 10.0.0.0/24"
echo -e " - Multiple entries separated by spaces: 10.0.0.10 10.0.1.0/24 192.168.1.1"
echo -e "${YELLOW}If unsure, you can leave empty and configure later${NC}"
read -r -p "$(echo -e "${YELLOW}Trusted proxy IPs/CIDRs (space-separated): ${NC}")" trusted_proxies_input
# Validate and clean up the input
local validated_proxies=()
if [[ -n "$trusted_proxies_input" ]]; then
# Split input by spaces
IFS=' ' read -ra proxy_array <<< "$trusted_proxies_input"
for proxy in "${proxy_array[@]}"; do
# Trim whitespace
proxy=$(echo "$proxy" | xargs)
if [[ -n "$proxy" ]]; then
# Validate IP or CIDR
if [[ "$proxy" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}(/[0-9]{1,2})?$ ]]; then
# Basic IP format validation
IFS='/' read -r ip cidr <<< "$proxy"
IFS='.' read -r o1 o2 o3 o4 <<< "$ip"
if [[ $o1 -le 255 && $o2 -le 255 && $o3 -le 255 && $o4 -le 255 ]]; then
if [[ -z "$cidr" ]] || [[ $cidr -ge 0 && $cidr -le 32 ]]; then
validated_proxies+=("$proxy")
else
echo -e "${RED}Invalid CIDR prefix length for $proxy (must be 0-32)${NC}"
fi
else
echo -e "${RED}Invalid IP octets in $proxy${NC}"
fi
else
echo -e "${RED}Invalid IP/CIDR format: $proxy${NC}"
echo -e "${YELLOW}Example valid formats: 10.0.0.10, 10.0.0.0/24, 192.168.1.1${NC}"
fi
fi
done
if [[ ${#validated_proxies[@]} -eq 0 ]]; then
echo -e "${YELLOW}No valid proxy IPs provided. Will skip set_real_ip_from configuration.${NC}"
echo -e "${YELLOW}You can manually edit /etc/nginx/conf.d/wallarm.conf later to add set_real_ip_from directives.${NC}"
trusted_proxies=""
else
trusted_proxies="${validated_proxies[*]}"
echo -e "${GREEN}Trusted proxies configured: $trusted_proxies${NC}"
fi
else
echo -e "${YELLOW}No trusted proxies specified. The node will see the last hop IP only.${NC}"
echo -e "${YELLOW}For real client IP detection, you'll need to manually configure set_real_ip_from in nginx config.${NC}"
fi
# 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"
if [[ -n "$trusted_proxies" ]]; then
log_message "SUCCESS" " Trusted Proxies: $trusted_proxies"
else
log_message "INFO" " Trusted Proxies: Not configured (will need manual setup)"
fi
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"
TRUSTED_PROXIES="$trusted_proxies"
}
# ==============================================================================
# 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 (priority: GitLab -> local dir -> current dir -> internal proxy)
local binary_file="docker-$DOCKER_VERSION.tgz"
local binary_path=""
# 1. Try GitLab download (primary source)
log_message "INFO" "Attempting to download Docker binary from GitLab..."
if download_from_gitlab "$GITLAB_DOCKER_BINARY_URL" "$binary_file" "Docker binary"; then
if verify_checksum "$binary_file" "$GITLAB_DOCKER_CHECKSUM_URL" "Docker binary"; then
binary_path="$binary_file"
log_message "SUCCESS" "Docker binary downloaded from GitLab and checksum verified"
else
log_message "WARNING" "GitLab Docker binary checksum verification failed, trying other sources"
# Remove corrupted download
rm -f "$binary_file"
fi
fi
# 2. Check local binaries directory
if [ -z "$binary_path" ] && [ -d "$LOCAL_BINARY_DIR" ]; then
log_message "INFO" "Checking local binaries directory: $LOCAL_BINARY_DIR"
local local_binary="$LOCAL_BINARY_DIR/docker-29.2.1.tgz"
local local_checksum="$LOCAL_BINARY_DIR/docker-29.2.1.tgz.sha256"
if [ -f "$local_binary" ]; then
log_message "INFO" "Found local Docker binary: $local_binary"
# Copy to current directory for consistency with extraction logic
cp "$local_binary" "$binary_file"
if verify_checksum "$binary_file" "$local_checksum" "local Docker binary"; then
binary_path="$binary_file"
log_message "SUCCESS" "Using local Docker binary from binaries directory"
else
log_message "WARNING" "Local Docker binary checksum verification failed"
rm -f "$binary_file"
fi
fi
fi
# 3. Check current directory for any docker-*.tgz (existing fallback)
if [ -z "$binary_path" ]; then
log_message "INFO" "Checking current directory for Docker binaries..."
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"
# Optional: Try to verify checksum if .sha256 file exists
local checksum_file="${local_files}.sha256"
if [ -f "$checksum_file" ]; then
if verify_checksum "$binary_path" "$checksum_file" "Docker binary"; then
log_message "SUCCESS" "Local Docker binary checksum verified"
else
log_message "WARNING" "Local Docker binary checksum verification failed, but continuing"
fi
fi
fi
fi
# 4. Try internal proxy (if reachable per preflight check)
if [ -z "$binary_path" ] && [ "$DOWNLOAD_REACHABLE" = "true" ]; then
# Download Docker static binary from internal server
log_message "INFO" "Downloading Docker static binary for $ARCHITECTURE from internal proxy..."
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 from internal proxy: $binary_file"
binary_path="$binary_file"
else
log_message "ERROR" "Failed to download Docker binary from $download_url"
binary_path=""
fi
fi
# 5. Final fallback: no binary available
if [ -z "$binary_path" ]; then
fail_with_remediation "No Docker binary available" \
"Please provide a Docker static binary using one of these methods:
1. GitLab (primary): Ensure network access to $GITLAB_BASE_URL
2. Local binaries directory: Place docker-29.2.1.tgz and .sha256 in $LOCAL_BINARY_DIR/
3. Current directory: Place any docker-*.tgz file in current directory
4. Internal proxy: Ensure network access to $DOCKER_DOWNLOAD_HOST
Download manually: curl -L '$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz' -o docker.tgz
Re-run the script after providing the binary."
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. Try alternative sources:
a) GitLab: curl -L '$GITLAB_DOCKER_BINARY_URL' -o docker.tgz
b) Local directory: Check $LOCAL_BINARY_DIR/docker-29.2.1.tgz
c) Internal proxy: curl -v -L '$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz' -o test.tgz
4. Verify downloaded file: file test.tgz && tar -tzf test.tgz
5. 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/"
}
# Ensure binaries are executable
log_message "INFO" "Setting executable permissions on Docker binaries..."
sudo chmod +x /usr/bin/dockerd /usr/bin/docker 2>/dev/null || {
log_message "WARNING" "Could not set executable permissions on Docker binaries"
}
# Verify Docker binaries work
log_message "INFO" "Verifying Docker binaries..."
if ! sudo /usr/bin/dockerd --version 2>/dev/null; then
fail_with_remediation "Docker binary verification failed" \
"Docker binary (/usr/bin/dockerd) appears to be corrupted or incompatible.
The binary was extracted from $binary_path but doesn't run.
Check the binary:
sudo file /usr/bin/dockerd
sudo ls -la /usr/bin/dockerd
sudo /usr/bin/dockerd --version
The Docker static binary might be for wrong architecture or corrupted.
Try downloading manually from one of these sources:
1. GitLab: curl -L '$GITLAB_DOCKER_BINARY_URL' -o docker.tgz
2. Local directory: Check $LOCAL_BINARY_DIR/docker-29.2.1.tgz
3. Internal proxy: curl -L '$DOCKER_STATIC_BASE_URL/$ARCHITECTURE/docker-$DOCKER_VERSION.tgz' -o docker.tgz
Then extract and install:
tar xzvf docker.tgz
sudo cp docker/* /usr/bin/"
else
local docker_version
docker_version=$(sudo /usr/bin/dockerd --version 2>&1 | head -1)
log_message "SUCCESS" "Docker binary verified: $docker_version"
fi
# Cleanup extracted directory
log_message "INFO" "Cleaning up extracted Docker binaries directory..."
rm -rf docker
log_message "INFO" "Cleanup completed"
# DEBUG: Mark start of docker group section
log_message "INFO" "=== Starting docker group creation ==="
# Create docker group (required for systemd socket configuration and dockerd --group)
log_message "INFO" "Creating docker group for Docker socket access..."
# Check if group already exists
log_message "INFO" "Checking if docker group exists..."
local getent_output
if getent_output=$(getent group docker 2>&1); then
getent_exit=0
else
getent_exit=$?
fi
log_message "INFO" "getent group docker result: exit=$getent_exit, output='$getent_output'"
if [ $getent_exit -eq 0 ]; then
log_message "SUCCESS" "Docker group already exists: $getent_output"
else
# Attempt to create docker group with error capture
log_message "INFO" "Attempting to create docker group with sudo groupadd docker..."
local groupadd_output
if groupadd_output=$(sudo groupadd docker 2>&1); then
groupadd_exit=0
else
groupadd_exit=$?
fi
if [ $groupadd_exit -eq 0 ]; then
log_message "SUCCESS" "Created docker group"
else
log_message "ERROR" "Failed to create docker group (exit code: $groupadd_exit)"
log_message "INFO" "groupadd command output: $groupadd_output"
# Check if group was somehow created despite error
log_message "INFO" "Checking if docker group was created despite groupadd failure..."
local check_getent_output
if check_getent_output=$(getent group docker 2>&1); then
check_getent_exit=0
else
check_getent_exit=$?
fi
log_message "INFO" "Post-failure check: exit=$check_getent_exit, output='$check_getent_output'"
if [ $check_getent_exit -eq 0 ]; then
log_message "WARNING" "Docker group exists despite groupadd failure, continuing..."
else
fail_with_remediation "Cannot create docker group" \
"The docker group is required for Docker socket access. Please create it manually:
1. Check if groupadd command is available: which groupadd
2. Check permissions: sudo -v
3. Manual group creation: sudo groupadd docker
4. Verify: getent group docker
If groupadd fails, you may need to:
- Check system user/group database
- Use alternative: sudo addgroup docker (Debian/Ubuntu)
- Edit /etc/group manually (advanced users only)"
fi
fi
fi
# Final verification that docker group exists
log_message "INFO" "Final verification of docker group existence..."
local final_getent_output
if final_getent_output=$(getent group docker 2>&1); then
final_getent_exit=0
else
final_getent_exit=$?
fi
log_message "INFO" "Final getent result: exit=$final_getent_exit, output='$final_getent_output'"
if [ $final_getent_exit -ne 0 ]; then
fail_with_remediation "Docker group verification failed" \
"The docker group does not exist after creation attempts. This will cause Docker startup to fail.
Please create the docker group manually and re-run the script:
1. sudo groupadd docker
2. Verify: getent group docker | grep docker
3. Re-run this script"
fi
# Log group details for debugging
local docker_gid
docker_gid=$(echo "$final_getent_output" | cut -d: -f3)
log_message "INFO" "Docker group details: GID=$docker_gid"
log_message "SUCCESS" "Docker group verified and ready (GID: $docker_gid)"
# DEBUG: Mark end of docker group section
log_message "INFO" "=== Finished docker group creation ==="
# 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 <<EOF
{
"storage-driver": "vfs",
"exec-opts": ["native.cgroupdriver=cgroupfs"],
"log-driver": "json-file",
"log-opts": {
"max-size": "100m",
"max-file": "3"
}
}
EOF
# Configure based on init system
log_message "INFO" "Configuring Docker service for init system: $INIT_SYSTEM"
case "$INIT_SYSTEM" in
"systemd")
# DEBUG: Mark start of systemd configuration
log_message "INFO" "=== Starting systemd configuration ==="
# Create systemd service files
sudo tee /etc/systemd/system/docker.service > /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
log_message "INFO" "Enabling Docker service to start on boot..."
if ! sudo systemctl enable docker; then
log_message "ERROR" "systemctl enable docker failed with exit code: $?"
fail_with_remediation "Failed to enable Docker service" \
"Docker service could not be enabled to start on boot. Common causes:
1. Docker socket unit (docker.socket) has configuration errors
2. The docker group may not exist
3. Systemd unit file has syntax errors
Check docker.socket status:
sudo systemctl status docker.socket --no-pager
Verify docker group exists:
getent group docker
Check systemd unit files:
sudo systemctl cat docker.socket
sudo systemctl cat docker.service"
fi
log_message "INFO" "Starting Docker service (systemd)..."
# Start the service and capture exit code
if sudo systemctl start docker; then
start_exit=0
else
start_exit=$?
fi
# Give Docker a moment to start or fail
sleep 2
# Check if service is actually active
if ! sudo systemctl is-active docker --quiet; then
log_message "ERROR" "Docker service failed to start (systemctl start exit: $start_exit)"
log_message "INFO" "Checking docker.socket status..."
sudo systemctl status docker.socket --no-pager 2>&1 | head -20 || true
log_message "INFO" "Checking docker.service status..."
sudo systemctl status docker.service --no-pager 2>&1 | head -30 || true
log_message "INFO" "Checking Docker daemon logs..."
sudo journalctl -u docker --no-pager -n 30 2>&1 | head -50 || true
log_message "INFO" "Checking Docker socket logs..."
sudo journalctl -u docker.socket --no-pager -n 20 2>&1 | head -30 || true
fail_with_remediation "Failed to start Docker service" \
"Docker service failed to start. Common causes:
1. Missing iptables (Docker static binaries require iptables v1.4+ for network bridge)
2. Docker daemon configuration error (check /etc/docker/daemon.json)
3. Storage driver issues (VFS may not be compatible)
4. Cgroup configuration problems
5. Port conflicts or resource limits
Latest Docker daemon logs:
$(sudo journalctl -u docker --no-pager -n 30 2>&1 | tail -20)
Check Docker configuration:
sudo cat /etc/docker/daemon.json
Verify iptables is installed:
which iptables || echo 'iptables not found'
iptables --version 2>/dev/null || echo 'Cannot check version'
Install iptables if missing:
# Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y iptables
# RHEL/CentOS: sudo yum install -y iptables
# Alpine: sudo apk add iptables
Verify docker group exists:
getent group docker
Manual start attempt for debugging:
sudo dockerd --group docker --debug"
fi
;;
"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
log_message "INFO" "Starting Docker service (OpenRC)..."
if ! sudo rc-service docker start; then
log_message "ERROR" "rc-service docker start failed with exit code: $?"
fail_with_remediation "Failed to start Docker service (OpenRC)" \
"Docker service failed to start under OpenRC. Common causes:
1. Missing iptables (Docker static binaries require iptables v1.4+ for network bridge)
2. Docker socket or port conflicts
3. Missing dependencies
4. Docker configuration errors
Check OpenRC logs:
sudo rc-service docker status
sudo cat /var/log/docker.log 2>/dev/null || echo "No docker.log found"
Verify iptables is installed:
which iptables || echo 'iptables not found'
iptables --version 2>/dev/null || echo 'Cannot check version'
Install iptables if missing:
# Alpine: sudo apk add iptables
# Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y iptables
# RHEL/CentOS: sudo yum install -y iptables
Verify docker group exists:
getent group docker
Manual start attempt:
sudo dockerd --group docker --debug"
fi
;;
"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
log_message "INFO" "Starting Docker service (SysV init)..."
if ! sudo service docker start; then
log_message "ERROR" "service docker start failed with exit code: $?"
fail_with_remediation "Failed to start Docker service (SysV init)" \
"Docker service failed to start under SysV init. Common causes:
1. Missing iptables (Docker static binaries require iptables v1.4+ for network bridge)
2. Docker socket or port conflicts
3. Missing dependencies or configuration errors
4. The docker group may not exist or be accessible
Check service status:
sudo service docker status
Verify iptables is installed:
which iptables || echo 'iptables not found'
iptables --version 2>/dev/null || echo 'Cannot check version'
Install iptables if missing:
# Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y iptables
# RHEL/CentOS: sudo yum install -y iptables
# Alpine: sudo apk add iptables
Verify docker group exists:
getent group docker
Check for Docker logs:
sudo dockerd --group docker --debug 2>&1 | head -50"
fi
;;
*)
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 iptables is installed (Docker static binaries require iptables v1.4+):
which iptables || echo 'iptables not found'
iptables --version 2>/dev/null || echo 'Cannot check version'
3. Install iptables if missing:
# Debian/Ubuntu: sudo apt-get update && sudo apt-get install -y iptables
# RHEL/CentOS: sudo yum install -y iptables
# Alpine: sudo apk add iptables
4. Verify configuration: sudo dockerd --debug
5. 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
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..."
# Load Wallarm Docker image (priority: GitLab -> local dir -> current dir -> internal registry)
log_message "INFO" "Loading Wallarm Docker image..."
local image_loaded=false
# 1. Try GitLab download (primary source)
local gitlab_image_file="wallarm-node-6.11.0-rc1.tar.gz"
if [ "$image_loaded" = "false" ]; then
log_message "INFO" "Attempting to download Wallarm image from GitLab..."
if download_from_gitlab "$GITLAB_WALLARM_IMAGE_URL" "$gitlab_image_file" "Wallarm Docker image"; then
if verify_checksum "$gitlab_image_file" "$GITLAB_WALLARM_CHECKSUM_URL" "Wallarm Docker image"; then
log_message "INFO" "Loading Wallarm image from GitLab download..."
if gunzip -c "$gitlab_image_file" | sudo docker load; then
log_message "SUCCESS" "Wallarm image loaded from GitLab download"
image_loaded=true
else
log_message "ERROR" "Failed to load Wallarm image from GitLab download"
fi
# Cleanup downloaded file
rm -f "$gitlab_image_file"
else
log_message "WARNING" "GitLab Wallarm image checksum verification failed"
rm -f "$gitlab_image_file"
fi
fi
fi
# 2. Check local images directory
if [ "$image_loaded" = "false" ] && [ -d "$LOCAL_IMAGE_DIR" ]; then
log_message "INFO" "Checking local images directory: $LOCAL_IMAGE_DIR"
local local_image="$LOCAL_IMAGE_DIR/wallarm-node-6.11.0-rc1.tar.gz"
local local_checksum="$LOCAL_IMAGE_DIR/wallarm-node-6.11.0-rc1.tar.gz.sha256"
if [ -f "$local_image" ]; then
log_message "INFO" "Found local Wallarm image: $local_image"
if verify_checksum "$local_image" "$local_checksum" "local Wallarm image"; then
log_message "INFO" "Loading Wallarm image from local directory..."
if gunzip -c "$local_image" | sudo docker load; then
log_message "SUCCESS" "Wallarm image loaded from local directory"
image_loaded=true
else
log_message "ERROR" "Failed to load Wallarm image from local directory"
fi
else
log_message "WARNING" "Local Wallarm image checksum verification failed"
fi
fi
fi
# 3. Check current directory for compressed image (tar.gz)
if [ "$image_loaded" = "false" ]; then
log_message "INFO" "Checking current directory for Wallarm image (tar.gz)..."
local gz_image
gz_image=$(ls wallarm-node-*.tar.gz 2>/dev/null | head -1)
if [ -n "$gz_image" ]; then
log_message "INFO" "Found compressed Wallarm image: $gz_image"
# Verify checksum if .sha256 file exists
local checksum_file="${gz_image}.sha256"
if [ -f "$checksum_file" ]; then
if ! verify_checksum "$gz_image" "$checksum_file" "Wallarm image"; then
log_message "WARNING" "Wallarm image checksum verification failed, but attempting load anyway"
fi
fi
log_message "INFO" "Loading compressed Wallarm image..."
if gunzip -c "$gz_image" | sudo docker load; then
log_message "SUCCESS" "Wallarm image loaded from compressed file"
image_loaded=true
else
log_message "ERROR" "Failed to load Wallarm image from $gz_image"
fi
fi
fi
# 4. Check current directory for uncompressed image (tar) - existing fallback
if [ "$image_loaded" = "false" ]; then
log_message "INFO" "Checking current directory for Wallarm image (tar)..."
local tar_image
tar_image=$(ls wallarm-node-*.tar 2>/dev/null | head -1)
if [ -n "$tar_image" ]; then
log_message "INFO" "Found uncompressed Wallarm image: $tar_image"
if ! sudo docker load -i "$tar_image"; then
log_message "ERROR" "Failed to load Wallarm image from $tar_image"
else
log_message "SUCCESS" "Wallarm image loaded from uncompressed file"
image_loaded=true
fi
fi
fi
# 5. Try internal registry (if reachable per preflight check)
if [ "$image_loaded" = "false" ] && [ "$REGISTRY_REACHABLE" = "true" ]; then
log_message "INFO" "Pulling Wallarm Docker image from internal registry: $WALLARM_IMAGE_SOURCE"
if ! sudo docker pull "$WALLARM_IMAGE_SOURCE"; then
log_message "ERROR" "Failed to pull Wallarm image from internal registry"
else
# Re-tag to standard name
sudo docker tag "$WALLARM_IMAGE_SOURCE" "$WALLARM_IMAGE_TARGET"
log_message "SUCCESS" "Wallarm image pulled and tagged successfully from internal registry"
image_loaded=true
fi
fi
# 6. Final fallback: no image available
if [ "$image_loaded" = "false" ]; then
fail_with_remediation "No Wallarm image available" \
"Please provide a Wallarm Docker image using one of these methods:
1. GitLab (primary): Ensure network access to $GITLAB_BASE_URL
2. Local images directory: Place wallarm-node-6.11.0-rc1.tar.gz and .sha256 in $LOCAL_IMAGE_DIR/
3. Current directory: Place wallarm-node-*.tar.gz or wallarm-node-*.tar file in current directory
4. Internal registry: Ensure network access to $DOCKER_REGISTRY_HOST
Download manually: docker pull $WALLARM_IMAGE_SOURCE
Save for offline use: docker save $WALLARM_IMAGE_TARGET -o wallarm-node-latest.tar
Re-run the script after providing the image."
fi
# Ensure image is tagged with standard name (for consistency)
if [ "$image_loaded" = "true" ] && [ "$REGISTRY_REACHABLE" = "false" ]; then
# If we loaded from local file, tag the loaded image with standard name
local loaded_image_id
loaded_image_id=$(sudo docker images --format "{{.ID}}" --filter "dangling=false" | head -1)
if [ -n "$loaded_image_id" ]; then
sudo docker tag "$loaded_image_id" "$WALLARM_IMAGE_TARGET"
log_message "INFO" "Tagged loaded image as $WALLARM_IMAGE_TARGET"
fi
fi
# Create nginx configuration
log_message "INFO" "Creating nginx configuration..."
local nginx_config="$INSTANCE_DIR/nginx.conf"
# Start building the configuration file
sudo tee "$nginx_config" > /dev/null <<EOF
server {
listen 80;
server_name _;
EOF
# Add set_real_ip_from directives if trusted proxies are configured
if [[ -n "$TRUSTED_PROXIES" ]]; then
# Add each trusted proxy IP/CIDR as a separate line
for proxy in $TRUSTED_PROXIES; do
sudo tee -a "$nginx_config" > /dev/null <<EOF
set_real_ip_from $proxy;
EOF
done
# Add the real_ip configuration
sudo tee -a "$nginx_config" > /dev/null <<EOF
real_ip_header X-Real-IP;
real_ip_recursive on;
EOF
fi
# Add the rest of the configuration
sudo tee -a "$nginx_config" > /dev/null <<EOF
location / {
proxy_pass http://$UPSTREAM_IP:$UPSTREAM_PORT;
proxy_set_header Host \$host;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-Server \$http_x_forwarded_server;
# Wallarm-specific headers
wallarm_mode monitoring;
wallarm_instance 1;
}
# Health check endpoint
location /health {
access_log off;
return 200 "healthy\n";
add_header Content-Type text/plain;
}
}
EOF
log_message "SUCCESS" "Nginx configuration created: $nginx_config"
if [[ -n "$TRUSTED_PROXIES" ]]; then
log_message "INFO" " Configured trusted proxies: $TRUSTED_PROXIES"
else
log_message "INFO" " No trusted proxies configured - real client IP detection may be limited"
fi
# Create start.sh script for persistence
log_message "INFO" "Creating start script for persistence..."
local start_script="$INSTANCE_DIR/start.sh"
sudo tee "$start_script" > /dev/null <<EOF
#!/bin/bash
# Start script for Wallarm filtering node: $INSTANCE_NAME
# Generated: $(date)
CONTAINER_NAME="$INSTANCE_NAME"
NGINX_CONFIG="$nginx_config"
LOG_FILE="$INSTANCE_DIR/container.log"
echo "\$(date) - Starting Wallarm node \$CONTAINER_NAME" >> "\$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 \\
-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 <<EOF
[Unit]
Description=Wallarm Filtering Node: $INSTANCE_NAME
After=docker.service
Requires=docker.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=$start_script
ExecStop=/usr/bin/docker stop $INSTANCE_NAME
ExecStopPost=/usr/bin/docker rm $INSTANCE_NAME
WorkingDirectory=$INSTANCE_DIR
User=root
Group=root
[Install]
WantedBy=multi-user.target
EOF
sudo systemctl daemon-reload
sudo systemctl enable "wallarm-$INSTANCE_NAME.service" 2>/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 <<EOF
#!/sbin/openrc-run
description="Wallarm Filtering Node: $INSTANCE_NAME"
command="$start_script"
command_args=""
pidfile="/run/wallarm-\${RC_SVCNAME}.pid"
command_background=true
depend() {
need docker
after docker
}
start() {
ebegin "Starting Wallarm node: $INSTANCE_NAME"
\$command
eend \$?
}
stop() {
ebegin "Stopping Wallarm node: $INSTANCE_NAME"
docker stop $INSTANCE_NAME 2>/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 <<EOF
#!/bin/bash
### BEGIN INIT INFO
# Provides: wallarm-$INSTANCE_NAME
# Required-Start: \$local_fs \$network docker
# Required-Stop: \$local_fs \$network
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Wallarm Filtering Node: $INSTANCE_NAME
# Description: Wallarm security filtering node
### END INIT INFO
DESC="Wallarm Filtering Node: $INSTANCE_NAME"
SCRIPT="$start_script"
PIDFILE="/var/run/wallarm-$INSTANCE_NAME.pid"
. /lib/lsb/init-functions
case "\$1" in
start)
log_daemon_msg "Starting \$DESC" "$INSTANCE_NAME"
\$SCRIPT
log_end_msg \$?
;;
stop)
log_daemon_msg "Stopping \$DESC" "$INSTANCE_NAME"
docker stop $INSTANCE_NAME 2>/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.2 ║"
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 "$@"