Browse Source

utils/filtron.sh: add script to install filtron middleware

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Markus Heiser 5 years ago
parent
commit
4139c63d23

+ 1 - 0
Makefile

@@ -78,6 +78,7 @@ test: test.pep8 test.unit test.sh test.robot
 
 test.sh:
 	shellcheck -x utils/lib.sh
+	shellcheck -x utils/filtron.sh
 
 test.pep8: pyenvinstall
 	$(PY_ENV_ACT); ./manage.sh pep8_check

+ 214 - 0
utils/filtron.sh

@@ -0,0 +1,214 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh -*-
+# shellcheck disable=SC2119
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+FILTRON_ETC="/etc/filtron"
+
+SERVICE_NAME="filtron"
+SERVICE_USER="${SERVICE_NAME}"
+SERVICE_HOME="/home/${SERVICE_USER}"
+SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
+
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+
+GO_ENV="${SERVICE_HOME}/.go_env"
+GO_PKG_URL="https://dl.google.com/go/go1.13.5.linux-amd64.tar.gz"
+GO_TAR=$(basename "$GO_PKG_URL")
+
+# ----------------------------------------------------------------------------
+usage(){
+# ----------------------------------------------------------------------------
+
+    # shellcheck disable=SC1117
+    cat <<EOF
+
+usage:
+
+  $(basename "$0") shell
+  $(basename "$0") install    [all|user]
+  $(basename "$0") remove     [all]
+  $(basename "$0") activate   [server]
+  $(basename "$0") deactivate [server]
+
+shell        - start interactive shell with user ${SERVICE_USER}
+install user - add service user '$SERVICE_USER' at $SERVICE_HOME
+
+EOF
+    [ ! -z ${1+x} ] &&  echo -e "$1"
+}
+
+main(){
+    rst_title "$SERVICE_NAME" part
+
+    local _usage="ERROR: unknown or missing $1 command $2"
+
+    case $1 in
+	--source-only)  ;;
+        -h|--help) usage ;;
+
+	shell)
+	    sudo_or_exit
+	    interactive_shell
+	    ;;
+        install)
+            sudo_or_exit
+            case $2 in
+                all) install_all ;;
+		user) assert_user ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        remove)
+            sudo_or_exit
+            case $2 in
+                all) remove_all;;
+		user) remove_user ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        activate)
+            sudo_or_exit
+            case $2 in
+                server)  activate_server ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        deactivate)
+            sudo_or_exit
+            case $2 in
+                server)  deactivate_server ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        *) usage "ERROR: unknown or missing command $1"; exit 42;;
+    esac
+}
+
+install_all() {
+    rst_title "Install $SERVICE_NAME (service)"
+    assert_user
+    install_go
+    install_filtron
+    install_server
+}
+
+remove_all() {
+    rst_title "De-Install $SERVICE_NAME (service)"
+    remove_server
+    remove_user
+    rm -rf "$FILTRON_ETC"
+    wait_key
+}
+
+install_server() {
+    rst_title "Install System-D Unit ${SERVICE_NAME}.service ..." section
+    install_template ${SERVICE_SYSTEMD_UNIT} root root 644
+    wait_key
+    activate_server
+}
+
+remove_server() {
+    if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
+        return
+    fi
+    deactivate_server
+    rm "${SERVICE_SYSTEMD_UNIT}"
+}
+
+
+activate_server () {
+    rst_title "Activate $SERVICE_NAME (service)" section
+    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+systemctl enable $SERVICE_NAME.service
+systemctl restart $SERVICE_NAME.service
+EOF
+    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+systemctl status $SERVICE_NAME.service
+EOF
+    wait_key
+}
+
+deactivate_server () {
+    rst_title "De-Activate $SERVICE_NAME (service)" section
+    echo
+    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+systemctl stop $SERVICE_NAME.service
+systemctl disable $SERVICE_NAME.service
+EOF
+    wait_key
+}
+
+assert_user() {
+    rst_title "user $SERVICE_USER" section
+    echo
+    tee_stderr 1 <<EOF | bash | prefix_stdout
+sudo -H adduser --shell /bin/bash --system --home $SERVICE_HOME --group --gecos 'Filtron' $SERVICE_USER
+sudo -H usermod -a -G shadow $SERVICE_USER
+groups $SERVICE_USER
+EOF
+    SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
+    export SERVICE_HOME
+    echo "export SERVICE_HOME=$SERVICE_HOME"
+
+    cat > "$GO_ENV" <<EOF
+export GOPATH=\$HOME/go-apps
+export PATH=\$PATH:\$HOME/local/go/bin:\$GOPATH/bin
+EOF
+    echo "Environment $GO_ENV has been setup."
+
+    tee_stderr <<EOF | sudo -i -u $SERVICE_USER
+grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
+EOF
+}
+
+remove_user() {
+    rst_title "Drop $SERVICE_USER HOME" section
+    if ask_yn "Do you really want to drop $SERVICE_USER home folder?"; then
+        userdel -r -f "$SERVICE_USER"
+    else
+        rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
+    fi
+}
+
+interactive_shell(){
+    echo "// exit with STRG-D"
+    sudo -H -u ${SERVICE_USER} -i
+}
+
+_service_prefix="$SERVICE_USER@$(hostname) -->| "
+
+install_go(){
+    rst_title "Install Go in user's HOME" section
+
+    rst_para "download and install go binary .."
+    cache_download "${GO_PKG_URL}" "${GO_TAR}"
+
+    tee_stderr 0.1 <<EOF | sudo -i -u "$SERVICE_USER" | prefix_stdout "$_service_prefix"
+echo \$PATH
+echo \$GOPATH
+mkdir -p \$HOME/local
+rm -rf \$HOME/local/go
+tar -C \$HOME/local -xzf ${CACHE}/${GO_TAR}
+EOF
+    echo
+    sudo -i -u "$SERVICE_USER" <<EOF | prefix_stdout
+! which go >/dev/null &&  echo "Go Installation not found in PATH!?!"
+which go >/dev/null &&  go version && echo "congratulations -- Go installation OK :)"
+EOF
+    wait_key
+}
+
+install_filtron() {
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" | prefix_stdout "$_service_prefix"
+go get -v -u github.com/asciimoo/filtron 2>&1
+EOF
+    install_template "$FILTRON_ETC/rules.json" root root 644
+}
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------

