#!/bin/bash # ZitiNexus Router Enrollment UI Installation Script # For Ubuntu 22.04/24.04 LTS set -euo pipefail # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Configuration WEB_DIR="/var/www/ziti-enrollment" DOMAIN="ziti-enrollment.local" WEB_USER="www-data" PHP_VERSION="" # Logging function log() { local level=$1 shift local message="$*" local timestamp=$(date '+%Y-%m-%d %H:%M:%S') case $level in "ERROR") echo -e "${RED}[ERROR]${NC} $message" >&2 ;; "SUCCESS") echo -e "${GREEN}[SUCCESS]${NC} $message" ;; "WARNING") echo -e "${YELLOW}[WARNING]${NC} $message" ;; "INFO") echo -e "${BLUE}[INFO]${NC} $message" ;; *) echo "$message" ;; esac } # Error handling error_exit() { log "ERROR" "$1" exit 1 } # Check if running as root check_root() { if [[ $EUID -ne 0 ]]; then error_exit "This script must be run as root (use sudo)" fi } # Detect available PHP version detect_php_version() { log "INFO" "Detecting available PHP version..." # Update package cache first apt update >/dev/null 2>&1 # Check for available PHP versions in order of preference for version in "8.3" "8.2" "8.1" "8.0"; do log "INFO" "Checking for PHP $version..." # Check multiple ways to ensure package availability if apt-cache show "php${version}" >/dev/null 2>&1 || \ apt-cache show "php${version}-cli" >/dev/null 2>&1 || \ apt list "php${version}" 2>/dev/null | grep -q "php${version}"; then # Double-check that the FPM package exists for Nginx if apt-cache show "php${version}-fpm" >/dev/null 2>&1 || \ apt list "php${version}-fpm" 2>/dev/null | grep -q "php${version}-fpm"; then PHP_VERSION="$version" log "SUCCESS" "Found PHP $PHP_VERSION with FPM support" break else log "WARNING" "PHP $version found but FPM package not available" fi else log "INFO" "PHP $version not available" fi done if [[ -z "$PHP_VERSION" ]]; then log "ERROR" "No compatible PHP version found. Available packages:" apt list --installed | grep php 2>/dev/null || echo "No PHP packages found" log "INFO" "Trying to install default PHP..." # Fallback: try to install default php package if apt-cache show "php" >/dev/null 2>&1; then log "INFO" "Found default PHP package, will use system default" # Get the default PHP version PHP_VERSION=$(apt-cache show php | grep "^Depends:" | grep -o "php[0-9]\+\.[0-9]\+" | head -1 | sed 's/php//') if [[ -n "$PHP_VERSION" ]]; then log "SUCCESS" "Will use system default PHP $PHP_VERSION" else error_exit "Could not determine PHP version from default package" fi else error_exit "No PHP packages available. Please install PHP manually first." fi fi log "SUCCESS" "Selected PHP version: $PHP_VERSION" } # Detect web server preference detect_web_server() { echo log "INFO" "Which web server would you like to use?" echo "1) Apache (recommended)" echo "2) Nginx" read -p "Enter your choice (1-2): " choice case $choice in 1) WEB_SERVER="apache" ;; 2) WEB_SERVER="nginx" ;; *) log "WARNING" "Invalid choice, defaulting to Apache" WEB_SERVER="apache" ;; esac log "INFO" "Selected web server: $WEB_SERVER" } # Install web server and PHP install_web_server() { log "INFO" "Installing web server and PHP $PHP_VERSION..." # Update package list apt update || error_exit "Failed to update package list" # Determine required PHP packages PHP_PACKAGES="php${PHP_VERSION} php${PHP_VERSION}-curl" # JSON support is bundled in modern PHP versions (php-cli, php-fpm, etc.) log "INFO" "Skipping php${PHP_VERSION}-json as it's bundled in php${PHP_VERSION}-cli" if [[ "$WEB_SERVER" == "apache" ]]; then # Install Apache and PHP apt install -y apache2 $PHP_PACKAGES libapache2-mod-php${PHP_VERSION} || error_exit "Failed to install Apache and PHP" # Enable and start Apache systemctl enable apache2 || error_exit "Failed to enable Apache" systemctl start apache2 || error_exit "Failed to start Apache" log "SUCCESS" "Apache and PHP $PHP_VERSION installed successfully" elif [[ "$WEB_SERVER" == "nginx" ]]; then # Install Nginx and PHP-FPM apt install -y nginx php${PHP_VERSION}-fpm $PHP_PACKAGES || error_exit "Failed to install Nginx and PHP" # Enable and start services systemctl enable nginx php${PHP_VERSION}-fpm || error_exit "Failed to enable Nginx and PHP-FPM" systemctl start nginx php${PHP_VERSION}-fpm || error_exit "Failed to start Nginx and PHP-FPM" log "SUCCESS" "Nginx and PHP $PHP_VERSION installed successfully" fi } # Deploy UI files deploy_ui() { log "INFO" "Deploying UI files..." # Create web directory mkdir -p "$WEB_DIR" || error_exit "Failed to create web directory" # Check for required directories log "INFO" "Current directory: $(pwd)" log "INFO" "Directory contents: $(ls -la)" if [[ ! -d "public" ]]; then error_exit "public directory not found. Current directory: $(pwd)" fi if [[ ! -d "includes" ]]; then error_exit "includes directory not found. Current directory: $(pwd)" fi log "INFO" "Found required directories: public and includes" # Copy main UI files (assets are now inside public) cp -r public includes "$WEB_DIR/" || error_exit "Failed to copy UI files" # Copy root-level PHP files for direct access (when document root is main directory) if [[ -f "index.php" ]]; then cp index.php "$WEB_DIR/" || log "WARNING" "Failed to copy root index.php" fi if [[ -f "dashboard.php" ]]; then cp dashboard.php "$WEB_DIR/" || log "WARNING" "Failed to copy root dashboard.php" fi # Copy optional files if they exist if [[ -f "README.md" ]]; then cp README.md "$WEB_DIR/" || log "WARNING" "Failed to copy README.md" fi if [[ -f ".htaccess" ]]; then cp .htaccess "$WEB_DIR/" || log "WARNING" "Failed to copy .htaccess" fi # Create logs and temp directories if they don't exist if [[ -d "logs" ]]; then cp -r logs "$WEB_DIR/" || log "WARNING" "Failed to copy logs directory" else mkdir -p "$WEB_DIR/logs" || error_exit "Failed to create logs directory" log "INFO" "Created logs directory" fi if [[ -d "temp" ]]; then cp -r temp "$WEB_DIR/" || log "WARNING" "Failed to copy temp directory" else mkdir -p "$WEB_DIR/temp" || error_exit "Failed to create temp directory" log "INFO" "Created temp directory" fi # Set proper permissions chown -R "$WEB_USER:$WEB_USER" "$WEB_DIR" || error_exit "Failed to set ownership" chmod -R 755 "$WEB_DIR" || error_exit "Failed to set permissions" chmod -R 777 "$WEB_DIR/logs" "$WEB_DIR/temp" || error_exit "Failed to set log/temp permissions" log "SUCCESS" "UI files deployed successfully" } # Configure Apache configure_apache() { log "INFO" "Configuring Apache virtual host..." # Create virtual host configuration cat > "/etc/apache2/sites-available/ziti-enrollment.conf" << EOF ServerName $DOMAIN DocumentRoot $WEB_DIR/public AllowOverride All Require all granted DirectoryIndex index.php ErrorLog \${APACHE_LOG_DIR}/ziti-enrollment_error.log CustomLog \${APACHE_LOG_DIR}/ziti-enrollment_access.log combined EOF # Enable site and modules a2ensite ziti-enrollment.conf || error_exit "Failed to enable site" a2enmod rewrite || error_exit "Failed to enable rewrite module" # Disable default site a2dissite 000-default.conf || log "WARNING" "Failed to disable default site" # Reload Apache systemctl reload apache2 || error_exit "Failed to reload Apache" log "SUCCESS" "Apache configured successfully" } # Configure Nginx configure_nginx() { log "INFO" "Configuring Nginx virtual host..." # Create virtual host configuration cat > "/etc/nginx/sites-available/ziti-enrollment" << EOF server { listen 80; server_name $DOMAIN; root $WEB_DIR/public; index index.php; location / { try_files \$uri \$uri/ /index.php?\$query_string; } location ~ \.php$ { fastcgi_pass unix:/var/run/php/php${PHP_VERSION}-fpm.sock; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME \$realpath_root\$fastcgi_script_name; include fastcgi_params; } location ~ /\. { deny all; } } EOF # Enable site ln -sf "/etc/nginx/sites-available/ziti-enrollment" "/etc/nginx/sites-enabled/" || error_exit "Failed to enable site" # Remove default site rm -f "/etc/nginx/sites-enabled/default" || log "WARNING" "Failed to remove default site" # Test and reload Nginx nginx -t || error_exit "Nginx configuration test failed" systemctl reload nginx || error_exit "Failed to reload Nginx" log "SUCCESS" "Nginx configured successfully" } # Configure PHP configure_php() { log "INFO" "Configuring PHP..." # Find PHP configuration file if [[ "$WEB_SERVER" == "apache" ]]; then PHP_INI="/etc/php/${PHP_VERSION}/apache2/php.ini" else PHP_INI="/etc/php/${PHP_VERSION}/fpm/php.ini" fi # Check if exec functions are disabled if grep -q "disable_functions.*exec" "$PHP_INI"; then log "WARNING" "PHP exec functions may be disabled. Please check $PHP_INI" log "INFO" "Ensure exec, shell_exec, proc_open are NOT in disable_functions" fi # Restart web server to apply PHP changes if [[ "$WEB_SERVER" == "apache" ]]; then systemctl restart apache2 || error_exit "Failed to restart Apache" else systemctl restart php${PHP_VERSION}-fpm || error_exit "Failed to restart PHP-FPM" fi log "SUCCESS" "PHP configured successfully" } # Set up sudo access setup_sudo() { log "INFO" "Setting up comprehensive sudo access for web server..." log "INFO" "This configuration works on both normal Ubuntu and CloudStack instances" # Create comprehensive sudoers file cat > "/etc/sudoers.d/ziti-enrollment" << 'EOF' # Allow www-data to run system commands for Ziti enrollment # Comprehensive permissions for all environments (normal Ubuntu + CloudStack) # Core system commands www-data ALL=(ALL) NOPASSWD: /usr/bin/apt-get www-data ALL=(ALL) NOPASSWD: /usr/bin/systemctl www-data ALL=(ALL) NOPASSWD: /usr/bin/mkdir www-data ALL=(ALL) NOPASSWD: /usr/bin/chmod www-data ALL=(ALL) NOPASSWD: /usr/bin/chown www-data ALL=(ALL) NOPASSWD: /usr/bin/cp www-data ALL=(ALL) NOPASSWD: /usr/bin/mv www-data ALL=(ALL) NOPASSWD: /usr/bin/rm www-data ALL=(ALL) NOPASSWD: /usr/bin/ln # Network and download commands www-data ALL=(ALL) NOPASSWD: /usr/bin/curl www-data ALL=(ALL) NOPASSWD: /usr/bin/wget # GPG and security commands www-data ALL=(ALL) NOPASSWD: /usr/bin/gpg www-data ALL=(ALL) NOPASSWD: /usr/bin/ziti # Information gathering commands www-data ALL=(ALL) NOPASSWD: /usr/bin/which www-data ALL=(ALL) NOPASSWD: /usr/bin/hostname www-data ALL=(ALL) NOPASSWD: /usr/bin/uname www-data ALL=(ALL) NOPASSWD: /usr/bin/lsb_release www-data ALL=(ALL) NOPASSWD: /usr/bin/whoami www-data ALL=(ALL) NOPASSWD: /usr/bin/id www-data ALL=(ALL) NOPASSWD: /usr/bin/pwd www-data ALL=(ALL) NOPASSWD: /usr/bin/date # File operations www-data ALL=(ALL) NOPASSWD: /usr/bin/tee www-data ALL=(ALL) NOPASSWD: /usr/bin/cat www-data ALL=(ALL) NOPASSWD: /usr/bin/test www-data ALL=(ALL) NOPASSWD: /usr/bin/ls www-data ALL=(ALL) NOPASSWD: /usr/bin/touch www-data ALL=(ALL) NOPASSWD: /usr/bin/echo www-data ALL=(ALL) NOPASSWD: /usr/bin/head www-data ALL=(ALL) NOPASSWD: /usr/bin/tail www-data ALL=(ALL) NOPASSWD: /usr/bin/wc www-data ALL=(ALL) NOPASSWD: /usr/bin/grep www-data ALL=(ALL) NOPASSWD: /usr/bin/sed www-data ALL=(ALL) NOPASSWD: /usr/bin/awk www-data ALL=(ALL) NOPASSWD: /usr/bin/cut www-data ALL=(ALL) NOPASSWD: /usr/bin/sort www-data ALL=(ALL) NOPASSWD: /usr/bin/uniq # Network diagnostic commands www-data ALL=(ALL) NOPASSWD: /usr/bin/nslookup www-data ALL=(ALL) NOPASSWD: /usr/bin/ping www-data ALL=(ALL) NOPASSWD: /usr/bin/dig www-data ALL=(ALL) NOPASSWD: /usr/bin/host # Process and system monitoring www-data ALL=(ALL) NOPASSWD: /usr/bin/ps www-data ALL=(ALL) NOPASSWD: /usr/bin/top www-data ALL=(ALL) NOPASSWD: /usr/bin/htop www-data ALL=(ALL) NOPASSWD: /usr/bin/free www-data ALL=(ALL) NOPASSWD: /usr/bin/df www-data ALL=(ALL) NOPASSWD: /usr/bin/du # Text processing and utilities www-data ALL=(ALL) NOPASSWD: /usr/bin/find www-data ALL=(ALL) NOPASSWD: /usr/bin/xargs www-data ALL=(ALL) NOPASSWD: /usr/bin/basename www-data ALL=(ALL) NOPASSWD: /usr/bin/dirname www-data ALL=(ALL) NOPASSWD: /usr/bin/realpath www-data ALL=(ALL) NOPASSWD: /usr/bin/readlink # Archive and compression www-data ALL=(ALL) NOPASSWD: /usr/bin/tar www-data ALL=(ALL) NOPASSWD: /usr/bin/gzip www-data ALL=(ALL) NOPASSWD: /usr/bin/gunzip www-data ALL=(ALL) NOPASSWD: /usr/bin/zip www-data ALL=(ALL) NOPASSWD: /usr/bin/unzip # Shell and environment www-data ALL=(ALL) NOPASSWD: /bin/bash www-data ALL=(ALL) NOPASSWD: /bin/sh www-data ALL=(ALL) NOPASSWD: /usr/bin/env www-data ALL=(ALL) NOPASSWD: /usr/bin/sleep www-data ALL=(ALL) NOPASSWD: /usr/bin/timeout EOF # Validate sudoers file if visudo -c -f "/etc/sudoers.d/ziti-enrollment"; then log "SUCCESS" "Comprehensive sudo access configured successfully" log "INFO" "Configuration includes all permissions needed for any environment" else error_exit "Invalid sudoers configuration" fi } # Update hosts file update_hosts() { log "INFO" "Updating hosts file..." # Check if entry already exists if ! grep -q "$DOMAIN" /etc/hosts; then echo "127.0.0.1 $DOMAIN" >> /etc/hosts log "SUCCESS" "Added $DOMAIN to hosts file" else log "INFO" "Domain already exists in hosts file" fi } # Test installation test_installation() { log "INFO" "Testing installation..." # Test web server if [[ "$WEB_SERVER" == "apache" ]]; then if systemctl is-active --quiet apache2; then log "SUCCESS" "Apache is running" else log "ERROR" "Apache is not running" fi else if systemctl is-active --quiet nginx && systemctl is-active --quiet php${PHP_VERSION}-fpm; then log "SUCCESS" "Nginx and PHP-FPM are running" else log "ERROR" "Nginx or PHP-FPM is not running" fi fi # Test PHP if php -v > /dev/null 2>&1; then log "SUCCESS" "PHP is working" # Test JSON support if php -r 'var_dump(function_exists("json_encode"));' 2>/dev/null | grep -q "bool(true)"; then log "SUCCESS" "PHP JSON support is available" else log "WARNING" "PHP JSON support may not be available" fi else log "ERROR" "PHP is not working" fi # Test sudo access if sudo -u www-data sudo -n systemctl --version > /dev/null 2>&1; then log "SUCCESS" "Sudo access is working" else log "WARNING" "Sudo access may not be working properly" fi # Test file permissions if [[ -r "$WEB_DIR/public/index.php" ]]; then log "SUCCESS" "File permissions are correct" else log "ERROR" "File permissions may be incorrect" fi } # Show final information show_final_info() { echo echo "==============================================" echo " INSTALLATION COMPLETED" echo "==============================================" echo log "SUCCESS" "ZitiNexus Router Enrollment UI installed successfully!" echo echo "Access Information:" echo " URL: http://$DOMAIN" echo " Username: admin" echo " Password: admin123" echo echo "Important Notes:" echo " 1. Change the default password in production" echo " 2. Consider setting up HTTPS for production use" echo " 3. Review security settings in $WEB_DIR/includes/config.php" echo echo "File Locations:" echo " Web Directory: $WEB_DIR" echo " Configuration: $WEB_DIR/includes/config.php" echo " Logs: $WEB_DIR/logs/" echo echo "Useful Commands:" if [[ "$WEB_SERVER" == "apache" ]]; then echo " Check status: systemctl status apache2" echo " View logs: tail -f /var/log/apache2/ziti-enrollment_error.log" else echo " Check status: systemctl status nginx php${PHP_VERSION}-fpm" echo " View logs: tail -f /var/log/nginx/error.log" fi echo " Test sudo: sudo -u www-data sudo -l" echo } # Main installation function main() { echo "==============================================" echo " ZitiNexus Router Enrollment UI Installer" echo "==============================================" echo # Check if running as root check_root # Detect available PHP version detect_php_version # Detect web server preference detect_web_server # Install web server and PHP install_web_server # Deploy UI files deploy_ui # Configure web server if [[ "$WEB_SERVER" == "apache" ]]; then configure_apache else configure_nginx fi # Configure PHP configure_php # Set up sudo access setup_sudo # Update hosts file update_hosts # Test installation test_installation # Show final information show_final_info } # Run main function main "$@"