From e2d28b5208dc088091164c065b1f893ab767a6e5 Mon Sep 17 00:00:00 2001 From: Kelin Date: Mon, 26 Jan 2026 16:34:25 -0500 Subject: [PATCH] feat: Improve TLS handling for multi-server deployments - Add CORE_SERVER_IP variable for remote server configuration - Implement setup_multi_server_tls() function for shared CA management - Change TLS failure handling from exit-on-error to warning-based approach - Add TLS_ISSUES_SUMMARY for end-of-deployment remediation guidance - Update documentation for automated TLS setup process - Add comprehensive AI assistant instructions for project management This allows deployments to complete successfully even with TLS issues, providing clear remediation steps instead of failing the entire setup. --- .github/copilot-instructions.md | 26 ++ docs/Ondemand-Remote-Services.md | 62 +++- scripts/ez-homelab.sh | 541 ++++++++++++++++++++----------- 3 files changed, 445 insertions(+), 184 deletions(-) diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 4b429de..bbcef6a 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -47,6 +47,12 @@ docker-compose/ - **Gluetun network mode**: Download clients use `network_mode: "service:gluetun"` for VPN routing - **Port mapping**: Only core services expose ports (80/443 for Traefik); others route via Traefik labels +### TLS and Multi-Server Architecture +- **Shared CA**: Core server generates CA for signing certificates across all servers +- **Docker TLS**: Remote servers use TCP 2376 with mutual TLS for Sablier access +- **Certificate Management**: Automated generation and distribution of client/server certificates +- **Sablier Integration**: Core Sablier connects to remote Docker daemons via TLS for lazy loading + ## Critical Operational Principles ### 1. Security-First SSO Strategy @@ -284,6 +290,15 @@ Secrets auto-generated by `ez-homelab.sh`: - **Renewal**: Traefik handles automatically (90-day Let's Encrypt certs) - **Usage**: Services use `tls.certresolver=letsencrypt` label (no per-service cert requests) +### Remote Server TLS Setup +For multi-server deployments with Sablier lazy loading: +- **Core server**: Generates shared CA and client certificates +- **Remote servers**: Use `ez-homelab.sh` option 3, specify core server IP for CA import +- **Certificate chain**: Core has CA + client certs; remotes have CA + server certs +- **Sablier connection**: Uses TCP 2376 with mutual TLS to remote Docker daemons +- **Security**: All Docker API access encrypted and authenticated +- **Failure handling**: Setup fails if CA cannot be copied (prevents inconsistent TLS state) + ### Homepage Dashboard AI Configuration Homepage (`/opt/stacks/dashboards/`) uses dynamic variable replacement: - Services configured in `homepage/config/services.yaml` @@ -357,6 +372,17 @@ docker logs gluetun | grep -i wireguard # Verify connection ``` Verify: `SURFSHARK_PRIVATE_KEY` set in `.env`, service using `network_mode: "service:gluetun"`, ports mapped in Gluetun +### Sablier TLS Connection Issues +```bash +# Test Docker TLS connection from core to remote +cd /opt/stacks/core/shared-ca +docker --tlsverify --tlscacert=ca.pem --tlscert=cert.pem --tlskey=key.pem --host=tcp://REMOTE_IP:2376 ps + +# Check Sablier logs +docker compose -f /opt/stacks/core/docker-compose.yml logs sablier-service +``` +Verify: Remote Docker daemon configured with shared CA, certificates properly signed, firewall allows 2376/tcp + ### Wildcard Certificate Issues ```bash docker logs traefik | grep -i certificate diff --git a/docs/Ondemand-Remote-Services.md b/docs/Ondemand-Remote-Services.md index 4c87c36..78fe268 100644 --- a/docs/Ondemand-Remote-Services.md +++ b/docs/Ondemand-Remote-Services.md @@ -1,6 +1,66 @@ # On Demand Remote Services with Authelia, Sablier & Traefik -## 4 Step Process +## Overview + +This guide explains how to set up lazy-loading services on remote servers (like Raspberry Pi) that start automatically when accessed via Traefik. The core server runs Sablier, which connects to remote Docker daemons via TLS to manage container lifecycle. + +## Prerequisites + +- Core server with Traefik, Authelia, and Sablier deployed +- Remote server with Docker installed +- Shared TLS CA configured between core and remote servers + +## Automated Setup + +For new remote servers, use the automated script: + +1. On the remote server, run `ez-homelab.sh` and select option 3 (Infrastructure Only) +2. When prompted, enter the core server IP for shared TLS CA +3. The script will automatically: + - Copy shared CA from core server via SSH + - Configure Docker TLS with shared certificates + - Generate server certificates signed by shared CA + - Set up Docker daemon for TLS on port 2376 + +**Important**: The script will fail if it cannot copy the shared CA from the core server. Ensure SSH access is configured between servers before running option 3. + +## Manual Setup (if automated fails) + +If the automated setup fails, manually configure TLS: + +### On Core Server: +```bash +# Generate server certificates for remote server +cd /opt/stacks/core/shared-ca +openssl genrsa -out server-key.pem 4096 +openssl req -subj "/CN=" -new -key server-key.pem -out server.csr +echo "subjectAltName = DNS:,IP:,IP:127.0.0.1" > extfile.cnf +openssl x509 -req -days 365 -in server.csr -CA ca.pem -CAkey ca-key.pem -CAcreateserial -out server-cert.pem -extfile extfile.cnf +``` + +### On Remote Server: +```bash +# Copy certificates +scp user@core-server:/opt/stacks/core/shared-ca/ca.pem /opt/stacks/core/shared-ca/ +scp user@core-server:/opt/stacks/core/shared-ca/server-cert.pem /opt/stacks/core/shared-ca/ +scp user@core-server:/opt/stacks/core/shared-ca/server-key.pem /opt/stacks/core/shared-ca/ + +# Update Docker daemon +sudo tee /etc/docker/daemon.json > /dev/null </dev/null; then + log_success "Shared CA copied from core server" + else + log_warning "Failed to copy shared CA from core server $CORE_SERVER_IP" + log_warning "This will create TLS certificate mismatches between core and remote servers" + log_warning "Sablier will not be able to connect to this remote Docker daemon" + TLS_ISSUES_SUMMARY="⚠️ TLS Configuration Issue: Could not copy shared CA from core server $CORE_SERVER_IP + This will prevent Sablier from connecting to remote Docker daemons. + + To fix this: + 1. Ensure SSH access works: ssh $ACTUAL_USER@$CORE_SERVER_IP + 2. Verify core server has: /opt/stacks/core/shared-ca/ca.pem + 3. Manually copy CA: scp $ACTUAL_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem $ca_dir/ + 4. Regenerate server certificates on this server with shared CA + 5. Restart Docker: sudo systemctl restart docker + 6. Test connection: docker --tlsverify --tlscacert=$ca_dir/ca.pem --tlscert=$ca_dir/cert.pem --tlskey=$ca_dir/key.pem --host=tcp://localhost:2376 ps + + Then restart Sablier on the core server to reconnect." + generate_shared_ca + fi + + # Setup Docker TLS with shared CA + setup_docker_tls + else + log_info "No core server specified, setting up local TLS..." + generate_shared_ca + setup_docker_tls + fi +} + # Get script directory and repo directory SCRIPT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" REPO_DIR="$( cd "$SCRIPT_DIR/.." && pwd )" @@ -43,6 +124,7 @@ fi # Default values DOMAIN="" SERVER_IP="" +CORE_SERVER_IP="" ADMIN_USER="" ADMIN_EMAIL="" ADMIN_PASSWORD="" @@ -50,6 +132,7 @@ DEPLOY_CORE=false DEPLOY_INFRASTRUCTURE=false DEPLOY_DASHBOARDS=false SETUP_STACKS=false +TLS_ISSUES_SUMMARY="" # Load existing .env file if it exists load_env_file() { @@ -158,6 +241,7 @@ prompt_for_values() { # Set defaults from env file or hardcoded fallbacks DEFAULT_DOMAIN="${DOMAIN:-example.duckdns.org}" DEFAULT_SERVER_IP="${SERVER_IP:-$(hostname -I | awk '{print $1}')}" + DEFAULT_CORE_SERVER_IP="${CORE_SERVER_IP:-}" DEFAULT_SERVER_HOSTNAME="${SERVER_HOSTNAME:-$(hostname)}" DEFAULT_TZ="${TZ:-America/New_York}" @@ -168,6 +252,12 @@ prompt_for_values() { echo " Server Hostname: $DEFAULT_SERVER_HOSTNAME" echo " Timezone: $DEFAULT_TZ" + if [ "$DEPLOY_CORE" = false ] && [ -z "$DEFAULT_CORE_SERVER_IP" ]; then + echo " Core Server IP: [Will be prompted for multi-server TLS]" + elif [ -n "$DEFAULT_CORE_SERVER_IP" ]; then + echo " Core Server IP: $DEFAULT_CORE_SERVER_IP" + fi + if [ "$DEPLOY_CORE" = true ]; then DEFAULT_ADMIN_USER="${DEFAULT_USER:-admin}" DEFAULT_ADMIN_EMAIL="${DEFAULT_EMAIL:-${DEFAULT_ADMIN_USER}@${DEFAULT_DOMAIN}}" @@ -199,6 +289,12 @@ prompt_for_values() { read -p "Timezone [$DEFAULT_TZ]: " TZ TZ="${TZ:-$DEFAULT_TZ}" + # Core server IP (for multi-server setup) + if [ "$DEPLOY_CORE" = false ]; then + echo "" + read -p "Core server IP (for shared TLS CA): " CORE_SERVER_IP + fi + # Admin credentials (only if deploying core) if [ "$DEPLOY_CORE" = true ]; then echo "" @@ -229,6 +325,7 @@ prompt_for_values() { SERVER_IP="$DEFAULT_SERVER_IP" SERVER_HOSTNAME="$DEFAULT_SERVER_HOSTNAME" TZ="$DEFAULT_TZ" + CORE_SERVER_IP="$DEFAULT_CORE_SERVER_IP" if [ "$DEPLOY_CORE" = true ]; then ADMIN_USER="$DEFAULT_ADMIN_USER" @@ -239,14 +336,14 @@ prompt_for_values() { echo "" } -# Certificate sharing function for infrastructure-only deployments -share_certs_with_core() { - log_info "Infrastructure-only deployment detected. Setting up certificate sharing for remote Docker control..." +# Certificate fetching function for infrastructure-only deployments +get_certs_from_core_server() { + log_info "Infrastructure-only deployment detected. Fetching certificate from core server for remote Docker control..." # Prompt for core server IP read -p "Enter the IP address of your core server: " CORE_SERVER_IP while [ -z "$CORE_SERVER_IP" ]; do - log_warning "Core server IP is required for certificate sharing" + log_warning "Core server IP is required for certificate fetching" read -p "Enter the IP address of your core server: " CORE_SERVER_IP done @@ -278,75 +375,89 @@ share_certs_with_core() { read -p "Do you want to continue anyway? (y/N): " -n 1 -r echo "" if [[ ! $REPLY =~ ^[Yy]$ ]]; then - log_error "Certificate sharing cancelled. Please verify SSH access and try again." + log_error "Certificate fetching cancelled. Please verify SSH access and try again." exit 1 fi USE_SSHPASS=true # Assume password auth for copying fi fi - # Copy shared CA certificates from core server - log_info "Copying shared CA certificates from core server..." - mkdir -p "/opt/stacks/core/shared-ca" + # Fetch shared CA certificates from core server + log_info "Fetching shared CA certificates from core server..." + sudo mkdir -p "/opt/stacks/core/shared-ca" - # First check if shared CA exists on core server + # First check if shared CA exists on core server (check both old and new locations) SHARED_CA_EXISTS=false if [ "$USE_SSHPASS" = true ] && [ -n "$SSH_PASSWORD" ]; then - if sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/shared-ca/ca.pem ] && [ -f /opt/stacks/core/shared-ca/ca-key.pem ]" 2>/dev/null; then + if sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/shared-ca/ca.pem ] && [ -f /opt/stacks/core/shared-ca/ca-key.pem ] && [ -r /opt/stacks/core/shared-ca/ca.pem ] && [ -r /opt/stacks/core/shared-ca/ca-key.pem ]" 2>/dev/null; then SHARED_CA_EXISTS=true + SHARED_CA_PATH="/opt/stacks/core/shared-ca" + log_info "Detected CA certificate and key in shared-ca location" + elif sshpass -p "$SSH_PASSWORD" ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/docker-tls/ca.pem ] && [ -f /opt/stacks/core/docker-tls/ca-key.pem ] && [ -r /opt/stacks/core/docker-tls/ca.pem ] && [ -r /opt/stacks/core/docker-tls/ca-key.pem ]" 2>/dev/null; then + SHARED_CA_EXISTS=true + SHARED_CA_PATH="/opt/stacks/core/docker-tls" + log_info "Detected CA certificate and key in docker-tls location" fi else - if ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/shared-ca/ca.pem ] && [ -f /opt/stacks/core/shared-ca/ca-key.pem ]" 2>/dev/null; then + if ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/shared-ca/ca.pem ] && [ -f /opt/stacks/core/shared-ca/ca-key.pem ] && [ -r /opt/stacks/core/shared-ca/ca.pem ] && [ -r /opt/stacks/core/shared-ca/ca-key.pem ]" 2>/dev/null; then SHARED_CA_EXISTS=true + SHARED_CA_PATH="/opt/stacks/core/shared-ca" + log_info "Detected CA certificate and key in shared-ca location" + elif ssh -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP" "[ -f /opt/stacks/core/docker-tls/ca.pem ] && [ -f /opt/stacks/core/docker-tls/ca-key.pem ] && [ -r /opt/stacks/core/docker-tls/ca.pem ] && [ -r /opt/stacks/core/docker-tls/ca-key.pem ]" 2>/dev/null; then + SHARED_CA_EXISTS=true + SHARED_CA_PATH="/opt/stacks/core/docker-tls" + log_info "Detected CA certificate and key in docker-tls location" fi fi if [ "$SHARED_CA_EXISTS" = true ]; then # Copy existing shared CA from core server if [ "$USE_SSHPASS" = true ] && [ -n "$SSH_PASSWORD" ]; then - log_info "Running: sshpass -p [PASSWORD] scp -o StrictHostKeyChecking=no $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem /opt/stacks/core/shared-ca/" - if sshpass -p "$SSH_PASSWORD" scp -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem" "$SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca-key.pem" "/opt/stacks/core/shared-ca/" 2>&1; then - log_success "Shared CA certificates copied from core server" + log_info "Running: sshpass -p [PASSWORD] scp -o StrictHostKeyChecking=no $SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca.pem $SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca-key.pem /opt/stacks/core/shared-ca/" + if sshpass -p "$SSH_PASSWORD" scp -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca.pem" "$SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca-key.pem" "/opt/stacks/core/shared-ca/" 2>&1; then + log_success "Shared CA certificate and key fetched from core server" + # Generate server certificates signed by the shared CA + setup_docker_tls else - log_warning "Failed to copy shared CA certificates from core server" + log_warning "Failed to fetch shared CA certificate and key from core server" SHARED_CA_EXISTS=false fi else - log_info "Running: scp -o StrictHostKeyChecking=no $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem /opt/stacks/core/shared-ca/" - if scp -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem" "$SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca-key.pem" "/opt/stacks/core/shared-ca/" 2>&1; then - log_success "Shared CA certificates copied from core server" + log_info "Running: scp -o StrictHostKeyChecking=no $SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca.pem $SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca-key.pem /opt/stacks/core/shared-ca/" + if scp -o StrictHostKeyChecking=no "$SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca.pem" "$SSH_USER@$CORE_SERVER_IP:$SHARED_CA_PATH/ca-key.pem" "/opt/stacks/core/shared-ca/" 2>&1; then + log_success "Shared CA certificate and key fetched from core server" + # Generate server certificates signed by the shared CA + setup_docker_tls else - log_warning "Failed to copy shared CA certificates from core server" + log_warning "Failed to fetch shared CA certificate and key from core server" SHARED_CA_EXISTS=false fi fi fi if [ "$SHARED_CA_EXISTS" = false ]; then - # Generate local shared CA if not available from core server log_warning "Shared CA certificates not found on core server." - log_info "Generating local shared CA for infrastructure server..." - - openssl genrsa -out "/opt/stacks/core/shared-ca/ca-key.pem" 4096 - openssl req -new -x509 -days 365 -key "/opt/stacks/core/shared-ca/ca-key.pem" -sha256 -out "/opt/stacks/core/shared-ca/ca.pem" -subj "/C=US/ST=State/L=City/O=Homelab/CN=Homelab-CA" - - log_success "Local shared CA generated" - log_info "IMPORTANT: Copy these certificates to your core server at /opt/stacks/core/shared-ca/" - log_info "Run this command on your CORE server:" + log_info "Please ensure the core server has been set up first and has generated the shared CA certificates." + log_info "You can manually copy the certificates later (check both locations on core server):" echo " sudo mkdir -p /opt/stacks/core/shared-ca" - echo " sudo scp $SSH_USER@$SERVER_IP:/opt/stacks/core/shared-ca/ca.pem /opt/stacks/core/shared-ca/" - echo " sudo scp $SSH_USER@$SERVER_IP:/opt/stacks/core/shared-ca/ca-key.pem /opt/stacks/core/shared-ca/" + echo " # Try shared-ca location:" + echo " sudo scp $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca.pem /opt/stacks/core/shared-ca/" + echo " sudo scp $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/shared-ca/ca-key.pem /opt/stacks/core/shared-ca/" + echo " # Or docker-tls location:" + echo " sudo scp $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/docker-tls/ca.pem /opt/stacks/core/shared-ca/" + echo " sudo scp $SSH_USER@$CORE_SERVER_IP:/opt/stacks/core/docker-tls/ca-key.pem /opt/stacks/core/shared-ca/" echo " sudo chown -R $SSH_USER:$SSH_USER /opt/stacks/core/shared-ca" echo "" - log_info "After copying, restart the core server's Docker services for the changes to take effect." + log_info "Then restart the Docker daemon: sudo systemctl reload docker" echo "" fi # Update Docker daemon configuration to use shared CA - log_info "Updating Docker daemon to use shared CA for TLS..." - if [ -f "/opt/stacks/core/shared-ca/ca.pem" ]; then - # Update daemon.json to use the shared CA for both server and client verification - cat > /tmp/daemon.json < /tmp/daemon.json </dev/null && log_success "Created homelab-network" || log_info "homelab-network already exists" docker network create traefik-network 2>/dev/null && log_success "Created traefik-network" || log_info "traefik-network already exists" docker network create media-network 2>/dev/null && log_success "Created media-network" || log_info "media-network already exists" echo "" - # Step 3: Deploy Dockge (always deployed) - log_info "Step 3: Deploying Dockge stack manager..." - log_info " - Dockge (Docker Compose Manager)" - echo "" + # Step 4: Deploy Dockge (always deployed) + deploy_dockge - # Copy Dockge stack files - sudo cp "$REPO_DIR/docker-compose/dockge/docker-compose.yml" /opt/dockge/docker-compose.yml - sudo cp "$REPO_DIR/.env" /opt/dockge/.env - - # Deploy Dockge stack - cd /opt/dockge - docker compose up -d - log_success "Dockge deployed" - echo "" - - # Deploy core infrastructure + # Deploy core stack if [ "$DEPLOY_CORE" = true ]; then - log_info "Step 4: Deploying core infrastructure stack..." - log_info " - DuckDNS (Dynamic DNS)" - log_info " - Traefik (Reverse Proxy with SSL)" - log_info " - Authelia (Single Sign-On)" - echo "" - - # Copy core stack files - sudo cp "$REPO_DIR/docker-compose/core/docker-compose.yml" /opt/stacks/core/docker-compose.yml - sudo cp "$REPO_DIR/.env" /opt/stacks/core/.env - - # Copy configs - if [ -d "/opt/stacks/core/traefik" ]; then - mv /opt/stacks/core/traefik /opt/stacks/core/traefik.backup.$(date +%Y%m%d_%H%M%S) - fi - cp -r "$REPO_DIR/config-templates/traefik" /opt/stacks/core/ - - # Replace ACME email placeholder - sed -i "s/ACME_EMAIL_PLACEHOLDER/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/traefik/traefik.yml - - # Replace domain placeholders in traefik dynamic configs - find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s/\${DOMAIN}/${DOMAIN}/g" {} \; - find /opt/stacks/core/traefik/dynamic -name "*.yml" -exec sed -i "s/\${SERVER_HOSTNAME}/${SERVER_HOSTNAME}/g" {} \; - - if [ -d "/opt/stacks/core/authelia" ]; then - mv /opt/stacks/core/authelia /opt/stacks/core/authelia.backup.$(date +%Y%m%d_%H%M%S) - fi - cp -r "$REPO_DIR/config-templates/authelia" /opt/stacks/core/ - - # Replace domain placeholders - sed -i "s/your-domain.duckdns.org/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml - sed -i "s/\${DOMAIN}/${DOMAIN}/g" /opt/stacks/core/authelia/configuration.yml - - # Replace secret placeholders - sed -i "s|\${AUTHELIA_JWT_SECRET}|${AUTHELIA_JWT_SECRET}|g" /opt/stacks/core/authelia/configuration.yml - sed -i "s|\${AUTHELIA_SESSION_SECRET}|${AUTHELIA_SESSION_SECRET}|g" /opt/stacks/core/authelia/configuration.yml - sed -i "s|\${AUTHELIA_STORAGE_ENCRYPTION_KEY}|${AUTHELIA_STORAGE_ENCRYPTION_KEY}|g" /opt/stacks/core/authelia/configuration.yml - sed -i "s/admin/${AUTHELIA_ADMIN_USER}/g" /opt/stacks/core/authelia/users_database.yml - sed -i "s/admin@example.com/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/authelia/users_database.yml - sed -i "s/\${DEFAULT_EMAIL}/${AUTHELIA_ADMIN_EMAIL}/g" /opt/stacks/core/authelia/users_database.yml - sed -i "s|\$argon2id\$v=19\$m=65536,t=3,p=4\$CHANGEME|${AUTHELIA_ADMIN_PASSWORD}|g" /opt/stacks/core/authelia/users_database.yml - - # Generate shared CA for multi-server TLS - log_info "Generating shared CA certificate for multi-server TLS..." - mkdir -p /opt/stacks/core/shared-ca - openssl genrsa -out /opt/stacks/core/shared-ca/ca-key.pem 4096 - openssl req -new -x509 -days 365 -key /opt/stacks/core/shared-ca/ca-key.pem -sha256 -out /opt/stacks/core/shared-ca/ca.pem -subj "/C=US/ST=State/L=City/O=Homelab/CN=Homelab-CA" - chown -R "$ACTUAL_USER:$ACTUAL_USER" /opt/stacks/core/shared-ca - - # Deploy core stack - cd /opt/stacks/core - docker compose up -d - log_success "Core infrastructure deployed" - echo "" + deploy_core fi # Deploy infrastructure stack if [ "$DEPLOY_INFRASTRUCTURE" = true ]; then - step_num=$([ "$DEPLOY_CORE" = true ] && echo "5" || echo "4") - log_info "Step $step_num: Deploying infrastructure stack..." - log_info " - Pi-hole (DNS Ad Blocker)" - log_info " - Watchtower (Container Updates)" - log_info " - Dozzle (Log Viewer)" - log_info " - Glances (System Monitor)" - log_info " - Docker Proxy (Security)" - echo "" - - # Copy infrastructure stack - cp "$REPO_DIR/docker-compose/infrastructure/docker-compose.yml" /opt/stacks/infrastructure/docker-compose.yml - cp "$REPO_DIR/.env" /opt/stacks/infrastructure/.env - - # Copy any additional config directories - for config_dir in "$REPO_DIR/docker-compose/infrastructure"/*/; do - if [ -d "$config_dir" ] && [ "$(basename "$config_dir")" != "." ]; then - cp -r "$config_dir" /opt/stacks/infrastructure/ - fi - done - - # If core is not deployed, remove Authelia middleware references - if [ "$DEPLOY_CORE" = false ]; then - log_info "Core infrastructure not deployed - removing Authelia middleware references..." - sed -i '/middlewares=authelia@docker/d' /opt/stacks/infrastructure/docker-compose.yml - fi - - # Deploy infrastructure stack - cd /opt/stacks/infrastructure - docker compose up -d - log_success "Infrastructure stack deployed" - echo "" + step_num=$([ "$DEPLOY_CORE" = true ] && echo "6" || echo "5") + deploy_infrastructure fi # Deploy dashboard stack if [ "$DEPLOY_DASHBOARDS" = true ]; then if [ "$DEPLOY_CORE" = true ] && [ "$DEPLOY_INFRASTRUCTURE" = true ]; then - step_num=6 + step_num=7 elif [ "$DEPLOY_CORE" = true ] || [ "$DEPLOY_INFRASTRUCTURE" = true ]; then - step_num=5 + step_num=6 else - step_num=4 + step_num=5 fi - log_info "Step $step_num: Deploying dashboard stack..." - log_info " - Homepage (Application Dashboard)" - log_info " - Homarr (Modern Dashboard)" - echo "" - - # Create dashboards directory - sudo mkdir -p /opt/stacks/dashboards - - # Copy dashboards compose file - cp "$REPO_DIR/docker-compose/dashboards/docker-compose.yml" /opt/stacks/dashboards/docker-compose.yml - cp "$REPO_DIR/.env" /opt/stacks/dashboards/.env - - # Copy homepage config - if [ -d "$REPO_DIR/docker-compose/dashboards/homepage" ]; then - cp -r "$REPO_DIR/docker-compose/dashboards/homepage" /opt/stacks/dashboards/ - fi - - # Deploy dashboards stack - cd /opt/stacks/dashboards - docker compose up -d - log_success "Dashboard stack deployed" - echo "" + deploy_dashboards fi # Setup stacks for Dockge if [ "$SETUP_STACKS" = true ]; then setup_stacks_for_dockge fi + + # Report any missing variables + if [ -n "$MISSING_VARS_SUMMARY" ]; then + log_warning "The following environment variables were missing and may cause issues:" + echo "$MISSING_VARS_SUMMARY" + log_info "Please update your .env file and redeploy affected stacks." + fi + + # Report any TLS issues + if [ -n "$TLS_ISSUES_SUMMARY" ]; then + echo "" + log_warning "TLS Configuration Issues Detected:" + echo "$TLS_ISSUES_SUMMARY" + echo "" + fi } # Setup Docker TLS function @@ -675,7 +846,7 @@ setup_docker_tls() { openssl x509 -req -days 365 -in "$TLS_DIR/client.csr" -CA "$TLS_DIR/ca.pem" -CAkey "$TLS_DIR/ca-key.pem" -CAcreateserial -out "$TLS_DIR/client-cert.pem" # Configure Docker daemon - cat > /etc/docker/daemon.json < /dev/null <