searxng.sh 32 KB

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