searxng.sh 34 KB

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