searxng.sh 32 KB

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