#!/bin/bash ############################################################################### # Layer 0: Foundation Setup # # This script sets up your laptop (lapy) as the Ansible control node. # It prepares all the prerequisites needed for the infrastructure deployment. ############################################################################### set -e # Exit on error # Colors for output RED='\033[0;31m' GREEN='\033[0;32m' YELLOW='\033[1;33m' BLUE='\033[0;34m' NC='\033[0m' # No Color # Project root directory SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" ############################################################################### # Helper Functions ############################################################################### print_header() { echo -e "\n${BLUE}========================================${NC}" echo -e "${BLUE}$1${NC}" echo -e "${BLUE}========================================${NC}\n" } print_success() { echo -e "${GREEN}✓${NC} $1" } print_error() { echo -e "${RED}✗${NC} $1" } print_warning() { echo -e "${YELLOW}⚠${NC} $1" } print_info() { echo -e "${BLUE}ℹ${NC} $1" } prompt_user() { local prompt="$1" local default="$2" local result if [ -n "$default" ]; then read -p "$(echo -e ${BLUE}${prompt}${NC} [${default}]: )" result result="${result:-$default}" else read -p "$(echo -e ${BLUE}${prompt}${NC}: )" result fi echo "$result" } confirm_action() { local prompt="$1" local response read -p "$(echo -e ${YELLOW}${prompt}${NC} [y/N]: )" response [[ "$response" =~ ^[Yy]$ ]] } ############################################################################### # Main Setup Functions ############################################################################### check_prerequisites() { print_header "Checking Prerequisites" # Check if we're in the right directory if [ ! -f "$PROJECT_ROOT/README.md" ] || [ ! -d "$PROJECT_ROOT/ansible" ]; then print_error "Not in the correct project directory" echo "Expected: $PROJECT_ROOT" exit 1 fi print_success "Running from correct directory: $PROJECT_ROOT" # Check if Python 3 is installed if ! command -v python3 &> /dev/null; then print_error "Python 3 is not installed. Please install Python 3 first." exit 1 fi print_success "Python 3 found: $(python3 --version)" # Check if git is installed if ! command -v git &> /dev/null; then print_warning "Git is not installed. Some features may not work." else print_success "Git found: $(git --version | head -n1)" fi } setup_python_venv() { print_header "Setting Up Python Virtual Environment" cd "$PROJECT_ROOT" if [ -d "venv" ]; then print_info "Virtual environment already exists" if confirm_action "Recreate virtual environment?"; then rm -rf venv python3 -m venv venv print_success "Virtual environment recreated" else print_success "Using existing virtual environment" fi else python3 -m venv venv print_success "Virtual environment created" fi # Activate venv source venv/bin/activate print_success "Virtual environment activated" # Upgrade pip print_info "Upgrading pip..." pip install --upgrade pip > /dev/null 2>&1 print_success "pip upgraded" } install_python_requirements() { print_header "Installing Python Requirements" cd "$PROJECT_ROOT" if [ ! -f "requirements.txt" ]; then print_error "requirements.txt not found" exit 1 fi print_info "Installing packages from requirements.txt..." pip install -r requirements.txt print_success "Python requirements installed" # Verify Ansible installation if ! command -v ansible &> /dev/null; then print_error "Ansible installation failed" exit 1 fi print_success "Ansible installed: $(ansible --version | head -n1)" } install_ansible_collections() { print_header "Installing Ansible Galaxy Collections" cd "$PROJECT_ROOT/ansible" if [ ! -f "requirements.yml" ]; then print_warning "requirements.yml not found, skipping Ansible collections" return fi print_info "Installing Ansible Galaxy collections..." ansible-galaxy collection install -r requirements.yml print_success "Ansible Galaxy collections installed" } setup_inventory_file() { print_header "Setting Up Inventory File" cd "$PROJECT_ROOT/ansible" if [ -f "inventory.ini" ]; then print_info "inventory.ini already exists" cat inventory.ini echo "" if ! confirm_action "Do you want to update it?"; then print_success "Using existing inventory.ini" return fi fi print_info "Let's configure your infrastructure hosts" echo "" # Collect information echo -e -n "${BLUE}SSH key path${NC} [~/.ssh/counterganzua]: " read ssh_key ssh_key="${ssh_key:-~/.ssh/counterganzua}" echo "" echo "Enter the IP addresses for your infrastructure (VMs will be added later):" echo "" echo -e -n "${BLUE}vipy${NC} (main VPS) IP: " read vipy_ip echo -e -n "${BLUE}watchtower${NC} (monitoring VPS) IP: " read watchtower_ip echo -e -n "${BLUE}spacey${NC} (headscale VPS) IP: " read spacey_ip echo -e -n "${BLUE}nodito${NC} (Proxmox server) IP [optional]: " read nodito_ip echo "" echo -e -n "${BLUE}Your username on lapy${NC} [$(whoami)]: " read lapy_user lapy_user="${lapy_user:-$(whoami)}" echo -e -n "${BLUE}GPG recipient email${NC} [optional, for encrypted backups]: " read gpg_email echo -e -n "${BLUE}GPG key ID${NC} [optional, for encrypted backups]: " read gpg_key # Generate inventory.ini cat > inventory.ini << EOF # Ansible Inventory File # Generated by setup_layer_0.sh EOF if [ -n "$vipy_ip" ]; then cat >> inventory.ini << EOF [vipy] $vipy_ip ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=$ssh_key EOF fi if [ -n "$watchtower_ip" ]; then cat >> inventory.ini << EOF [watchtower] $watchtower_ip ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=$ssh_key EOF fi if [ -n "$spacey_ip" ]; then cat >> inventory.ini << EOF [spacey] $spacey_ip ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=$ssh_key EOF fi if [ -n "$nodito_ip" ]; then cat >> inventory.ini << EOF [nodito] $nodito_ip ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=$ssh_key EOF fi # Add nodito-vms placeholder for VMs that will be created later cat >> inventory.ini << EOF # Nodito VMs - These don't exist yet and will be created on the Proxmox server # Add them here once you create VMs on nodito (e.g., memos-box, etc.) [nodito-vms] # Example: # 192.168.1.150 ansible_user=counterweight ansible_port=22 ansible_ssh_private_key_file=$ssh_key hostname=memos-box EOF # Add lapy cat >> inventory.ini << EOF # Local connection to laptop: this assumes you're running ansible commands from your personal laptop [lapy] localhost ansible_connection=local ansible_user=$lapy_user EOF if [ -n "$gpg_email" ] && [ -n "$gpg_key" ]; then echo " gpg_recipient=$gpg_email gpg_key_id=$gpg_key" >> inventory.ini fi print_success "inventory.ini created" echo "" print_info "Review your inventory file:" cat inventory.ini echo "" } setup_infra_vars() { print_header "Setting Up Infrastructure Variables" cd "$PROJECT_ROOT/ansible" if [ -f "infra_vars.yml" ]; then print_info "infra_vars.yml already exists" cat infra_vars.yml echo "" if ! confirm_action "Do you want to update it?"; then print_success "Using existing infra_vars.yml" return fi fi echo "" echo -e -n "${BLUE}Your root domain${NC} (e.g., contrapeso.xyz): " read domain while [ -z "$domain" ]; do print_warning "Domain cannot be empty" echo -e -n "${BLUE}Your root domain${NC}: " read domain done cat > infra_vars.yml << EOF # Infrastructure Variables # Generated by setup_layer_0.sh new_user: counterweight ssh_port: 22 allow_ssh_from: "any" root_domain: $domain EOF print_success "infra_vars.yml created" echo "" print_info "Contents:" cat infra_vars.yml echo "" } setup_services_config() { print_header "Setting Up Services Configuration" cd "$PROJECT_ROOT/ansible" if [ -f "services_config.yml" ]; then print_info "services_config.yml already exists" if ! confirm_action "Do you want to recreate it from template?"; then print_success "Using existing services_config.yml" return fi fi if [ ! -f "services_config.yml.example" ]; then print_error "services_config.yml.example not found" return fi cp services_config.yml.example services_config.yml print_success "services_config.yml created" echo "" print_info "This file centralizes all service subdomains and Caddy settings" print_info "Customize subdomains in: ansible/services_config.yml" echo "" } setup_infra_secrets() { print_header "Setting Up Infrastructure Secrets" cd "$PROJECT_ROOT/ansible" if [ -f "infra_secrets.yml" ]; then print_warning "infra_secrets.yml already exists" if ! confirm_action "Do you want to recreate the template?"; then print_success "Using existing infra_secrets.yml" return fi fi cat > infra_secrets.yml << EOF # Infrastructure Secrets # Generated by setup_layer_0.sh # # IMPORTANT: This file contains sensitive credentials # It is already in .gitignore - DO NOT commit it to git # # You'll need to fill in the Uptime Kuma credentials after Layer 4 # when you deploy Uptime Kuma # Uptime Kuma Credentials (fill these in after deploying Uptime Kuma in Layer 4) uptime_kuma_username: "" uptime_kuma_password: "" EOF print_success "infra_secrets.yml template created" print_warning "You'll need to fill in Uptime Kuma credentials after Layer 4" echo "" } validate_ssh_key() { print_header "Validating SSH Key" cd "$PROJECT_ROOT/ansible" # Extract SSH key path from inventory if [ -f "inventory.ini" ]; then ssh_key=$(grep "ansible_ssh_private_key_file" inventory.ini | head -n1 | sed 's/.*ansible_ssh_private_key_file=\([^ ]*\).*/\1/') # Expand tilde ssh_key="${ssh_key/#\~/$HOME}" if [ -f "$ssh_key" ]; then print_success "SSH key found: $ssh_key" # Check permissions perms=$(stat -c "%a" "$ssh_key" 2>/dev/null || stat -f "%OLp" "$ssh_key" 2>/dev/null) if [ "$perms" != "600" ]; then print_warning "SSH key permissions are $perms (should be 600)" if confirm_action "Fix permissions?"; then chmod 600 "$ssh_key" print_success "Permissions fixed" fi else print_success "SSH key permissions are correct (600)" fi else print_error "SSH key not found: $ssh_key" print_warning "Make sure to create your SSH key before proceeding to Layer 1" echo "" echo "To generate a new SSH key:" echo " ssh-keygen -t ed25519 -f $ssh_key -C \"your-email@example.com\"" fi else print_warning "inventory.ini not found, skipping SSH key validation" fi } print_summary() { print_header "Layer 0 Setup Complete! 🎉" echo "Summary of what was configured:" echo "" print_success "Python virtual environment created and activated" print_success "Ansible and dependencies installed" print_success "Ansible Galaxy collections installed" print_success "inventory.ini configured with your hosts" print_success "infra_vars.yml configured with your domain" print_success "services_config.yml created with subdomain settings" print_success "infra_secrets.yml template created" echo "" print_info "Before proceeding to Layer 1:" echo " 1. Ensure your SSH key is added to all VPS root users" echo " 2. Verify you can SSH into each machine manually" echo " 3. Configure DNS nameservers for your domain (if not done)" echo "" print_info "Note about inventory groups:" echo " • [nodito-vms] group created as placeholder" echo " • These VMs will be created later on Proxmox" echo " • Add their IPs to inventory.ini once created" echo "" print_info "To test SSH access to a host:" echo " ssh -i ~/.ssh/counterganzua root@" echo "" print_info "Next steps:" echo " 1. Review the files in ansible/" echo " 2. Test SSH connections to your hosts" echo " 3. Proceed to Layer 1: ./scripts/setup_layer_1.sh" echo "" print_warning "Remember to activate the venv before running other commands:" echo " source venv/bin/activate" echo "" } ############################################################################### # Main Execution ############################################################################### main() { clear print_header "🚀 Layer 0: Foundation Setup" echo "This script will set up your laptop (lapy) as the Ansible control node." echo "It will install all prerequisites and configure basic settings." echo "" if ! confirm_action "Continue with Layer 0 setup?"; then echo "Setup cancelled." exit 0 fi check_prerequisites setup_python_venv install_python_requirements install_ansible_collections setup_inventory_file setup_infra_vars setup_services_config setup_infra_secrets validate_ssh_key print_summary } # Run main function main "$@"