filtron.sh 12 KB

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