filtron.sh 17 KB

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