searxng.sh 34 KB

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