diff --git a/onepush.sh b/onepush.sh index f4be09a..058b9dc 100644 --- a/onepush.sh +++ b/onepush.sh @@ -1,34 +1,42 @@ #!/bin/bash # ============================================================================== -# Automated Open WebUI & SearXNG Installer (v26 - The Definitive Version) +# Automated Open WebUI Installer for Debian (v8 - Robust Cron Job) # -# This script is the final, consolidated version incorporating all bug fixes -# and best practices discovered through our collaborative debugging process. -# -# Key Fixes: -# 1. Uses a robust "port-publishing" method for Nginx-to-Docker communication. -# 2. Uses environment variables to configure SearXNG, the correct method. -# 3. Uses a safe, non-blocking command to generate secrets. -# 4. Builds Docker commands safely in an array to prevent errors. -# 5. All previous logic (UFW, cron, etc.) is complete and verified. +# This script will: +# 1. Use a robust /etc/cron.d file for the scheduled reboot, fixing a script hang. +# 2. Detect if UFW is active and handle it decisively. +# 3. Install all dependencies and deploy the full stack. +# 4. Configure all automations (updates, reboots, security). # ============================================================================== # --- Safety Checks --- set -euo pipefail # --- Pre-flight Checks --- -if [[ "$EUID" -ne 0 ]]; then echo "❌ This script must be run as root."; exit 1; fi -if ! grep -qi "debian" /etc/os-release; then echo "⚠️ This script is optimized for Debian."; fi +if [[ "$EUID" -ne 0 ]]; then + echo "❌ This script must be run as root. Please use 'sudo ./setup-webui.sh'" + exit 1 +fi +if ! grep -qi "debian" /etc/os-release; then + echo "⚠️ This script is optimized for Debian. Running on another OS may have unintended results." +fi # --- User Input --- echo "---" -echo "Welcome to the Full Stack Open WebUI & SearXNG Installer." +echo "Welcome to the Automated Open WebUI Installer." +echo "Please provide the following information:" echo "---" -read -p "Enter your domain for Open WebUI (e.g., ai.example.com): " UI_DOMAIN + +read -p "Enter your domain or subdomain (e.g., webui.example.com): " DOMAIN +if [[ -z "$DOMAIN" ]]; then echo "❌ Domain name cannot be empty."; exit 1; fi + read -p "Enter your email address (for Let's Encrypt): " EMAIL -read -p "Also secure the 'www' version of this domain (www.$UI_DOMAIN)? [y/N]: " UI_INCLUDE_WWW +if [[ -z "$EMAIL" ]]; then echo "❌ Email address cannot be empty."; exit 1; fi + +read -p "Also secure the 'www' version of this domain (www.$DOMAIN)? [y/N]: " INCLUDE_WWW read -p "Use Let's Encrypt staging environment (for testing)? [y/N]: " USE_STAGING + echo "" if command -v ufw &> /dev/null; then echo "ℹ️ UFW firewall is detected on this system." @@ -36,113 +44,70 @@ if command -v ufw &> /dev/null; then else read -p "Install and configure UFW firewall? (Recommended) [Y/n]: " MANAGE_UFW fi -echo "" -read -p "Deploy a private SearXNG instance for the research tool? [y/N]: " DEPLOY_SEARXNG -BRAVE_API_KEY="" -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then - SEARCH_DOMAIN="search.r21.io" - echo "ℹ️ SearXNG will be deployed to https://$SEARCH_DOMAIN" - read -p "Enter your Brave Search API key (highly recommended, or press Enter): " BRAVE_API_KEY -fi -# --- Main Script --- -echo "---"; echo "✅ Thank you. Starting the setup."; sleep 3 +echo "---" +echo "✅ Thank you. Starting the setup for https://$DOMAIN" +echo "This process will take several minutes. Please do not interrupt it." +sleep 3 -# --- Configuration --- -UI_CONTAINER="open-webui" -SEARXNG_CONTAINER="searxng" -NETWORK_NAME="open-webui-net" +# --- Configuration Variables --- +CONTAINER_NAME="open-webui" +DATA_VOLUME="open-webui" +NGINX_CONF_PATH="/etc/nginx/sites-available/$DOMAIN" +NGINX_LOG_PATH="/var/log/nginx/$DOMAIN.access.log" -# --- Step 1: Dependencies --- -echo "▶️ [1/9] Installing dependencies..." +# --- Step 1: System Preparation and Dependencies --- +echo "▶️ [1/9] Updating system and installing dependencies..." export DEBIAN_FRONTEND=noninteractive apt-get update -# Add openssl for robust secret generation -BASE_PACKAGES="ca-certificates curl gnupg nginx certbot python3-certbot-nginx fail2ban unattended-upgrades openssl" -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then apt-get install -y $BASE_PACKAGES apache2-utils; else apt-get install -y $BASE_PACKAGES; fi +apt-get install -y ca-certificates curl gnupg nginx certbot python3-certbot-nginx fail2ban unattended-upgrades -# --- Step 2: Firewall Management --- -echo "▶️ [2/9] Managing Firewall..." -if [[ -z "${MANAGE_UFW+x}" ]]; then MANAGE_UFW="y"; fi +# --- Step 2: Configure Firewall (Decisive Handling) --- if [[ "${MANAGE_UFW,,}" != "n" ]]; then + echo "▶️ [2/9] Managing Firewall (UFW)..." if ! command -v ufw &> /dev/null; then apt-get install -y ufw; fi - ufw allow ssh > /dev/null; ufw allow 'Nginx Full' > /dev/null; ufw --force enable + ufw allow ssh > /dev/null + ufw allow 'Nginx Full' > /dev/null + ufw --force enable else + echo "▶️ [2/9] Skipping firewall rule management as requested." if command -v ufw &> /dev/null && ufw status | grep -q "Status: active"; then - echo " - ⚠️ UFW is active but not managed. Disabling to prevent setup failure." + echo " - ⚠️ UFW is active but not managed by this script. Disabling it now to prevent cert issuance failure." ufw disable fi fi -# --- Step 3: Docker & Network --- -echo "▶️ [3/9] Installing Docker and setting up network..." +# --- Step 3: Install Docker --- +echo "▶️ [3/9] Installing Docker..." install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg chmod a+r /etc/apt/keyrings/docker.gpg -echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | tee /etc/apt/sources.list.d/docker.list > /dev/null +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/debian \ + $(. /etc/os-release && echo "$VERSION_CODENAME") stable" | \ + tee /etc/apt/sources.list.d/docker.list > /dev/null apt-get update apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin -if ! docker network ls | grep -q "$NETWORK_NAME"; then docker network create $NETWORK_NAME; fi -# --- Step 4: Stop Old Containers --- -echo "▶️ [4/9] Stopping and removing any old containers..." -docker stop $UI_CONTAINER $SEARXNG_CONTAINER 2>/dev/null || true -docker rm $UI_CONTAINER $SEARXNG_CONTAINER 2>/dev/null || true +# --- Step 4: Run Open WebUI Container --- +echo "▶️ [4/9] Deploying Open WebUI container..." +if [ "$(docker ps -q -f name=$CONTAINER_NAME)" ]; then docker stop $CONTAINER_NAME > /dev/null; fi +if [ "$(docker ps -aq -f status=exited -f name=$CONTAINER_NAME)" ]; then docker rm $CONTAINER_NAME > /dev/null; fi +docker run -d -p 127.0.0.1:3000:8080 \ + --add-host=host.docker.internal:host-gateway \ + -v $DATA_VOLUME:/app/backend/data \ + --name $CONTAINER_NAME \ + --restart always \ + ghcr.io/open-webui/open-webui:main -# --- Step 5: Configure and Deploy SearXNG (Optional) --- -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then - echo "▶️ [5/9] Deploying SearXNG..." - - # Generate a robust, shell-safe secret key - SECRET_KEY=$(openssl rand -hex 32) - - # Build the docker run command safely in an array - docker_cmd=( - docker run -d - --name "$SEARXNG_CONTAINER" - --network "$NETWORK_NAME" - # Publish port to localhost for Nginx to connect to - -p "127.0.0.1:8081:8080" - # Core settings via environment variables - -e "SEARXNG_SECRET=$SECRET_KEY" - -e "SEARXNG_BIND_ADDRESS=0.0.0.0" # Listen on all interfaces inside the container - -e "SEARXNG_BASE_URL=https://$SEARCH_DOMAIN" - --restart always - ) - - # Add optional Brave integration - if [[ -n "$BRAVE_API_KEY" ]]; then - echo " - Enabling Brave engine with API key..." - docker_cmd+=( - -e "SEARXNG_ENGINES_BRAVE_API_KEY=$BRAVE_API_KEY" - -e "SEARXNG_ENGINES_BRAVE_DISABLED=false" - # Disable a noisy engine if a key is present - -e "SEARXNG_ENGINES_DUCKDUCKGO_DISABLED=true" - ) - else - echo " - No Brave API key provided, using default search engines." - fi - - # Add the image name to the end of the command - docker_cmd+=(searxng/searxng) - - # Execute the final, safe command - "${docker_cmd[@]}" -else - echo "▶️ [5/9] Skipping SearXNG deployment." -fi - -# --- Step 6: Deploy Open WebUI --- -echo "▶️ [6/9] Deploying Open WebUI..." -docker run -d -p 127.0.0.1:3000:8080 -v open-webui:/app/backend/data --name $UI_CONTAINER --network $NETWORK_NAME --restart always ghcr.io/open-webui/open-webui:main - -# --- Step 7: Configure Nginx --- -echo "▶️ [7/9] Configuring Nginx sites..." -NGINX_UI_CONF="/etc/nginx/sites-available/$UI_DOMAIN" -if [[ "${UI_INCLUDE_WWW,,}" == "y" ]]; then UI_SERVER_NAME="$UI_DOMAIN www.$UI_DOMAIN"; else UI_SERVER_NAME="$UI_DOMAIN"; fi -cat < "$NGINX_UI_CONF" +# --- Step 5: Configure Nginx Reverse Proxy --- +echo "▶️ [5/9] Configuring Nginx..." +if [[ "${INCLUDE_WWW,,}" == "y" ]]; then NGINX_SERVER_NAME="$DOMAIN www.$DOMAIN"; else NGINX_SERVER_NAME="$DOMAIN"; fi +cat < "$NGINX_CONF_PATH" server { - listen 80; listen [::]:80; server_name $UI_SERVER_NAME; + listen 80; listen [::]:80; + server_name $NGINX_SERVER_NAME; + access_log $NGINX_LOG_PATH; error_log /var/log/nginx/$DOMAIN.error.log; location / { proxy_pass http://127.0.0.1:3000; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; @@ -151,42 +116,21 @@ server { } } EOF -ln -sfn "$NGINX_UI_CONF" "/etc/nginx/sites-enabled/$UI_DOMAIN" +ln -sfn "$NGINX_CONF_PATH" "/etc/nginx/sites-enabled/$DOMAIN" +nginx -t && systemctl reload nginx -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then - NGINX_SEARCH_CONF="/etc/nginx/sites-available/$SEARCH_DOMAIN" - echo "Please set a password for the public SearXNG instance." - sudo htpasswd -c /etc/nginx/.htpasswd admin - cat < "$NGINX_SEARCH_CONF" -server { - listen 80; listen [::]:80; server_name $SEARCH_DOMAIN; - location / { - # Proxy directly to the port we published on the host's localhost - proxy_pass http://127.0.0.1:8081; - auth_basic "Private Search Instance"; - auth_basic_user_file /etc/nginx/.htpasswd; - 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 X-Forwarded-Host \$server_name; - } -} -EOF - ln -sfn "$NGINX_SEARCH_CONF" "/etc/nginx/sites-enabled/$SEARCH_DOMAIN" -fi - -# --- Step 8: SSL Certificates & Automations --- -echo "▶️ [8/9] Obtaining SSL certificates and configuring automations..." -nginx -t +# --- Step 6: Obtain SSL Certificate --- +echo "▶️ [6/9] Obtaining SSL certificate with Certbot..." +if [[ "${INCLUDE_WWW,,}" == "y" ]]; then CERTBOT_DOMAINS="-d $DOMAIN -d www.$DOMAIN"; else CERTBOT_DOMAINS="-d $DOMAIN"; fi CERTBOT_STAGING_FLAG="" -if [[ "${USE_STAGING,,}" == "y" ]]; then CERTBOT_STAGING_FLAG="--staging"; fi -certbot --nginx -d $UI_SERVER_NAME $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then - certbot --nginx -d $SEARCH_DOMAIN $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect +if [[ "${USE_STAGING,,}" == "y" ]]; then + echo " - Using Let's Encrypt STAGING environment for testing." + CERTBOT_STAGING_FLAG="--staging" fi -NGINX_UI_LOG="/var/log/nginx/$UI_DOMAIN.access.log" -touch $NGINX_UI_LOG && chown www-data:adm $NGINX_UI_LOG +certbot --nginx $CERTBOT_DOMAINS $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect + +# --- Step 7: Configure Security Automations --- +echo "▶️ [7/9] Configuring Fail2Ban and automatic system updates..." cat < /etc/fail2ban/filter.d/open-webui.conf [Definition] failregex = ^ .* "POST /api/v1/auths/signin.*" 400 @@ -195,30 +139,42 @@ EOF cat < /etc/fail2ban/jail.d/open-webui.local [open-webui] enabled = true; port = http,https; filter = open-webui; -logpath = $NGINX_UI_LOG; maxretry = 5; findtime = 3600; bantime = 86400; +logpath = $NGINX_LOG_PATH; maxretry = 5; findtime = 3600; bantime = 86400; EOF systemctl restart fail2ban dpkg-reconfigure -plow unattended-upgrades + +# --- Step 8: Configure Service Automations --- +echo "▶️ [8/9] Deploying Watchtower and scheduling reboots..." if ! [ "$(docker ps -q -f name=watchtower)" ]; then docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock --restart always containrrr/watchtower fi -echo "0 9 * * 5 root /sbin/reboot" > /etc/cron.d/weekly-reboot + +# MODIFIED: Using a robust cron drop-in file instead of piping to crontab. +echo " - Scheduling weekly reboot for Friday at 09:00 UTC..." +echo "0 9 * * 5 root /sbin/reboot # Weekly reboot for system health" > /etc/cron.d/open-webui-reboot # --- Step 9: Finalization --- -echo "▶️ [9/9] Finalizing..." +echo "▶️ [9/9] Finalizing and cleaning up..." systemctl restart nginx +systemctl restart fail2ban -echo "---"; echo "✅🎉 **DEPLOYMENT COMPLETE!** 🎉✅"; echo "" -echo "--- ACCESS ---" -echo " - Open WebUI: https://$UI_DOMAIN" -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then echo " - SearXNG: https://$SEARCH_DOMAIN (user: admin)"; fi echo "" -echo "--- NEXT STEPS: ADDING THE RESEARCH TOOL ---" -if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then - echo "1. Go to Open WebUI -> Settings -> Tools." - echo "2. Paste the Python code from https://github.com/iamarcel/open-webui-utils/blob/main/research_tool.py" - echo "3. Go to the Settings tab in the tool editor." - echo "4. Add Environment Variable: Key: SEARXNG_BASE_URL, Value: http://searxng:8080" - echo "5. Click 'Save'." +echo "✅🎉 **DEPLOYMENT COMPLETE!** 🎉✅" +echo "" +echo "Open WebUI is now running and accessible at: https://$DOMAIN" +echo "" +echo "IMPORTANT NOTES:" +if [[ "${MANAGE_UFW,,}" == "n" ]]; then + echo " - ⚠️ UFW was detected and has been DISABLED to allow this script to complete." + echo " Your server's firewall is currently off. You are responsible for its configuration." fi -echo "---" \ No newline at end of file +if [[ "${USE_STAGING,,}" == "y" ]]; then + echo " - ⚠️ You used a STAGING certificate. Your site will show a browser security warning." + echo " To fix this, re-run this script after your rate limit expires and choose 'N' for staging." +fi +echo " - The Fail2Ban rule is active and will protect your login endpoint." +echo " - The first user to sign up will become the administrator." +echo " - Your system and the WebUI container will update automatically." +echo " - The server will reboot every Friday at 09:00 UTC." +echo "" \ No newline at end of file