#!/bin/bash ############################################################################### # Layer 7: Core Services # # This script deploys Vaultwarden, Forgejo, and LNBits on vipy. # Must be run after Layers 0, 1A, 2, and 3 are complete. ############################################################################### 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)" ANSIBLE_DIR="$PROJECT_ROOT/ansible" ############################################################################### # 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" } confirm_action() { local prompt="$1" local response read -p "$(echo -e ${YELLOW}${prompt}${NC} [y/N]: )" response [[ "$response" =~ ^[Yy]$ ]] } ############################################################################### # Verification Functions ############################################################################### check_prerequisites() { print_header "Verifying Prerequisites" local errors=0 if [ -z "$VIRTUAL_ENV" ]; then print_error "Virtual environment not activated" echo "Run: source venv/bin/activate" ((errors++)) else print_success "Virtual environment activated" fi if ! command -v ansible &> /dev/null; then print_error "Ansible not found" ((errors++)) else print_success "Ansible found" fi if [ ! -f "$ANSIBLE_DIR/inventory.ini" ]; then print_error "inventory.ini not found" ((errors++)) else print_success "inventory.ini exists" fi # Check if vipy is configured if ! grep -q "^\[vipy\]" "$ANSIBLE_DIR/inventory.ini"; then print_error "vipy not configured in inventory.ini" print_info "Layer 7 requires vipy VPS" ((errors++)) else print_success "vipy configured in inventory" fi if [ $errors -gt 0 ]; then print_error "Prerequisites not met" exit 1 fi print_success "Prerequisites verified" } get_hosts_from_inventory() { local group="$1" cd "$ANSIBLE_DIR" ansible-inventory -i inventory.ini --list | \ python3 -c "import sys, json; data=json.load(sys.stdin); print(' '.join(data.get('$group', {}).get('hosts', [])))" 2>/dev/null || echo "" } check_dns_configuration() { print_header "Validating DNS Configuration" cd "$ANSIBLE_DIR" # Get vipy IP local vipy_ip=$(ansible-inventory -i inventory.ini --list | python3 -c "import sys, json; data=json.load(sys.stdin); hosts=data.get('vipy', {}).get('hosts', []); print(hosts[0] if hosts else '')" 2>/dev/null) if [ -z "$vipy_ip" ]; then print_error "Could not determine vipy IP from inventory" return 1 fi print_info "Vipy IP: $vipy_ip" echo "" # Get domain from infra_vars.yml local root_domain=$(grep "^root_domain:" "$ANSIBLE_DIR/infra_vars.yml" | awk '{print $2}' 2>/dev/null) if [ -z "$root_domain" ]; then print_error "Could not determine root_domain from infra_vars.yml" return 1 fi # Get subdomains from centralized config local vw_subdomain="vault" local fg_subdomain="git" local ln_subdomain="lnbits" if [ -f "$ANSIBLE_DIR/services_config.yml" ]; then vw_subdomain=$(grep "^ vaultwarden:" "$ANSIBLE_DIR/services_config.yml" | awk '{print $2}' 2>/dev/null || echo "vault") fg_subdomain=$(grep "^ forgejo:" "$ANSIBLE_DIR/services_config.yml" | awk '{print $2}' 2>/dev/null || echo "git") ln_subdomain=$(grep "^ lnbits:" "$ANSIBLE_DIR/services_config.yml" | awk '{print $2}' 2>/dev/null || echo "lnbits") fi print_info "Checking DNS records..." echo "" local dns_ok=true if command -v dig &> /dev/null; then # Check each subdomain for service in "vaultwarden:$vw_subdomain" "forgejo:$fg_subdomain" "lnbits:$ln_subdomain"; do local name=$(echo "$service" | cut -d: -f1) local subdomain=$(echo "$service" | cut -d: -f2) local fqdn="${subdomain}.${root_domain}" print_info "Checking $fqdn..." local resolved=$(dig +short "$fqdn" | head -n1) if [ "$resolved" = "$vipy_ip" ]; then print_success "$fqdn → $resolved ✓" elif [ -n "$resolved" ]; then print_error "$fqdn → $resolved (expected $vipy_ip)" dns_ok=false else print_error "$fqdn does not resolve" dns_ok=false fi done else print_warning "dig command not found, skipping DNS validation" print_info "Install dnsutils/bind-tools to enable DNS validation" return 1 fi echo "" if [ "$dns_ok" = false ]; then print_error "DNS validation failed" print_info "Please configure DNS records for all services" echo "" print_warning "DNS changes can take time to propagate" echo "" if ! confirm_action "Continue anyway? (SSL certificates will fail without proper DNS)"; then exit 1 fi else print_success "DNS validation passed" fi } ############################################################################### # Service Deployment ############################################################################### deploy_vaultwarden() { print_header "Deploying Vaultwarden (Password Manager)" cd "$ANSIBLE_DIR" print_info "This will:" echo " • Deploy Vaultwarden via Docker" echo " • Configure Caddy reverse proxy" echo " • Set up fail2ban protection" echo " • Enable sign-ups (disable after first user)" echo "" if ! confirm_action "Proceed with Vaultwarden deployment?"; then print_warning "Skipped Vaultwarden deployment" return 0 fi print_info "Running: ansible-playbook -i inventory.ini services/vaultwarden/deploy_vaultwarden_playbook.yml" echo "" if ansible-playbook -i inventory.ini services/vaultwarden/deploy_vaultwarden_playbook.yml; then print_success "Vaultwarden deployed" echo "" print_warning "POST-DEPLOYMENT:" echo " 1. Visit your Vaultwarden subdomain" echo " 2. Create your first user account" echo " 3. Run: ansible-playbook -i inventory.ini services/vaultwarden/disable_vaultwarden_sign_ups_playbook.yml" return 0 else print_error "Vaultwarden deployment failed" return 0 fi } deploy_forgejo() { print_header "Deploying Forgejo (Git Server)" cd "$ANSIBLE_DIR" print_info "This will:" echo " • Install Forgejo binary" echo " • Create git user and directories" echo " • Configure Caddy reverse proxy" echo " • Enable SSH cloning on port 22" echo "" if ! confirm_action "Proceed with Forgejo deployment?"; then print_warning "Skipped Forgejo deployment" return 0 fi print_info "Running: ansible-playbook -i inventory.ini services/forgejo/deploy_forgejo_playbook.yml" echo "" if ansible-playbook -i inventory.ini services/forgejo/deploy_forgejo_playbook.yml; then print_success "Forgejo deployed" echo "" print_warning "POST-DEPLOYMENT:" echo " 1. Visit your Forgejo subdomain" echo " 2. Create admin account on first visit" echo " 3. Add your SSH key for git cloning" return 0 else print_error "Forgejo deployment failed" return 0 fi } deploy_lnbits() { print_header "Deploying LNBits (Lightning Wallet)" cd "$ANSIBLE_DIR" print_info "This will:" echo " • Install system dependencies and uv (Python 3.12 tooling)" echo " • Clone LNBits repository (version v1.3.1)" echo " • Sync dependencies with uv targeting Python 3.12" echo " • Configure with FakeWallet (testing)" echo " • Create systemd service" echo " • Configure Caddy reverse proxy" echo "" if ! confirm_action "Proceed with LNBits deployment?"; then print_warning "Skipped LNBits deployment" return 0 fi print_info "Running: ansible-playbook -i inventory.ini services/lnbits/deploy_lnbits_playbook.yml" echo "" if ansible-playbook -i inventory.ini services/lnbits/deploy_lnbits_playbook.yml; then print_success "LNBits deployed" echo "" print_warning "POST-DEPLOYMENT:" echo " 1. Visit your LNBits subdomain" echo " 2. Create superuser on first visit" echo " 3. Configure real Lightning backend (FakeWallet is for testing only)" echo " 4. Disable new user registration" return 0 else print_error "LNBits deployment failed" return 0 fi } ############################################################################### # Backup Configuration ############################################################################### setup_backups() { print_header "Setting Up Backups (Optional)" cd "$ANSIBLE_DIR" print_info "Configure automated backups to lapy" echo "" # Vaultwarden backup if confirm_action "Set up Vaultwarden backup to lapy?"; then print_info "Running: ansible-playbook -i inventory.ini services/vaultwarden/setup_backup_vaultwarden_to_lapy.yml" if ansible-playbook -i inventory.ini services/vaultwarden/setup_backup_vaultwarden_to_lapy.yml; then print_success "Vaultwarden backup configured" else print_error "Vaultwarden backup setup failed" fi echo "" fi # LNBits backup if confirm_action "Set up LNBits backup to lapy (GPG encrypted)?"; then print_info "Running: ansible-playbook -i inventory.ini services/lnbits/setup_backup_lnbits_to_lapy.yml" if ansible-playbook -i inventory.ini services/lnbits/setup_backup_lnbits_to_lapy.yml; then print_success "LNBits backup configured" else print_error "LNBits backup setup failed" fi echo "" fi print_warning "Forgejo backups are not automated - set up manually if needed" } ############################################################################### # Verification ############################################################################### verify_services() { print_header "Verifying Service Deployments" cd "$ANSIBLE_DIR" local ssh_key=$(grep "ansible_ssh_private_key_file" "$ANSIBLE_DIR/inventory.ini" | head -n1 | sed 's/.*ansible_ssh_private_key_file=\([^ ]*\).*/\1/') ssh_key="${ssh_key/#\~/$HOME}" local vipy_host=$(get_hosts_from_inventory "vipy") if [ -z "$vipy_host" ]; then print_error "Could not determine vipy host" return fi print_info "Checking services on vipy ($vipy_host)..." echo "" # Check Vaultwarden if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$vipy_host "docker ps | grep vaultwarden" &>/dev/null; then print_success "Vaultwarden container running" else print_warning "Vaultwarden container not running (may not be deployed)" fi # Check Forgejo if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$vipy_host "systemctl is-active forgejo" &>/dev/null; then print_success "Forgejo service running" else print_warning "Forgejo service not running (may not be deployed)" fi # Check LNBits if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$vipy_host "systemctl is-active lnbits" &>/dev/null; then print_success "LNBits service running" else print_warning "LNBits service not running (may not be deployed)" fi # Check Caddy configs if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$vipy_host "ls /etc/caddy/sites-enabled/*.conf 2>/dev/null" &>/dev/null; then print_success "Caddy configs exist" local configs=$(timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$vipy_host "ls /etc/caddy/sites-enabled/*.conf 2>/dev/null" | xargs -n1 basename) print_info "Configured services:" echo "$configs" | sed 's/^/ /' else print_warning "No Caddy configs found" fi echo "" } ############################################################################### # Summary ############################################################################### print_summary() { print_header "Layer 7 Setup Complete! 🎉" echo "Summary of what was deployed:" echo "" print_success "Core services deployed on vipy" echo "" print_warning "CRITICAL POST-DEPLOYMENT STEPS:" echo "" echo "For each service you deployed, you MUST:" echo "" echo "1. Vaultwarden (if deployed):" echo " • Visit web UI and create first user" echo " • Disable sign-ups: ansible-playbook -i inventory.ini services/vaultwarden/disable_vaultwarden_sign_ups_playbook.yml" echo " • Optional: Set up backup" echo "" echo "2. Forgejo (if deployed):" echo " • Visit web UI and create admin account" echo " • Add your SSH public key for git operations" echo " • Test cloning: git clone git@.:username/repo.git" echo "" echo "3. LNBits (if deployed):" echo " • Visit web UI and create superuser" echo " • Configure real Lightning backend (currently FakeWallet)" echo " • Disable new user registration" echo " • Optional: Set up encrypted backup" echo "" print_info "Services are now accessible:" echo " • Vaultwarden: https://." echo " • Forgejo: https://." echo " • LNBits: https://." echo "" print_success "Uptime Kuma monitors automatically created:" echo " • Check Uptime Kuma web UI" echo " • Look in 'services' monitor group" echo " • Monitors for Vaultwarden, Forgejo, LNBits should appear" echo "" print_info "Next steps:" echo " 1. Complete post-deployment steps above" echo " 2. Test each service" echo " 3. Check Uptime Kuma monitors are working" echo " 4. Proceed to Layer 8: ./scripts/setup_layer_8_secondary_services.sh" echo "" } ############################################################################### # Main Execution ############################################################################### main() { clear print_header "🚀 Layer 7: Core Services" echo "This script will deploy core services on vipy:" echo " • Vaultwarden (password manager)" echo " • Forgejo (git server)" echo " • LNBits (Lightning wallet)" echo "" if ! confirm_action "Continue with Layer 7 setup?"; then echo "Setup cancelled." exit 0 fi check_prerequisites check_dns_configuration # Deploy services deploy_vaultwarden echo "" deploy_forgejo echo "" deploy_lnbits echo "" verify_services echo "" setup_backups print_summary } # Run main function main "$@"