filtron.sh 17 KB

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