filtron.sh 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454
  1. #!/usr/bin/env bash
  2. # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
  3. # SPDX-License-Identifier: AGPL-3.0-or-later
  4. # shellcheck disable=SC2119,SC2001
  5. # shellcheck source=utils/lib.sh
  6. source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
  7. source_dot_config
  8. # ----------------------------------------------------------------------------
  9. # config
  10. # ----------------------------------------------------------------------------
  11. FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
  12. | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
  13. [[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
  14. FILTRON_ETC="/etc/filtron"
  15. FILTRON_RULES="$FILTRON_ETC/rules.json"
  16. FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
  17. FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
  18. FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
  19. SERVICE_NAME="filtron"
  20. SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
  21. SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
  22. SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
  23. SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
  24. # shellcheck disable=SC2034
  25. SERVICE_GROUP="${SERVICE_USER}"
  26. # shellcheck disable=SC2034
  27. SERVICE_GROUP="${SERVICE_USER}"
  28. GO_ENV="${SERVICE_HOME}/.go_env"
  29. GO_PKG_URL="https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
  30. GO_TAR=$(basename "$GO_PKG_URL")
  31. # Apache Settings
  32. APACHE_FILTRON_SITE="searx.conf"
  33. # shellcheck disable=SC2034
  34. CONFIG_FILES=(
  35. "${FILTRON_RULES}"
  36. "${SERVICE_SYSTEMD_UNIT}"
  37. )
  38. # ----------------------------------------------------------------------------
  39. usage() {
  40. # ----------------------------------------------------------------------------
  41. # shellcheck disable=SC1117
  42. cat <<EOF
  43. usage::
  44. $(basename "$0") shell
  45. $(basename "$0") install [all|user|rules]
  46. $(basename "$0") update [filtron]
  47. $(basename "$0") remove [all]
  48. $(basename "$0") activate [service]
  49. $(basename "$0") deactivate [service]
  50. $(basename "$0") inspect [service]
  51. $(basename "$0") option [debug-on|debug-off]
  52. $(basename "$0") apache [install|remove]
  53. shell
  54. start interactive shell from user ${SERVICE_USER}
  55. install / remove
  56. :all: complete setup of filtron service
  57. :user: add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
  58. :rules: reinstall filtron rules $FILTRON_RULES
  59. update filtron
  60. Update filtron installation ($SERVICE_HOME)
  61. activate service
  62. activate and start service daemon (systemd unit)
  63. deactivate service
  64. stop and deactivate service daemon (systemd unit)
  65. inspect service
  66. show service status and log
  67. option
  68. set one of the available options
  69. apache : ${PUBLIC_URL}
  70. :install: apache site with a reverse proxy (ProxyPass)
  71. :remove: apache site ${APACHE_FILTRON_SITE}
  72. filtron rules: ${FILTRON_RULES}
  73. If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
  74. PUBLIC_URL : ${PUBLIC_URL}
  75. PUBLIC_HOST : ${PUBLIC_HOST}
  76. SERVICE_USER : ${SERVICE_USER}
  77. FILTRON_API : ${FILTRON_API}
  78. FILTRON_LISTEN : ${FILTRON_LISTEN}
  79. FILTRON_TARGET : ${FILTRON_TARGET}
  80. EOF
  81. [[ -n ${1} ]] && err_msg "$1"
  82. }
  83. main() {
  84. rst_title "$SERVICE_NAME" part
  85. required_commands \
  86. sudo install git wget curl \
  87. || exit
  88. local _usage="unknown or missing $1 command $2"
  89. case $1 in
  90. --source-only) ;;
  91. -h|--help) usage; exit 0;;
  92. shell)
  93. sudo_or_exit
  94. interactive_shell "${SERVICE_USER}"
  95. ;;
  96. inspect)
  97. case $2 in
  98. service)
  99. sudo_or_exit
  100. inspect_service
  101. ;;
  102. *) usage "$_usage"; exit 42;;
  103. esac ;;
  104. install)
  105. sudo_or_exit
  106. case $2 in
  107. all) install_all ;;
  108. user) assert_user ;;
  109. rules)
  110. rst_title "Re-Install filtron rules"
  111. echo
  112. install_template --no-eval "$FILTRON_RULES" root root 644
  113. systemd_restart_service "${SERVICE_NAME}"
  114. ;;
  115. *) usage "$_usage"; exit 42;;
  116. esac ;;
  117. update)
  118. sudo_or_exit
  119. case $2 in
  120. filtron) update_filtron ;;
  121. *) usage "$_usage"; exit 42;;
  122. esac ;;
  123. remove)
  124. sudo_or_exit
  125. case $2 in
  126. all) remove_all;;
  127. user) drop_service_account "${SERVICE_USER}" ;;
  128. *) usage "$_usage"; exit 42;;
  129. esac ;;
  130. activate)
  131. sudo_or_exit
  132. case $2 in
  133. service) systemd_activate_service "${SERVICE_NAME}" ;;
  134. *) usage "$_usage"; exit 42;;
  135. esac ;;
  136. deactivate)
  137. sudo_or_exit
  138. case $2 in
  139. service) systemd_deactivate_service "${SERVICE_NAME}" ;;
  140. *) usage "$_usage"; exit 42;;
  141. esac ;;
  142. apache)
  143. sudo_or_exit
  144. case $2 in
  145. install) install_apache_site ;;
  146. remove) remove_apache_site ;;
  147. *) usage "$_usage"; exit 42;;
  148. esac ;;
  149. option)
  150. sudo_or_exit
  151. case $2 in
  152. debug-on) echo; enable_debug ;;
  153. debug-off) echo; disable_debug ;;
  154. *) usage "$_usage"; exit 42;;
  155. esac ;;
  156. *) usage "unknown or missing command $1"; exit 42;;
  157. esac
  158. }
  159. install_all() {
  160. rst_title "Install $SERVICE_NAME (service)"
  161. assert_user
  162. wait_key
  163. install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
  164. wait_key
  165. install_filtron
  166. wait_key
  167. systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
  168. wait_key
  169. echo
  170. if ! service_is_available "http://${FILTRON_LISTEN}" ; then
  171. err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
  172. fi
  173. if apache_is_installed; then
  174. info_msg "Apache is installed on this host."
  175. if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
  176. install_apache_site
  177. fi
  178. fi
  179. if ask_yn "Do you want to inspect the installation?" Ny; then
  180. inspect_service
  181. fi
  182. }
  183. remove_all() {
  184. rst_title "De-Install $SERVICE_NAME (service)"
  185. rst_para "\
  186. It goes without saying that this script can only be used to remove
  187. installations that were installed with this script."
  188. if ! systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
  189. return 42
  190. fi
  191. drop_service_account "${SERVICE_USER}"
  192. rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
  193. if service_is_available "${PUBLIC_URL}"; then
  194. MSG="** Don't forget to remove your public site! (${PUBLIC_URL}) **" wait_key 10
  195. fi
  196. }
  197. assert_user() {
  198. rst_title "user $SERVICE_USER" section
  199. echo
  200. tee_stderr 1 <<EOF | bash | prefix_stdout
  201. useradd --shell /bin/bash --system \
  202. --home-dir "$SERVICE_HOME" \
  203. --comment 'Reverse HTTP proxy to filter requests' $SERVICE_USER
  204. mkdir "$SERVICE_HOME"
  205. chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
  206. groups $SERVICE_USER
  207. EOF
  208. SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
  209. export SERVICE_HOME
  210. echo "export SERVICE_HOME=$SERVICE_HOME"
  211. cat > "$GO_ENV" <<EOF
  212. export GOPATH=\$HOME/go-apps
  213. export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
  214. EOF
  215. echo "Environment $GO_ENV has been setup."
  216. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
  217. grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
  218. EOF
  219. }
  220. filtron_is_installed() {
  221. [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
  222. }
  223. _svcpr=" |${SERVICE_USER}| "
  224. install_filtron() {
  225. rst_title "Install filtron in user's ~/go-apps" section
  226. echo
  227. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  228. go get -v -u github.com/asciimoo/filtron
  229. EOF
  230. install_template --no-eval "$FILTRON_RULES" root root 644
  231. }
  232. update_filtron() {
  233. rst_title "Update filtron" section
  234. echo
  235. tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
  236. go get -v -u github.com/asciimoo/filtron
  237. EOF
  238. }
  239. inspect_service() {
  240. rst_title "service status & log"
  241. cat <<EOF
  242. sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
  243. PUBLIC_URL : ${PUBLIC_URL}
  244. PUBLIC_HOST : ${PUBLIC_HOST}
  245. FILTRON_URL_PATH : ${FILTRON_URL_PATH}
  246. FILTRON_API : ${FILTRON_API}
  247. FILTRON_LISTEN : ${FILTRON_LISTEN}
  248. FILTRON_TARGET : ${FILTRON_TARGET}
  249. EOF
  250. apache_is_installed && info_msg "Apache is installed."
  251. if service_account_is_available "$SERVICE_USER"; then
  252. info_msg "service account $SERVICE_USER available."
  253. else
  254. err_msg "service account $SERVICE_USER not available!"
  255. fi
  256. if go_is_available "$SERVICE_USER"; then
  257. info_msg "~$SERVICE_USER: go is installed"
  258. else
  259. err_msg "~$SERVICE_USER: go is not installed"
  260. fi
  261. if filtron_is_installed; then
  262. info_msg "~$SERVICE_USER: filtron app is installed"
  263. else
  264. err_msg "~$SERVICE_USER: filtron app is not installed!"
  265. fi
  266. if ! service_is_available "http://${FILTRON_API}"; then
  267. err_msg "API not available at: http://${FILTRON_API}"
  268. fi
  269. if ! service_is_available "http://${FILTRON_LISTEN}" ; then
  270. err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
  271. fi
  272. if service_is_available "http://${FILTRON_TARGET}" ; then
  273. info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
  274. fi
  275. if ! service_is_available "${PUBLIC_URL}"; then
  276. err_msg "Public service at ${PUBLIC_URL} is not available!"
  277. wait_key
  278. fi
  279. local _debug_on
  280. if ask_yn "Enable filtron debug mode?"; then
  281. enable_debug
  282. _debug_on=1
  283. fi
  284. echo
  285. systemctl --no-pager -l status "${SERVICE_NAME}"
  286. echo
  287. info_msg "public URL --> ${PUBLIC_URL}"
  288. # shellcheck disable=SC2059
  289. printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
  290. read -r -s -n1 -t 2
  291. echo
  292. while true; do
  293. trap break 2
  294. journalctl -f -u "${SERVICE_NAME}"
  295. done
  296. if [[ $_debug_on == 1 ]]; then
  297. disable_debug
  298. fi
  299. return 0
  300. }
  301. enable_debug() {
  302. info_msg "try to enable debug mode ..."
  303. python <<EOF
  304. import sys, json
  305. debug = {
  306. u'name': u'debug request'
  307. , u'filters': []
  308. , u'interval': 0
  309. , u'limit': 0
  310. , u'actions': [{u'name': u'log'}]
  311. }
  312. with open('$FILTRON_RULES') as rules:
  313. j = json.load(rules)
  314. pos = None
  315. for i in range(len(j)):
  316. if j[i].get('name') == 'debug request':
  317. pos = i
  318. break
  319. if pos is not None:
  320. j[pos] = debug
  321. else:
  322. j.append(debug)
  323. with open('$FILTRON_RULES', 'w') as rules:
  324. json.dump(j, rules, indent=2, sort_keys=True)
  325. EOF
  326. systemctl restart "${SERVICE_NAME}.service"
  327. }
  328. disable_debug() {
  329. info_msg "try to disable debug mode ..."
  330. python <<EOF
  331. import sys, json
  332. with open('$FILTRON_RULES') as rules:
  333. j = json.load(rules)
  334. pos = None
  335. for i in range(len(j)):
  336. if j[i].get('name') == 'debug request':
  337. pos = i
  338. break
  339. if pos is not None:
  340. del j[pos]
  341. with open('$FILTRON_RULES', 'w') as rules:
  342. json.dump(j, rules, indent=2, sort_keys=True)
  343. EOF
  344. systemctl restart "${SERVICE_NAME}.service"
  345. }
  346. install_apache_site() {
  347. rst_title "Install Apache site $APACHE_FILTRON_SITE"
  348. rst_para "\
  349. This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
  350. ! apache_is_installed && err_msg "Apache is not installed."
  351. if ! ask_yn "Do you really want to continue?"; then
  352. return
  353. fi
  354. a2enmod headers
  355. a2enmod proxy
  356. a2enmod proxy_http
  357. echo
  358. apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
  359. info_msg "testing public url .."
  360. if ! service_is_available "${PUBLIC_URL}"; then
  361. err_msg "Public service at ${PUBLIC_URL} is not available!"
  362. fi
  363. }
  364. remove_apache_site() {
  365. rst_title "Remove Apache site $APACHE_FILTRON_SITE"
  366. rst_para "\
  367. This removes apache site ${APACHE_FILTRON_SITE}."
  368. ! apache_is_installed && err_msg "Apache is not installed."
  369. if ! ask_yn "Do you really want to continue?"; then
  370. return
  371. fi
  372. apache_remove_site "$APACHE_FILTRON_SITE"
  373. }
  374. # ----------------------------------------------------------------------------
  375. main "$@"
  376. # ----------------------------------------------------------------------------