filtron.sh 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589
  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="${SEARX_BIND_ADDRESS}:${SEARX_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://dl.google.com/go/go1.13.5.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 does 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. rst_para "Diff between origin's rules file (+) and current (-):"
  272. echo "${FILTRON_RULES}" "${FILTRON_RULES_TEMPLATE}"
  273. $DIFF_CMD "${FILTRON_RULES}" "${FILTRON_RULES_TEMPLATE}"
  274. local action
  275. choose_one action "What should happen to the rules file? " \
  276. "keep configuration unchanged" \
  277. "use origin rules" \
  278. "start interactive shell"
  279. case $action in
  280. "keep configuration unchanged")
  281. info_msg "leave rules file unchanged"
  282. ;;
  283. "use origin rules")
  284. backup_file "${FILTRON_RULES}"
  285. info_msg "install origin rules"
  286. cp "${FILTRON_RULES_TEMPLATE}" "${FILTRON_RULES}"
  287. ;;
  288. "start interactive shell")
  289. backup_file "${FILTRON_RULES}"
  290. echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
  291. sudo -H -i
  292. rst_para 'Diff between new rules file (-) and current (+):'
  293. echo
  294. $DIFF_CMD "${FILTRON_RULES_TEMPLATE}" "${FILTRON_RULES}"
  295. wait_key
  296. ;;
  297. esac
  298. }
  299. inspect_service() {
  300. rst_title "service status & log"
  301. cat <<EOF
  302. sourced ${DOT_CONFIG} :
  303. SERVICE_USER : ${SERVICE_USER}
  304. SERVICE_HOME : ${SERVICE_HOME}
  305. FILTRON_TARGET : ${FILTRON_TARGET}
  306. FILTRON_API : ${FILTRON_API}
  307. FILTRON_LISTEN : ${FILTRON_LISTEN}
  308. FILTRON_URL_PATH : ${FILTRON_URL_PATH}
  309. EOF
  310. install_log_searx_instance
  311. if service_account_is_available "$SERVICE_USER"; then
  312. info_msg "service account $SERVICE_USER available."
  313. else
  314. err_msg "service account $SERVICE_USER not available!"
  315. fi
  316. if go_is_available "$SERVICE_USER"; then
  317. info_msg "~$SERVICE_USER: go is installed"
  318. else
  319. err_msg "~$SERVICE_USER: go is not installed"
  320. fi
  321. if filtron_is_installed; then
  322. info_msg "~$SERVICE_USER: filtron app is installed"
  323. else
  324. err_msg "~$SERVICE_USER: filtron app is not installed!"
  325. fi
  326. if ! service_is_available "http://${FILTRON_API}"; then
  327. err_msg "API not available at: http://${FILTRON_API}"
  328. fi
  329. if ! service_is_available "http://${FILTRON_LISTEN}" ; then
  330. err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
  331. fi
  332. if service_is_available "http://${FILTRON_TARGET}" ; then
  333. info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
  334. fi
  335. if ! service_is_available "${PUBLIC_URL}"; then
  336. warn_msg "Public service at ${PUBLIC_URL} is not available!"
  337. if ! in_container; then
  338. warn_msg "Check if public name is correct and routed or use the public IP from above."
  339. fi
  340. fi
  341. if in_container; then
  342. lxc_suite_info
  343. else
  344. info_msg "public URL --> ${PUBLIC_URL}"
  345. info_msg "internal URL --> http://${FILTRON_LISTEN}"
  346. fi
  347. local _debug_on
  348. if ask_yn "Enable filtron debug mode?"; then
  349. enable_debug
  350. _debug_on=1
  351. fi
  352. echo
  353. systemctl --no-pager -l status "${SERVICE_NAME}"
  354. echo
  355. info_msg "public URL --> ${PUBLIC_URL}"
  356. # shellcheck disable=SC2059
  357. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  358. read -r -s -n1 -t 5
  359. echo
  360. while true; do
  361. trap break 2
  362. journalctl -f -u "${SERVICE_NAME}"
  363. done
  364. if [[ $_debug_on == 1 ]]; then
  365. disable_debug
  366. fi
  367. return 0
  368. }
  369. enable_debug() {
  370. info_msg "try to enable debug mode ..."
  371. python <<EOF
  372. import sys, json
  373. debug = {
  374. u'name': u'debug request'
  375. , u'filters': []
  376. , u'interval': 0
  377. , u'limit': 0
  378. , u'actions': [{u'name': u'log'}]
  379. }
  380. with open('$FILTRON_RULES') as rules:
  381. j = json.load(rules)
  382. pos = None
  383. for i in range(len(j)):
  384. if j[i].get('name') == 'debug request':
  385. pos = i
  386. break
  387. if pos is not None:
  388. j[pos] = debug
  389. else:
  390. j.append(debug)
  391. with open('$FILTRON_RULES', 'w') as rules:
  392. json.dump(j, rules, indent=2, sort_keys=True)
  393. EOF
  394. systemctl restart "${SERVICE_NAME}.service"
  395. }
  396. disable_debug() {
  397. info_msg "try to disable debug mode ..."
  398. python <<EOF
  399. import sys, json
  400. with open('$FILTRON_RULES') as rules:
  401. j = json.load(rules)
  402. pos = None
  403. for i in range(len(j)):
  404. if j[i].get('name') == 'debug request':
  405. pos = i
  406. break
  407. if pos is not None:
  408. del j[pos]
  409. with open('$FILTRON_RULES', 'w') as rules:
  410. json.dump(j, rules, indent=2, sort_keys=True)
  411. EOF
  412. systemctl restart "${SERVICE_NAME}.service"
  413. }
  414. install_apache_site() {
  415. rst_title "Install Apache site $APACHE_FILTRON_SITE"
  416. rst_para "\
  417. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
  418. ! apache_is_installed && info_msg "Apache is not installed."
  419. if ! ask_yn "Do you really want to continue?" Yn; then
  420. return
  421. else
  422. install_apache
  423. fi
  424. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  425. apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
  426. info_msg "testing public url .."
  427. if ! service_is_available "${PUBLIC_URL}"; then
  428. err_msg "Public service at ${PUBLIC_URL} is not available!"
  429. fi
  430. }
  431. remove_apache_site() {
  432. rst_title "Remove Apache site $APACHE_FILTRON_SITE"
  433. rst_para "\
  434. This removes apache site ${APACHE_FILTRON_SITE}."
  435. ! apache_is_installed && err_msg "Apache is not installed."
  436. if ! ask_yn "Do you really want to continue?" Yn; then
  437. return
  438. fi
  439. apache_remove_site "$APACHE_FILTRON_SITE"
  440. }
  441. install_nginx_site() {
  442. rst_title "Install nginx site $NGINX_FILTRON_SITE"
  443. rst_para "\
  444. This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE})"
  445. ! nginx_is_installed && info_msg "nginx is not installed."
  446. if ! ask_yn "Do you really want to continue?" Yn; then
  447. return
  448. else
  449. install_nginx
  450. fi
  451. "${REPO_ROOT}/utils/searx.sh" install uwsgi
  452. # shellcheck disable=SC2034
  453. SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
  454. # shellcheck disable=SC2034
  455. SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
  456. nginx_install_app --variant=filtron "${NGINX_FILTRON_SITE}"
  457. info_msg "testing public url .."
  458. if ! service_is_available "${PUBLIC_URL}"; then
  459. err_msg "Public service at ${PUBLIC_URL} is not available!"
  460. fi
  461. }
  462. remove_nginx_site() {
  463. rst_title "Remove nginx site $NGINX_FILTRON_SITE"
  464. rst_para "\
  465. This removes nginx site ${NGINX_FILTRON_SITE}."
  466. ! nginx_is_installed && err_msg "nginx is not installed."
  467. if ! ask_yn "Do you really want to continue?" Yn; then
  468. return
  469. fi
  470. nginx_remove_site "$FILTRON_FILTRON_SITE"
  471. }
  472. rst-doc() {
  473. eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/filtron.rst")\""
  474. echo -e "\n.. START install systemd unit"
  475. cat <<EOF
  476. .. tabs::
  477. .. group-tab:: systemd
  478. .. code:: bash
  479. EOF
  480. eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout " "
  481. echo -e "\n.. END install systemd unit"
  482. # for DIST_NAME in ubuntu-20.04 arch fedora centos; do
  483. # (
  484. # DIST_ID=${DIST_NAME%-*}
  485. # DIST_VERS=${DIST_NAME#*-}
  486. # [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
  487. # # ...
  488. # )
  489. # done
  490. }
  491. # ----------------------------------------------------------------------------
  492. main "$@"
  493. # ----------------------------------------------------------------------------