searxng.sh 34 KB

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