filtron.sh 17 KB

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