searx.sh 16 KB

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