searx.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584
  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. 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. warn_msg "Do not enable debug in production enviroments!!"
  389. info_msg "try to enable debug mode ..."
  390. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  391. cd ${SEARX_SRC}
  392. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  393. EOF
  394. uWSGI_restart
  395. }
  396. disable_debug() {
  397. info_msg "try to disable debug mode ..."
  398. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  399. cd ${SEARX_SRC}
  400. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  401. EOF
  402. uWSGI_restart
  403. }
  404. inspect_service() {
  405. rst_title "service status & log"
  406. cat <<EOF
  407. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  408. PUBLIC_URL : ${PUBLIC_URL}
  409. PUBLIC_HOST : ${PUBLIC_HOST}
  410. SEARX_URL_PATH : ${SEARX_URL_PATH}
  411. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  412. SEARX_INTERNAL_URL : ${SEARX_INTERNAL_URL}
  413. EOF
  414. apache_is_installed && info_msg "Apache is installed."
  415. if service_account_is_available "$SERVICE_USER"; then
  416. info_msg "Service account $SERVICE_USER exists."
  417. else
  418. err_msg "Service account $SERVICE_USER does not exists!"
  419. fi
  420. if pyenv_is_available; then
  421. info_msg "~$SERVICE_USER: python environment is available."
  422. else
  423. err_msg "~$SERVICE_USER: python environment is not available!"
  424. fi
  425. if clone_is_available; then
  426. info_msg "~$SERVICE_USER: Searx software is installed."
  427. else
  428. err_msg "~$SERVICE_USER: Missing searx software!"
  429. fi
  430. if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
  431. info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
  432. else
  433. err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
  434. fi
  435. uWSGI_app_available "$SEARX_UWSGI_APP" \
  436. || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
  437. if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
  438. err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
  439. echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
  440. wait_key
  441. fi
  442. if ! service_is_available "${PUBLIC_URL}"; then
  443. err_msg "Public service at ${PUBLIC_URL} is not available!"
  444. fi
  445. local _debug_on
  446. if ask_yn "Enable searx debug mode?"; then
  447. enable_debug
  448. _debug_on=1
  449. fi
  450. echo
  451. systemctl --no-pager -l status "${SERVICE_NAME}"
  452. echo
  453. # shellcheck disable=SC2059
  454. info_msg "public URL --> ${PUBLIC_URL}"
  455. info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
  456. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  457. read -r -s -n1 -t 2
  458. echo
  459. while true; do
  460. trap break 2
  461. #journalctl -f -u "${SERVICE_NAME}"
  462. tail -f /var/log/uwsgi/app/searx.log
  463. done
  464. if [[ $_debug_on == 1 ]]; then
  465. disable_debug
  466. fi
  467. return 0
  468. }
  469. install_apache_site() {
  470. rst_title "Install Apache site $APACHE_SEARX_SITE"
  471. rst_para "\
  472. This installs the searx uwsgi app as apache site. If your server ist public to
  473. the internet you should instead use a reverse proxy (filtron) to block
  474. excessively bot queries."
  475. ! apache_is_installed && err_msg "Apache is not installed."
  476. if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
  477. return
  478. fi
  479. pkg_install "$APACHE_APT_PACKAGES"
  480. a2enmod uwsgi
  481. echo
  482. apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
  483. if ! service_is_available "${PUBLIC_URL}"; then
  484. err_msg "Public service at ${PUBLIC_URL} is not available!"
  485. fi
  486. }
  487. remove_apache_site() {
  488. rst_title "Remove Apache site ${APACHE_SEARX_SITE}"
  489. rst_para "\
  490. This removes apache site ${APACHE_SEARX_SITE}."
  491. ! apache_is_installed && err_msg "Apache is not installed."
  492. if ! ask_yn "Do you really want to continue?"; then
  493. return
  494. fi
  495. apache_remove_site "${APACHE_SEARX_SITE}"
  496. }
  497. # ----------------------------------------------------------------------------
  498. main "$@"
  499. # ----------------------------------------------------------------------------