searx.sh 17 KB

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