setup-nginx.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. #!/bin/bash
  2. # Smartbotic Nginx + SSL Setup Script
  3. # Sets up nginx as reverse proxy with Let's Encrypt SSL
  4. #
  5. # Usage:
  6. # sudo ./setup-nginx.sh --domain console.example.com --email admin@example.com
  7. # sudo ./setup-nginx.sh --domain console.example.com --email admin@example.com --staging
  8. set -e
  9. # Colors
  10. RED='\033[0;31m'
  11. GREEN='\033[0;32m'
  12. YELLOW='\033[1;33m'
  13. BLUE='\033[0;34m'
  14. NC='\033[0m'
  15. print_info() { echo -e "${GREEN}[INFO]${NC} $1"; }
  16. print_warn() { echo -e "${YELLOW}[WARN]${NC} $1"; }
  17. print_error() { echo -e "${RED}[ERROR]${NC} $1"; }
  18. print_step() { echo -e "${BLUE}[STEP]${NC} $1"; }
  19. usage() {
  20. cat <<EOF
  21. Smartbotic Nginx + SSL Setup
  22. Usage: $0 [OPTIONS]
  23. Required:
  24. --domain DOMAIN Domain name(s) for SSL certificate. Can be specified multiple times
  25. or as comma-separated list (e.g., --domain app.example.com,api.example.com)
  26. --email EMAIL Email for Let's Encrypt notifications
  27. Options:
  28. --staging Use Let's Encrypt staging server (for testing)
  29. --no-ssl Skip SSL setup (HTTP only)
  30. --nginx-only Only install/configure nginx, skip certbot
  31. --renew Renew existing certificates
  32. --dry-run Show what would be done
  33. -h, --help Show this help
  34. Examples:
  35. # Single domain with SSL
  36. sudo $0 --domain app.smartbotics.ai --email admin@smartbotics.ai
  37. # Multiple domains (comma-separated)
  38. sudo $0 --domain app.smartbotics.ai,api.smartbotics.ai --email admin@smartbotics.ai
  39. # Test with staging certificates first
  40. sudo $0 --domain app.smartbotics.ai --email admin@smartbotics.ai --staging
  41. # Renew certificates
  42. sudo $0 --renew
  43. EOF
  44. }
  45. DOMAINS=()
  46. EMAIL=""
  47. STAGING=false
  48. NO_SSL=false
  49. NGINX_ONLY=false
  50. RENEW=false
  51. DRY_RUN=false
  52. while [[ $# -gt 0 ]]; do
  53. case $1 in
  54. --domain)
  55. # Support comma-separated domains
  56. IFS=',' read -ra NEW_DOMAINS <<< "$2"
  57. for d in "${NEW_DOMAINS[@]}"; do
  58. # Trim whitespace
  59. d=$(echo "$d" | xargs)
  60. [ -n "$d" ] && DOMAINS+=("$d")
  61. done
  62. shift 2
  63. ;;
  64. --email) EMAIL="$2"; shift 2 ;;
  65. --staging) STAGING=true; shift ;;
  66. --no-ssl) NO_SSL=true; shift ;;
  67. --nginx-only) NGINX_ONLY=true; shift ;;
  68. --renew) RENEW=true; shift ;;
  69. --dry-run) DRY_RUN=true; shift ;;
  70. -h|--help) usage; exit 0 ;;
  71. *) print_error "Unknown option: $1"; usage; exit 1 ;;
  72. esac
  73. done
  74. # Primary domain is the first one (used for certificate directory)
  75. PRIMARY_DOMAIN="${DOMAINS[0]:-}"
  76. # Build server_name string (space-separated for nginx)
  77. SERVER_NAMES="${DOMAINS[*]}"
  78. # Build certbot domain args (-d domain1 -d domain2 ...)
  79. CERTBOT_DOMAINS=""
  80. for d in "${DOMAINS[@]}"; do
  81. CERTBOT_DOMAINS="$CERTBOT_DOMAINS -d $d"
  82. done
  83. # Validate arguments
  84. if [ "$RENEW" = false ]; then
  85. if [ ${#DOMAINS[@]} -eq 0 ]; then
  86. print_error "--domain is required"
  87. usage
  88. exit 1
  89. fi
  90. if [ -z "$EMAIL" ] && [ "$NO_SSL" = false ] && [ "$NGINX_ONLY" = false ]; then
  91. print_error "--email is required for SSL setup"
  92. usage
  93. exit 1
  94. fi
  95. fi
  96. # Check root
  97. if [ "$EUID" -ne 0 ] && [ "$DRY_RUN" = false ]; then
  98. print_error "Must run as root"
  99. exit 1
  100. fi
  101. # Paths
  102. NGINX_CONF="/etc/nginx/sites-available/smartbotic"
  103. NGINX_ENABLED="/etc/nginx/sites-enabled/smartbotic"
  104. CERTBOT_WEBROOT="/var/www/certbot"
  105. SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
  106. TEMPLATE_DIR="$(dirname "$SCRIPT_DIR")/nginx"
  107. # Find template
  108. find_template() {
  109. local template=""
  110. local search_paths=(
  111. "$TEMPLATE_DIR/smartbotic.conf.template"
  112. "/opt/smartbotic/share/smartbotic/nginx/smartbotic.conf.template"
  113. "./smartbotic.conf.template"
  114. )
  115. for path in "${search_paths[@]}"; do
  116. if [ -f "$path" ]; then
  117. template="$path"
  118. break
  119. fi
  120. done
  121. echo "$template"
  122. }
  123. run_cmd() {
  124. if [ "$DRY_RUN" = true ]; then
  125. echo " [DRY-RUN] $*"
  126. else
  127. "$@"
  128. fi
  129. }
  130. # Install nginx
  131. install_nginx() {
  132. print_step "Installing nginx..."
  133. if command -v nginx &> /dev/null; then
  134. print_info " nginx is already installed"
  135. return
  136. fi
  137. if [ "$DRY_RUN" = true ]; then
  138. echo " [DRY-RUN] apt-get update && apt-get install -y nginx"
  139. else
  140. apt-get update -qq
  141. apt-get install -y nginx
  142. fi
  143. }
  144. # Install certbot
  145. install_certbot() {
  146. print_step "Installing certbot..."
  147. if command -v certbot &> /dev/null; then
  148. print_info " certbot is already installed"
  149. return
  150. fi
  151. if [ "$DRY_RUN" = true ]; then
  152. echo " [DRY-RUN] apt-get install -y certbot python3-certbot-nginx"
  153. else
  154. apt-get update -qq
  155. apt-get install -y certbot python3-certbot-nginx
  156. fi
  157. }
  158. # Create initial HTTP-only config for certificate issuance
  159. create_http_config() {
  160. print_step "Creating initial HTTP configuration..."
  161. run_cmd mkdir -p "$CERTBOT_WEBROOT"
  162. local http_config="# Temporary HTTP config for Let's Encrypt challenge
  163. server {
  164. listen 80;
  165. listen [::]:80;
  166. server_name $SERVER_NAMES;
  167. location /.well-known/acme-challenge/ {
  168. root $CERTBOT_WEBROOT;
  169. }
  170. location / {
  171. return 503 'SSL certificate pending';
  172. add_header Content-Type text/plain;
  173. }
  174. }
  175. "
  176. if [ "$DRY_RUN" = true ]; then
  177. echo " [DRY-RUN] Creating $NGINX_CONF"
  178. echo "$http_config"
  179. else
  180. echo "$http_config" > "$NGINX_CONF"
  181. ln -sf "$NGINX_CONF" "$NGINX_ENABLED" 2>/dev/null || true
  182. # Remove default site if exists
  183. rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
  184. nginx -t && systemctl reload nginx
  185. fi
  186. }
  187. # Obtain SSL certificate
  188. obtain_certificate() {
  189. print_step "Obtaining SSL certificate from Let's Encrypt..."
  190. print_info " Domains: $SERVER_NAMES"
  191. local certbot_args="certonly --webroot -w $CERTBOT_WEBROOT $CERTBOT_DOMAINS --email $EMAIL --agree-tos --non-interactive"
  192. if [ "$STAGING" = true ]; then
  193. certbot_args="$certbot_args --staging"
  194. print_warn " Using Let's Encrypt STAGING server (certificates won't be trusted)"
  195. fi
  196. if [ "$DRY_RUN" = true ]; then
  197. echo " [DRY-RUN] certbot $certbot_args"
  198. else
  199. certbot $certbot_args
  200. fi
  201. }
  202. # Create full HTTPS config
  203. create_https_config() {
  204. print_step "Creating HTTPS configuration..."
  205. local template
  206. template=$(find_template)
  207. if [ -z "$template" ]; then
  208. print_error "Cannot find nginx template file"
  209. print_info "Expected locations:"
  210. print_info " - $TEMPLATE_DIR/smartbotic.conf.template"
  211. print_info " - /opt/smartbotic/share/smartbotic/nginx/smartbotic.conf.template"
  212. exit 1
  213. fi
  214. print_info " Using template: $template"
  215. print_info " Primary domain: $PRIMARY_DOMAIN"
  216. print_info " All domains: $SERVER_NAMES"
  217. if [ "$DRY_RUN" = true ]; then
  218. echo " [DRY-RUN] Generating config from template"
  219. else
  220. # Replace placeholders
  221. sed -e "s/{{DOMAIN}}/$PRIMARY_DOMAIN/g" \
  222. -e "s/{{SERVER_NAMES}}/$SERVER_NAMES/g" \
  223. "$template" > "$NGINX_CONF"
  224. nginx -t && systemctl reload nginx
  225. fi
  226. }
  227. # Create HTTP-only config (no SSL)
  228. create_http_only_config() {
  229. print_step "Creating HTTP-only configuration (no SSL)..."
  230. local http_config="# Smartbotic Nginx Configuration (HTTP only)
  231. # Domains: $SERVER_NAMES
  232. upstream smartbotic_http {
  233. server 127.0.0.1:8090;
  234. keepalive 32;
  235. }
  236. server {
  237. listen 80;
  238. listen [::]:80;
  239. server_name $SERVER_NAMES;
  240. access_log /var/log/nginx/smartbotic_access.log;
  241. error_log /var/log/nginx/smartbotic_error.log;
  242. client_max_body_size 50M;
  243. # Gzip compression
  244. gzip on;
  245. gzip_vary on;
  246. gzip_proxied any;
  247. gzip_comp_level 6;
  248. gzip_types text/plain text/css text/xml application/json application/javascript application/rss+xml application/atom+xml image/svg+xml;
  249. location /api/ {
  250. proxy_pass http://smartbotic_http;
  251. proxy_http_version 1.1;
  252. proxy_set_header Host \$host;
  253. proxy_set_header X-Real-IP \$remote_addr;
  254. proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  255. proxy_set_header X-Forwarded-Proto \$scheme;
  256. proxy_set_header Connection \"\";
  257. proxy_read_timeout 300s;
  258. proxy_buffering off;
  259. }
  260. location / {
  261. proxy_pass http://smartbotic_http;
  262. proxy_http_version 1.1;
  263. proxy_set_header Host \$host;
  264. proxy_set_header X-Real-IP \$remote_addr;
  265. proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
  266. proxy_set_header X-Forwarded-Proto \$scheme;
  267. proxy_set_header Connection \"\";
  268. }
  269. }
  270. "
  271. if [ "$DRY_RUN" = true ]; then
  272. echo " [DRY-RUN] Creating $NGINX_CONF"
  273. else
  274. echo "$http_config" > "$NGINX_CONF"
  275. ln -sf "$NGINX_CONF" "$NGINX_ENABLED" 2>/dev/null || true
  276. rm -f /etc/nginx/sites-enabled/default 2>/dev/null || true
  277. nginx -t && systemctl reload nginx
  278. fi
  279. }
  280. # Setup certificate renewal
  281. setup_renewal() {
  282. print_step "Setting up automatic certificate renewal..."
  283. if [ "$DRY_RUN" = true ]; then
  284. echo " [DRY-RUN] Configuring certbot renewal timer"
  285. else
  286. systemctl enable certbot.timer 2>/dev/null || true
  287. systemctl start certbot.timer 2>/dev/null || true
  288. print_info " Certbot renewal timer enabled"
  289. fi
  290. }
  291. # Renew certificates
  292. renew_certificates() {
  293. print_step "Renewing certificates..."
  294. if [ "$DRY_RUN" = true ]; then
  295. echo " [DRY-RUN] certbot renew"
  296. else
  297. certbot renew
  298. systemctl reload nginx
  299. fi
  300. }
  301. # Main
  302. main() {
  303. echo ""
  304. print_info "Smartbotic Nginx + SSL Setup"
  305. print_info "============================"
  306. if [ ${#DOMAINS[@]} -gt 0 ]; then
  307. print_info "Domains: $SERVER_NAMES"
  308. fi
  309. if [ -n "$EMAIL" ]; then
  310. print_info "Email: $EMAIL"
  311. fi
  312. if [ "$STAGING" = true ]; then
  313. print_warn "Mode: STAGING (test certificates)"
  314. fi
  315. if [ "$DRY_RUN" = true ]; then
  316. print_warn "DRY-RUN MODE - no changes will be made"
  317. fi
  318. echo ""
  319. if [ "$RENEW" = true ]; then
  320. renew_certificates
  321. echo ""
  322. print_info "Certificate renewal complete!"
  323. exit 0
  324. fi
  325. # Install packages
  326. install_nginx
  327. if [ "$NO_SSL" = true ]; then
  328. # HTTP only mode
  329. create_http_only_config
  330. echo ""
  331. print_info "Nginx configured (HTTP only)"
  332. print_info "Access WebUI at: http://$PRIMARY_DOMAIN/"
  333. elif [ "$NGINX_ONLY" = true ]; then
  334. # Just nginx, no certbot
  335. create_http_only_config
  336. echo ""
  337. print_info "Nginx installed and configured"
  338. print_info "Run with --email to set up SSL later"
  339. else
  340. # Full SSL setup
  341. install_certbot
  342. # Check if certificate already exists
  343. if [ -d "/etc/letsencrypt/live/$PRIMARY_DOMAIN" ] && [ "$DRY_RUN" = false ]; then
  344. print_info "Certificate already exists for $PRIMARY_DOMAIN"
  345. create_https_config
  346. else
  347. create_http_config
  348. obtain_certificate
  349. create_https_config
  350. fi
  351. setup_renewal
  352. echo ""
  353. print_info "Setup complete!"
  354. echo ""
  355. echo "Your Smartbotic instance is now available at:"
  356. for d in "${DOMAINS[@]}"; do
  357. echo " https://$d/"
  358. done
  359. echo ""
  360. echo "Certificate renewal is automatic via certbot timer."
  361. echo "To manually renew: sudo certbot renew"
  362. echo ""
  363. fi
  364. # Enable and start nginx
  365. if [ "$DRY_RUN" = false ]; then
  366. systemctl enable nginx
  367. systemctl start nginx 2>/dev/null || systemctl reload nginx
  368. fi
  369. }
  370. main