filtron.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601
  1. #!/usr/bin/env bash
  2. # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. # shellcheck disable=SC2119,SC2001
  5. # shellcheck source=utils/lib.sh
  6. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  7. # shellcheck source=utils/brand.env
  8. source "${REPO_ROOT}/utils/brand.env"
  9. source_dot_config
  10. source "${REPO_ROOT}/utils/lxc-searx.env"
  11. in_container && lxc_set_suite_env
  12. # ----------------------------------------------------------------------------
  13. # config
  14. # ----------------------------------------------------------------------------
  15. PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
  16. PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
  17. FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
  18. | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
  19. [[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
  20. FILTRON_ETC="/etc/filtron"
  21. FILTRON_RULES="$FILTRON_ETC/rules.json"
  22. FILTRON_RULES_TEMPLATE="${FILTRON_RULES_TEMPLATE:-${REPO_ROOT}/utils/templates/etc/filtron/rules.json}"
  23. FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
  24. FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
  25. FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
  26. SERVICE_NAME="filtron"
  27. SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
  28. SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
  29. SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
  30. SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
  31. # shellcheck disable=SC2034
  32. SERVICE_GROUP="${SERVICE_USER}"
  33. # shellcheck disable=SC2034
  34. SERVICE_GROUP="${SERVICE_USER}"
  35. GO_ENV="${SERVICE_HOME}/.go_env"
  36. GO_PKG_URL="https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
  37. GO_TAR=$(basename "$GO_PKG_URL")
  38. APACHE_FILTRON_SITE="searx.conf"
  39. NGINX_FILTRON_SITE="searx.conf"
  40. # shellcheck disable=SC2034
  41. CONFIG_FILES=(
  42. "${FILTRON_RULES}"
  43. "${SERVICE_SYSTEMD_UNIT}"
  44. )
  45. # ----------------------------------------------------------------------------
  46. usage() {
  47. # ----------------------------------------------------------------------------
  48. # shellcheck disable=SC1117
  49. cat <<EOF
  50. usage::
  51. $(basename "$0") shell
  52. $(basename "$0") install [all|user|rules]
  53. $(basename "$0") update [filtron]
  54. $(basename "$0") remove [all]
  55. $(basename "$0") activate [service]
  56. $(basename "$0") deactivate [service]
  57. $(basename "$0") inspect [service]
  58. $(basename "$0") option [debug-on|debug-off]
  59. $(basename "$0") apache [install|remove]
  60. $(basename "$0") nginx [install|remove]
  61. shell
  62. start interactive shell from user ${SERVICE_USER}
  63. install / remove
  64. :all: complete setup of filtron service
  65. :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
  66. :rules: reinstall filtron rules $FILTRON_RULES
  67. update filtron
  68. Update filtron installation ($SERVICE_HOME)
  69. activate service
  70. activate and start service daemon (systemd unit)
  71. deactivate service
  72. stop and deactivate service daemon (systemd unit)
  73. inspect service
  74. show service status and log
  75. option
  76. set one of the available options
  77. apache (${PUBLIC_URL})
  78. :install: apache site with a reverse proxy (ProxyPass)
  79. :remove: apache site ${APACHE_FILTRON_SITE}
  80. nginx (${PUBLIC_URL})
  81. :install: nginx site with a reverse proxy (ProxyPass)
  82. :remove: nginx site ${NGINX_FILTRON_SITE}
  83. filtron rules: ${FILTRON_RULES_TEMPLATE}
  84. If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
  85. PUBLIC_URL : ${PUBLIC_URL}
  86. PUBLIC_HOST : ${PUBLIC_HOST}
  87. SERVICE_USER : ${SERVICE_USER}
  88. FILTRON_TARGET : ${FILTRON_TARGET}
  89. FILTRON_API : ${FILTRON_API}
  90. FILTRON_LISTEN : ${FILTRON_LISTEN}
  91. EOF
  92. if in_container; then
  93. # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
  94. for ip in $(global_IPs) ; do
  95. if [[ $ip =~ .*:.* ]]; then
  96. echo " container URL (IPv6): http://[${ip#*|}]:4005/"
  97. else
  98. # IPv4:
  99. echo " container URL (IPv4): http://${ip#*|}:4005/"
  100. fi
  101. done
  102. fi
  103. [[ -n ${1} ]] && err_msg "$1"
  104. }
  105. main() {
  106. required_commands \
  107. sudo install git wget curl \
  108. || exit
  109. local _usage="unknown or missing $1 command $2"
  110. case $1 in
  111. --getenv) var="$2"; echo "${!var}"; exit 0;;
  112. -h|--help) usage; exit 0;;
  113. shell)
  114. sudo_or_exit
  115. interactive_shell "${SERVICE_USER}"
  116. ;;
  117. inspect)
  118. case $2 in
  119. service)
  120. sudo_or_exit
  121. inspect_service
  122. ;;
  123. *) usage "$_usage"; exit 42;;
  124. esac ;;
  125. install)
  126. rst_title "$SERVICE_NAME" part
  127. sudo_or_exit
  128. case $2 in
  129. all) install_all ;;
  130. user) assert_user ;;
  131. rules)
  132. install_rules
  133. systemd_restart_service "${SERVICE_NAME}"
  134. ;;
  135. *) usage "$_usage"; exit 42;;
  136. esac ;;
  137. update)
  138. sudo_or_exit
  139. case $2 in
  140. filtron) update_filtron ;;
  141. *) usage "$_usage"; exit 42;;
  142. esac ;;
  143. remove)
  144. sudo_or_exit
  145. case $2 in
  146. all) remove_all;;
  147. user) drop_service_account "${SERVICE_USER}" ;;
  148. *) usage "$_usage"; exit 42;;
  149. esac ;;
  150. activate)
  151. sudo_or_exit
  152. case $2 in
  153. service) systemd_activate_service "${SERVICE_NAME}" ;;
  154. *) usage "$_usage"; exit 42;;
  155. esac ;;
  156. deactivate)
  157. sudo_or_exit
  158. case $2 in
  159. service) systemd_deactivate_service "${SERVICE_NAME}" ;;
  160. *) usage "$_usage"; exit 42;;
  161. esac ;;
  162. apache)
  163. sudo_or_exit
  164. case $2 in
  165. install) install_apache_site ;;
  166. remove) remove_apache_site ;;
  167. *) usage "$_usage"; exit 42;;
  168. esac ;;
  169. nginx)
  170. sudo_or_exit
  171. case $2 in
  172. install) install_nginx_site ;;
  173. remove) remove_nginx_site ;;
  174. *) usage "$_usage"; exit 42;;
  175. esac ;;
  176. option)
  177. sudo_or_exit
  178. case $2 in
  179. debug-on) echo; enable_debug ;;
  180. debug-off) echo; disable_debug ;;
  181. *) usage "$_usage"; exit 42;;
  182. esac ;;
  183. doc) rst-doc ;;
  184. *) usage "unknown or missing command $1"; exit 42;;
  185. esac
  186. }
  187. install_all() {
  188. rst_title "Install $SERVICE_NAME (service)"
  189. assert_user
  190. wait_key
  191. install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
  192. wait_key
  193. install_filtron
  194. install_rules
  195. wait_key
  196. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  197. wait_key
  198. echo
  199. if ! service_is_available "http://${FILTRON_LISTEN}" ; then
  200. err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
  201. fi
  202. if apache_is_installed; then
  203. info_msg "Apache is installed on this host."
  204. if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
  205. install_apache_site
  206. fi
  207. elif nginx_is_installed; then
  208. info_msg "nginx is installed on this host."
  209. if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
  210. install_nginx_site
  211. fi
  212. fi
  213. if ask_yn "Do you want to inspect the installation?" Ny; then
  214. inspect_service
  215. fi
  216. }
  217. remove_all() {
  218. rst_title "De-Install $SERVICE_NAME (service)"
  219. rst_para "\
  220. It goes without saying that this script can only be used to remove
  221. installations that were installed with this script."
  222. if ! systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  223. return 42
  224. fi
  225. drop_service_account "${SERVICE_USER}"
  226. rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
  227. if service_is_available "${PUBLIC_URL}"; then
  228. MSG="** Don't forget to remove your public site! (${PUBLIC_URL}) **" wait_key 10
  229. fi
  230. }
  231. assert_user() {
  232. rst_title "user $SERVICE_USER" section
  233. echo
  234. tee_stderr 1 <<EOF | bash | prefix_stdout
  235. useradd --shell /bin/bash --system \
  236. --home-dir "$SERVICE_HOME" \
  237. --comment 'Reverse HTTP proxy to filter requests' $SERVICE_USER
  238. mkdir "$SERVICE_HOME"
  239. chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
  240. groups $SERVICE_USER
  241. EOF
  242. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  243. export SERVICE_HOME
  244. echo "export SERVICE_HOME=$SERVICE_HOME"
  245. cat > "$GO_ENV" <<EOF
  246. export GOPATH=\$HOME/go-apps
  247. export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
  248. EOF
  249. echo "Environment $GO_ENV has been setup."
  250. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
  251. grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
  252. EOF
  253. }
  254. filtron_is_installed() {
  255. [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
  256. }
  257. _svcpr=" ${_Yellow}|${SERVICE_USER}|${_creset} "
  258. install_filtron() {
  259. rst_title "Install filtron in user's ~/go-apps" section
  260. echo
  261. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  262. go get -v -u github.com/asciimoo/filtron
  263. EOF
  264. }
  265. update_filtron() {
  266. rst_title "Update filtron" section
  267. echo
  268. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  269. go get -v -u github.com/asciimoo/filtron
  270. EOF
  271. }
  272. install_rules() {
  273. rst_title "Install filtron rules"
  274. echo
  275. if [[ ! -f "${FILTRON_RULES}" ]]; then
  276. info_msg "install rules ${FILTRON_RULES_TEMPLATE}"
  277. info_msg " --> ${FILTRON_RULES}"
  278. mkdir -p "$(dirname "${FILTRON_RULES}")"
  279. cp "${FILTRON_RULES_TEMPLATE}" "${FILTRON_RULES}"
  280. return
  281. fi
  282. rst_para "Diff between origin's rules file (+) and current (-):"
  283. echo "${FILTRON_RULES}" "${FILTRON_RULES_TEMPLATE}"
  284. $DIFF_CMD "${FILTRON_RULES}" "${FILTRON_RULES_TEMPLATE}"
  285. local action
  286. choose_one action "What should happen to the rules file? " \
  287. "keep configuration unchanged" \
  288. "use origin rules" \
  289. "start interactiv shell"
  290. case $action in
  291. "keep configuration unchanged")
  292. info_msg "leave rules file unchanged"
  293. ;;
  294. "use origin rules")
  295. backup_file "${FILTRON_RULES}"
  296. info_msg "install origin rules"
  297. cp "${FILTRON_RULES_TEMPLATE}" "${FILTRON_RULES}"
  298. ;;
  299. "start interactiv shell")
  300. backup_file "${FILTRON_RULES}"
  301. echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
  302. sudo -H -i
  303. rst_para 'Diff between new rules file (-) and current (+):'
  304. echo
  305. $DIFF_CMD "${FILTRON_RULES_TEMPLATE}" "${FILTRON_RULES}"
  306. wait_key
  307. ;;
  308. esac
  309. }
  310. inspect_service() {
  311. rst_title "service status & log"
  312. cat <<EOF
  313. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  314. PUBLIC_URL : ${PUBLIC_URL}
  315. PUBLIC_HOST : ${PUBLIC_HOST}
  316. FILTRON_URL_PATH : ${FILTRON_URL_PATH}
  317. FILTRON_API : ${FILTRON_API}
  318. FILTRON_LISTEN : ${FILTRON_LISTEN}
  319. FILTRON_TARGET : ${FILTRON_TARGET}
  320. EOF
  321. if service_account_is_available "$SERVICE_USER"; then
  322. info_msg "service account $SERVICE_USER available."
  323. else
  324. err_msg "service account $SERVICE_USER not available!"
  325. fi
  326. if go_is_available "$SERVICE_USER"; then
  327. info_msg "~$SERVICE_USER: go is installed"
  328. else
  329. err_msg "~$SERVICE_USER: go is not installed"
  330. fi
  331. if filtron_is_installed; then
  332. info_msg "~$SERVICE_USER: filtron app is installed"
  333. else
  334. err_msg "~$SERVICE_USER: filtron app is not installed!"
  335. fi
  336. if ! service_is_available "http://${FILTRON_API}"; then
  337. err_msg "API not available at: http://${FILTRON_API}"
  338. fi
  339. if ! service_is_available "http://${FILTRON_LISTEN}" ; then
  340. err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
  341. fi
  342. if service_is_available "http://${FILTRON_TARGET}" ; then
  343. info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
  344. fi
  345. if ! service_is_available "${PUBLIC_URL}"; then
  346. warn_msg "Public service at ${PUBLIC_URL} is not available!"
  347. if ! in_container; then
  348. warn_msg "Check if public name is correct and routed or use the public IP from above."
  349. fi
  350. fi
  351. if in_container; then
  352. lxc_suite_info
  353. else
  354. info_msg "public URL --> ${PUBLIC_URL}"
  355. info_msg "internal URL --> http://${FILTRON_LISTEN}"
  356. fi
  357. local _debug_on
  358. if ask_yn "Enable filtron debug mode?"; then
  359. enable_debug
  360. _debug_on=1
  361. fi
  362. echo
  363. systemctl --no-pager -l status "${SERVICE_NAME}"
  364. echo
  365. info_msg "public URL --> ${PUBLIC_URL}"
  366. # shellcheck disable=SC2059
  367. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  368. read -r -s -n1 -t 5
  369. echo
  370. while true; do
  371. trap break 2
  372. journalctl -f -u "${SERVICE_NAME}"
  373. done
  374. if [[ $_debug_on == 1 ]]; then
  375. disable_debug
  376. fi
  377. return 0
  378. }
  379. enable_debug() {
  380. info_msg "try to enable debug mode ..."
  381. python <<EOF
  382. import sys, json
  383. debug = {
  384. u'name': u'debug request'
  385. , u'filters': []
  386. , u'interval': 0
  387. , u'limit': 0
  388. , u'actions': [{u'name': u'log'}]
  389. }
  390. with open('$FILTRON_RULES') as rules:
  391. j = json.load(rules)
  392. pos = None
  393. for i in range(len(j)):
  394. if j[i].get('name') == 'debug request':
  395. pos = i
  396. break
  397. if pos is not None:
  398. j[pos] = debug
  399. else:
  400. j.append(debug)
  401. with open('$FILTRON_RULES', 'w') as rules:
  402. json.dump(j, rules, indent=2, sort_keys=True)
  403. EOF
  404. systemctl restart "${SERVICE_NAME}.service"
  405. }
  406. disable_debug() {
  407. info_msg "try to disable debug mode ..."
  408. python <<EOF
  409. import sys, json
  410. with open('$FILTRON_RULES') as rules:
  411. j = json.load(rules)
  412. pos = None
  413. for i in range(len(j)):
  414. if j[i].get('name') == 'debug request':
  415. pos = i
  416. break
  417. if pos is not None:
  418. del j[pos]
  419. with open('$FILTRON_RULES', 'w') as rules:
  420. json.dump(j, rules, indent=2, sort_keys=True)
  421. EOF
  422. systemctl restart "${SERVICE_NAME}.service"
  423. }
  424. install_apache_site() {
  425. rst_title "Install Apache site $APACHE_FILTRON_SITE"
  426. rst_para "\
  427. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
  428. ! apache_is_installed && info_msg "Apache is not installed."
  429. if ! ask_yn "Do you really want to continue?" Yn; then
  430. return
  431. else
  432. install_apache
  433. fi
  434. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  435. apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
  436. info_msg "testing public url .."
  437. if ! service_is_available "${PUBLIC_URL}"; then
  438. err_msg "Public service at ${PUBLIC_URL} is not available!"
  439. fi
  440. }
  441. remove_apache_site() {
  442. rst_title "Remove Apache site $APACHE_FILTRON_SITE"
  443. rst_para "\
  444. This removes apache site ${APACHE_FILTRON_SITE}."
  445. ! apache_is_installed && err_msg "Apache is not installed."
  446. if ! ask_yn "Do you really want to continue?" Yn; then
  447. return
  448. fi
  449. apache_remove_site "$APACHE_FILTRON_SITE"
  450. }
  451. install_nginx_site() {
  452. rst_title "Install nginx site $NGINX_FILTRON_SITE"
  453. rst_para "\
  454. This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE})"
  455. ! nginx_is_installed && info_msg "nginx is not installed."
  456. if ! ask_yn "Do you really want to continue?" Yn; then
  457. return
  458. else
  459. install_nginx
  460. fi
  461. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  462. # shellcheck disable=SC2034
  463. SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
  464. # shellcheck disable=SC2034
  465. SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
  466. nginx_install_app --variant=filtron "${NGINX_FILTRON_SITE}"
  467. info_msg "testing public url .."
  468. if ! service_is_available "${PUBLIC_URL}"; then
  469. err_msg "Public service at ${PUBLIC_URL} is not available!"
  470. fi
  471. }
  472. remove_nginx_site() {
  473. rst_title "Remove nginx site $NGINX_FILTRON_SITE"
  474. rst_para "\
  475. This removes nginx site ${NGINX_FILTRON_SITE}."
  476. ! nginx_is_installed && err_msg "nginx is not installed."
  477. if ! ask_yn "Do you really want to continue?" Yn; then
  478. return
  479. fi
  480. nginx_remove_site "$FILTRON_FILTRON_SITE"
  481. }
  482. rst-doc() {
  483. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/filtron.rst")\""
  484. echo -e "\n.. START install systemd unit"
  485. cat <<EOF
  486. .. tabs::
  487. .. group-tab:: systemd
  488. .. code:: bash
  489. EOF
  490. eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
  491. echo -e "\n.. END install systemd unit"
  492. # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
  493. # (
  494. # DIST_ID=${DIST_NAME%-*}
  495. # DIST_VERS=${DIST_NAME#*-}
  496. # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  497. # # ...
  498. # )
  499. # done
  500. }
  501. # ----------------------------------------------------------------------------
  502. main "$@"
  503. # ----------------------------------------------------------------------------