#!/bin/bash ################################################################################ # GCM VM Configuration Wizard # Version: 1.0.0 # Date: 2026-06-11 # # Purpose: Interactive wizard to configure VM network and system settings # for GCM OVF deployment # # Usage: ./configure_vm.sh # ################################################################################ set -euo pipefail # Color codes RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' CYAN='\033[0;36m' MAGENTA='\033[0;35m' NC='\033[0m' # Logging functions log_info() { echo -e "${BLUE}[INFO]${NC} $1"; } log_success() { echo -e "${GREEN}[✓]${NC} $1"; } log_warning() { echo -e "${YELLOW}[⚠]${NC} $1"; } log_error() { echo -e "${RED}[✗]${NC} $1"; } log_step() { echo -e "${MAGENTA}[STEP $1]${NC} $2"; } log_section() { echo "" echo -e "${CYAN}╔════════════════════════════════════════════════════════════════╗${NC}" echo -e "${CYAN}║${NC} $1" echo -e "${CYAN}╚════════════════════════════════════════════════════════════════╝${NC}" echo "" } # Configuration directory CONFIG_DIR="/etc/gcm" CONFIG_FILE="$CONFIG_DIR/vm-config.env" BACKUP_DIR="$CONFIG_DIR/backups" # Ensure running as root if [ "$EUID" -ne 0 ]; then log_error "This script must be run as root" exit 1 fi # Create configuration directory mkdir -p "$CONFIG_DIR" mkdir -p "$BACKUP_DIR" # Backup existing configuration if present if [ -f "$CONFIG_FILE" ]; then BACKUP_FILE="$BACKUP_DIR/vm-config.env.$(date +%Y%m%d_%H%M%S)" cp "$CONFIG_FILE" "$BACKUP_FILE" log_info "Backed up existing configuration to $BACKUP_FILE" fi ################################################################################ # Helper Functions ################################################################################ # Validate IP address format validate_ip() { local ip=$1 if [[ $ip =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then IFS='.' read -ra ADDR <<< "$ip" for i in "${ADDR[@]}"; do if [ "$i" -gt 255 ]; then return 1 fi done return 0 fi return 1 } # Convert subnet mask to CIDR notation subnet_to_cidr() { local mask=$1 local nbits=0 IFS='.' read -ra octets <<< "$mask" for octet in "${octets[@]}"; do case $octet in 255) ((nbits+=8));; 254) ((nbits+=7));; 252) ((nbits+=6));; 248) ((nbits+=5));; 240) ((nbits+=4));; 224) ((nbits+=3));; 192) ((nbits+=2));; 128) ((nbits+=1));; 0) ;; *) echo "24"; return;; # Default to /24 for invalid masks esac done echo "$nbits" } # Validate FQDN format validate_fqdn() { local fqdn=$1 if [[ $fqdn =~ ^[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$ ]]; then return 0 fi return 1 } # Get current network configuration get_current_ip() { hostname -I | awk '{print $1}' } get_current_gateway() { ip route | grep default | awk '{print $3}' | head -1 } get_current_dns() { grep "^nameserver" /etc/resolv.conf | head -1 | awk '{print $2}' } get_current_hostname() { hostname -s } get_current_domain() { hostname -d } # Prompt with default value prompt_with_default() { local prompt=$1 local default=$2 local result if [ -n "$default" ]; then read -p "$prompt [$default]: " result echo "${result:-$default}" else read -p "$prompt: " result echo "$result" fi } # Test network connectivity test_connectivity() { local target=$1 local description=$2 if ping -c 2 -W 2 "$target" &>/dev/null; then log_success "$description reachable" return 0 else log_error "$description not reachable" return 1 fi } # Test DNS resolution test_dns() { local hostname=$1 if host "$hostname" &>/dev/null; then local resolved_ip=$(host "$hostname" | grep "has address" | awk '{print $4}' | head -1) log_success "DNS resolution: $hostname -> $resolved_ip" return 0 else log_error "DNS resolution failed for $hostname" return 1 fi } ################################################################################ # Main Configuration Wizard ################################################################################ log_section "GCM VM Configuration Wizard" log_info "This wizard will configure your VM for GCM deployment." log_info "Current configuration will be detected and offered as defaults." echo "" # Get current configuration CURRENT_IP=$(get_current_ip) CURRENT_GATEWAY=$(get_current_gateway) CURRENT_DNS=$(get_current_dns) CURRENT_HOSTNAME=$(get_current_hostname) CURRENT_DOMAIN=$(get_current_domain) ################################################################################ # STEP 1: Network Configuration ################################################################################ log_step "1/6" "Network Configuration" echo "────────────────────────────────────────────────────────────────" log_info "Current IP: $CURRENT_IP" echo "" # Ask if they want static IP USE_STATIC=$(prompt_with_default "Configure static IP? (yes/no)" "yes") if [[ "$USE_STATIC" =~ ^[Yy] ]]; then # IP Address while true; do VM_IP_ADDRESS=$(prompt_with_default "Enter IP address" "$CURRENT_IP") if validate_ip "$VM_IP_ADDRESS"; then break else log_error "Invalid IP address format" fi done # Subnet Mask VM_SUBNET_MASK=$(prompt_with_default "Enter subnet mask" "255.255.255.0") # Gateway while true; do VM_GATEWAY=$(prompt_with_default "Enter gateway" "$CURRENT_GATEWAY") if validate_ip "$VM_GATEWAY"; then break else log_error "Invalid gateway IP address" fi done # DNS Servers while true; do VM_DNS_PRIMARY=$(prompt_with_default "Enter primary DNS" "$CURRENT_DNS") if validate_ip "$VM_DNS_PRIMARY"; then break else log_error "Invalid DNS IP address" fi done VM_DNS_SECONDARY=$(prompt_with_default "Enter secondary DNS (optional)" "") else # Use DHCP VM_IP_ADDRESS="$CURRENT_IP" VM_SUBNET_MASK="255.255.255.0" VM_GATEWAY="$CURRENT_GATEWAY" VM_DNS_PRIMARY="$CURRENT_DNS" VM_DNS_SECONDARY="" log_info "Using DHCP configuration" fi echo "" ################################################################################ # STEP 2: Hostname Configuration ################################################################################ log_step "2/6" "Hostname Configuration" echo "────────────────────────────────────────────────────────────────" if [ -n "$CURRENT_HOSTNAME" ] && [ "$CURRENT_HOSTNAME" != "localhost" ]; then log_info "Current hostname: $CURRENT_HOSTNAME" fi if [ -n "$CURRENT_DOMAIN" ]; then log_info "Current domain: $CURRENT_DOMAIN" fi echo "" # Hostname while true; do VM_HOSTNAME=$(prompt_with_default "Enter hostname (short name)" "${CURRENT_HOSTNAME:-gcm-server}") if [[ $VM_HOSTNAME =~ ^[a-zA-Z0-9-]+$ ]]; then break else log_error "Invalid hostname (use only letters, numbers, and hyphens)" fi done # Domain while true; do VM_DOMAIN=$(prompt_with_default "Enter domain name" "${CURRENT_DOMAIN:-example.com}") if [[ $VM_DOMAIN =~ ^[a-zA-Z0-9.-]+$ ]]; then break else log_error "Invalid domain name" fi done # Construct FQDN VM_FQDN="${VM_HOSTNAME}.${VM_DOMAIN}" echo "" log_info "FQDN will be: $VM_FQDN" echo "" CONFIRM_FQDN=$(prompt_with_default "Confirm FQDN? (yes/no)" "yes") if [[ ! "$CONFIRM_FQDN" =~ ^[Yy] ]]; then log_error "Configuration cancelled by user" exit 0 fi echo "" ################################################################################ # STEP 3: Time Configuration ################################################################################ log_step "3/6" "Time Configuration" echo "────────────────────────────────────────────────────────────────" CURRENT_TIMEZONE=$(timedatectl show -p Timezone --value 2>/dev/null || echo "UTC") log_info "Current timezone: $CURRENT_TIMEZONE" VM_TIMEZONE=$(prompt_with_default "Enter timezone" "$CURRENT_TIMEZONE") VM_NTP_SERVER=$(prompt_with_default "Enter NTP server (optional)" "pool.ntp.org") echo "" ################################################################################ # STEP 4: Firewall Configuration ################################################################################ log_step "4/6" "Firewall Configuration" echo "────────────────────────────────────────────────────────────────" log_info "GCM requires ports 30443 (OIDC) and 31443 (App) to be open" echo "" VM_FIREWALL_AUTO=$(prompt_with_default "Configure firewall automatically? (yes/no)" "yes") echo "" ################################################################################ # STEP 5: Configuration Summary ################################################################################ log_step "5/6" "Configuration Summary" echo "────────────────────────────────────────────────────────────────" echo "" log_info "Network:" log_info " IP Address: $VM_IP_ADDRESS" log_info " Subnet Mask: $VM_SUBNET_MASK" log_info " Gateway: $VM_GATEWAY" log_info " DNS Primary: $VM_DNS_PRIMARY" if [ -n "$VM_DNS_SECONDARY" ]; then log_info " DNS Secondary: $VM_DNS_SECONDARY" fi echo "" log_info "System:" log_info " Hostname: $VM_HOSTNAME" log_info " Domain: $VM_DOMAIN" log_info " FQDN: $VM_FQDN" log_info " Timezone: $VM_TIMEZONE" if [ -n "$VM_NTP_SERVER" ]; then log_info " NTP: $VM_NTP_SERVER" fi echo "" log_info "Firewall:" if [[ "$VM_FIREWALL_AUTO" =~ ^[Yy] ]]; then log_info " Auto-configure: Yes" log_info " Ports: 30443, 31443" else log_info " Auto-configure: No (manual configuration required)" fi echo "" while true; do ACTION=$(prompt_with_default "Apply this configuration? (yes/no/edit)" "yes") case "$ACTION" in [Yy]*) break ;; [Nn]*) log_info "Configuration cancelled by user" exit 0 ;; [Ee]*) log_info "Restarting configuration wizard..." exec "$0" ;; *) log_error "Please answer yes, no, or edit" ;; esac done echo "" ################################################################################ # STEP 6: Apply Configuration and Test ################################################################################ log_step "6/6" "Applying Configuration" echo "────────────────────────────────────────────────────────────────" echo "" # Save configuration file log_info "Saving configuration to $CONFIG_FILE..." cat > "$CONFIG_FILE" << EOF # GCM VM Configuration # Generated: $(date '+%Y-%m-%d %H:%M:%S') # Network Configuration VM_IP_ADDRESS="$VM_IP_ADDRESS" VM_SUBNET_MASK="$VM_SUBNET_MASK" VM_GATEWAY="$VM_GATEWAY" VM_DNS_PRIMARY="$VM_DNS_PRIMARY" VM_DNS_SECONDARY="$VM_DNS_SECONDARY" # Hostname Configuration VM_HOSTNAME="$VM_HOSTNAME" VM_DOMAIN="$VM_DOMAIN" VM_FQDN="$VM_FQDN" # Time Configuration VM_TIMEZONE="$VM_TIMEZONE" VM_NTP_SERVER="$VM_NTP_SERVER" # Firewall Configuration VM_FIREWALL_AUTO="$VM_FIREWALL_AUTO" VM_FIREWALL_PORTS="30443,31443" EOF chmod 600 "$CONFIG_FILE" log_success "Configuration saved" # Apply hostname log_info "Applying hostname configuration..." hostnamectl set-hostname "$VM_FQDN" log_success "Hostname set to $VM_FQDN" # Apply timezone if [ -n "$VM_TIMEZONE" ]; then log_info "Setting timezone to $VM_TIMEZONE..." timedatectl set-timezone "$VM_TIMEZONE" log_success "Timezone configured" fi # Configure NTP if specified if [ -n "$VM_NTP_SERVER" ]; then log_info "Configuring NTP server..." if command -v chronyc &>/dev/null; then if ! grep -q "^server $VM_NTP_SERVER" /etc/chrony.conf; then echo "server $VM_NTP_SERVER iburst" >> /etc/chrony.conf systemctl restart chronyd log_success "NTP configured" fi fi fi # Apply network configuration if static IP if [[ "$USE_STATIC" =~ ^[Yy] ]]; then # Check if IP is changing if [ "$VM_IP_ADDRESS" != "$CURRENT_IP" ]; then echo "" log_warning "═══════════════════════════════════════════════════════════════" log_warning "IMPORTANT: IP address will change from $CURRENT_IP to $VM_IP_ADDRESS" log_warning "You will lose SSH connectivity and need to reconnect to the new IP" log_warning "═══════════════════════════════════════════════════════════════" echo "" read -p "Press ENTER to continue or Ctrl+C to cancel..." echo "" fi log_info "Applying network configuration..." # Detect network interface INTERFACE=$(ip route | grep default | awk '{print $5}' | head -1) if [ -n "$INTERFACE" ]; then # Use NetworkManager if available if command -v nmcli &>/dev/null; then log_info "Configuring network via NetworkManager..." log_info "Detected interface: $INTERFACE" # Try to get connection name - try multiple methods CONNECTION=$(nmcli -t -f NAME,DEVICE connection show --active | grep ":$INTERFACE$" | cut -d: -f1 | head -1) # If not found in active connections, try all connections if [ -z "$CONNECTION" ]; then CONNECTION=$(nmcli -t -f NAME,DEVICE connection show | grep ":$INTERFACE$" | cut -d: -f1 | head -1) fi # If still not found, use the interface name itself (common in many systems) if [ -z "$CONNECTION" ]; then CONNECTION="$INTERFACE" log_info "Using interface name as connection name: $CONNECTION" else log_info "Found connection name: $CONNECTION" fi # Convert subnet mask to CIDR notation CIDR=$(subnet_to_cidr "$VM_SUBNET_MASK") log_info "Using CIDR notation: /$CIDR for subnet mask $VM_SUBNET_MASK" # Set all network parameters in one command to avoid errors log_info "Configuring connection '$CONNECTION'..." if [ -n "$VM_DNS_SECONDARY" ]; then nmcli connection modify "$CONNECTION" \ ipv4.method manual \ ipv4.addresses "$VM_IP_ADDRESS/$CIDR" \ ipv4.gateway "$VM_GATEWAY" \ ipv4.dns "$VM_DNS_PRIMARY $VM_DNS_SECONDARY" 2>&1 else nmcli connection modify "$CONNECTION" \ ipv4.method manual \ ipv4.addresses "$VM_IP_ADDRESS/$CIDR" \ ipv4.gateway "$VM_GATEWAY" \ ipv4.dns "$VM_DNS_PRIMARY" 2>&1 fi # Check if the command succeeded if [ $? -eq 0 ]; then log_success "Network configuration updated" log_info "Bringing up connection (you may lose connectivity)..." nmcli connection up "$CONNECTION" & sleep 2 log_success "Network configuration applied" else log_error "Failed to modify network connection '$CONNECTION'" log_info "Available connections:" nmcli connection show log_info "" log_info "You may need to configure the network manually using:" log_info " nmcli connection modify ipv4.method manual ipv4.addresses $VM_IP_ADDRESS/$CIDR ipv4.gateway $VM_GATEWAY ipv4.dns $VM_DNS_PRIMARY" fi else log_warning "NetworkManager not available - manual network configuration required" fi else log_warning "Could not detect network interface" fi fi # Configure firewall if requested if [[ "$VM_FIREWALL_AUTO" =~ ^[Yy] ]]; then if command -v firewall-cmd &>/dev/null; then if systemctl is-active --quiet firewalld; then log_info "Configuring firewall..." firewall-cmd --permanent --add-port=30443/tcp &>/dev/null firewall-cmd --permanent --add-port=31443/tcp &>/dev/null firewall-cmd --reload &>/dev/null log_success "Firewall configured (ports 30443, 31443 opened)" else log_info "Firewalld not active - skipping firewall configuration" fi else log_info "Firewalld not installed - skipping firewall configuration" fi fi echo "" log_section "Testing Configuration" # Test gateway connectivity log_info "Testing gateway connectivity..." if test_connectivity "$VM_GATEWAY" "Gateway ($VM_GATEWAY)"; then GATEWAY_OK=true else GATEWAY_OK=false log_warning "Gateway not reachable - check network configuration" fi # Test DNS log_info "Testing DNS resolution..." if test_dns "google.com"; then DNS_OK=true else DNS_OK=false log_warning "DNS resolution failed - check DNS configuration" fi # Test internet connectivity log_info "Testing internet connectivity..." if test_connectivity "8.8.8.8" "Internet (8.8.8.8)"; then INTERNET_OK=true else INTERNET_OK=false log_warning "Internet not reachable - check gateway and routing" fi # Test FQDN resolution (if DNS is working) if [ "$DNS_OK" = true ]; then log_info "Testing FQDN resolution..." if test_dns "$VM_FQDN"; then FQDN_OK=true else FQDN_OK=false log_warning "FQDN not resolvable - you may need to add DNS record" log_info " Add A record: $VM_FQDN -> $VM_IP_ADDRESS" fi fi echo "" log_section "Configuration Complete!" log_success "VM configuration has been applied and saved" echo "" log_info "Configuration file: $CONFIG_FILE" log_info "Hostname: $VM_FQDN" log_info "IP Address: $VM_IP_ADDRESS" echo "" # Summary of test results log_info "Connectivity Tests:" if [ "$GATEWAY_OK" = true ]; then log_success " Gateway: OK" else log_error " Gateway: FAILED" fi if [ "$DNS_OK" = true ]; then log_success " DNS: OK" else log_error " DNS: FAILED" fi if [ "$INTERNET_OK" = true ]; then log_success " Internet: OK" else log_error " Internet: FAILED" fi if [ "${FQDN_OK:-false}" = true ]; then log_success " FQDN Resolution: OK" elif [ "$DNS_OK" = true ]; then log_warning " FQDN Resolution: Not configured (add DNS record)" fi echo "" if [ "$GATEWAY_OK" = true ] && [ "$DNS_OK" = true ] && [ "$INTERNET_OK" = true ]; then log_success "All connectivity tests passed!" echo "" log_info "Next step: Run ./02-configure_gcm.sh to configure GCM settings" else log_warning "Some connectivity tests failed - please review and fix before proceeding" echo "" log_info "You can re-run this script to reconfigure: ./configure_vm.sh" fi echo "" # Made with Bob