searxng.sh 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027
  1. #!/usr/bin/env bash
  2. # SPDX-License-Identifier: AGPL-3.0-or-later
  3. # shellcheck disable=SC2001
  4. # Script options from the environment:
  5. SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET:-true}"
  6. # shellcheck source=utils/lib.sh
  7. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  8. # shellcheck source=utils/lib_redis.sh
  9. source "$(dirname "${BASH_SOURCE[0]}")/lib_redis.sh"
  10. # shellcheck source=utils/brand.sh
  11. source "${REPO_ROOT}/utils/brand.sh"
  12. SERVICE_NAME="searxng"
  13. SERVICE_USER="searxng"
  14. SERVICE_HOME="/usr/local/searxng"
  15. SERVICE_GROUP="searxng"
  16. SEARXNG_SRC="${SERVICE_HOME}/searxng-src"
  17. # shellcheck disable=SC2034
  18. SEARXNG_STATIC="${SEARXNG_SRC}/searx/static"
  19. SEARXNG_PYENV="${SERVICE_HOME}/searx-pyenv"
  20. SEARXNG_SETTINGS_PATH="/etc/searxng/settings.yml"
  21. SEARXNG_UWSGI_APP="searxng.ini"
  22. SEARXNG_INTERNAL_HTTP="${SEARXNG_BIND_ADDRESS}:${SEARXNG_PORT}"
  23. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  24. SEARXNG_UWSGI_SOCKET="${SERVICE_HOME}/run/socket"
  25. else
  26. SEARXNG_UWSGI_SOCKET=
  27. fi
  28. # SEARXNG_URL: the public URL of the instance (https://example.org/searxng). The
  29. # value is taken from environment ${SEARXNG_URL} in ./utils/brand.env. This
  30. # variable is an empty string if server.base_url in the settings.yml is set to
  31. # 'false'.
  32. SEARXNG_URL="${SEARXNG_URL:-http://$(uname -n)/searxng}"
  33. SEARXNG_URL="${SEARXNG_URL%/}" # if exists, remove trailing slash
  34. if in_container; then
  35. # hint: Linux containers do not have DNS entries, lets use IPs
  36. SEARXNG_URL="http://$(primary_ip)/searxng"
  37. fi
  38. SEARXNG_URL_PATH="$(echo "${SEARXNG_URL}" | sed -e 's,^.*://[^/]*\(/.*\),\1,g')"
  39. [[ "${SEARXNG_URL_PATH}" == "${SEARXNG_URL}" ]] && SEARXNG_URL_PATH=/
  40. # Apache settings
  41. APACHE_SEARXNG_SITE="searxng.conf"
  42. # nginx settings
  43. NGINX_SEARXNG_SITE="searxng.conf"
  44. # apt packages
  45. SEARXNG_PACKAGES_debian="\
  46. python3-dev python3-babel python3-venv
  47. uwsgi uwsgi-plugin-python3
  48. git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
  49. SEARXNG_BUILD_PACKAGES_debian="\
  50. graphviz imagemagick texlive-xetex librsvg2-bin
  51. texlive-latex-recommended texlive-extra-utils fonts-dejavu
  52. latexmk shellcheck"
  53. # pacman packages
  54. SEARXNG_PACKAGES_arch="\
  55. python python-pip python-lxml python-babel
  56. uwsgi uwsgi-plugin-python
  57. git base-devel libxml2"
  58. SEARXNG_BUILD_PACKAGES_arch="\
  59. graphviz imagemagick texlive-bin extra/librsvg
  60. texlive-core texlive-latexextra ttf-dejavu shellcheck"
  61. # dnf packages
  62. SEARXNG_PACKAGES_fedora="\
  63. python python-pip python-lxml python-babel python3-devel
  64. uwsgi uwsgi-plugin-python3
  65. git @development-tools libxml2 openssl"
  66. SEARXNG_BUILD_PACKAGES_fedora="\
  67. graphviz graphviz-gd ImageMagick librsvg2-tools
  68. texlive-xetex-bin texlive-collection-fontsrecommended
  69. texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
  70. dejavu-sans-mono-fonts ShellCheck"
  71. case $DIST_ID-$DIST_VERS in
  72. ubuntu-18.04)
  73. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian}"
  74. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  75. APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"
  76. ;;
  77. ubuntu-*|debian-*)
  78. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian} python-is-python3"
  79. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  80. ;;
  81. arch-*)
  82. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_arch}"
  83. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_arch}"
  84. ;;
  85. fedora-*)
  86. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_fedora}"
  87. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_fedora}"
  88. ;;
  89. esac
  90. _service_prefix=" ${_Yellow}|${SERVICE_USER}|${_creset} "
  91. # ----------------------------------------------------------------------------
  92. usage() {
  93. # ----------------------------------------------------------------------------
  94. # shellcheck disable=SC1117
  95. cat <<EOF
  96. usage:
  97. $(basename "$0") install [all|user|pyenv|settings|uwsgi|redis|nginx|apache|searxng-src|packages|buildhost]
  98. $(basename "$0") remove [all|user|pyenv|settings|uwsgi|redis|nginx|apache]
  99. $(basename "$0") instance [cmd|update|check|localtest|inspect]
  100. install|remove:
  101. all : complete (de-) installation of the SearXNG service
  102. user : service user '${SERVICE_USER}' (${SERVICE_HOME})
  103. pyenv : virtualenv (python) in ${SEARXNG_PYENV}
  104. settings : settings from ${SEARXNG_SETTINGS_PATH}
  105. uwsgi : SearXNG's uWSGI app ${SEARXNG_UWSGI_APP}
  106. redis : build & install or remove a local redis server ${REDIS_HOME}/run/redis.sock
  107. nginx : HTTP site ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}
  108. apache : HTTP site ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}
  109. install:
  110. searxng-src : clone ${GIT_URL} into ${SEARXNG_SRC}
  111. packages : installs packages from OS package manager required by SearXNG
  112. buildhost : installs packages from OS package manager required by a SearXNG buildhost
  113. instance:
  114. update : update SearXNG instance (git fetch + reset & update settings.yml)
  115. check : run checks from utils/searxng_check.py in the active installation
  116. inspect : run some small tests and inspect SearXNG's server status and log
  117. get_setting : get settings value from running SearXNG instance
  118. cmd : run command in SearXNG instance's environment (e.g. bash)
  119. EOF
  120. searxng.instance.env
  121. [[ -n ${1} ]] && err_msg "$1"
  122. }
  123. searxng.instance.env() {
  124. echo "uWSGI:"
  125. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  126. echo " SEARXNG_UWSGI_SOCKET : ${SEARXNG_UWSGI_SOCKET}"
  127. else
  128. echo " SEARXNG_INTERNAL_HTTP: ${SEARXNG_INTERNAL_HTTP}"
  129. fi
  130. cat <<EOF
  131. environment:
  132. GIT_URL : ${GIT_URL}
  133. GIT_BRANCH : ${GIT_BRANCH}
  134. SEARXNG_URL : ${SEARXNG_URL}
  135. SEARXNG_PORT : ${SEARXNG_PORT}
  136. SEARXNG_BIND_ADDRESS : ${SEARXNG_BIND_ADDRESS}
  137. EOF
  138. }
  139. main() {
  140. case $1 in
  141. install|remove|instance)
  142. nginx_distro_setup
  143. apache_distro_setup
  144. uWSGI_distro_setup
  145. required_commands \
  146. sudo systemctl install git wget curl \
  147. || exit
  148. ;;
  149. esac
  150. local _usage="unknown or missing $1 command $2"
  151. case $1 in
  152. --getenv) var="$2"; echo "${!var}"; exit 0;;
  153. --cmd) shift; "$@";;
  154. -h|--help) usage; exit 0;;
  155. install)
  156. sudo_or_exit
  157. case $2 in
  158. all) searxng.install.all;;
  159. user) searxng.install.user;;
  160. pyenv) searxng.install.pyenv;;
  161. searxng-src) searxng.install.clone;;
  162. settings) searxng.install.settings;;
  163. uwsgi) searxng.install.uwsgi;;
  164. packages) searxng.install.packages;;
  165. buildhost) searxng.install.buildhost;;
  166. nginx) searxng.nginx.install;;
  167. apache) searxng.apache.install;;
  168. redis) searxng.install.redis;;
  169. *) usage "$_usage"; exit 42;;
  170. esac
  171. ;;
  172. remove)
  173. sudo_or_exit
  174. case $2 in
  175. all) searxng.remove.all;;
  176. user) drop_service_account "${SERVICE_USER}";;
  177. pyenv) searxng.remove.pyenv;;
  178. settings) searxng.remove.settings;;
  179. uwsgi) searxng.remove.uwsgi;;
  180. apache) searxng.apache.remove;;
  181. remove) searxng.nginx.remove;;
  182. redis) searxng.remove.redis;;
  183. *) usage "$_usage"; exit 42;;
  184. esac
  185. ;;
  186. instance)
  187. case $2 in
  188. update)
  189. sudo_or_exit
  190. searxng.instance.update
  191. ;;
  192. check)
  193. sudo_or_exit
  194. searxng.instance.self.call searxng.check
  195. ;;
  196. inspect)
  197. sudo_or_exit
  198. searxng.instance.inspect
  199. ;;
  200. cmd)
  201. sudo_or_exit
  202. shift; shift; searxng.instance.exec "$@"
  203. ;;
  204. get_setting)
  205. shift; shift; searxng.instance.get_setting "$@"
  206. ;;
  207. call)
  208. # call a function in instance's environment
  209. shift; shift; searxng.instance.self.call "$@"
  210. ;;
  211. _call)
  212. shift; shift; "$@"
  213. ;;
  214. *) usage "$_usage"; exit 42;;
  215. esac
  216. ;;
  217. *)
  218. local cmd="$1"
  219. _type="$(type -t "$cmd")"
  220. if [ "$_type" != 'function' ]; then
  221. usage "unknown or missing command $1"
  222. exit 42
  223. else
  224. "$cmd" "$@"
  225. fi
  226. ;;
  227. esac
  228. }
  229. searxng.install.all() {
  230. rst_title "SearXNG installation" part
  231. local redis_url
  232. rst_title "SearXNG"
  233. searxng.install.packages
  234. wait_key 10
  235. searxng.install.user
  236. wait_key 10
  237. searxng.install.clone
  238. wait_key
  239. searxng.install.pyenv
  240. wait_key
  241. searxng.install.settings
  242. wait_key
  243. searxng.instance.localtest
  244. wait_key
  245. searxng.install.uwsgi
  246. wait_key
  247. rst_title "Redis DB"
  248. searxng.install.redis.db
  249. rst_title "HTTP Server"
  250. searxng.install.http.site
  251. rst_title "Finalize installation"
  252. if ask_yn "Do you want to run some checks?" Yn; then
  253. searxng.instance.self.call searxng.check
  254. fi
  255. }
  256. searxng.install.redis.db() {
  257. local redis_url
  258. redis_url=$(searxng.instance.get_setting redis.url)
  259. rst_para "\
  260. In your instance, redis DB connector is configured at:
  261. ${redis_url}
  262. "
  263. if searxng.instance.exec python -c "from searx import redisdb; redisdb.initialize() or exit(42)"; then
  264. info_msg "SearXNG instance is able to connect redis DB."
  265. return
  266. fi
  267. if ! [[ ${redis_url} = unix://${REDIS_HOME}/run/redis.sock* ]]; then
  268. err_msg "SearXNG instance can't connect redis DB / check redis & your settings"
  269. return
  270. fi
  271. rst_para ".. but this redis DB is not installed yet."
  272. case $DIST_ID-$DIST_VERS in
  273. fedora-*)
  274. # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the
  275. # Emperor will run the vassal using the UID/GID of the vassal
  276. # configuration file [1] (user and group of the app .ini file).
  277. #
  278. # HINT: without option ``emperor-tyrant-initgroups=true`` in
  279. # ``/etc/uwsgi.ini`` the process won't get the additional groups,
  280. # but this option is not available in 2.0.x branch [2][3] / on
  281. # fedora35 there is v2.0.20 installed --> no way to get additional
  282. # groups on fedora's tyrant mode.
  283. #
  284. # ERROR:searx.redisdb: [searxng (993)] can't connect redis DB ...
  285. # ERROR:searx.redisdb: Error 13 connecting to unix socket: /usr/local/searxng-redis/run/redis.sock. Permission denied.
  286. # ERROR:searx.plugins.limiter: init limiter DB failed!!!
  287. #
  288. # $ ps -aef | grep '/usr/sbin/uwsgi --ini searxng.ini'
  289. # searxng 93 92 0 12:43 ? 00:00:00 /usr/sbin/uwsgi --ini searxng.ini
  290. # searxng 186 93 0 12:44 ? 00:00:01 /usr/sbin/uwsgi --ini searxng.ini
  291. #
  292. # Additional groups:
  293. #
  294. # $ groups searxng
  295. # searxng : searxng searxng-redis
  296. #
  297. # Here you can see that the additional "Groups" of PID 186 are unset
  298. # (missing gid of searxng-redis)
  299. #
  300. # $ cat /proc/186/task/186/status
  301. # ...
  302. # Uid: 993 993 993 993
  303. # Gid: 993 993 993 993
  304. # FDSize: 128
  305. # Groups:
  306. # ...
  307. #
  308. # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting
  309. # [2] https://github.com/unbit/uwsgi/issues/2099
  310. # [3] https://github.com/unbit/uwsgi/pull/752
  311. rst_para "\
  312. Fedora uses emperor-tyrant mode / in this mode we had a lot of trouble with
  313. sockets and permissions of the vasals. We recommend to setup a redis DB
  314. and using redis:// TCP protocol in the settings.yml configuration."
  315. ;;
  316. *)
  317. if ask_yn "Do you want to install the redis DB now?" Yn; then
  318. searxng.install.redis
  319. uWSGI_restart "$SEARXNG_UWSGI_APP"
  320. fi
  321. ;;
  322. esac
  323. }
  324. searxng.install.http.site() {
  325. if apache_is_installed; then
  326. info_msg "Apache is installed on this host."
  327. if ask_yn "Do you want to install a reverse proxy" Yn; then
  328. searxng.apache.install
  329. fi
  330. elif nginx_is_installed; then
  331. info_msg "Nginx is installed on this host."
  332. if ask_yn "Do you want to install a reverse proxy" Yn; then
  333. searxng.nginx.install
  334. fi
  335. else
  336. info_msg "Don't forget to install HTTP site."
  337. fi
  338. }
  339. searxng.remove.all() {
  340. local redis_url
  341. rst_title "De-Install SearXNG (service)"
  342. if ! ask_yn "Do you really want to deinstall SearXNG?"; then
  343. return
  344. fi
  345. redis_url=$(searxng.instance.get_setting redis.url)
  346. if ! [[ ${redis_url} = unix://${REDIS_HOME}/run/redis.sock* ]]; then
  347. searxng.remove.redis
  348. fi
  349. searxng.remove.uwsgi
  350. drop_service_account "${SERVICE_USER}"
  351. searxng.remove.settings
  352. wait_key
  353. if service_is_available "${SEARXNG_URL}"; then
  354. MSG="** Don't forget to remove your public site! (${SEARXNG_URL}) **" wait_key 10
  355. fi
  356. }
  357. searxng.install.user() {
  358. rst_title "SearXNG -- install user" section
  359. echo
  360. if getent passwd "${SERVICE_USER}" > /dev/null; then
  361. echo "user already exists"
  362. return 0
  363. fi
  364. tee_stderr 1 <<EOF | bash | prefix_stdout
  365. useradd --shell /bin/bash --system \
  366. --home-dir "${SERVICE_HOME}" \
  367. --comment 'Privacy-respecting metasearch engine' ${SERVICE_USER}
  368. mkdir "${SERVICE_HOME}"
  369. chown -R "${SERVICE_GROUP}:${SERVICE_GROUP}" "${SERVICE_HOME}"
  370. groups ${SERVICE_USER}
  371. EOF
  372. }
  373. searxng.install.packages() {
  374. TITLE="SearXNG -- install packages" pkg_install "${SEARXNG_PACKAGES}"
  375. }
  376. searxng.install.buildhost() {
  377. TITLE="SearXNG -- install buildhost packages" pkg_install \
  378. "${SEARXNG_PACKAGES} ${SEARXNG_BUILD_PACKAGES}"
  379. }
  380. searxng.install.clone() {
  381. rst_title "Clone SearXNG sources" section
  382. if ! service_account_is_available "${SERVICE_USER}"; then
  383. die 42 "To clone SearXNG, first install user ${SERVICE_USER}."
  384. fi
  385. echo
  386. if ! sudo -i -u "${SERVICE_USER}" ls -d "$REPO_ROOT" > /dev/null; then
  387. die 42 "user '${SERVICE_USER}' missed read permission: $REPO_ROOT"
  388. fi
  389. # SERVICE_HOME="$(sudo -i -u "${SERVICE_USER}" echo \$HOME 2>/dev/null)"
  390. if [[ ! "${SERVICE_HOME}" ]]; then
  391. err_msg "to clone SearXNG sources, user ${SERVICE_USER} hast to be created first"
  392. return 42
  393. fi
  394. if [[ ! $(git show-ref "refs/heads/${GIT_BRANCH}") ]]; then
  395. warn_msg "missing local branch ${GIT_BRANCH}"
  396. info_msg "create local branch ${GIT_BRANCH} from start point: origin/${GIT_BRANCH}"
  397. git branch "${GIT_BRANCH}" "origin/${GIT_BRANCH}"
  398. fi
  399. if [[ ! $(git rev-parse --abbrev-ref HEAD) == "${GIT_BRANCH}" ]]; then
  400. warn_msg "take into account, installing branch $GIT_BRANCH while current branch is $(git rev-parse --abbrev-ref HEAD)"
  401. fi
  402. # export SERVICE_HOME
  403. # clone repo and add a safe.directory entry to git's system config / see
  404. # https://github.com/searxng/searxng/issues/1251
  405. git config --system --add safe.directory "${REPO_ROOT}/.git"
  406. git_clone "$REPO_ROOT" "${SEARXNG_SRC}" \
  407. "$GIT_BRANCH" "${SERVICE_USER}"
  408. git config --system --add safe.directory "${SEARXNG_SRC}"
  409. pushd "${SEARXNG_SRC}" > /dev/null
  410. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  411. cd "${SEARXNG_SRC}"
  412. git remote set-url origin ${GIT_URL}
  413. git config user.email "${ADMIN_EMAIL}"
  414. git config user.name "${ADMIN_NAME}"
  415. git config --list
  416. EOF
  417. popd > /dev/null
  418. }
  419. searxng.install.link_src() {
  420. rst_title "link SearXNG's sources to: $2" chapter
  421. echo
  422. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  423. mv -f "${SEARXNG_SRC}" "${SEARXNG_SRC}.backup"
  424. ln -s "${2}" "${SEARXNG_SRC}"
  425. ls -ld /usr/local/searxng/searxng-src
  426. EOF
  427. echo
  428. uWSGI_restart "$SEARXNG_UWSGI_APP"
  429. }
  430. searxng.install.pyenv() {
  431. rst_title "Create virtualenv (python)" section
  432. echo
  433. if [[ ! -f "${SEARXNG_SRC}/manage" ]]; then
  434. die 42 "To create pyenv for SearXNG, first install searxng-src."
  435. fi
  436. info_msg "create pyenv in ${SEARXNG_PYENV}"
  437. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  438. rm -rf "${SEARXNG_PYENV}"
  439. python -m venv "${SEARXNG_PYENV}"
  440. grep -qFs -- 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile \
  441. || echo 'source ${SEARXNG_PYENV}/bin/activate' >> ~/.profile
  442. EOF
  443. info_msg "inspect python's virtual environment"
  444. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  445. command -v python && python --version
  446. EOF
  447. wait_key
  448. info_msg "install needed python packages"
  449. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  450. pip install -U pip
  451. pip install -U setuptools
  452. pip install -U wheel
  453. pip install -U pyyaml
  454. cd ${SEARXNG_SRC}
  455. pip install --use-pep517 --no-build-isolation -e .
  456. EOF
  457. }
  458. searxng.remove.pyenv() {
  459. rst_title "Remove virtualenv (python)" section
  460. if ! ask_yn "Do you really want to drop ${SEARXNG_PYENV} ?"; then
  461. return
  462. fi
  463. info_msg "remove pyenv activation from ~/.profile"
  464. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  465. grep -v 'source ${SEARXNG_PYENV}/bin/activate' ~/.profile > ~/.profile.##
  466. mv ~/.profile.## ~/.profile
  467. EOF
  468. rm -rf "${SEARXNG_PYENV}"
  469. }
  470. searxng.install.settings() {
  471. rst_title "install ${SEARXNG_SETTINGS_PATH}" section
  472. if ! [[ -f "${SEARXNG_SRC}/.git/config" ]]; then
  473. die "Before install settings, first install SearXNG."
  474. fi
  475. mkdir -p "$(dirname "${SEARXNG_SETTINGS_PATH}")"
  476. DEFAULT_SELECT=1 \
  477. install_template --no-eval \
  478. "${SEARXNG_SETTINGS_PATH}" \
  479. "${SERVICE_USER}" "${SERVICE_GROUP}"
  480. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "root"
  481. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  482. EOF
  483. }
  484. searxng.remove.settings() {
  485. rst_title "remove ${SEARXNG_SETTINGS_PATH}" section
  486. if ask_yn "Do you want to delete the SearXNG settings?" Yn; then
  487. rm -f "${SEARXNG_SETTINGS_PATH}"
  488. fi
  489. }
  490. searxng.check() {
  491. rst_title "SearXNG checks" section
  492. "${SEARXNG_PYENV}/bin/python" "${SEARXNG_SRC}/utils/searxng_check.py"
  493. }
  494. searxng.instance.update() {
  495. rst_title "Update SearXNG instance"
  496. rst_para "fetch from $GIT_URL and reset to origin/$GIT_BRANCH"
  497. tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  498. cd ${SEARXNG_SRC}
  499. git fetch origin "$GIT_BRANCH"
  500. git reset --hard "origin/$GIT_BRANCH"
  501. pip install -U pip
  502. pip install -U setuptools
  503. pip install -U wheel
  504. pip install -U pyyaml
  505. pip install -U --use-pep517 --no-build-isolation -e .
  506. EOF
  507. rst_para "update instance's settings.yml from ${SEARXNG_SETTINGS_PATH}"
  508. DEFAULT_SELECT=2 \
  509. install_template --no-eval \
  510. "${SEARXNG_SETTINGS_PATH}" \
  511. "${SERVICE_USER}" "${SERVICE_GROUP}"
  512. sudo -H -i <<EOF
  513. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  514. EOF
  515. uWSGI_restart "${SEARXNG_UWSGI_APP}"
  516. }
  517. searxng.install.uwsgi() {
  518. rst_title "SearXNG (install uwsgi)"
  519. install_uwsgi
  520. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  521. searxng.install.uwsgi.socket
  522. else
  523. searxng.install.uwsgi.http
  524. fi
  525. }
  526. searxng.install.uwsgi.http() {
  527. rst_para "Install ${SEARXNG_UWSGI_APP} at: http://${SEARXNG_INTERNAL_HTTP}"
  528. uWSGI_install_app "${SEARXNG_UWSGI_APP}"
  529. if ! searxng.uwsgi.available; then
  530. err_msg "URL http://${SEARXNG_INTERNAL_HTTP} not available, check SearXNG & uwsgi setup!"
  531. fi
  532. }
  533. searxng.install.uwsgi.socket() {
  534. rst_para "Install ${SEARXNG_UWSGI_APP} using socket at: ${SEARXNG_UWSGI_SOCKET}"
  535. mkdir -p "$(dirname "${SEARXNG_UWSGI_SOCKET}")"
  536. chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "$(dirname "${SEARXNG_UWSGI_SOCKET}")"
  537. case $DIST_ID-$DIST_VERS in
  538. fedora-*)
  539. # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the
  540. # Emperor will run the vassal using the UID/GID of the vassal
  541. # configuration file [1] (user and group of the app .ini file).
  542. # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting
  543. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}" "${SERVICE_USER}" "${SERVICE_GROUP}"
  544. ;;
  545. *)
  546. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}"
  547. ;;
  548. esac
  549. sleep 5
  550. if ! searxng.uwsgi.available; then
  551. err_msg "uWSGI socket not available at: ${SEARXNG_UWSGI_SOCKET}"
  552. fi
  553. }
  554. searxng.uwsgi.available() {
  555. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  556. [[ -S "${SEARXNG_UWSGI_SOCKET}" ]]
  557. exit_val=$?
  558. if [[ $exit_val = 0 ]]; then
  559. info_msg "uWSGI socket is located at: ${SEARXNG_UWSGI_SOCKET}"
  560. fi
  561. else
  562. service_is_available "http://${SEARXNG_INTERNAL_HTTP}"
  563. exit_val=$?
  564. fi
  565. return "$exit_val"
  566. }
  567. searxng.remove.uwsgi() {
  568. rst_title "Remove SearXNG's uWSGI app (${SEARXNG_UWSGI_APP})" section
  569. echo
  570. uWSGI_remove_app "${SEARXNG_UWSGI_APP}"
  571. }
  572. searxng.install.redis() {
  573. rst_title "SearXNG (install redis)"
  574. redis.build
  575. redis.install
  576. redis.addgrp "${SERVICE_USER}"
  577. }
  578. searxng.remove.redis() {
  579. rst_title "SearXNG (remove redis)"
  580. redis.rmgrp "${SERVICE_USER}"
  581. redis.remove
  582. }
  583. searxng.instance.localtest() {
  584. rst_title "Test SearXNG instance locally" section
  585. rst_para "Activate debug mode, start a minimal SearXNG "\
  586. "service and debug a HTTP request/response cycle."
  587. if service_is_available "http://${SEARXNG_INTERNAL_HTTP}" &>/dev/null; then
  588. err_msg "URL/port http://${SEARXNG_INTERNAL_HTTP} is already in use, you"
  589. err_msg "should stop that service before starting local tests!"
  590. if ! ask_yn "Continue with local tests?"; then
  591. return
  592. fi
  593. fi
  594. echo
  595. searxng.instance.debug.on
  596. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  597. export SEARXNG_SETTINGS_PATH="${SEARXNG_SETTINGS_PATH}"
  598. cd ${SEARXNG_SRC}
  599. timeout 10 python searx/webapp.py &
  600. sleep 3
  601. curl --location --verbose --head --insecure ${SEARXNG_INTERNAL_HTTP}
  602. EOF
  603. echo
  604. searxng.instance.debug.off
  605. }
  606. searxng.install.http.pre() {
  607. if ! searxng.uwsgi.available; then
  608. rst_para "\
  609. To install uWSGI use::
  610. $(basename "$0") install uwsgi
  611. "
  612. die 42 "SearXNG's uWSGI app not available"
  613. fi
  614. if ! searxng.instance.exec python -c "from searx import redisdb; redisdb.initialize() or exit(42)"; then
  615. rst_para "\
  616. The configured redis DB is not available: If your server is public to the
  617. internet, you should setup a bot protection to block excessively bot queries.
  618. Bot protection requires a redis DB. About bot protection visit the official
  619. SearXNG documentation and query for the word 'limiter'.
  620. "
  621. fi
  622. }
  623. searxng.apache.install() {
  624. rst_title "Install Apache site ${APACHE_SEARXNG_SITE}"
  625. rst_para "\
  626. This installs SearXNG's uWSGI app as apache site. The apache site is located at:
  627. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}."
  628. searxng.install.http.pre
  629. if ! apache_is_installed; then
  630. err_msg "Apache packages are not installed"
  631. if ! ask_yn "Do you really want to continue and install apache packages?" Yn; then
  632. return
  633. else
  634. FORCE_SELECTION=Y install_apache
  635. fi
  636. else
  637. info_msg "Apache packages are installed [OK]"
  638. fi
  639. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  640. apache_install_site --variant=socket "${APACHE_SEARXNG_SITE}"
  641. else
  642. apache_install_site "${APACHE_SEARXNG_SITE}"
  643. fi
  644. if ! service_is_available "${SEARXNG_URL}"; then
  645. err_msg "Public service at ${SEARXNG_URL} is not available!"
  646. fi
  647. }
  648. searxng.apache.remove() {
  649. rst_title "Remove Apache site ${APACHE_SEARXNG_SITE}"
  650. rst_para "\
  651. This removes apache site ${APACHE_SEARXNG_SITE}::
  652. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}"
  653. ! apache_is_installed && err_msg "Apache is not installed."
  654. if ! ask_yn "Do you really want to continue?" Yn; then
  655. return
  656. fi
  657. apache_remove_site "${APACHE_SEARXNG_SITE}"
  658. }
  659. searxng.nginx.install() {
  660. rst_title "Install nginx site ${NGINX_SEARXNG_SITE}"
  661. rst_para "\
  662. This installs SearXNG's uWSGI app as Nginx site. The Nginx site is located at:
  663. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE} and requires a uWSGI."
  664. searxng.install.http.pre
  665. if ! nginx_is_installed ; then
  666. err_msg "Nginx packages are not installed"
  667. if ! ask_yn "Do you really want to continue and install Nginx packages?" Yn; then
  668. return
  669. else
  670. FORCE_SELECTION=Y install_nginx
  671. fi
  672. else
  673. info_msg "Nginx packages are installed [OK]"
  674. fi
  675. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  676. nginx_install_app --variant=socket "${NGINX_SEARXNG_SITE}"
  677. else
  678. nginx_install_app "${NGINX_SEARXNG_SITE}"
  679. fi
  680. if ! service_is_available "${SEARXNG_URL}"; then
  681. err_msg "Public service at ${SEARXNG_URL} is not available!"
  682. fi
  683. }
  684. searxng.nginx.remove() {
  685. rst_title "Remove Nginx site ${NGINX_SEARXNG_SITE}"
  686. rst_para "\
  687. This removes Nginx site ${NGINX_SEARXNG_SITE}::
  688. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}"
  689. ! nginx_is_installed && err_msg "Nginx is not installed."
  690. if ! ask_yn "Do you really want to continue?" Yn; then
  691. return
  692. fi
  693. nginx_remove_app "${NGINX_SEARXNG_SITE}"
  694. }
  695. searxng.instance.exec() {
  696. if ! service_account_is_available "${SERVICE_USER}"; then
  697. die 42 "can't execute: instance does not exist (missed account ${SERVICE_USER})"
  698. fi
  699. sudo -H -i -u "${SERVICE_USER}" \
  700. SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET}" \
  701. "$@"
  702. }
  703. searxng.instance.self.call() {
  704. # wrapper to call a function in instance's environment
  705. info_msg "wrapper: utils/searxng.sh instance _call $*"
  706. searxng.instance.exec "${SEARXNG_SRC}/utils/searxng.sh" instance _call "$@"
  707. }
  708. searxng.instance.get_setting() {
  709. searxng.instance.exec python <<EOF
  710. from searx import get_setting
  711. print(get_setting('$1'))
  712. EOF
  713. }
  714. searxng.instance.debug.on() {
  715. warn_msg "Do not enable debug in a production environment!"
  716. info_msg "try to enable debug mode ..."
  717. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  718. cd ${SEARXNG_SRC}
  719. sed -i -e "s/debug: false/debug: true/g" "$SEARXNG_SETTINGS_PATH"
  720. EOF
  721. uWSGI_restart "$SEARXNG_UWSGI_APP"
  722. }
  723. searxng.instance.debug.off() {
  724. info_msg "try to disable debug mode ..."
  725. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  726. cd ${SEARXNG_SRC}
  727. sed -i -e "s/debug: true/debug: false/g" "$SEARXNG_SETTINGS_PATH"
  728. EOF
  729. uWSGI_restart "$SEARXNG_UWSGI_APP"
  730. }
  731. searxng.instance.inspect() {
  732. rst_title "Inspect SearXNG instance"
  733. echo
  734. searxng.instance.self.call _searxng.instance.inspect
  735. local _debug_on
  736. if ask_yn "Enable SearXNG debug mode?"; then
  737. searxng.instance.debug.on
  738. _debug_on=1
  739. fi
  740. echo
  741. case $DIST_ID-$DIST_VERS in
  742. ubuntu-*|debian-*)
  743. # For uWSGI debian uses the LSB init process; for each configuration
  744. # file new uWSGI daemon instance is started with additional option.
  745. service uwsgi status "${SERVICE_NAME}"
  746. ;;
  747. arch-*)
  748. systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
  749. ;;
  750. fedora-*)
  751. systemctl --no-pager -l status uwsgi
  752. ;;
  753. esac
  754. echo -e "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  755. read -r -s -n1 -t 5
  756. echo
  757. while true; do
  758. trap break 2
  759. case $DIST_ID-$DIST_VERS in
  760. ubuntu-*|debian-*) tail -f "/var/log/uwsgi/app/${SERVICE_NAME%.*}.log" ;;
  761. arch-*) journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
  762. fedora-*) journalctl -f -u uwsgi ;;
  763. esac
  764. done
  765. if [[ $_debug_on == 1 ]]; then
  766. searxng.instance.debug.off
  767. fi
  768. return 0
  769. }
  770. _searxng.instance.inspect() {
  771. searxng.instance.env
  772. if in_container; then
  773. # shellcheck source=utils/lxc-searxng.env
  774. source "${REPO_ROOT}/utils/lxc-searxng.env"
  775. lxc_suite_info
  776. fi
  777. MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue${_creset}"
  778. if ! searxng.uwsgi.available; then
  779. err_msg "SearXNG's uWSGI app not available"
  780. wait_key
  781. fi
  782. if ! service_is_available "${SEARXNG_URL}"; then
  783. err_msg "Public service at ${SEARXNG_URL} is not available!"
  784. wait_key
  785. fi
  786. }
  787. searxng.doc.rst() {
  788. local APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
  789. local NGINX_APPS_AVAILABLE="/etc/nginx/default.apps-available"
  790. local debian="${SEARXNG_PACKAGES_debian}"
  791. local arch="${SEARXNG_PACKAGES_arch}"
  792. local fedora="${SEARXNG_PACKAGES_fedora}"
  793. local debian_build="${SEARXNG_BUILD_PACKAGES_debian}"
  794. local arch_build="${SEARXNG_BUILD_PACKAGES_arch}"
  795. local fedora_build="${SEARXNG_BUILD_PACKAGES_fedora}"
  796. debian="$(echo "${debian}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  797. arch="$(echo "${arch}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  798. fedora="$(echo "${fedora}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  799. debian_build="$(echo "${debian_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  800. arch_build="$(echo "${arch_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  801. fedora_build="$(echo "${fedora_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  802. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  803. uwsgi_variant=':socket'
  804. else
  805. uwsgi_variant=':socket'
  806. fi
  807. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searxng.rst")\""
  808. # I use ubuntu-20.04 here to demonstrate that versions are also supported,
  809. # normally debian-* and ubuntu-* are most the same.
  810. for DIST_NAME in ubuntu-20.04 arch fedora; do
  811. (
  812. DIST_ID=${DIST_NAME%-*}
  813. DIST_VERS=${DIST_NAME#*-}
  814. [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  815. uWSGI_distro_setup
  816. echo -e "\n.. START searxng uwsgi-description $DIST_NAME"
  817. case $DIST_ID-$DIST_VERS in
  818. ubuntu-*|debian-*) cat <<EOF
  819. .. code:: bash
  820. # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
  821. # For uWSGI debian uses the LSB init process, this might be changed
  822. # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
  823. create ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}
  824. enable: sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
  825. start: sudo -H service uwsgi start ${SEARXNG_UWSGI_APP%.*}
  826. restart: sudo -H service uwsgi restart ${SEARXNG_UWSGI_APP%.*}
  827. stop: sudo -H service uwsgi stop ${SEARXNG_UWSGI_APP%.*}
  828. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  829. EOF
  830. ;;
  831. arch-*) cat <<EOF
  832. .. code:: bash
  833. # systemd --> /usr/lib/systemd/system/uwsgi@.service
  834. # For uWSGI archlinux uses systemd template units, see
  835. # - http://0pointer.de/blog/projects/instances.html
  836. # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
  837. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  838. enable: sudo -H systemctl enable uwsgi@${SEARXNG_UWSGI_APP%.*}
  839. start: sudo -H systemctl start uwsgi@${SEARXNG_UWSGI_APP%.*}
  840. restart: sudo -H systemctl restart uwsgi@${SEARXNG_UWSGI_APP%.*}
  841. stop: sudo -H systemctl stop uwsgi@${SEARXNG_UWSGI_APP%.*}
  842. disable: sudo -H systemctl disable uwsgi@${SEARXNG_UWSGI_APP%.*}
  843. EOF
  844. ;;
  845. fedora-*|centos-7) cat <<EOF
  846. .. code:: bash
  847. # systemd --> /usr/lib/systemd/system/uwsgi.service
  848. # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
  849. # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
  850. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  851. restart: sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  852. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  853. EOF
  854. ;;
  855. esac
  856. echo -e ".. END searxng uwsgi-description $DIST_NAME"
  857. local _show_cursor="" # prevent from prefix_stdout's trailing show-cursor
  858. echo -e "\n.. START searxng uwsgi-appini $DIST_NAME"
  859. echo ".. code:: bash"
  860. echo
  861. eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}${uwsgi_variant}")\"" | prefix_stdout " "
  862. echo -e "\n.. END searxng uwsgi-appini $DIST_NAME"
  863. echo -e "\n.. START nginx socket"
  864. echo ".. code:: nginx"
  865. echo
  866. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  867. echo -e "\n.. END nginx socket"
  868. echo -e "\n.. START nginx http"
  869. echo ".. code:: nginx"
  870. echo
  871. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}")\"" | prefix_stdout " "
  872. echo -e "\n.. END nginx http"
  873. echo -e "\n.. START apache socket"
  874. echo ".. code:: apache"
  875. echo
  876. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  877. echo -e "\n.. END apache socket"
  878. echo -e "\n.. START apache http"
  879. echo ".. code:: apache"
  880. echo
  881. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}")\"" | prefix_stdout " "
  882. echo -e "\n.. END apache http"
  883. )
  884. done
  885. }
  886. # ----------------------------------------------------------------------------
  887. main "$@"
  888. # ----------------------------------------------------------------------------