From c25cbf510153f85f8ccb140ae60c90e45a233a69 Mon Sep 17 00:00:00 2001 From: Joey Yakimowich-Payne Date: Thu, 15 Jan 2026 08:40:40 -0700 Subject: [PATCH] Add better prod setup --- docs/PRODUCTION.md | 65 +++++++++-- scripts/setup-prod.sh | 244 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 300 insertions(+), 9 deletions(-) create mode 100755 scripts/setup-prod.sh diff --git a/docs/PRODUCTION.md b/docs/PRODUCTION.md index a0afd4a..208616d 100644 --- a/docs/PRODUCTION.md +++ b/docs/PRODUCTION.md @@ -2,7 +2,35 @@ This guide provides instructions for deploying Kaboot to a production environment with Caddy reverse proxy and automatic HTTPS. -## Quick Start (Caddy + Automated Authentik Setup) +## Quick Start (Automated Setup) + +The easiest way to deploy Kaboot is using the automated setup script: + +```bash +# Run the production setup script with your domains +./scripts/setup-prod.sh --domain kaboot.example.com --auth-domain auth.example.com + +# Or run interactively (will prompt for domains) +./scripts/setup-prod.sh +``` + +This script automatically: +- Generates all required secrets (database passwords, API tokens, etc.) +- Creates the production `.env` file +- Creates the `Caddyfile` with your domains +- Creates the Authentik production blueprint +- Removes the development blueprint +- Builds the frontend with production URLs + +After running the script, start the stack: + +```bash +docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d +``` + +## Manual Setup + +If you prefer to configure manually, follow these steps: ```bash # 1. Generate secrets @@ -59,16 +87,22 @@ In production, the stack consists of: ## Environment Variables -Create a production `.env` file. Do not commit this file to version control. +The `setup-prod.sh` script generates the `.env` file automatically. If you need to create it manually, here's the required configuration: ### Backend & Authentik Configuration ```env -# Database Passwords (Generated by setup.sh) +# Database (generate with: openssl rand -base64 36) PG_PASS=your_strong_postgres_password +PG_USER=authentik +PG_DB=authentik + +# Authentik Secrets (generate with: openssl rand -base64 60) AUTHENTIK_SECRET_KEY=your_strong_authentik_secret -# Bootstrap credentials (Generated by setup.sh) +# Bootstrap credentials - used for initial Authentik setup +# AUTHENTIK_BOOTSTRAP_PASSWORD: Admin password (generate with: openssl rand -base64 24) +# AUTHENTIK_BOOTSTRAP_TOKEN: API token (generate with: openssl rand -hex 32) AUTHENTIK_BOOTSTRAP_PASSWORD=your_admin_password AUTHENTIK_BOOTSTRAP_TOKEN=your_api_token @@ -78,15 +112,18 @@ OIDC_JWKS_URI=https://auth.example.com/application/o/kaboot/jwks/ # Security CORS_ORIGIN=https://kaboot.example.com +NODE_ENV=production LOG_REQUESTS=true ``` ### Frontend Configuration -The frontend requires environment variables at build time: +The frontend requires environment variables at **build time** (not runtime): - `VITE_API_URL`: `https://kaboot.example.com/api` - `VITE_OIDC_AUTHORITY`: `https://auth.example.com/application/o/kaboot/` +The `setup-prod.sh` script sets these automatically when building. + ## Docker Compose Files The project includes pre-configured compose files: @@ -236,9 +273,19 @@ server { ## Authentik Configuration for Production -### Option A: Automated Setup with Blueprint (Recommended) +### Option A: Automated Setup (Recommended) -Use the production blueprint for automated configuration: +The `setup-prod.sh` script handles all Authentik configuration automatically: + +```bash +./scripts/setup-prod.sh --domain your-app.com --auth-domain auth.your-app.com +``` + +This creates the production blueprint with your domains and removes the development blueprint. + +### Option B: Manual Blueprint Setup + +If configuring manually: ```bash # 1. Copy and configure the production blueprint @@ -257,7 +304,7 @@ rm authentik/blueprints/kaboot-setup.yaml docker compose -f docker-compose.prod.yml up -d ``` -### Option B: Manual Configuration +### Option C: Manual UI Configuration 1. **Update Redirect URIs**: In the Authentik Admin interface, go to **Applications** > **Providers** > **Kaboot OAuth2**. Update the **Redirect URIs** to use your production domain: - `https://kaboot.example.com/callback` @@ -291,7 +338,7 @@ docker exec kaboot-postgresql pg_dump -U authentik authentik > authentik_backup_ ## Security Checklist -- [ ] Run `./scripts/setup.sh` to generate strong secrets (don't use defaults). +- [ ] Run `./scripts/setup-prod.sh` to generate strong secrets (don't use defaults). - [ ] Ensure `NODE_ENV` is set to `production`. - [ ] Use HTTPS for all connections (Caddy handles this automatically). - [ ] Set `CORS_ORIGIN` to your specific frontend domain (e.g., `https://kaboot.example.com`). diff --git a/scripts/setup-prod.sh b/scripts/setup-prod.sh new file mode 100755 index 0000000..ee029bf --- /dev/null +++ b/scripts/setup-prod.sh @@ -0,0 +1,244 @@ +#!/bin/bash +set -e + +GREEN='\033[0;32m' +YELLOW='\033[1;33m' +RED='\033[0;31m' +BLUE='\033[0;34m' +BOLD='\033[1m' +NC='\033[0m' + +print_header() { + echo "" + echo -e "${BLUE}${BOLD}════════════════════════════════════════════════════════════${NC}" + echo -e "${BLUE}${BOLD} Kaboot Production Setup${NC}" + echo -e "${BLUE}${BOLD}════════════════════════════════════════════════════════════${NC}" + echo "" +} + +print_step() { + echo -e "${GREEN}${BOLD}▶ $1${NC}" +} + +print_warning() { + echo -e "${YELLOW}⚠ $1${NC}" +} + +print_error() { + echo -e "${RED}✖ $1${NC}" +} + +print_success() { + echo -e "${GREEN}✔ $1${NC}" +} + +print_header + +KABOOT_DOMAIN="" +AUTH_DOMAIN="" + +while [[ $# -gt 0 ]]; do + case $1 in + --domain) + KABOOT_DOMAIN="$2" + shift 2 + ;; + --auth-domain) + AUTH_DOMAIN="$2" + shift 2 + ;; + --help|-h) + echo "Usage: $0 [OPTIONS]" + echo "" + echo "Options:" + echo " --domain DOMAIN Main application domain (e.g., kaboot.example.com)" + echo " --auth-domain DOMAIN Authentication domain (e.g., auth.example.com)" + echo " --help, -h Show this help message" + echo "" + echo "If options are not provided, you will be prompted for them." + exit 0 + ;; + *) + print_error "Unknown option: $1" + exit 1 + ;; + esac +done + +if [ ! -f ".env.example" ]; then + print_error "Error: .env.example not found. Run this script from the project root." + exit 1 +fi + +if [ -f ".env" ]; then + print_warning ".env file already exists." + read -p "Overwrite existing configuration? (y/N): " -n 1 -r + echo "" + if [[ ! $REPLY =~ ^[Yy]$ ]]; then + echo "Aborting. Existing configuration preserved." + exit 0 + fi +fi + +echo -e "${BOLD}Domain Configuration${NC}" +echo "────────────────────────────────────────────────────────────" + +if [ -z "$KABOOT_DOMAIN" ]; then + read -p "Enter your main domain (e.g., kaboot.example.com): " KABOOT_DOMAIN +fi + +if [ -z "$KABOOT_DOMAIN" ]; then + print_error "Domain is required." + exit 1 +fi + +if [ -z "$AUTH_DOMAIN" ]; then + DEFAULT_AUTH="auth.${KABOOT_DOMAIN}" + read -p "Enter your auth domain [$DEFAULT_AUTH]: " AUTH_DOMAIN + AUTH_DOMAIN=${AUTH_DOMAIN:-$DEFAULT_AUTH} +fi + +echo "" +print_step "Generating secrets..." + +PG_PASS=$(openssl rand -base64 36 | tr -d '\n' | tr -d '/') +AUTHENTIK_SECRET_KEY=$(openssl rand -base64 60 | tr -d '\n' | tr -d '/') +AUTHENTIK_BOOTSTRAP_PASSWORD=$(openssl rand -base64 24 | tr -d '\n' | tr -d '/') +AUTHENTIK_BOOTSTRAP_TOKEN=$(openssl rand -hex 32) + +print_success "Secrets generated" + +print_step "Creating .env file..." + +cat > .env << EOF +# Kaboot Production Configuration +# Generated by setup-prod.sh on $(date) + +# Database +PG_PASS=${PG_PASS} +PG_USER=authentik +PG_DB=authentik + +# Authentik Secrets +AUTHENTIK_SECRET_KEY=${AUTHENTIK_SECRET_KEY} +AUTHENTIK_BOOTSTRAP_PASSWORD=${AUTHENTIK_BOOTSTRAP_PASSWORD} +AUTHENTIK_BOOTSTRAP_TOKEN=${AUTHENTIK_BOOTSTRAP_TOKEN} +AUTHENTIK_ERROR_REPORTING=false + +# OIDC Configuration (Production) +OIDC_ISSUER=https://${AUTH_DOMAIN}/application/o/kaboot/ +OIDC_JWKS_URI=https://${AUTH_DOMAIN}/application/o/kaboot/jwks/ + +# Security +CORS_ORIGIN=https://${KABOOT_DOMAIN} +NODE_ENV=production +LOG_REQUESTS=true +EOF + +print_success "Created .env" + +print_step "Creating Caddyfile..." + +cat > Caddyfile << EOF +# Kaboot Production Caddyfile +# Generated by setup-prod.sh on $(date) + +${KABOOT_DOMAIN} { + root * /srv/frontend + file_server + try_files {path} /index.html + + handle /api/* { + reverse_proxy kaboot-backend:3001 + } + + handle /health { + reverse_proxy kaboot-backend:3001 + } +} + +${AUTH_DOMAIN} { + reverse_proxy authentik-server:9000 { + header_up X-Forwarded-Proto {scheme} + header_up X-Forwarded-Host {host} + transport http { + keepalive 30s + } + } +} +EOF + +print_success "Created Caddyfile" + +print_step "Creating production Authentik blueprint..." + +BLUEPRINT_DIR="authentik/blueprints" +PROD_BLUEPRINT="${BLUEPRINT_DIR}/kaboot-setup-production.yaml" +DEV_BLUEPRINT="${BLUEPRINT_DIR}/kaboot-setup.yaml" + +if [ -f "${BLUEPRINT_DIR}/kaboot-setup-production.yaml.example" ]; then + sed "s/kaboot.example.com/${KABOOT_DOMAIN}/g; s/auth.example.com/${AUTH_DOMAIN}/g" \ + "${BLUEPRINT_DIR}/kaboot-setup-production.yaml.example" > "$PROD_BLUEPRINT" + print_success "Created production blueprint" +else + print_warning "Production blueprint example not found, skipping..." +fi + +if [ -f "$DEV_BLUEPRINT" ]; then + rm "$DEV_BLUEPRINT" + print_success "Removed development blueprint" +fi + +print_step "Building frontend with production URLs..." + +VITE_API_URL="https://${KABOOT_DOMAIN}/api" \ +VITE_OIDC_AUTHORITY="https://${AUTH_DOMAIN}/application/o/kaboot/" \ +npm run build --silent 2>/dev/null || npm run build + +print_success "Frontend built" + +echo "" +echo -e "${GREEN}${BOLD}════════════════════════════════════════════════════════════${NC}" +echo -e "${GREEN}${BOLD} Setup Complete!${NC}" +echo -e "${GREEN}${BOLD}════════════════════════════════════════════════════════════${NC}" +echo "" +echo -e "${BOLD}Configuration Summary${NC}" +echo "────────────────────────────────────────────────────────────" +echo -e " Main Domain: ${BLUE}https://${KABOOT_DOMAIN}${NC}" +echo -e " Auth Domain: ${BLUE}https://${AUTH_DOMAIN}${NC}" +echo "" +echo -e "${BOLD}Authentik Admin Credentials${NC}" +echo "────────────────────────────────────────────────────────────" +echo -e " Username: ${YELLOW}akadmin${NC}" +echo -e " Password: ${YELLOW}${AUTHENTIK_BOOTSTRAP_PASSWORD}${NC}" +echo "" +echo -e "${RED}${BOLD}⚠ SAVE THESE CREDENTIALS - They won't be shown again!${NC}" +echo "" +echo -e "${BOLD}Files Created${NC}" +echo "────────────────────────────────────────────────────────────" +echo " .env - Environment variables" +echo " Caddyfile - Reverse proxy config" +echo " authentik/blueprints/kaboot-setup-production.yaml" +echo " dist/ - Built frontend" +echo "" +echo -e "${BOLD}Next Steps${NC}" +echo "────────────────────────────────────────────────────────────" +echo "" +echo " 1. Ensure DNS records point to this server:" +echo -e " ${KABOOT_DOMAIN} → ${BLUE}${NC}" +echo -e " ${AUTH_DOMAIN} → ${BLUE}${NC}" +echo "" +echo " 2. Start the production stack:" +echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml up -d${NC}" +echo "" +echo " 3. Wait for services to start (~60 seconds for Authentik)" +echo "" +echo " 4. Verify all services are running:" +echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml -f docker-compose.caddy.yml ps${NC}" +echo "" +echo " 5. Check Authentik blueprint was applied:" +echo -e " ${YELLOW}docker compose -f docker-compose.prod.yml logs authentik-server | grep -i blueprint${NC}" +echo "" +echo " 6. Access your app at:" +echo -e " ${BLUE}https://${KABOOT_DOMAIN}${NC}" +echo ""