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