searx.sh 17 KB

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