filtron.sh 16 KB

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