searx.sh 16 KB

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