searx.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594
  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
  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) remove_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
  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. remove_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. user_is_available() {
  247. sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
  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. remove_user() {
  263. rst_title "Drop $SERVICE_USER HOME" section
  264. if ask_yn "Do you really want to drop $SERVICE_USER home folder?"; then
  265. userdel -r -f "$SERVICE_USER" 2>&1 | prefix_stdout
  266. else
  267. rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
  268. fi
  269. }
  270. clone_is_available() {
  271. [[ -f "$SEARX_SETTINGS" ]]
  272. }
  273. # shellcheck disable=SC2164
  274. clone_searx() {
  275. rst_title "Clone searx sources" section
  276. echo
  277. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
  278. if [[ ! "${SERVICE_HOME}" ]]; then
  279. err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"
  280. return 42
  281. fi
  282. export SERVICE_HOME
  283. git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
  284. "$SEARX_GIT_BRANCH" "$SERVICE_USER"
  285. pushd "${SEARX_SRC}" > /dev/null
  286. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  287. cd "${SEARX_SRC}"
  288. git config user.email "$ADMIN_EMAIL"
  289. git config user.name "$ADMIN_NAME"
  290. git config --list
  291. EOF
  292. popd > /dev/null
  293. }
  294. remove_searx() {
  295. rst_title "Drop searx sources" section
  296. if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
  297. rm -rf "$SEARX_SRC"
  298. else
  299. rst_para "Leave searx sources unchanged."
  300. fi
  301. }
  302. pyenv_is_available() {
  303. [[ -f "${SEARX_PYENV}/bin/activate" ]]
  304. }
  305. create_pyenv() {
  306. rst_title "Create virtualenv (python)" section
  307. echo
  308. if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
  309. err_msg "to create pyenv for searx, searx has to be cloned first"
  310. return 42
  311. fi
  312. info_msg "create pyenv in ${SEARX_PYENV}"
  313. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  314. rm -rf "${SEARX_PYENV}"
  315. python3 -m venv "${SEARX_PYENV}"
  316. grep -qFs -- 'source ${SEARX_PYENV}/bin/activate' ~/.profile \
  317. || echo 'source ${SEARX_PYENV}/bin/activate' >> ~/.profile
  318. EOF
  319. info_msg "inspect python's virtual environment"
  320. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  321. command -v python && python --version
  322. EOF
  323. wait_key
  324. info_msg "install needed python packages"
  325. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  326. ${SEARX_SRC}/manage.sh update_packages
  327. EOF
  328. }
  329. remove_pyenv() {
  330. rst_title "Remove virtualenv (python)" section
  331. if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
  332. return
  333. fi
  334. info_msg "remove pyenv activation from ~/.profile"
  335. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  336. grep -v 'source ${SEARX_PYENV}/bin/activate' ~/.profile > ~/.profile.##
  337. mv ~/.profile.## ~/.profile
  338. EOF
  339. rm -rf "${SEARX_PYENV}"
  340. }
  341. configure_searx() {
  342. rst_title "Configure searx" section
  343. rst_para "Setup searx config located at $SEARX_SETTINGS"
  344. echo
  345. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  346. cd ${SEARX_SRC}
  347. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS"
  348. sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
  349. EOF
  350. }
  351. test_local_searx() {
  352. rst_title "Testing searx instance localy" section
  353. echo
  354. if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
  355. err_msg "URL/port http://$SEARX_INTERNAL_URL is already in use, you"
  356. err_msg "should stop that service before starting local tests!"
  357. if ! ask_yn "Continue with local tests?"; then
  358. return
  359. fi
  360. fi
  361. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  362. cd ${SEARX_SRC}
  363. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  364. timeout 5 python3 searx/webapp.py &
  365. sleep 1
  366. curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
  367. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  368. EOF
  369. }
  370. install_searx_uwsgi() {
  371. rst_title "Install searx's uWSGI app (searx.ini)" section
  372. echo
  373. uWSGI_install_app "$SEARX_UWSGI_APP"
  374. }
  375. remove_searx_uwsgi() {
  376. rst_title "Remove searx's uWSGI app (searx.ini)" section
  377. echo
  378. uWSGI_remove_app "$SEARX_UWSGI_APP"
  379. }
  380. activate_service() {
  381. rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
  382. echo
  383. uWSGI_enable_app "$SEARX_UWSGI_APP"
  384. uWSGI_restart
  385. }
  386. deactivate_service() {
  387. rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
  388. echo
  389. uWSGI_disable_app "$SEARX_UWSGI_APP"
  390. uWSGI_restart
  391. }
  392. interactive_shell() {
  393. echo "// exit with CTRL-D"
  394. sudo -H -u "${SERVICE_USER}" -i
  395. }
  396. git_diff() {
  397. sudo -H -u "${SERVICE_USER}" -i <<EOF
  398. cd ${SEARX_REPO_FOLDER}
  399. git --no-pager diff
  400. EOF
  401. }
  402. enable_debug() {
  403. info_msg "try to enable debug mode ..."
  404. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  405. cd ${SEARX_SRC}
  406. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  407. EOF
  408. uWSGI_restart
  409. }
  410. disable_debug() {
  411. info_msg "try to disable debug mode ..."
  412. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  413. cd ${SEARX_SRC}
  414. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  415. EOF
  416. uWSGI_restart
  417. }
  418. inspect_service() {
  419. rst_title "service status & log"
  420. cat <<EOF
  421. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  422. PUBLIC_URL : ${PUBLIC_URL}
  423. PUBLIC_HOST : ${PUBLIC_HOST}
  424. SEARX_URL_PATH : ${SEARX_URL_PATH}
  425. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  426. SEARX_INTERNAL_URL : ${SEARX_INTERNAL_URL}
  427. EOF
  428. apache_is_installed && info_msg "Apache is installed."
  429. if user_is_available; then
  430. info_msg "Service account $SERVICE_USER exists."
  431. else
  432. err_msg "Service account $SERVICE_USER does not exists!"
  433. fi
  434. if pyenv_is_available; then
  435. info_msg "~$SERVICE_USER: python environment is available."
  436. else
  437. err_msg "~$SERVICE_USER: python environment is not available!"
  438. fi
  439. if clone_is_available; then
  440. info_msg "~$SERVICE_USER: Searx software is installed."
  441. else
  442. err_msg "~$SERVICE_USER: Missing searx software!"
  443. fi
  444. if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
  445. info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
  446. else
  447. err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
  448. fi
  449. uWSGI_app_available "$SEARX_UWSGI_APP" \
  450. || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
  451. if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
  452. err_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is not available!"
  453. fi
  454. if ! service_is_available "${PUBLIC_URL}"; then
  455. err_msg "Public service at ${PUBLIC_URL} is not available!"
  456. fi
  457. local _debug_on
  458. if ask_yn "Enable searx debug mode?"; then
  459. enable_debug
  460. _debug_on=1
  461. fi
  462. echo
  463. systemctl --no-pager -l status uwsgi.service
  464. echo
  465. read -r -s -n1 -t 2 -p "// use CTRL-C to stop monitoring the log"
  466. echo
  467. while true; do
  468. trap break 2
  469. #journalctl -f -u uwsgi.service
  470. tail -f /var/log/uwsgi/app/searx.log
  471. done
  472. if [[ $_debug_on == 1 ]]; then
  473. disable_debug
  474. fi
  475. return 0
  476. }
  477. install_apache_site() {
  478. rst_title "Install Apache site $APACHE_SEARX_SITE"
  479. rst_para "\
  480. This installs the searx uwsgi app as apache site. If your server ist public to
  481. the internet you should instead use a reverse proxy (filtron) to block
  482. excessively bot queries."
  483. ! apache_is_installed && err_msg "Apache is not installed."
  484. if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
  485. return
  486. fi
  487. pkg_install "$APACHE_APT_PACKAGES"
  488. a2enmod uwsgi
  489. echo
  490. apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
  491. if ! service_is_available "${PUBLIC_URL}"; then
  492. err_msg "Public service at ${PUBLIC_URL} is not available!"
  493. fi
  494. }
  495. remove_apache_site() {
  496. rst_title "Remove Apache site ${APACHE_SEARX_SITE}"
  497. rst_para "\
  498. This removes apache site ${APACHE_SEARX_SITE}."
  499. ! apache_is_installed && err_msg "Apache is not installed."
  500. if ! ask_yn "Do you really want to continue?"; then
  501. return
  502. fi
  503. apache_remove_site "${APACHE_SEARX_SITE}"
  504. }
  505. # ----------------------------------------------------------------------------
  506. main "$@"
  507. # ----------------------------------------------------------------------------