searxng.sh 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030
  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. 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. firefox 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. firefox 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. firefox 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-20.04)
  78. # https://wiki.ubuntu.com/FocalFossa/ReleaseNotes#Python3_by_default
  79. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian} python-is-python3"
  80. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  81. ;;
  82. ubuntu-*|debian-*)
  83. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_debian}"
  84. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_debian}"
  85. ;;
  86. arch-*)
  87. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_arch}"
  88. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_arch}"
  89. ;;
  90. fedora-*)
  91. SEARXNG_PACKAGES="${SEARXNG_PACKAGES_fedora}"
  92. SEARXNG_BUILD_PACKAGES="${SEARXNG_BUILD_PACKAGES_fedora}"
  93. ;;
  94. esac
  95. _service_prefix=" ${_Yellow}|${SERVICE_USER}|${_creset} "
  96. # ----------------------------------------------------------------------------
  97. usage() {
  98. # ----------------------------------------------------------------------------
  99. # shellcheck disable=SC1117
  100. cat <<EOF
  101. usage:
  102. $(basename "$0") install [all|user|pyenv|settings|uwsgi|redis|nginx|apache|searxng-src|packages|buildhost]
  103. $(basename "$0") remove [all|user|pyenv|settings|uwsgi|redis|nginx|apache]
  104. $(basename "$0") instance [cmd|update|check|localtest|inspect]
  105. install|remove:
  106. all : complete (de-) installation of the SearXNG service
  107. user : service user '${SERVICE_USER}' (${SERVICE_HOME})
  108. pyenv : virtualenv (python) in ${SEARXNG_PYENV}
  109. settings : settings from ${SEARXNG_SETTINGS_PATH}
  110. uwsgi : SearXNG's uWSGI app ${SEARXNG_UWSGI_APP}
  111. redis : build & install or remove a local redis server ${REDIS_HOME}/run/redis.sock
  112. nginx : HTTP site ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}
  113. apache : HTTP site ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}
  114. install:
  115. searxng-src : clone ${GIT_URL} into ${SEARXNG_SRC}
  116. packages : installs packages from OS package manager required by SearXNG
  117. buildhost : installs packages from OS package manager required by a SearXNG buildhost
  118. instance:
  119. update : update SearXNG instance (git fetch + reset & update settings.yml)
  120. check : run checks from utils/searxng_check.py in the active installation
  121. inspect : run some small tests and inspect SearXNG's server status and log
  122. get_setting : get settings value from running SearXNG instance
  123. cmd : run command in SearXNG instance's environment (e.g. bash)
  124. EOF
  125. searxng.instance.env
  126. [[ -n ${1} ]] && err_msg "$1"
  127. }
  128. searxng.instance.env() {
  129. echo "uWSGI:"
  130. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  131. echo " SEARXNG_UWSGI_SOCKET : ${SEARXNG_UWSGI_SOCKET}"
  132. else
  133. echo " SEARXNG_INTERNAL_HTTP: ${SEARXNG_INTERNAL_HTTP}"
  134. fi
  135. cat <<EOF
  136. environment ${SEARXNG_SRC}/utils/brand.env:
  137. GIT_URL : ${GIT_URL}
  138. GIT_BRANCH : ${GIT_BRANCH}
  139. SEARXNG_URL : ${SEARXNG_URL}
  140. SEARXNG_PORT : ${SEARXNG_PORT}
  141. SEARXNG_BIND_ADDRESS : ${SEARXNG_BIND_ADDRESS}
  142. EOF
  143. }
  144. main() {
  145. required_commands \
  146. sudo systemctl install git wget curl \
  147. || exit
  148. local _usage="unknown or missing $1 command $2"
  149. case $1 in
  150. --getenv) var="$2"; echo "${!var}"; exit 0;;
  151. --cmd) shift; "$@";;
  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 ${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. "${SEARXNG_SETTINGS_PATH}" \
  477. "${SERVICE_USER}" "${SERVICE_GROUP}"
  478. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "root"
  479. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  480. EOF
  481. }
  482. searxng.remove.settings() {
  483. rst_title "remove ${SEARXNG_SETTINGS_PATH}" section
  484. if ask_yn "Do you want to delete the SearXNG settings?" Yn; then
  485. rm -f "${SEARXNG_SETTINGS_PATH}"
  486. fi
  487. }
  488. searxng.check() {
  489. rst_title "SearXNG checks" section
  490. for NAME in "searx" "filtron" "morty"; do
  491. if service_account_is_available "${NAME}"; then
  492. err_msg "There exists an old '${NAME}' account from a previous installation."
  493. else
  494. info_msg "[OK] (old) account '${NAME}' does not exists"
  495. fi
  496. done
  497. "${SEARXNG_PYENV}/bin/python" "${SEARXNG_SRC}/utils/searxng_check.py"
  498. }
  499. searxng.instance.update() {
  500. rst_title "Update SearXNG instance"
  501. rst_para "fetch from $GIT_URL and reset to origin/$GIT_BRANCH"
  502. tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  503. cd ${SEARXNG_SRC}
  504. git fetch origin "$GIT_BRANCH"
  505. git reset --hard "origin/$GIT_BRANCH"
  506. pip install -U pip
  507. pip install -U setuptools
  508. pip install -U wheel
  509. pip install -U pyyaml
  510. pip install -U -e .
  511. EOF
  512. rst_para "update instance's settings.yml from ${SEARXNG_SETTINGS_PATH}"
  513. DEFAULT_SELECT=2 \
  514. install_template --no-eval \
  515. "${SEARXNG_SETTINGS_PATH}" \
  516. "${SERVICE_USER}" "${SERVICE_GROUP}"
  517. sudo -H -i <<EOF
  518. sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "${SEARXNG_SETTINGS_PATH}"
  519. EOF
  520. uWSGI_restart "${SEARXNG_UWSGI_APP}"
  521. }
  522. searxng.install.uwsgi() {
  523. rst_title "SearXNG (install uwsgi)"
  524. install_uwsgi
  525. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  526. searxng.install.uwsgi.socket
  527. else
  528. searxng.install.uwsgi.http
  529. fi
  530. }
  531. searxng.install.uwsgi.http() {
  532. rst_para "Install ${SEARXNG_UWSGI_APP} at: http://${SEARXNG_INTERNAL_HTTP}"
  533. uWSGI_install_app "${SEARXNG_UWSGI_APP}"
  534. if ! searxng.uwsgi.available; then
  535. err_msg "URL http://${SEARXNG_INTERNAL_HTTP} not available, check SearXNG & uwsgi setup!"
  536. fi
  537. }
  538. searxng.install.uwsgi.socket() {
  539. rst_para "Install ${SEARXNG_UWSGI_APP} using socket at: ${SEARXNG_UWSGI_SOCKET}"
  540. mkdir -p "$(dirname ${SEARXNG_UWSGI_SOCKET})"
  541. chown -R "${SERVICE_USER}:${SERVICE_GROUP}" "$(dirname ${SEARXNG_UWSGI_SOCKET})"
  542. case $DIST_ID-$DIST_VERS in
  543. fedora-*)
  544. # Fedora runs uWSGI in emperor-tyrant mode: in Tyrant mode the
  545. # Emperor will run the vassal using the UID/GID of the vassal
  546. # configuration file [1] (user and group of the app .ini file).
  547. # [1] https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html#tyrant-mode-secure-multi-user-hosting
  548. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}" "${SERVICE_USER}" "${SERVICE_GROUP}"
  549. ;;
  550. *)
  551. uWSGI_install_app --variant=socket "${SEARXNG_UWSGI_APP}"
  552. ;;
  553. esac
  554. sleep 5
  555. if ! searxng.uwsgi.available; then
  556. err_msg "uWSGI socket not available at: ${SEARXNG_UWSGI_SOCKET}"
  557. fi
  558. }
  559. searxng.uwsgi.available() {
  560. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  561. [[ -S "${SEARXNG_UWSGI_SOCKET}" ]]
  562. exit_val=$?
  563. if [[ $exit_val = 0 ]]; then
  564. info_msg "uWSGI socket is located at: ${SEARXNG_UWSGI_SOCKET}"
  565. fi
  566. else
  567. service_is_available "http://${SEARXNG_INTERNAL_HTTP}"
  568. exit_val=$?
  569. fi
  570. return "$exit_val"
  571. }
  572. searxng.remove.uwsgi() {
  573. rst_title "Remove SearXNG's uWSGI app (${SEARXNG_UWSGI_APP})" section
  574. echo
  575. uWSGI_remove_app "${SEARXNG_UWSGI_APP}"
  576. }
  577. searxng.install.redis() {
  578. rst_title "SearXNG (install redis)"
  579. redis.build
  580. redis.install
  581. redis.addgrp "${SERVICE_USER}"
  582. }
  583. searxng.remove.redis() {
  584. rst_title "SearXNG (remove redis)"
  585. redis.rmgrp "${SERVICE_USER}"
  586. redis.remove
  587. }
  588. searxng.instance.localtest() {
  589. rst_title "Test SearXNG instance locally" section
  590. rst_para "Activate debug mode, start a minimal SearXNG "\
  591. "service and debug a HTTP request/response cycle."
  592. if service_is_available "http://${SEARXNG_INTERNAL_HTTP}" &>/dev/null; then
  593. err_msg "URL/port http://${SEARXNG_INTERNAL_HTTP} is already in use, you"
  594. err_msg "should stop that service before starting local tests!"
  595. if ! ask_yn "Continue with local tests?"; then
  596. return
  597. fi
  598. fi
  599. echo
  600. searxng.instance.debug.on
  601. tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
  602. export SEARXNG_SETTINGS_PATH="${SEARXNG_SETTINGS_PATH}"
  603. cd ${SEARXNG_SRC}
  604. timeout 10 python searx/webapp.py &
  605. sleep 3
  606. curl --location --verbose --head --insecure ${SEARXNG_INTERNAL_HTTP}
  607. EOF
  608. echo
  609. searxng.instance.debug.off
  610. }
  611. searxng.install.http.pre() {
  612. if ! searxng.uwsgi.available; then
  613. rst_para "\
  614. To install uWSGI use::
  615. $(basename "$0") install uwsgi
  616. "
  617. die 42 "SearXNG's uWSGI app not available"
  618. fi
  619. if ! searxng.instance.exec python -c "from searx.shared import redisdb; redisdb.initialize() or exit(42)"; then
  620. rst_para "\
  621. The configured redis DB is not available: If your server is public to the
  622. internet, you should setup a bot protection to block excessively bot queries.
  623. Bot protection requires a redis DB. About bot protection visit the official
  624. SearXNG documentation and query for the word 'limiter'.
  625. "
  626. fi
  627. }
  628. searxng.apache.install() {
  629. rst_title "Install Apache site ${APACHE_SEARXNG_SITE}"
  630. rst_para "\
  631. This installs SearXNG's uWSGI app as apache site. The apache site is located at:
  632. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}."
  633. searxng.install.http.pre
  634. if ! apache_is_installed; then
  635. err_msg "Apache packages are not installed"
  636. if ! ask_yn "Do you really want to continue and install apache packages?" Yn; then
  637. return
  638. else
  639. FORCE_SELECTION=Y install_apache
  640. fi
  641. else
  642. info_msg "Apache packages are installed [OK]"
  643. fi
  644. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  645. apache_install_site --variant=socket "${APACHE_SEARXNG_SITE}"
  646. else
  647. apache_install_site "${APACHE_SEARXNG_SITE}"
  648. fi
  649. if ! service_is_available "${SEARXNG_URL}"; then
  650. err_msg "Public service at ${SEARXNG_URL} is not available!"
  651. fi
  652. }
  653. searxng.apache.remove() {
  654. rst_title "Remove Apache site ${APACHE_SEARXNG_SITE}"
  655. rst_para "\
  656. This removes apache site ${APACHE_SEARXNG_SITE}::
  657. ${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}"
  658. ! apache_is_installed && err_msg "Apache is not installed."
  659. if ! ask_yn "Do you really want to continue?" Yn; then
  660. return
  661. fi
  662. apache_remove_site "${APACHE_SEARXNG_SITE}"
  663. }
  664. searxng.nginx.install() {
  665. rst_title "Install nginx site ${NGINX_SEARXNG_SITE}"
  666. rst_para "\
  667. This installs SearXNG's uWSGI app as Nginx site. The Nginx site is located at:
  668. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE} and requires a uWSGI."
  669. searxng.install.http.pre
  670. if ! nginx_is_installed ; then
  671. err_msg "Nginx packages are not installed"
  672. if ! ask_yn "Do you really want to continue and install Nginx packages?" Yn; then
  673. return
  674. else
  675. FORCE_SELECTION=Y install_nginx
  676. fi
  677. else
  678. info_msg "Nginx packages are installed [OK]"
  679. fi
  680. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  681. nginx_install_app --variant=socket "${NGINX_SEARXNG_SITE}"
  682. else
  683. nginx_install_app "${NGINX_SEARXNG_SITE}"
  684. fi
  685. if ! service_is_available "${SEARXNG_URL}"; then
  686. err_msg "Public service at ${SEARXNG_URL} is not available!"
  687. fi
  688. }
  689. searxng.nginx.remove() {
  690. rst_title "Remove Nginx site ${NGINX_SEARXNG_SITE}"
  691. rst_para "\
  692. This removes Nginx site ${NGINX_SEARXNG_SITE}::
  693. ${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}"
  694. ! nginx_is_installed && err_msg "Nginx is not installed."
  695. if ! ask_yn "Do you really want to continue?" Yn; then
  696. return
  697. fi
  698. nginx_remove_app "${NGINX_SEARXNG_SITE}"
  699. }
  700. searxng.instance.exec() {
  701. if ! service_account_is_available "${SERVICE_USER}"; then
  702. die 42 "can't execute: instance does not exist (missed account ${SERVICE_USER})"
  703. fi
  704. sudo -H -i -u "${SERVICE_USER}" \
  705. SEARXNG_UWSGI_USE_SOCKET="${SEARXNG_UWSGI_USE_SOCKET}" \
  706. "$@"
  707. }
  708. searxng.instance.self.call() {
  709. # wrapper to call a function in instance's environment
  710. info_msg "wrapper: utils/searxng.sh instance _call $*"
  711. searxng.instance.exec "${SEARXNG_SRC}/utils/searxng.sh" instance _call "$@"
  712. }
  713. searxng.instance.get_setting() {
  714. searxng.instance.exec python <<EOF
  715. from searx import get_setting
  716. print(get_setting('$1'))
  717. EOF
  718. }
  719. searxng.instance.debug.on() {
  720. warn_msg "Do not enable debug in a production environment!"
  721. info_msg "try to enable debug mode ..."
  722. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  723. cd ${SEARXNG_SRC}
  724. sed -i -e "s/debug: false/debug: true/g" "$SEARXNG_SETTINGS_PATH"
  725. EOF
  726. uWSGI_restart "$SEARXNG_UWSGI_APP"
  727. }
  728. searxng.instance.debug.off() {
  729. info_msg "try to disable debug mode ..."
  730. tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 | prefix_stdout "$_service_prefix"
  731. cd ${SEARXNG_SRC}
  732. sed -i -e "s/debug: true/debug: false/g" "$SEARXNG_SETTINGS_PATH"
  733. EOF
  734. uWSGI_restart "$SEARXNG_UWSGI_APP"
  735. }
  736. searxng.instance.inspect() {
  737. rst_title "Inspect SearXNG instance"
  738. echo
  739. searxng.instance.self.call _searxng.instance.inspect
  740. local _debug_on
  741. if ask_yn "Enable SearXNG debug mode?"; then
  742. searxng.instance.debug.on
  743. _debug_on=1
  744. fi
  745. echo
  746. case $DIST_ID-$DIST_VERS in
  747. ubuntu-*|debian-*)
  748. # For uWSGI debian uses the LSB init process; for each configuration
  749. # file new uWSGI daemon instance is started with additional option.
  750. service uwsgi status "${SERVICE_NAME}"
  751. ;;
  752. arch-*)
  753. systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
  754. ;;
  755. fedora-*)
  756. systemctl --no-pager -l status uwsgi
  757. ;;
  758. esac
  759. echo -e "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  760. read -r -s -n1 -t 5
  761. echo
  762. while true; do
  763. trap break 2
  764. case $DIST_ID-$DIST_VERS in
  765. ubuntu-*|debian-*) tail -f "/var/log/uwsgi/app/${SERVICE_NAME%.*}.log" ;;
  766. arch-*) journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
  767. fedora-*) journalctl -f -u uwsgi ;;
  768. esac
  769. done
  770. if [[ $_debug_on == 1 ]]; then
  771. searxng.instance.debug.off
  772. fi
  773. return 0
  774. }
  775. _searxng.instance.inspect() {
  776. searxng.instance.env
  777. if in_container; then
  778. # shellcheck source=utils/lxc-searxng.env
  779. source "${REPO_ROOT}/utils/lxc-searxng.env"
  780. lxc_suite_info
  781. fi
  782. MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue${_creset}"
  783. if ! searxng.uwsgi.available; then
  784. err_msg "SearXNG's uWSGI app not available"
  785. wait_key
  786. fi
  787. if ! service_is_available "${SEARXNG_URL}"; then
  788. err_msg "Public service at ${SEARXNG_URL} is not available!"
  789. wait_key
  790. fi
  791. }
  792. searxng.doc.rst() {
  793. local debian="${SEARXNG_PACKAGES_debian}"
  794. local arch="${SEARXNG_PACKAGES_arch}"
  795. local fedora="${SEARXNG_PACKAGES_fedora}"
  796. local debian_build="${SEARXNG_BUILD_PACKAGES_debian}"
  797. local arch_build="${SEARXNG_BUILD_PACKAGES_arch}"
  798. local fedora_build="${SEARXNG_BUILD_PACKAGES_fedora}"
  799. debian="$(echo "${debian}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  800. arch="$(echo "${arch}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  801. fedora="$(echo "${fedora}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  802. debian_build="$(echo "${debian_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  803. arch_build="$(echo "${arch_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  804. fedora_build="$(echo "${fedora_build}" | sed 's/.*/ & \\/' | sed '$ s/.$//')"
  805. if [[ ${SEARXNG_UWSGI_USE_SOCKET} == true ]]; then
  806. uwsgi_variant=':socket'
  807. else
  808. uwsgi_variant=':socket'
  809. fi
  810. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searxng.rst")\""
  811. # I use ubuntu-20.04 here to demonstrate that versions are also supported,
  812. # normally debian-* and ubuntu-* are most the same.
  813. for DIST_NAME in ubuntu-20.04 arch fedora; do
  814. (
  815. DIST_ID=${DIST_NAME%-*}
  816. DIST_VERS=${DIST_NAME#*-}
  817. [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  818. uWSGI_distro_setup
  819. echo -e "\n.. START searxng uwsgi-description $DIST_NAME"
  820. case $DIST_ID-$DIST_VERS in
  821. ubuntu-*|debian-*) cat <<EOF
  822. .. code:: bash
  823. # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
  824. # For uWSGI debian uses the LSB init process, this might be changed
  825. # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
  826. create ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}
  827. enable: sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
  828. start: sudo -H service uwsgi start ${SEARXNG_UWSGI_APP%.*}
  829. restart: sudo -H service uwsgi restart ${SEARXNG_UWSGI_APP%.*}
  830. stop: sudo -H service uwsgi stop ${SEARXNG_UWSGI_APP%.*}
  831. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  832. EOF
  833. ;;
  834. arch-*) cat <<EOF
  835. .. code:: bash
  836. # systemd --> /usr/lib/systemd/system/uwsgi@.service
  837. # For uWSGI archlinux uses systemd template units, see
  838. # - http://0pointer.de/blog/projects/instances.html
  839. # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
  840. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  841. enable: sudo -H systemctl enable uwsgi@${SEARXNG_UWSGI_APP%.*}
  842. start: sudo -H systemctl start uwsgi@${SEARXNG_UWSGI_APP%.*}
  843. restart: sudo -H systemctl restart uwsgi@${SEARXNG_UWSGI_APP%.*}
  844. stop: sudo -H systemctl stop uwsgi@${SEARXNG_UWSGI_APP%.*}
  845. disable: sudo -H systemctl disable uwsgi@${SEARXNG_UWSGI_APP%.*}
  846. EOF
  847. ;;
  848. fedora-*|centos-7) cat <<EOF
  849. .. code:: bash
  850. # systemd --> /usr/lib/systemd/system/uwsgi.service
  851. # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
  852. # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
  853. create: ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  854. restart: sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  855. disable: sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARXNG_UWSGI_APP}
  856. EOF
  857. ;;
  858. esac
  859. echo -e ".. END searxng uwsgi-description $DIST_NAME"
  860. local _show_cursor="" # prevent from prefix_stdout's trailing show-cursor
  861. echo -e "\n.. START searxng uwsgi-appini $DIST_NAME"
  862. echo ".. code:: bash"
  863. echo
  864. eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARXNG_UWSGI_APP}${uwsgi_variant}")\"" | prefix_stdout " "
  865. echo -e "\n.. END searxng uwsgi-appini $DIST_NAME"
  866. echo -e "\n.. START nginx socket"
  867. echo ".. code:: nginx"
  868. echo
  869. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  870. echo -e "\n.. END nginx socket"
  871. echo -e "\n.. START nginx http"
  872. echo ".. code:: nginx"
  873. echo
  874. eval "echo \"$(< "${TEMPLATES}/${NGINX_APPS_AVAILABLE}/${NGINX_SEARXNG_SITE}")\"" | prefix_stdout " "
  875. echo -e "\n.. END nginx http"
  876. echo -e "\n.. START apache socket"
  877. echo ".. code:: apache"
  878. echo
  879. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}:socket")\"" | prefix_stdout " "
  880. echo -e "\n.. END apache socket"
  881. echo -e "\n.. START apache http"
  882. echo ".. code:: apache"
  883. echo
  884. eval "echo \"$(< "${TEMPLATES}/${APACHE_SITES_AVAILABLE}/${APACHE_SEARXNG_SITE}")\"" | prefix_stdout " "
  885. echo -e "\n.. END apache http"
  886. )
  887. done
  888. }
  889. # ----------------------------------------------------------------------------
  890. main "$@"
  891. # ----------------------------------------------------------------------------