346 lines
11 KiB
Bash
346 lines
11 KiB
Bash
|
|
#!/bin/bash
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# Layer 3: Reverse Proxy (Caddy)
|
|||
|
|
#
|
|||
|
|
# This script deploys Caddy reverse proxy on VPS machines.
|
|||
|
|
# Must be run after Layer 1A (VPS setup) is 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_layer_0_complete() {
|
|||
|
|
print_header "Verifying Layer 0 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
|
|||
|
|
|
|||
|
|
if [ $errors -gt 0 ]; then
|
|||
|
|
print_error "Layer 0 is not complete"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
print_success "Layer 0 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_target_hosts() {
|
|||
|
|
print_header "Checking Target Hosts"
|
|||
|
|
|
|||
|
|
local has_hosts=false
|
|||
|
|
|
|||
|
|
print_info "Caddy will be deployed to these hosts:"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
for group in vipy watchtower spacey; do
|
|||
|
|
local hosts=$(get_hosts_from_inventory "$group")
|
|||
|
|
if [ -n "$hosts" ]; then
|
|||
|
|
echo " [$group]: $hosts"
|
|||
|
|
has_hosts=true
|
|||
|
|
else
|
|||
|
|
print_warning "[$group]: not configured (skipping)"
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
if [ "$has_hosts" = false ]; then
|
|||
|
|
print_error "No target hosts configured for Caddy"
|
|||
|
|
print_info "Caddy needs vipy, watchtower, or spacey in inventory.ini"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
print_success "Target hosts verified"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
check_ssh_connectivity() {
|
|||
|
|
print_header "Testing SSH Connectivity"
|
|||
|
|
|
|||
|
|
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 all_good=true
|
|||
|
|
|
|||
|
|
for group in vipy watchtower spacey; do
|
|||
|
|
local hosts=$(get_hosts_from_inventory "$group")
|
|||
|
|
if [ -n "$hosts" ]; then
|
|||
|
|
for host in $hosts; do
|
|||
|
|
print_info "Testing SSH to $host as counterweight..."
|
|||
|
|
if timeout 10 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$host "echo 'SSH OK'" &>/dev/null; then
|
|||
|
|
print_success "SSH to $host: OK"
|
|||
|
|
else
|
|||
|
|
print_error "Cannot SSH to $host as counterweight"
|
|||
|
|
print_warning "Make sure Layer 1A is complete for this host"
|
|||
|
|
all_good=false
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
|
|||
|
|
if [ "$all_good" = false ]; then
|
|||
|
|
echo ""
|
|||
|
|
print_error "SSH connectivity test failed"
|
|||
|
|
print_info "Ensure Layer 1A (VPS setup) is complete"
|
|||
|
|
echo ""
|
|||
|
|
if ! confirm_action "Continue anyway?"; then
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
print_success "SSH connectivity verified"
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# Caddy Deployment
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
deploy_caddy() {
|
|||
|
|
print_header "Deploying Caddy"
|
|||
|
|
|
|||
|
|
cd "$ANSIBLE_DIR"
|
|||
|
|
|
|||
|
|
print_info "This will:"
|
|||
|
|
echo " • Install Caddy from official repositories"
|
|||
|
|
echo " • Configure Caddy service"
|
|||
|
|
echo " • Open firewall ports 80/443"
|
|||
|
|
echo " • Create sites-enabled directory structure"
|
|||
|
|
echo " • Enable automatic HTTPS with Let's Encrypt"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_info "Target hosts: vipy, watchtower, spacey (if configured)"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_warning "Important:"
|
|||
|
|
echo " • Caddy will start with empty configuration"
|
|||
|
|
echo " • Services will add their own config files in later layers"
|
|||
|
|
echo " • Ports 80/443 must be available on the VPSs"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
if ! confirm_action "Proceed with Caddy deployment?"; then
|
|||
|
|
print_warning "Skipped Caddy deployment"
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
print_info "Running: ansible-playbook -i inventory.ini services/caddy_playbook.yml"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
if ansible-playbook -i inventory.ini services/caddy_playbook.yml; then
|
|||
|
|
print_success "Caddy deployment complete"
|
|||
|
|
return 0
|
|||
|
|
else
|
|||
|
|
print_error "Caddy deployment failed"
|
|||
|
|
return 1
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# Verification Functions
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
verify_caddy() {
|
|||
|
|
print_header "Verifying Caddy Installation"
|
|||
|
|
|
|||
|
|
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}"
|
|||
|
|
|
|||
|
|
echo "Checking Caddy on each host..."
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
for group in vipy watchtower spacey; do
|
|||
|
|
local hosts=$(get_hosts_from_inventory "$group")
|
|||
|
|
if [ -n "$hosts" ]; then
|
|||
|
|
for host in $hosts; do
|
|||
|
|
print_info "Checking $host..."
|
|||
|
|
|
|||
|
|
# Check if caddy is installed
|
|||
|
|
if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$host "command -v caddy" &>/dev/null; then
|
|||
|
|
print_success "$host: Caddy installed"
|
|||
|
|
else
|
|||
|
|
print_error "$host: Caddy not found"
|
|||
|
|
continue
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# Check if caddy service is running
|
|||
|
|
if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$host "sudo systemctl is-active caddy" &>/dev/null; then
|
|||
|
|
print_success "$host: Caddy service running"
|
|||
|
|
else
|
|||
|
|
print_error "$host: Caddy service not running"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# Check if sites-enabled directory exists
|
|||
|
|
if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$host "test -d /etc/caddy/sites-enabled" &>/dev/null; then
|
|||
|
|
print_success "$host: sites-enabled directory exists"
|
|||
|
|
else
|
|||
|
|
print_warning "$host: sites-enabled directory not found"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
# Check if ports 80/443 are open
|
|||
|
|
if timeout 5 ssh -i "$ssh_key" -o StrictHostKeyChecking=no -o BatchMode=yes counterweight@$host "sudo ufw status | grep -E '80|443'" &>/dev/null; then
|
|||
|
|
print_success "$host: Firewall ports 80/443 open"
|
|||
|
|
else
|
|||
|
|
print_warning "$host: Could not verify firewall ports"
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
echo ""
|
|||
|
|
done
|
|||
|
|
fi
|
|||
|
|
done
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# Summary Functions
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
print_summary() {
|
|||
|
|
print_header "Layer 3 Setup Complete! 🎉"
|
|||
|
|
|
|||
|
|
echo "Summary of what was configured:"
|
|||
|
|
echo ""
|
|||
|
|
print_success "Caddy installed on VPS hosts"
|
|||
|
|
print_success "Caddy service running"
|
|||
|
|
print_success "Firewall ports 80/443 opened"
|
|||
|
|
print_success "Sites-enabled directory structure created"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_info "What Caddy provides:"
|
|||
|
|
echo " • Automatic HTTPS with Let's Encrypt"
|
|||
|
|
echo " • Reverse proxy for all web services"
|
|||
|
|
echo " • HTTP/2 support"
|
|||
|
|
echo " • Simple per-service configuration"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_info "How services use Caddy:"
|
|||
|
|
echo " • Each service adds a config file to /etc/caddy/sites-enabled/"
|
|||
|
|
echo " • Main Caddyfile imports all configs"
|
|||
|
|
echo " • Caddy automatically manages SSL certificates"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_warning "Important Notes:"
|
|||
|
|
echo " • Caddy is currently running with default/empty config"
|
|||
|
|
echo " • Services deployed in later layers will add their configs"
|
|||
|
|
echo " • DNS must point to your VPS IPs for SSL to work"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
print_info "Next steps:"
|
|||
|
|
echo " 1. Verify Caddy is accessible (optional): curl http://<vps-ip>"
|
|||
|
|
echo " 2. Proceed to Layer 4: ./scripts/setup_layer_4_monitoring.sh"
|
|||
|
|
echo ""
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
###############################################################################
|
|||
|
|
# Main Execution
|
|||
|
|
###############################################################################
|
|||
|
|
|
|||
|
|
main() {
|
|||
|
|
clear
|
|||
|
|
|
|||
|
|
print_header "🌐 Layer 3: Reverse Proxy (Caddy)"
|
|||
|
|
|
|||
|
|
echo "This script will deploy Caddy reverse proxy on your VPS machines."
|
|||
|
|
echo ""
|
|||
|
|
print_info "Targets: vipy, watchtower, spacey"
|
|||
|
|
echo ""
|
|||
|
|
|
|||
|
|
if ! confirm_action "Continue with Layer 3 setup?"; then
|
|||
|
|
echo "Setup cancelled."
|
|||
|
|
exit 0
|
|||
|
|
fi
|
|||
|
|
|
|||
|
|
check_layer_0_complete
|
|||
|
|
check_target_hosts
|
|||
|
|
check_ssh_connectivity
|
|||
|
|
|
|||
|
|
# Deploy Caddy
|
|||
|
|
if deploy_caddy; then
|
|||
|
|
verify_caddy
|
|||
|
|
print_summary
|
|||
|
|
else
|
|||
|
|
print_error "Caddy deployment failed"
|
|||
|
|
exit 1
|
|||
|
|
fi
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
# Run main function
|
|||
|
|
main "$@"
|
|||
|
|
|