searxng.sh 35 KB

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