searx.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588
  1. #!/usr/bin/env bash
  2. # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. # shellcheck disable=SC2001
  5. # shellcheck source=utils/lib.sh
  6. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  7. source_dot_config
  8. # ----------------------------------------------------------------------------
  9. # config
  10. # ----------------------------------------------------------------------------
  11. SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
  12. SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
  13. | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
  14. [[ "${SEARX_URL_PATH}" == "${PUBLIC_URL}" ]] && SEARX_URL_PATH=/
  15. SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$PUBLIC_URL" \
  16. | sed -e 's,^.*://\([^\:/]*\).*,\1,g') }"
  17. SERVICE_NAME="searx"
  18. SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
  19. SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
  20. SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
  21. # shellcheck disable=SC2034
  22. SERVICE_GROUP="${SERVICE_USER}"
  23. SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
  24. SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
  25. SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
  26. SEARX_SRC="${SERVICE_HOME}/searx-src"
  27. SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
  28. SEARX_UWSGI_APP="searx.ini"
  29. # shellcheck disable=SC2034
  30. SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
  31. # FIXME: Arch Linux & RHEL should be added
  32. SEARX_APT_PACKAGES="\
  33. uwsgi uwsgi-plugin-python3 \
  34. git build-essential \
  35. libxslt-dev python3-dev python3-babel\
  36. zlib1g-dev libffi-dev libssl-dev \
  37. "
  38. # Apache Settings
  39. APACHE_APT_PACKAGES="\
  40. libapache2-mod-uwsgi \
  41. "
  42. APACHE_SEARX_SITE="searx.conf"
  43. # shellcheck disable=SC2034
  44. CONFIG_FILES=(
  45. "${uWSGI_SETUP}/apps-available/${SEARX_UWSGI_APP}"
  46. )
  47. # shellcheck disable=SC2034
  48. CONFIG_BACKUP_ENCRYPTED=(
  49. "${SEARX_SETTINGS}"
  50. )
  51. # ----------------------------------------------------------------------------
  52. usage() {
  53. # ----------------------------------------------------------------------------
  54. # shellcheck disable=SC1117
  55. cat <<EOF
  56. usage::
  57. $(basename "$0") shell
  58. $(basename "$0") install [all|user|pyenv|searx-src|apache]
  59. $(basename "$0") update [searx]
  60. $(basename "$0") remove [all|user|pyenv|searx-src]
  61. $(basename "$0") activate [service]
  62. $(basename "$0") deactivate [service]
  63. $(basename "$0") inspect [service]
  64. $(basename "$0") option [debug-on|debug-off]
  65. $(basename "$0") apache [install|remove]
  66. shell
  67. start interactive shell from user ${SERVICE_USER}
  68. install / remove
  69. :all: complete (de-) installation of searx service
  70. :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
  71. :searx-src: clone $SEARX_GIT_URL
  72. :pyenv: create/remove virtualenv (python) in $SEARX_PYENV
  73. update searx
  74. Update searx installation ($SERVICE_HOME)
  75. activate service
  76. activate and start service daemon (systemd unit)
  77. deactivate service
  78. stop and deactivate service daemon (systemd unit)
  79. inspect service
  80. run some small tests and inspect service's status and log
  81. option
  82. set one of the available options
  83. apache
  84. :install: apache site with the searx uwsgi app
  85. :remove: apache site ${APACHE_FILTRON_SITE}
  86. If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
  87. PUBLIC_URL : ${PUBLIC_URL}
  88. PUBLIC_HOST : ${PUBLIC_HOST}
  89. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  90. SERVICE_USER : ${SERVICE_USER}
  91. EOF
  92. [ ! -z "${1+x}" ] && err_msg "$1"
  93. }
  94. main() {
  95. rst_title "$SEARX_INSTANCE_NAME" part
  96. required_commands \
  97. dpkg systemctl apt-get install git wget curl \
  98. || exit
  99. local _usage="unknown or missing $1 command $2"
  100. case $1 in
  101. --source-only) ;;
  102. -h|--help) usage; exit 0;;
  103. shell)
  104. sudo_or_exit
  105. interactive_shell "${SERVICE_USER}"
  106. ;;
  107. inspect)
  108. case $2 in
  109. service)
  110. sudo_or_exit
  111. inspect_service
  112. ;;
  113. *) usage "$_usage"; exit 42;;
  114. esac ;;
  115. install)
  116. sudo_or_exit
  117. case $2 in
  118. all) install_all ;;
  119. user) assert_user ;;
  120. pyenv) create_pyenv ;;
  121. searx-src) clone_searx ;;
  122. *) usage "$_usage"; exit 42;;
  123. esac ;;
  124. update)
  125. sudo_or_exit
  126. case $2 in
  127. searx) update_searx;;
  128. *) usage "$_usage"; exit 42;;
  129. esac ;;
  130. remove)
  131. sudo_or_exit
  132. case $2 in
  133. all) remove_all;;
  134. user) drop_service_account "${SERVICE_USER}";;
  135. pyenv) remove_pyenv ;;
  136. searx-src) remove_searx ;;
  137. *) usage "$_usage"; exit 42;;
  138. esac ;;
  139. activate)
  140. sudo_or_exit
  141. case $2 in
  142. service)
  143. activate_service ;;
  144. *) usage "$_usage"; exit 42;;
  145. esac ;;
  146. deactivate)
  147. sudo_or_exit
  148. case $2 in
  149. service) deactivate_service ;;
  150. *) usage "$_usage"; exit 42;;
  151. esac ;;
  152. option)
  153. sudo_or_exit
  154. case $2 in
  155. debug-on) echo; enable_debug ;;
  156. debug-off) echo; disable_debug ;;
  157. *) usage "$_usage"; exit 42;;
  158. esac ;;
  159. apache)
  160. sudo_or_exit
  161. case $2 in
  162. install) install_apache_site ;;
  163. remove) remove_apache_site ;;
  164. *) usage "$_usage"; exit 42;;
  165. esac ;;
  166. *) usage "unknown or missing command $1"; exit 42;;
  167. esac
  168. }
  169. _service_prefix=" |$SERVICE_USER| "
  170. install_all() {
  171. rst_title "Install $SEARX_INSTANCE_NAME (service)"
  172. pkg_install "$SEARX_APT_PACKAGES"
  173. wait_key
  174. assert_user
  175. wait_key
  176. clone_searx
  177. wait_key
  178. create_pyenv
  179. wait_key
  180. configure_searx
  181. wait_key
  182. test_local_searx
  183. wait_key
  184. install_searx_uwsgi
  185. if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
  186. err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
  187. fi
  188. if ask_yn "Do you want to inspect the installation?" Yn; then
  189. inspect_service
  190. fi
  191. }
  192. update_searx() {
  193. rst_title "Update searx instance"
  194. echo
  195. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  196. cd ${SEARX_SRC}
  197. cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
  198. git stash push -m "BACKUP -- 'update server' at ($(date))"
  199. git checkout -b $SEARX_GIT_BRANCH" --track "$SEARX_GIT_BRANCH"
  200. git pull "$SEARX_GIT_BRANCH"
  201. ${SEARX_SRC}/manage.sh update_packages
  202. EOF
  203. configure_searx
  204. rst_title "${SEARX_SETTINGS}" section
  205. rst_para 'Diff between new setting file (<) and backup (>):'
  206. echo
  207. $DIFF_CMD "${SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
  208. local action
  209. choose_one action "What should happen to the settings file? " \
  210. "keep new configuration" \
  211. "revert to the old configuration (backup file)" \
  212. "start interactiv shell"
  213. case $action in
  214. "keep new configuration")
  215. info_msg "continue using new settings file"
  216. ;;
  217. "revert to the old configuration (backup file)")
  218. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  219. cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
  220. EOF
  221. ;;
  222. "start interactiv shell")
  223. interactive_shell "${SERVICE_USER}"
  224. ;;
  225. esac
  226. chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
  227. # shellcheck disable=SC2016
  228. rst_para 'Diff between local modified settings (<) and $SEARX_GIT_BRANCH branch (>):'
  229. echo
  230. git_diff
  231. wait_key
  232. uWSGI_restart
  233. }
  234. remove_all() {
  235. rst_title "De-Install $SEARX_INSTANCE_NAME (service)"
  236. rst_para "\
  237. It goes without saying that this script can only be used to remove
  238. installations that were installed with this script."
  239. if ! ask_yn "Do you really want to deinstall $SEARX_INSTANCE_NAME?"; then
  240. return
  241. fi
  242. remove_searx_uwsgi
  243. wait_key
  244. drop_service_account "${SERVICE_USER}"
  245. if service_is_available "${PUBLIC_URL}"; then
  246. MSG="** Don't forgett to remove your public site! (${PUBLIC_URL}) **" wait_key 10
  247. fi
  248. }
  249. assert_user() {
  250. rst_title "user $SERVICE_USER" section
  251. echo
  252. tee_stderr 1 <<EOF | bash | prefix_stdout
  253. sudo -H adduser --shell /bin/bash --system --home "$SERVICE_HOME" \
  254. --disabled-password --group --gecos 'searx' $SERVICE_USER
  255. sudo -H usermod -a -G shadow $SERVICE_USER
  256. groups $SERVICE_USER
  257. EOF
  258. #SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  259. #export SERVICE_HOME
  260. #echo "export SERVICE_HOME=$SERVICE_HOME"
  261. }
  262. clone_is_available() {
  263. [[ -f "$SEARX_SETTINGS" ]]
  264. }
  265. # shellcheck disable=SC2164
  266. clone_searx() {
  267. rst_title "Clone searx sources" section
  268. echo
  269. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
  270. if [[ ! "${SERVICE_HOME}" ]]; then
  271. err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"
  272. return 42
  273. fi
  274. export SERVICE_HOME
  275. git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
  276. "$SEARX_GIT_BRANCH" "$SERVICE_USER"
  277. pushd "${SEARX_SRC}" > /dev/null
  278. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  279. cd "${SEARX_SRC}"
  280. git config user.email "$ADMIN_EMAIL"
  281. git config user.name "$ADMIN_NAME"
  282. git config --list
  283. EOF
  284. popd > /dev/null
  285. }
  286. remove_searx() {
  287. rst_title "Drop searx sources" section
  288. if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
  289. rm -rf "$SEARX_SRC"
  290. else
  291. rst_para "Leave searx sources unchanged."
  292. fi
  293. }
  294. pyenv_is_available() {
  295. [[ -f "${SEARX_PYENV}/bin/activate" ]]
  296. }
  297. create_pyenv() {
  298. rst_title "Create virtualenv (python)" section
  299. echo
  300. if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
  301. err_msg "to create pyenv for searx, searx has to be cloned first"
  302. return 42
  303. fi
  304. info_msg "create pyenv in ${SEARX_PYENV}"
  305. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  306. rm -rf "${SEARX_PYENV}"
  307. python3 -m venv "${SEARX_PYENV}"
  308. grep -qFs -- 'source ${SEARX_PYENV}/bin/activate' ~/.profile \
  309. || echo 'source ${SEARX_PYENV}/bin/activate' >> ~/.profile
  310. EOF
  311. info_msg "inspect python's virtual environment"
  312. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  313. command -v python && python --version
  314. EOF
  315. wait_key
  316. info_msg "install needed python packages"
  317. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  318. ${SEARX_SRC}/manage.sh update_packages
  319. EOF
  320. }
  321. remove_pyenv() {
  322. rst_title "Remove virtualenv (python)" section
  323. if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
  324. return
  325. fi
  326. info_msg "remove pyenv activation from ~/.profile"
  327. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  328. grep -v 'source ${SEARX_PYENV}/bin/activate' ~/.profile > ~/.profile.##
  329. mv ~/.profile.## ~/.profile
  330. EOF
  331. rm -rf "${SEARX_PYENV}"
  332. }
  333. configure_searx() {
  334. rst_title "Configure searx" section
  335. rst_para "Setup searx config located at $SEARX_SETTINGS"
  336. echo
  337. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  338. cd ${SEARX_SRC}
  339. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS"
  340. sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
  341. EOF
  342. }
  343. test_local_searx() {
  344. rst_title "Testing searx instance localy" section
  345. echo
  346. if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
  347. err_msg "URL/port http://$SEARX_INTERNAL_URL is already in use, you"
  348. err_msg "should stop that service before starting local tests!"
  349. if ! ask_yn "Continue with local tests?"; then
  350. return
  351. fi
  352. fi
  353. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  354. cd ${SEARX_SRC}
  355. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  356. timeout 10 python3 searx/webapp.py &
  357. sleep 3
  358. curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
  359. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  360. EOF
  361. }
  362. install_searx_uwsgi() {
  363. rst_title "Install searx's uWSGI app (searx.ini)" section
  364. echo
  365. uWSGI_install_app "$SEARX_UWSGI_APP"
  366. }
  367. remove_searx_uwsgi() {
  368. rst_title "Remove searx's uWSGI app (searx.ini)" section
  369. echo
  370. uWSGI_remove_app "$SEARX_UWSGI_APP"
  371. }
  372. activate_service() {
  373. rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
  374. echo
  375. uWSGI_enable_app "$SEARX_UWSGI_APP"
  376. uWSGI_restart
  377. }
  378. deactivate_service() {
  379. rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
  380. echo
  381. uWSGI_disable_app "$SEARX_UWSGI_APP"
  382. uWSGI_restart
  383. }
  384. git_diff() {
  385. sudo -H -u "${SERVICE_USER}" -i <<EOF
  386. cd ${SEARX_SRC}
  387. git --no-pager diff
  388. EOF
  389. }
  390. enable_debug() {
  391. warn_msg "Do not enable debug in production enviroments!!"
  392. info_msg "try to enable debug mode ..."
  393. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  394. cd ${SEARX_SRC}
  395. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  396. EOF
  397. uWSGI_restart
  398. }
  399. disable_debug() {
  400. info_msg "try to disable debug mode ..."
  401. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  402. cd ${SEARX_SRC}
  403. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  404. EOF
  405. uWSGI_restart
  406. }
  407. inspect_service() {
  408. rst_title "service status & log"
  409. cat <<EOF
  410. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  411. PUBLIC_URL : ${PUBLIC_URL}
  412. PUBLIC_HOST : ${PUBLIC_HOST}
  413. SEARX_URL_PATH : ${SEARX_URL_PATH}
  414. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  415. SEARX_INTERNAL_URL : ${SEARX_INTERNAL_URL}
  416. EOF
  417. apache_is_installed && info_msg "Apache is installed."
  418. if service_account_is_available "$SERVICE_USER"; then
  419. info_msg "Service account $SERVICE_USER exists."
  420. else
  421. err_msg "Service account $SERVICE_USER does not exists!"
  422. fi
  423. if pyenv_is_available; then
  424. info_msg "~$SERVICE_USER: python environment is available."
  425. else
  426. err_msg "~$SERVICE_USER: python environment is not available!"
  427. fi
  428. if clone_is_available; then
  429. info_msg "~$SERVICE_USER: Searx software is installed."
  430. else
  431. err_msg "~$SERVICE_USER: Missing searx software!"
  432. fi
  433. if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
  434. info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
  435. else
  436. err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
  437. fi
  438. uWSGI_app_available "$SEARX_UWSGI_APP" \
  439. || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
  440. if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
  441. err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
  442. echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
  443. wait_key
  444. fi
  445. if ! service_is_available "${PUBLIC_URL}"; then
  446. err_msg "Public service at ${PUBLIC_URL} is not available!"
  447. fi
  448. local _debug_on
  449. if ask_yn "Enable searx debug mode?"; then
  450. enable_debug
  451. _debug_on=1
  452. fi
  453. echo
  454. systemctl --no-pager -l status "${SERVICE_NAME}"
  455. echo
  456. info_msg "public URL --> ${PUBLIC_URL}"
  457. info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
  458. # shellcheck disable=SC2059
  459. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  460. read -r -s -n1 -t 2
  461. echo
  462. while true; do
  463. trap break 2
  464. #journalctl -f -u "${SERVICE_NAME}"
  465. tail -f /var/log/uwsgi/app/searx.log
  466. done
  467. if [[ $_debug_on == 1 ]]; then
  468. disable_debug
  469. fi
  470. return 0
  471. }
  472. install_apache_site() {
  473. rst_title "Install Apache site $APACHE_SEARX_SITE"
  474. rst_para "\
  475. This installs the searx uwsgi app as apache site. If your server ist public to
  476. the internet you should instead use a reverse proxy (filtron) to block
  477. excessively bot queries."
  478. ! apache_is_installed && err_msg "Apache is not installed."
  479. if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
  480. return
  481. fi
  482. pkg_install "$APACHE_APT_PACKAGES"
  483. a2enmod uwsgi
  484. echo
  485. apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
  486. if ! service_is_available "${PUBLIC_URL}"; then
  487. err_msg "Public service at ${PUBLIC_URL} is not available!"
  488. fi
  489. }
  490. remove_apache_site() {
  491. rst_title "Remove Apache site ${APACHE_SEARX_SITE}"
  492. rst_para "\
  493. This removes apache site ${APACHE_SEARX_SITE}."
  494. ! apache_is_installed && err_msg "Apache is not installed."
  495. if ! ask_yn "Do you really want to continue?"; then
  496. return
  497. fi
  498. apache_remove_site "${APACHE_SEARX_SITE}"
  499. }
  500. # ----------------------------------------------------------------------------
  501. main "$@"
  502. # ----------------------------------------------------------------------------