searx.sh 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  87. EOF
  88. [ ! -z ${1+x} ] && echo -e "$1"
  89. }
  90. main() {
  91. rst_title "$SEARX_INSTANCE_NAME" part
  92. required_commands \
  93. dpkg systemctl apt-get install git wget curl \
  94. || exit
  95. local _usage="ERROR: unknown or missing $1 command $2"
  96. case $1 in
  97. --source-only) ;;
  98. -h|--help) usage; exit 0;;
  99. shell)
  100. sudo_or_exit
  101. interactive_shell
  102. ;;
  103. inspect)
  104. case $2 in
  105. service)
  106. sudo_or_exit
  107. inspect_service
  108. ;;
  109. *) usage "$_usage"; exit 42;;
  110. esac ;;
  111. install)
  112. sudo_or_exit
  113. case $2 in
  114. all) install_all ;;
  115. user) assert_user ;;
  116. pyenv) create_pyenv ;;
  117. searx-src) clone_searx ;;
  118. *) usage "$_usage"; exit 42;;
  119. esac ;;
  120. update)
  121. sudo_or_exit
  122. case $2 in
  123. searx) update_searx;;
  124. *) usage "$_usage"; exit 42;;
  125. esac ;;
  126. remove)
  127. sudo_or_exit
  128. case $2 in
  129. all) remove_all;;
  130. user) remove_user ;;
  131. pyenv) remove_pyenv ;;
  132. searx-src) remove_searx ;;
  133. *) usage "$_usage"; exit 42;;
  134. esac ;;
  135. activate)
  136. sudo_or_exit
  137. case $2 in
  138. service)
  139. activate_service ;;
  140. *) usage "$_usage"; exit 42;;
  141. esac ;;
  142. deactivate)
  143. sudo_or_exit
  144. case $2 in
  145. service) deactivate_service ;;
  146. *) usage "$_usage"; exit 42;;
  147. esac ;;
  148. option)
  149. sudo_or_exit
  150. case $2 in
  151. debug-on) echo; enable_debug ;;
  152. debug-off) echo; disable_debug ;;
  153. *) usage "$_usage"; exit 42;;
  154. esac ;;
  155. apache)
  156. sudo_or_exit
  157. case $2 in
  158. install) install_apache_site ;;
  159. remove) remove_apache_site ;;
  160. *) usage "$_usage"; exit 42;;
  161. esac ;;
  162. *) usage "ERROR: unknown or missing command $1"; exit 42;;
  163. esac
  164. }
  165. _service_prefix=" |$SERVICE_USER| "
  166. install_all() {
  167. rst_title "Install $SEARX_INSTANCE_NAME (service)"
  168. pkg_install "$SEARX_APT_PACKAGES"
  169. wait_key
  170. assert_user
  171. wait_key
  172. clone_searx
  173. wait_key
  174. create_pyenv
  175. wait_key
  176. configure_searx
  177. wait_key
  178. test_local_searx
  179. wait_key
  180. install_searx_uwsgi
  181. if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
  182. err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
  183. fi
  184. if ask_yn "Do you want to inspect the installation?" Yn; then
  185. inspect_service
  186. fi
  187. }
  188. update_searx() {
  189. rst_title "Update searx instance"
  190. echo
  191. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  192. cd ${SEARX_SRC}
  193. cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
  194. git stash push -m "BACKUP -- 'update server' at ($(date))"
  195. git checkout -b $SEARX_GIT_BRANCH" --track "$SEARX_GIT_BRANCH"
  196. git pull "$SEARX_GIT_BRANCH"
  197. ${SEARX_SRC}/manage.sh update_packages
  198. EOF
  199. configure_searx
  200. rst_title "${SEARX_SETTINGS}" section
  201. rstBlock 'Diff between new setting file (<) and backup (>):'
  202. echo
  203. diff "$SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
  204. local action
  205. choose_one action "What should happen to the settings file? " \
  206. "keep new configuration" \
  207. "revert to the old configuration (backup file)" \
  208. "start interactiv shell"
  209. case $action in
  210. "keep new configuration")
  211. info_msg "continue using new settings file"
  212. ;;
  213. "revert to the old configuration (backup file)")
  214. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  215. cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
  216. EOF
  217. ;;
  218. "start interactiv shell")
  219. interactive_shell
  220. ;;
  221. esac
  222. chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
  223. # shellcheck disable=SC2016
  224. rst_para 'Diff between local modified settings (<) and $SEARX_GIT_BRANCH branch (>):'
  225. echo
  226. git_diff
  227. wait_key
  228. uWSGI_restart
  229. }
  230. remove_all() {
  231. rst_title "De-Install $SEARX_INSTANCE_NAME (service)"
  232. rst_para "\
  233. It goes without saying that this script can only be used to remove
  234. installations that were installed with this script."
  235. if ! ask_yn "Do you really want to deinstall $SEARX_INSTANCE_NAME?"; then
  236. return
  237. fi
  238. remove_searx_uwsgi
  239. wait_key
  240. remove_user
  241. }
  242. user_is_available() {
  243. sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
  244. }
  245. assert_user() {
  246. rst_title "user $SERVICE_USER" section
  247. echo
  248. tee_stderr 1 <<EOF | bash | prefix_stdout
  249. sudo -H adduser --shell /bin/bash --system --home "$SERVICE_HOME" \
  250. --disabled-password --group --gecos 'searx' $SERVICE_USER
  251. sudo -H usermod -a -G shadow $SERVICE_USER
  252. groups $SERVICE_USER
  253. EOF
  254. #SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  255. #export SERVICE_HOME
  256. #echo "export SERVICE_HOME=$SERVICE_HOME"
  257. }
  258. remove_user() {
  259. rst_title "Drop $SERVICE_USER HOME" section
  260. if ask_yn "Do you really want to drop $SERVICE_USER home folder?"; then
  261. userdel -r -f "$SERVICE_USER" 2>&1 | prefix_stdout
  262. else
  263. rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
  264. fi
  265. }
  266. clone_is_available() {
  267. [[ -f "$SEARX_SETTINGS" ]]
  268. }
  269. # shellcheck disable=SC2164
  270. clone_searx() {
  271. rst_title "Clone searx sources" section
  272. echo
  273. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
  274. if [[ ! "${SERVICE_HOME}" ]]; then
  275. err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"
  276. return 42
  277. fi
  278. export SERVICE_HOME
  279. git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
  280. "$SEARX_GIT_BRANCH" "$SERVICE_USER"
  281. pushd "${SEARX_SRC}" > /dev/null
  282. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  283. cd "${SEARX_SRC}"
  284. git config user.email "$ADMIN_EMAIL"
  285. git config user.name "$ADMIN_NAME"
  286. git config --list
  287. EOF
  288. popd > /dev/null
  289. }
  290. remove_searx() {
  291. rst_title "Drop searx sources" section
  292. if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
  293. rm -rf "$SEARX_SRC"
  294. else
  295. rst_para "Leave searx sources unchanged."
  296. fi
  297. }
  298. pyenv_is_available() {
  299. [[ -f "${SEARX_PYENV}/bin/activate" ]]
  300. }
  301. create_pyenv() {
  302. rst_title "Create virtualenv (python)" section
  303. echo
  304. if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
  305. err_msg "to create pyenv for searx, searx has to be cloned first"
  306. return 42
  307. fi
  308. info_msg "create pyenv in ${SEARX_PYENV}"
  309. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  310. rm -rf "${SEARX_PYENV}"
  311. python3 -m venv "${SEARX_PYENV}"
  312. grep -qFs -- 'source ${SEARX_PYENV}/bin/activate' ~/.profile \
  313. || echo 'source ${SEARX_PYENV}/bin/activate' >> ~/.profile
  314. EOF
  315. info_msg "inspect python's virtual environment"
  316. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  317. command -v python && python --version
  318. EOF
  319. wait_key
  320. info_msg "install needed python packages"
  321. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  322. ${SEARX_SRC}/manage.sh update_packages
  323. EOF
  324. }
  325. remove_pyenv() {
  326. rst_title "Remove virtualenv (python)" section
  327. if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
  328. return
  329. fi
  330. info_msg "remove pyenv activation from ~/.profile"
  331. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  332. grep -v 'source ${SEARX_PYENV}/bin/activate' ~/.profile > ~/.profile.##
  333. mv ~/.profile.## ~/.profile
  334. EOF
  335. rm -rf "${SEARX_PYENV}"
  336. }
  337. configure_searx() {
  338. rst_title "Configure searx" section
  339. rst_para "Setup searx config located at $SEARX_SETTINGS"
  340. echo
  341. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  342. cd ${SEARX_SRC}
  343. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS"
  344. sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
  345. EOF
  346. }
  347. test_local_searx() {
  348. rst_title "Testing searx instance localy" section
  349. echo
  350. if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
  351. err_msg "URL/port http://$SEARX_INTERNAL_URL is already in use, you"
  352. err_msg "should stop that service before starting local tests!"
  353. if ! ask_yn "Continue with local tests?"; then
  354. return
  355. fi
  356. fi
  357. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  358. cd ${SEARX_SRC}
  359. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  360. timeout 5 python3 searx/webapp.py &
  361. sleep 1
  362. curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
  363. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  364. EOF
  365. }
  366. install_searx_uwsgi() {
  367. rst_title "Install searx's uWSGI app (searx.ini)" section
  368. echo
  369. uWSGI_install_app "$SEARX_UWSGI_APP"
  370. }
  371. remove_searx_uwsgi() {
  372. rst_title "Remove searx's uWSGI app (searx.ini)" section
  373. echo
  374. uWSGI_remove_app "$SEARX_UWSGI_APP"
  375. }
  376. activate_service() {
  377. rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
  378. echo
  379. uWSGI_enable_app "$SEARX_UWSGI_APP"
  380. uWSGI_restart
  381. }
  382. deactivate_service() {
  383. rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
  384. echo
  385. uWSGI_disable_app "$SEARX_UWSGI_APP"
  386. uWSGI_restart
  387. }
  388. interactive_shell() {
  389. echo "// exit with CTRL-D"
  390. sudo -H -u "${SERVICE_USER}" -i
  391. }
  392. git_diff() {
  393. sudo -H -u "${SERVICE_USER}" -i <<EOF
  394. cd ${SEARX_REPO_FOLDER}
  395. git --no-pager diff
  396. EOF
  397. }
  398. enable_debug() {
  399. info_msg "try to enable debug mode ..."
  400. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  401. cd ${SEARX_SRC}
  402. sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
  403. EOF
  404. uWSGI_restart
  405. }
  406. disable_debug() {
  407. info_msg "try to disable debug mode ..."
  408. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  409. cd ${SEARX_SRC}
  410. sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
  411. EOF
  412. uWSGI_restart
  413. }
  414. inspect_service() {
  415. rst_title "service status & log"
  416. cat <<EOF
  417. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  418. PUBLIC_URL : ${PUBLIC_URL}
  419. SEARX_URL_PATH : ${SEARX_URL_PATH}
  420. SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
  421. SEARX_INTERNAL_URL : ${SEARX_INTERNAL_URL}
  422. EOF
  423. apache_is_installed && info_msg "Apache is installed."
  424. if user_is_available; then
  425. info_msg "Service account $SERVICE_USER exists."
  426. else
  427. err_msg "Service account $SERVICE_USER does not exists!"
  428. fi
  429. if pyenv_is_available; then
  430. info_msg "~$SERVICE_USER: python environment is available."
  431. else
  432. err_msg "~$SERVICE_USER: python environment is not available!"
  433. fi
  434. if clone_is_available; then
  435. info_msg "~$SERVICE_USER: Searx software is installed."
  436. else
  437. err_msg "~$SERVICE_USER: Missing searx software!"
  438. fi
  439. if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
  440. info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
  441. else
  442. err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
  443. fi
  444. uWSGI_app_available "$SEARX_UWSGI_APP" \
  445. || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
  446. if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
  447. err_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is not available!"
  448. fi
  449. if ! service_is_available "${PUBLIC_URL}"; then
  450. err_msg "Public service at ${PUBLIC_URL} is not available!"
  451. fi
  452. local _debug_on
  453. if ask_yn "Enable searx debug mode?"; then
  454. enable_debug
  455. _debug_on=1
  456. fi
  457. echo
  458. systemctl --no-pager -l status uwsgi.service
  459. echo
  460. read -r -s -n1 -t 2 -p "// use CTRL-C to stop monitoring the log"
  461. echo
  462. while true; do
  463. trap break 2
  464. #journalctl -f -u uwsgi.service
  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. # ----------------------------------------------------------------------------