| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435 |
- #!/bin/bash
- # Smartbotic Nginx + SSL Setup Script
- # Sets up nginx as reverse proxy with Let's Encrypt SSL
- #
- # Usage:
- # sudo ./setup-nginx.sh --domain console.example.com --email admin@example.com
- # sudo ./setup-nginx.sh --domain console.example.com --email admin@example.com --staging
- set -e
- # Colors
- RED='\033[0;31m'
- GREEN='\033[0;32m'
- YELLOW='\033[1;33m'
- BLUE='\033[0;34m'
- NC='\033[0m'
- print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
- print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
- print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
- print_step() { echo -e "${BLUE}[STEP]${NC} $1"; }
- usage() {
- cat <<EOF
- Smartbotic Nginx + SSL Setup
- Usage: $0 [OPTIONS]
- Required:
- --domain DOMAIN Domain name(s) for SSL certificate. Can be specified multiple times
- or as comma-separated list (e.g., --domain app.example.com,api.example.com)
- --email EMAIL Email for Let's Encrypt notifications
- Options:
- --staging Use Let's Encrypt staging server (for testing)
- --no-ssl Skip SSL setup (HTTP only)
- --nginx-only Only install/configure nginx, skip certbot
- --renew Renew existing certificates
- --dry-run Show what would be done
- -h, --help Show this help
- Examples:
- # Single domain with SSL
- sudo $0 --domain app.smartbotics.ai --email admin@smartbotics.ai
- # Multiple domains (comma-separated)
- sudo $0 --domain app.smartbotics.ai,api.smartbotics.ai --email admin@smartbotics.ai
- # Test with staging certificates first
- sudo $0 --domain app.smartbotics.ai --email admin@smartbotics.ai --staging
- # Renew certificates
- sudo $0 --renew
- EOF
- }
- DOMAINS=()
- EMAIL=""
- STAGING=false
- NO_SSL=false
- NGINX_ONLY=false
- RENEW=false
- DRY_RUN=false
- while [[ $# -gt 0 ]]; do
- case $1 in
- --domain)
- # Support comma-separated domains
- IFS=',' read -ra NEW_DOMAINS <<< "$2"
- for d in "${NEW_DOMAINS[@]}"; do
- # Trim whitespace
- d=$(echo "$d" | xargs)
- [ -n "$d" ] && DOMAINS+=("$d")
- done
- shift 2
- ;;
- --email) EMAIL="$2"; shift 2 ;;
- --staging) STAGING=true; shift ;;
- --no-ssl) NO_SSL=true; shift ;;
- --nginx-only) NGINX_ONLY=true; shift ;;
- --renew) RENEW=true; shift ;;
- --dry-run) DRY_RUN=true; shift ;;
- -h|--help) usage; exit 0 ;;
- *) print_error "Unknown option: $1"; usage; exit 1 ;;
- esac
- done
- # Primary domain is the first one (used for certificate directory)
- PRIMARY_DOMAIN="${DOMAINS[0]:-}"
- # Build server_name string (space-separated for nginx)
- SERVER_NAMES="${DOMAINS[*]}"
- # Build certbot domain args (-d domain1 -d domain2 ...)
- CERTBOT_DOMAINS=""
- for d in "${DOMAINS[@]}"; do
- CERTBOT_DOMAINS="$CERTBOT_DOMAINS -d $d"
- done
- # Validate arguments
- if [ "$RENEW" = false ]; then
- if [ ${#DOMAINS[@]} -eq 0 ]; then
- print_error "--domain is required"
- usage
- exit 1
- fi
- if [ -z "$EMAIL" ] && [ "$NO_SSL" = false ] && [ "$NGINX_ONLY" = false ]; then
- print_error "--email is required for SSL setup"
- usage
- exit 1
- fi
- fi
- # Check root
- if [ "$EUID" -ne 0 ] && [ "$DRY_RUN" = false ]; then
- print_error "Must run as root"
- exit 1
- fi
- # Paths
- NGINX_CONF="/etc/nginx/sites-available/smartbotic"
- NGINX_ENABLED="/etc/nginx/sites-enabled/smartbotic"
- CERTBOT_WEBROOT="/var/www/certbot"
- SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
- TEMPLATE_DIR="$(dirname "$SCRIPT_DIR")/nginx"
- # Find template
- find_template() {
- local template=""
- local search_paths=(
- "$TEMPLATE_DIR/smartbotic.conf.template"
- "/opt/smartbotic/share/smartbotic/nginx/smartbotic.conf.template"
- "./smartbotic.conf.template"
- )
- for path in "${search_paths[@]}"; do
- if [ -f "$path" ]; then
- template="$path"
- break
- fi
- done
- echo "$template"
- }
- run_cmd() {
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] $*"
- else
- "$@"
- fi
- }
- # Install nginx
- install_nginx() {
- print_step "Installing nginx..."
- if command -v nginx &> /dev/null; then
- print_info " nginx is already installed"
- return
- fi
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] apt-get update && apt-get install -y nginx"
- else
- apt-get update -qq
- apt-get install -y nginx
- fi
- }
- # Install certbot
- install_certbot() {
- print_step "Installing certbot..."
- if command -v certbot &> /dev/null; then
- print_info " certbot is already installed"
- return
- fi
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] apt-get install -y certbot python3-certbot-nginx"
- else
- apt-get update -qq
- apt-get install -y certbot python3-certbot-nginx
- fi
- }
- # Create initial HTTP-only config for certificate issuance
- create_http_config() {
- print_step "Creating initial HTTP configuration..."
- run_cmd mkdir -p "$CERTBOT_WEBROOT"
- local http_config="# Temporary HTTP config for Let's Encrypt challenge
- server {
- listen 80;
- listen [::]:80;
- server_name $SERVER_NAMES;
- location /.well-known/acme-challenge/ {
- root $CERTBOT_WEBROOT;
- }
- location / {
- return 503 'SSL certificate pending';
- add_header Content-Type text/plain;
- }
- }
- "
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] Creating $NGINX_CONF"
- echo "$http_config"
- else
- echo "$http_config" > "$NGINX_CONF"
- ln -sf "$NGINX_CONF" "$NGINX_ENABLED" 2>/dev/null || true
- # Remove default site if exists
- rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
- nginx -t && systemctl reload nginx
- fi
- }
- # Obtain SSL certificate
- obtain_certificate() {
- print_step "Obtaining SSL certificate from Let's Encrypt..."
- print_info " Domains: $SERVER_NAMES"
- local certbot_args="certonly --webroot -w $CERTBOT_WEBROOT $CERTBOT_DOMAINS --email $EMAIL --agree-tos --non-interactive"
- if [ "$STAGING" = true ]; then
- certbot_args="$certbot_args --staging"
- print_warn " Using Let's Encrypt STAGING server (certificates won't be trusted)"
- fi
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] certbot $certbot_args"
- else
- certbot $certbot_args
- fi
- }
- # Create full HTTPS config
- create_https_config() {
- print_step "Creating HTTPS configuration..."
- local template
- template=$(find_template)
- if [ -z "$template" ]; then
- print_error "Cannot find nginx template file"
- print_info "Expected locations:"
- print_info " - $TEMPLATE_DIR/smartbotic.conf.template"
- print_info " - /opt/smartbotic/share/smartbotic/nginx/smartbotic.conf.template"
- exit 1
- fi
- print_info " Using template: $template"
- print_info " Primary domain: $PRIMARY_DOMAIN"
- print_info " All domains: $SERVER_NAMES"
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] Generating config from template"
- else
- # Replace placeholders
- sed -e "s/{{DOMAIN}}/$PRIMARY_DOMAIN/g" \
- -e "s/{{SERVER_NAMES}}/$SERVER_NAMES/g" \
- "$template" > "$NGINX_CONF"
- nginx -t && systemctl reload nginx
- fi
- }
- # Create HTTP-only config (no SSL)
- create_http_only_config() {
- print_step "Creating HTTP-only configuration (no SSL)..."
- local http_config="# Smartbotic Nginx Configuration (HTTP only)
- # Domains: $SERVER_NAMES
- upstream smartbotic_http {
- server 127.0.0.1:8090;
- keepalive 32;
- }
- server {
- listen 80;
- listen [::]:80;
- server_name $SERVER_NAMES;
- access_log /var/log/nginx/smartbotic_access.log;
- error_log /var/log/nginx/smartbotic_error.log;
- client_max_body_size 50M;
- # Gzip compression
- gzip on;
- gzip_vary on;
- gzip_proxied any;
- gzip_comp_level 6;
- gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
- location /api/ {
- proxy_pass http://smartbotic_http;
- proxy_http_version 1.1;
- proxy_set_header Host \$host;
- proxy_set_header X-Real-IP \$remote_addr;
- proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto \$scheme;
- proxy_set_header Connection \"\";
- proxy_read_timeout 300s;
- proxy_buffering off;
- }
- location / {
- proxy_pass http://smartbotic_http;
- proxy_http_version 1.1;
- proxy_set_header Host \$host;
- proxy_set_header X-Real-IP \$remote_addr;
- proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
- proxy_set_header X-Forwarded-Proto \$scheme;
- proxy_set_header Connection \"\";
- }
- }
- "
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] Creating $NGINX_CONF"
- else
- echo "$http_config" > "$NGINX_CONF"
- ln -sf "$NGINX_CONF" "$NGINX_ENABLED" 2>/dev/null || true
- rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
- nginx -t && systemctl reload nginx
- fi
- }
- # Setup certificate renewal
- setup_renewal() {
- print_step "Setting up automatic certificate renewal..."
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] Configuring certbot renewal timer"
- else
- systemctl enable certbot.timer 2>/dev/null || true
- systemctl start certbot.timer 2>/dev/null || true
- print_info " Certbot renewal timer enabled"
- fi
- }
- # Renew certificates
- renew_certificates() {
- print_step "Renewing certificates..."
- if [ "$DRY_RUN" = true ]; then
- echo " [DRY-RUN] certbot renew"
- else
- certbot renew
- systemctl reload nginx
- fi
- }
- # Main
- main() {
- echo ""
- print_info "Smartbotic Nginx + SSL Setup"
- print_info "============================"
- if [ ${#DOMAINS[@]} -gt 0 ]; then
- print_info "Domains: $SERVER_NAMES"
- fi
- if [ -n "$EMAIL" ]; then
- print_info "Email: $EMAIL"
- fi
- if [ "$STAGING" = true ]; then
- print_warn "Mode: STAGING (test certificates)"
- fi
- if [ "$DRY_RUN" = true ]; then
- print_warn "DRY-RUN MODE - no changes will be made"
- fi
- echo ""
- if [ "$RENEW" = true ]; then
- renew_certificates
- echo ""
- print_info "Certificate renewal complete!"
- exit 0
- fi
- # Install packages
- install_nginx
- if [ "$NO_SSL" = true ]; then
- # HTTP only mode
- create_http_only_config
- echo ""
- print_info "Nginx configured (HTTP only)"
- print_info "Access WebUI at: http://$PRIMARY_DOMAIN/"
- elif [ "$NGINX_ONLY" = true ]; then
- # Just nginx, no certbot
- create_http_only_config
- echo ""
- print_info "Nginx installed and configured"
- print_info "Run with --email to set up SSL later"
- else
- # Full SSL setup
- install_certbot
- # Check if certificate already exists
- if [ -d "/etc/letsencrypt/live/$PRIMARY_DOMAIN" ] && [ "$DRY_RUN" = false ]; then
- print_info "Certificate already exists for $PRIMARY_DOMAIN"
- create_https_config
- else
- create_http_config
- obtain_certificate
- create_https_config
- fi
- setup_renewal
- echo ""
- print_info "Setup complete!"
- echo ""
- echo "Your Smartbotic instance is now available at:"
- for d in "${DOMAINS[@]}"; do
- echo " https://$d/"
- done
- echo ""
- echo "Certificate renewal is automatic via certbot timer."
- echo "To manually renew: sudo certbot renew"
- echo ""
- fi
- # Enable and start nginx
- if [ "$DRY_RUN" = false ]; then
- systemctl enable nginx
- systemctl start nginx 2>/dev/null || systemctl reload nginx
- fi
- }
- main
|