#!/usr/bin/env bash # ============================================================================= # setup_custom_tls.sh - Configure custom TLS certificates for Caddy # ============================================================================= # Updates caddy-addon/tls-snippet.conf to use corporate/internal certificates # instead of Let's Encrypt. # # Usage: # bash scripts/setup_custom_tls.sh # Interactive mode # bash scripts/setup_custom_tls.sh cert.crt key.key # Non-interactive mode # bash scripts/setup_custom_tls.sh --remove # Reset to Let's Encrypt # # Prerequisites: # - Place certificate files in ./certs/ directory # - Certificate paths are relative to container (/etc/caddy/certs/) # ============================================================================= set -euo pipefail source "$(dirname "$0")/utils.sh" && init_paths SNIPPET_FILE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf" SNIPPET_EXAMPLE="$PROJECT_ROOT/caddy-addon/tls-snippet.conf.example" CERTS_DIR="$PROJECT_ROOT/certs" # Legacy file that causes duplicate host errors (must be cleaned up on migration) # TODO: Remove OLD_CONFIG and cleanup_legacy_config() after v3.0 release (all users migrated) OLD_CONFIG="$PROJECT_ROOT/caddy-addon/custom-tls.conf" # ============================================================================= # FUNCTIONS # ============================================================================= cleanup_legacy_config() { # Remove old custom-tls.conf that causes duplicate host errors # This is needed for users upgrading from older versions if [[ -f "$OLD_CONFIG" ]]; then log_warning "Removing obsolete custom-tls.conf (causes duplicate host errors)" rm -f "$OLD_CONFIG" fi } show_help() { cat << EOF Setup Custom TLS Certificates for Caddy Usage: $(basename "$0") [OPTIONS] [CERT_FILE] [KEY_FILE] Options: -h, --help Show this help message --remove Reset to Let's Encrypt automatic certificates Arguments: CERT_FILE Path to certificate file (relative to ./certs/) KEY_FILE Path to private key file (relative to ./certs/) Examples: $(basename "$0") # Interactive mode $(basename "$0") wildcard.crt wildcard.key # Use specific files $(basename "$0") --remove # Reset to Let's Encrypt The script will: 1. Detect certificate files in ./certs/ 2. Update caddy-addon/tls-snippet.conf with your certificate paths 3. Optionally restart Caddy EOF } find_certificates() { # Find certificate files in certs directory local certs=() if [[ -d "$CERTS_DIR" ]]; then while IFS= read -r -d '' file; do certs+=("$(basename "$file")") done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.crt" -o -name "*.pem" -o -name "*.cer" \) -print0 2>/dev/null) fi echo "${certs[*]:-}" } find_keys() { # Find key files in certs directory local keys=() if [[ -d "$CERTS_DIR" ]]; then while IFS= read -r -d '' file; do keys+=("$(basename "$file")") done < <(find "$CERTS_DIR" -maxdepth 1 -type f \( -name "*.key" -o -name "*-key.pem" \) -print0 2>/dev/null) fi echo "${keys[*]:-}" } ensure_snippet_exists() { # Create tls-snippet.conf from example if it doesn't exist # This ensures the file survives git updates (it's gitignored) if [[ ! -f "$SNIPPET_FILE" ]]; then if [[ -f "$SNIPPET_EXAMPLE" ]]; then cp "$SNIPPET_EXAMPLE" "$SNIPPET_FILE" log_info "Created tls-snippet.conf from template" else # Fallback: create default content directly remove_config fi fi } generate_config() { local cert_file="$1" local key_file="$2" cat > "$SNIPPET_FILE" << EOF # TLS Configuration Snippet # Generated by setup_custom_tls.sh on $(date -Iseconds) # Using custom certificates instead of Let's Encrypt. # Reset to Let's Encrypt: make setup-tls --remove (service_tls) { tls /etc/caddy/certs/$cert_file /etc/caddy/certs/$key_file } EOF log_success "Generated $SNIPPET_FILE" } remove_config() { cat > "$SNIPPET_FILE" << 'EOF' # TLS Configuration Snippet # Imported by all service blocks in the main Caddyfile. # # Default: Empty (uses Let's Encrypt automatic certificates) # Custom: Overwritten by 'make setup-tls' with your certificate paths # Reset: Run 'make setup-tls --remove' to restore Let's Encrypt (service_tls) { # Default: Let's Encrypt automatic certificates (empty = no override) } EOF log_success "Reset to Let's Encrypt (automatic certificates)" } restart_caddy() { if wt_yesno "Restart Caddy" "Do you want to restart Caddy to apply the new configuration?" "yes"; then log_info "Restarting Caddy..." docker compose -p localai restart caddy log_success "Caddy restarted" else log_info "Skipped Caddy restart. Run manually: docker compose -p localai restart caddy" fi } # ============================================================================= # MAIN # ============================================================================= main() { # Handle arguments case "${1:-}" in -h|--help) show_help exit 0 ;; --remove) cleanup_legacy_config remove_config restart_caddy exit 0 ;; esac # Clean up legacy config that causes duplicate hosts cleanup_legacy_config # Ensure snippet file exists (survives git updates) ensure_snippet_exists # Ensure certs directory exists mkdir -p "$CERTS_DIR" local cert_file="" local key_file="" # Non-interactive mode if [[ $# -ge 2 ]]; then cert_file="$1" key_file="$2" if [[ ! -f "$CERTS_DIR/$cert_file" ]]; then log_error "Certificate not found: $CERTS_DIR/$cert_file" exit 1 fi if [[ ! -f "$CERTS_DIR/$key_file" ]]; then log_error "Key not found: $CERTS_DIR/$key_file" exit 1 fi else # Interactive mode require_whiptail # Find available certificates local certs_arr IFS=' ' read -ra certs_arr <<< "$(find_certificates)" if [[ ${#certs_arr[@]} -eq 0 ]]; then wt_msg "No Certificates Found" "No certificate files found in ./certs/\n\nPlease place your certificate (.crt, .pem, .cer) and key (.key) files in the certs/ directory first." exit 1 fi # Build menu items for certificates local cert_items=() for cert in "${certs_arr[@]}"; do cert_items+=("$cert" "") done cert_file=$(wt_menu "Select Certificate" "Choose your TLS certificate file:" "${cert_items[@]}") [[ -z "$cert_file" ]] && exit 1 # Find available keys local keys_arr IFS=' ' read -ra keys_arr <<< "$(find_keys)" if [[ ${#keys_arr[@]} -eq 0 ]]; then wt_msg "No Keys Found" "No key files found in ./certs/\n\nPlease place your private key (.key) file in the certs/ directory." exit 1 fi # Build menu items for keys local key_items=() for key in "${keys_arr[@]}"; do key_items+=("$key" "") done key_file=$(wt_menu "Select Private Key" "Choose your TLS private key file:" "${key_items[@]}") [[ -z "$key_file" ]] && exit 1 fi log_info "Using certificate: $cert_file" log_info "Using key: $key_file" # Ensure certificate files are readable by Caddy container # (Docker volume mounts preserve host permissions, Caddy may run as different UID) chmod 644 "$CERTS_DIR/$cert_file" "$CERTS_DIR/$key_file" # Generate configuration generate_config "$cert_file" "$key_file" echo "" log_info "Custom TLS configured successfully!" log_info "All services will use: /etc/caddy/certs/$cert_file" echo "" # Restart Caddy restart_caddy } main "$@"