searxng.sh 34 KB

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