personal_infra/scripts/setup_layer_0.sh
2025-11-06 23:09:44 +01:00

494 lines
14 KiB
Bash
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/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@<host-ip>"
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 "$@"