Files
n8n-install/scripts/setup_custom_tls.sh
Yury Kossakovsky adc5b94f1c fix(caddy): resolve duplicate hostname error with custom tls certificates
change architecture from generating separate site blocks to using
a shared tls snippet that all services import
2026-01-24 20:23:25 -07:00

252 lines
7.7 KiB
Bash
Executable File

#!/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"
# 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 "$@"