+ 7 - 5
utils/lib.sh

@@ -19,7 +19,7 @@ if [[ -z "$CACHE" ]]; then
 fi
 
 if [[ -z "$SYSTEMD_UNITS" ]]; then
-    SYSTEMD_UNITS="/lib/systemd/system/"
+    SYSTEMD_UNITS="/lib/systemd/system"
 fi
 
 sudo_or_exit() {
@@ -253,9 +253,9 @@ install_template() {
     #
     #     install_template --no-eval /etc/updatedb.conf root root 644
 
-    local do_eval=0
+    local do_eval=1
     if [[ "$1" == "--no-eval" ]]; then
-        do_eval=1; shift
+        do_eval=0; shift
     fi
     local dst="${1}"
     local owner=${2-$(id -un)}
@@ -286,6 +286,8 @@ install_template() {
         fi
     fi
 
+    mkdir -p "$(dirname "${dst}")"
+
     if [[ -f "${dst}" ]] ; then
         info_msg "file ${dst} allready exists on this host"
         choose_one _reply "choose next step with file $dst" \
@@ -296,7 +298,7 @@ install_template() {
             "replace file")
                 info_msg "install: ${template_file}"
                 sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
-                     "${template_file}" "${dst}"
+                     "${template_file}" "${dst}" | prefix_stdout
                 ;;
             "leave file unchanged")
                 ;;
@@ -309,7 +311,7 @@ install_template() {
     else
         info_msg "install: ${template_file}"
         sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
-             "${template_file}" "${dst}"
+             "${template_file}" "${dst}" | prefix_stdout
     fi
 
 }

+ 56 - 0
utils/templates/etc/filtron/rules.json

@@ -0,0 +1,56 @@
+[
+    {
+        "name": "api limit",
+        "interval": 60,
+        "limit": 1000,
+        "filters": ["Path=^/api"],
+        "aggregations": ["Path"],
+        "actions": [
+            {"name": "block"}
+        ],
+        "subrules": [
+            {
+                "name": "drop put",
+                "interval": 60,
+                "limit": 100,
+                "filters": ["Method=PUT"],
+                "aggregations": ["Header:X-Forwarded-For"],
+                "actions": [
+                    {"name": "shell",
+                     "params": {"cmd": "iptables -A INPUT -s %v -j DROP", "args": ["Header:X-Forwarded-For"]}}
+                ]
+            }
+        ]
+    },
+    {
+        "name": "log'n'block rss",
+        "interval": 300,
+        "limit": 2500,
+        "filters": ["Path=^/$", "GET:format=rss"],
+        "actions": [
+            {"name": "log"},
+            {"name": "block"}
+        ]
+    },
+    {
+        "name": "log rule",
+        "filters": ["Path=/"],
+        "actions": [ {"name": "log"} ],
+        "subrules": [
+            {
+                "name": "block missing accept-language",
+                "filters": ["!Header:Accept-Language"],
+                "actions": [
+                    {"name": "block"}
+                ]
+            },
+            {
+                "name": "block curl",
+                "filters": ["Header:User-Agent=[Cc]url"],
+                "actions": [
+                    {"name": "block"}
+                ]
+            }
+        ]
+    }
+]

+ 29 - 0
utils/templates/lib/systemd/system/filtron.service

@@ -0,0 +1,29 @@
+[Unit]
+
+Description=${SERVICE_NAME}
+After=syslog.target
+After=network.target
+
+[Service]
+
+Type=simple
+User=${SERVICE_USER}
+Group=${SERVICE_GROUP}
+WorkingDirectory=${SERVICE_HOME}
+ExecStart=${SERVICE_HOME}/go-apps/bin/filtron -rules ${FILTRON_RULES}
+
+Restart=always
+Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME}
+
+# Some distributions may not support these hardening directives.  If you cannot
+# start the service due to an unknown option, comment out the ones not supported
+# by your version of systemd.
+
+ProtectSystem=full
+PrivateDevices=yes
+PrivateTmp=yes
+NoNewPrivileges=true
+
+[Install]
+
+WantedBy=multi-user.target