This commit is contained in:
first 2025-07-11 06:53:44 +00:00
parent 00373de886
commit 816fc7e7ab

View file

@ -1,11 +1,11 @@
#!/bin/bash #!/bin/bash
# ============================================================================== # ==============================================================================
# Automated Open WebUI & SearXNG Installer (v12 - GPG-Powered) # Automated Open WebUI & SearXNG Installer (v19 - The Definitive)
# #
# This script will: # This script will:
# 1. Use `gpg` for random data generation, removing the `openssl` dependency. # 1. Use the user's superior method of `curl` to fetch the default SearXNG config.
# 2. Correctly configure Nginx to resolve Docker container names. # 2. Surgically inject the Brave API key into the downloaded config.
# 3. Deploy a complete, secure, and automated stack for Open WebUI and SearXNG. # 3. Deploy a complete, secure, and automated stack for Open WebUI and SearXNG.
# ============================================================================== # ==============================================================================
@ -13,28 +13,17 @@
set -euo pipefail set -euo pipefail
# --- Pre-flight Checks --- # --- Pre-flight Checks ---
if [[ "$EUID" -ne 0 ]]; then if [[ "$EUID" -ne 0 ]]; then echo "❌ This script must be run as root."; exit 1; fi
echo "❌ This script must be run as root. Please use 'sudo ./onepush.sh'" if ! grep -qi "debian" /etc/os-release; then echo "⚠️ This script is optimized for Debian."; fi
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 --- # --- User Input ---
echo "---" echo "---"
echo "Welcome to the Full Stack Open WebUI & SearXNG Installer." echo "Welcome to the Full Stack Open WebUI & SearXNG Installer."
echo "---" echo "---"
echo "--- Part 1: Open WebUI Configuration ---"
read -p "Enter your domain for Open WebUI (e.g., ai.example.com): " UI_DOMAIN read -p "Enter your domain for Open WebUI (e.g., ai.example.com): " UI_DOMAIN
if [[ -z "$UI_DOMAIN" ]]; then echo "❌ Domain name cannot be empty."; exit 1; fi
read -p "Enter your email address (for Let's Encrypt): " EMAIL read -p "Enter your email address (for Let's Encrypt): " EMAIL
if [[ -z "$EMAIL" ]]; then echo "❌ Email address cannot be empty."; exit 1; fi
read -p "Also secure the 'www' version of this domain (www.$UI_DOMAIN)? [y/N]: " UI_INCLUDE_WWW read -p "Also secure the 'www' version of this domain (www.$UI_DOMAIN)? [y/N]: " UI_INCLUDE_WWW
read -p "Use Let's Encrypt staging environment (for testing)? [y/N]: " USE_STAGING read -p "Use Let's Encrypt staging environment (for testing)? [y/N]: " USE_STAGING
echo "" echo ""
if command -v ufw &> /dev/null; then if command -v ufw &> /dev/null; then
echo " UFW firewall is detected on this system." echo " UFW firewall is detected on this system."
@ -42,59 +31,33 @@ if command -v ufw &> /dev/null; then
else else
read -p "Install and configure UFW firewall? (Recommended) [Y/n]: " MANAGE_UFW read -p "Install and configure UFW firewall? (Recommended) [Y/n]: " MANAGE_UFW
fi fi
echo "" echo ""
echo "--- Part 2: Optional SearXNG Search Engine ---"
read -p "Deploy a private SearXNG instance for the research tool? [y/N]: " DEPLOY_SEARXNG read -p "Deploy a private SearXNG instance for the research tool? [y/N]: " DEPLOY_SEARXNG
BRAVE_API_KEY="" BRAVE_API_KEY=""
RESEARCH_TOOL_URL=""
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
SEARCH_DOMAIN="search.r21.io" SEARCH_DOMAIN="search.r21.io"
echo " SearXNG will be deployed to https://$SEARCH_DOMAIN" echo " SearXNG will be deployed to https://$SEARCH_DOMAIN"
echo "You can provide a Brave Search API key for enhanced, ad-free results." read -p "Enter your Brave Search API key (highly recommended, or press Enter): " BRAVE_API_KEY
read -p "Enter your Brave Search API key (or press Enter to skip): " BRAVE_API_KEY
if [[ -z "$BRAVE_API_KEY" ]]; then
echo ""
echo "You can provide a URL to a custom research_tool.py file."
read -p "Enter URL for a custom research_tool.py (or press Enter for default): " RESEARCH_TOOL_URL
if [[ -n "$RESEARCH_TOOL_URL" ]]; then
echo "Validating URL..."
if ! curl --head --fail -sL "$RESEARCH_TOOL_URL" > /dev/null || [[ ! "$RESEARCH_TOOL_URL" == *.py ]]; then
echo "❌ Invalid or unreachable URL, or it doesn't end in .py. Proceeding with default."
RESEARCH_TOOL_URL=""
else
echo "✅ URL is valid."
fi
fi
fi
fi fi
echo "---" # --- Main Script ---
echo "✅ Thank you. Starting the setup." echo "---"; echo "✅ Thank you. Starting the setup."; sleep 3
sleep 3
# --- Configuration Variables --- # --- Configuration ---
UI_CONTAINER="open-webui" UI_CONTAINER="open-webui"
UI_VOLUME="open-webui" SEARXNG_CONTAINER="searxng"
NGINX_UI_CONF="/etc/nginx/sites-available/$UI_DOMAIN" NETWORK_NAME="open-webui-net"
NGINX_UI_LOG="/var/log/nginx/$UI_DOMAIN.access.log" SEARXNG_CONFIG_DIR="/srv/searxng"
# --- Step 1: System Preparation and Dependencies --- # --- Step 1: Dependencies ---
echo "▶️ [1/11] Updating system and installing dependencies..." echo "▶️ [1/9] Installing dependencies..."
export DEBIAN_FRONTEND=noninteractive export DEBIAN_FRONTEND=noninteractive
apt-get update apt-get update
# MODIFIED: Removed 'openssl' from the dependency list. 'gnupg' is already installed.
BASE_PACKAGES="ca-certificates curl gnupg nginx certbot python3-certbot-nginx fail2ban unattended-upgrades" BASE_PACKAGES="ca-certificates curl gnupg nginx certbot python3-certbot-nginx fail2ban unattended-upgrades"
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then 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 $BASE_PACKAGES apache2-utils
else
apt-get install -y $BASE_PACKAGES
fi
# --- Step 2: Configure Firewall --- # --- Step 2: Firewall Management ---
echo "▶️ [2/11] Managing Firewall..." echo "▶️ [2/9] Managing Firewall..."
if [[ "${MANAGE_UFW,,}" != "n" ]]; then if [[ "${MANAGE_UFW,,}" != "n" ]]; then
if ! command -v ufw &> /dev/null; then apt-get install -y ufw; fi 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
@ -105,41 +68,63 @@ else
fi fi
fi fi
# --- Step 3: Install Docker --- # --- Step 3: Docker & Network ---
echo "▶️ [3/11] Installing Docker..." echo "▶️ [3/9] Installing Docker and setting up network..."
install -m 0755 -d /etc/apt/keyrings install -m 0755 -d /etc/apt/keyrings
curl -fsSL https://download.docker.com/linux/debian/gpg | gpg --dearmor -o /etc/apt/keyrings/docker.gpg 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 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 update
apt-get install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin 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: Create Shared Docker Network --- # --- Step 4: Stop Old Containers ---
echo "▶️ [4/11] Setting up Docker network..." echo "▶️ [4/9] Stopping and removing any old containers..."
if ! docker network ls | grep -q "open-webui-net"; then docker stop $UI_CONTAINER $SEARXNG_CONTAINER 2>/dev/null || true
docker network create open-webui-net docker rm $UI_CONTAINER $SEARXNG_CONTAINER 2>/dev/null || true
# --- Step 5: Configure and Deploy SearXNG (Optional) ---
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
echo "▶️ [5/9] Configuring and deploying SearXNG..."
# 1. Fetch the default settings.yml directly from GitHub
echo " - Fetching default SearXNG configuration from GitHub..."
sudo mkdir -p $SEARXNG_CONFIG_DIR
sudo curl -sL "https://raw.githubusercontent.com/searxng/searxng/master/searx/settings.yml" -o "$SEARXNG_CONFIG_DIR/settings.yml"
# 2. Surgically inject the Brave API key if provided
if [[ -n "$BRAVE_API_KEY" ]]; then
echo " - Injecting Brave API key..."
sudo sed -i "/^- name: brave/a \ api_key: \"$BRAVE_API_KEY\"" "$SEARXNG_CONFIG_DIR/settings.yml"
else
echo " - No Brave API key provided, using default settings."
fi
# 3. Add a mandatory secret_key
SECRET_KEY=$(gpg --gen-random --armor 1 24)
sudo sed -i "s/ultrasecretkey/\"$SECRET_KEY\"/" "$SEARXNG_CONFIG_DIR/settings.yml"
# 4. Set correct permissions
sudo chown -R 1000:1000 $SEARXNG_CONFIG_DIR
# 5. Launch the final container
echo " - Starting SearXNG container..."
docker run -d --name $SEARXNG_CONTAINER --network $NETWORK_NAME -v $SEARXNG_CONFIG_DIR:/etc/searxng --restart always searxng/searxng
else
echo "▶️ [5/9] Skipping SearXNG deployment."
fi fi
# --- Step 5: Deploy Open WebUI Container --- # --- Step 6: Deploy Open WebUI ---
echo "▶️ [5/11] Deploying Open WebUI container..." echo "▶️ [6/9] Deploying Open WebUI..."
docker stop $UI_CONTAINER 2>/dev/null || true 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
docker rm $UI_CONTAINER 2>/dev/null || true
docker run -d -p 127.0.0.1:3000:8080 \
--add-host=host.docker.internal:host-gateway \
-v $UI_VOLUME:/app/backend/data \
--name $UI_CONTAINER \
--network open-webui-net \
--restart always \
ghcr.io/open-webui/open-webui:main
# --- Step 6: Configure Nginx for Open WebUI --- # --- Step 7: Configure Nginx ---
echo "▶️ [6/11] Configuring Nginx for Open WebUI..." echo "▶️ [7/9] Configuring Nginx sites..."
if [[ "${UI_INCLUDE_WWW,,}" == "y" ]]; then NGINX_UI_SERVER_NAME="$UI_DOMAIN www.$UI_DOMAIN"; else NGINX_UI_SERVER_NAME="$UI_DOMAIN"; fi 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 <<EOF > "$NGINX_UI_CONF" cat <<EOF > "$NGINX_UI_CONF"
server { server {
listen 80; listen [::]:80; listen 80; listen [::]:80; server_name $UI_SERVER_NAME;
server_name $NGINX_UI_SERVER_NAME;
access_log $NGINX_UI_LOG; error_log /var/log/nginx/$UI_DOMAIN.error.log;
location / { location / {
proxy_pass http://127.0.0.1:3000; proxy_pass http://127.0.0.1:3000;
proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr;
@ -150,70 +135,20 @@ server {
EOF EOF
ln -sfn "$NGINX_UI_CONF" "/etc/nginx/sites-enabled/$UI_DOMAIN" ln -sfn "$NGINX_UI_CONF" "/etc/nginx/sites-enabled/$UI_DOMAIN"
# --- Step 7: Deploy SearXNG (Optional) ---
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
echo "▶️ [7/11] Deploying SearXNG..."
sudo mkdir -p /srv/searxng
SECRET_KEY=$(gpg --gen-random --armor 1 24)
# Dynamically create settings.yml
{
echo "server:"
echo " https: false"
echo " secret_key: \"$SECRET_KEY\""
echo ""
echo "search:"
echo " default_lang: en"
echo ""
echo "engines:"
if [[ -n "$BRAVE_API_KEY" ]]; then
# MODIFIED: Changed engine name from "brave_search" to "brave"
echo " - name: brave"
echo " engine: brave"
echo " api_key: \"$BRAVE_API_KEY\""
echo " shortcut: b"
echo " disabled: false"
fi
echo " - name: google"
echo " engine: google"
echo " disabled: false"
} | sudo tee /srv/searxng/settings.yml > /dev/null
SEARXNG_CONTAINER="searxng"
docker stop $SEARXNG_CONTAINER 2>/dev/null || true
docker rm $SEARXNG_CONTAINER 2>/dev/null || true
docker run -d \
--name $SEARXNG_CONTAINER \
--network open-webui-net \
-v /srv/searxng:/etc/searxng \
--restart always \
searxng/searxng
else
echo "▶️ [7/11] Skipping SearXNG deployment."
fi
# --- Step 8: Configure Nginx for SearXNG (Optional) ---
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
echo "▶️ [8/11] Configuring Nginx for SearXNG..."
NGINX_SEARCH_CONF="/etc/nginx/sites-available/$SEARCH_DOMAIN" NGINX_SEARCH_CONF="/etc/nginx/sites-available/$SEARCH_DOMAIN"
echo "Please set a password for the public SearXNG instance." echo "Please set a password for the public SearXNG instance."
sudo htpasswd -c /etc/nginx/.htpasswd admin sudo htpasswd -c /etc/nginx/.htpasswd admin
cat <<EOF > "$NGINX_SEARCH_CONF" cat <<EOF > "$NGINX_SEARCH_CONF"
server { server {
listen 80; listen [::]:80; listen 80; listen [::]:80; server_name $SEARCH_DOMAIN;
server_name $SEARCH_DOMAIN;
location / { location / {
resolver 127.0.0.11; resolver 127.0.0.11;
set \$searxng_upstream http://searxng:8080; set \$searxng_upstream http://searxng:8080;
proxy_pass \$searxng_upstream; proxy_pass \$searxng_upstream;
auth_basic "Private Search Instance"; auth_basic "Private Search Instance";
auth_basic_user_file /etc/nginx/.htpasswd; auth_basic_user_file /etc/nginx/.htpasswd;
proxy_set_header Host \$host; proxy_set_header Host \$host; proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Real-IP \$remote_addr;
proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for; proxy_set_header X-Forwarded-For \$proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto \$scheme; proxy_set_header X-Forwarded-Proto \$scheme;
proxy_set_header X-Forwarded-Host \$server_name; proxy_set_header X-Forwarded-Host \$server_name;
@ -221,27 +156,19 @@ server {
} }
EOF EOF
ln -sfn "$NGINX_SEARCH_CONF" "/etc/nginx/sites-enabled/$SEARCH_DOMAIN" ln -sfn "$NGINX_SEARCH_CONF" "/etc/nginx/sites-enabled/$SEARCH_DOMAIN"
else
echo "▶️ [8/11] Skipping Nginx config for SearXNG."
fi fi
# --- Step 9: Obtain SSL Certificates --- # --- Step 8: SSL Certificates & Automations ---
echo "▶️ [9/11] Finalizing Nginx and obtaining SSL certificates..." echo "▶️ [8/9] Obtaining SSL certificates and configuring automations..."
nginx -t nginx -t
CERTBOT_STAGING_FLAG="" CERTBOT_STAGING_FLAG=""
if [[ "${USE_STAGING,,}" == "y" ]]; then CERTBOT_STAGING_FLAG="--staging"; fi 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
# Get cert for UI Domain
if [[ "${UI_INCLUDE_WWW,,}" == "y" ]]; then UI_DOMAINS="-d $UI_DOMAIN -d www.$UI_DOMAIN"; else UI_DOMAINS="-d $UI_DOMAIN"; fi
certbot --nginx $UI_DOMAINS $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect
# Get cert for Search Domain if deployed
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
certbot --nginx -d $SEARCH_DOMAIN $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect certbot --nginx -d $SEARCH_DOMAIN $CERTBOT_STAGING_FLAG --non-interactive --agree-tos -m "$EMAIL" --redirect
fi fi
NGINX_UI_LOG="/var/log/nginx/$UI_DOMAIN.access.log"
# --- Step 10: Configure Automations --- touch $NGINX_UI_LOG && chown www-data:adm $NGINX_UI_LOG
echo "▶️ [10/11] Configuring system and service automations..."
cat <<EOF > /etc/fail2ban/filter.d/open-webui.conf cat <<EOF > /etc/fail2ban/filter.d/open-webui.conf
[Definition] [Definition]
failregex = ^<HOST> .* "POST /api/v1/auths/signin.*" 400 failregex = ^<HOST> .* "POST /api/v1/auths/signin.*" 400
@ -257,39 +184,23 @@ dpkg-reconfigure -plow unattended-upgrades
if ! [ "$(docker ps -q -f name=watchtower)" ]; then 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 docker run -d --name watchtower -v /var/run/docker.sock:/var/run/docker.sock --restart always containrrr/watchtower
fi fi
echo "0 9 * * 5 root /sbin/reboot # Weekly reboot for system health" > /etc/cron.d/weekly-reboot echo "0 9 * * 5 root /sbin/reboot" > /etc/cron.d/weekly-reboot
# --- Step 11: Finalization --- # --- Step 9: Finalization ---
echo "▶️ [11/11] Finalizing..." echo "▶️ [9/9] Finalizing..."
systemctl restart nginx systemctl restart nginx
systemctl restart fail2ban
echo "---" echo "---"; echo "✅🎉 **DEPLOYMENT COMPLETE!** 🎉✅"; echo ""
echo "✅🎉 **DEPLOYMENT COMPLETE!** 🎉✅"
echo ""
echo "--- ACCESS ---" echo "--- ACCESS ---"
echo " - Open WebUI: https://$UI_DOMAIN" echo " - Open WebUI: https://$UI_DOMAIN"
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then echo " - SearXNG: https://$SEARCH_DOMAIN (user: admin)"; fi
echo " - SearXNG: https://$SEARCH_DOMAIN (user: admin)"
fi
echo "" echo ""
echo "--- NEXT STEPS: ADDING THE RESEARCH TOOL ---" echo "--- NEXT STEPS: ADDING THE RESEARCH TOOL ---"
if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then if [[ "${DEPLOY_SEARXNG,,}" == "y" ]]; then
echo "1. Go to Open WebUI -> Settings -> Tools." echo "1. Go to Open WebUI -> Settings -> Tools."
echo "2. Click 'Add Tool' and paste the Python code below into the editor." echo "2. Paste the Python code from https://github.com/iamarcel/open-webui-utils/blob/main/research_tool.py"
echo "3. Open the 'Settings' tab in the tool editor." echo "3. Go to the Settings tab in the tool editor."
echo "4. In the 'Environment Variables' section, add:" echo "4. Add Environment Variable: Key: SEARXNG_BASE_URL, Value: http://searxng:8080"
echo " - Key: SEARXNG_BASE_URL" echo "5. Click 'Save'."
echo " - Value: http://searxng:8080"
echo "5. Click 'Save' to finish."
echo ""
echo "--- Python Code for Research Tool ---"
if [[ -n "$RESEARCH_TOOL_URL" ]]; then
echo "Paste the content from your custom URL: $RESEARCH_TOOL_URL"
else
echo "Paste the default code from here: https://github.com/iamarcel/open-webui-utils/blob/main/research_tool.py"
fi
else
echo "SearXNG was not deployed, so no research tool was configured."
fi fi
echo "---" echo "---"