From bdf392093af92da7d8fc4e32b68ff0716700522d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 8 Jan 2020 18:09:36 +0100
Subject: [PATCH 001/125] build requirements: add a shell script static
 analysis tool

ShellCheck: https://github.com/koalaman/shellcheck

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile                  |  6 +++--
 docs/admin/buildhosts.rst | 50 ++++++++++++++++++++++++++++++++-------
 docs/dev/makefile.rst     |  4 +++-
 docs/dev/reST.rst         |  5 ++--
 4 files changed, 50 insertions(+), 15 deletions(-)

diff --git a/Makefile b/Makefile
index f35b86c41..6adba1037 100644
--- a/Makefile
+++ b/Makefile
@@ -70,12 +70,14 @@ $(GH_PAGES)::
 # test
 # ----
 
-PHONY += test test.pylint test.pep8 test.unit test.robot
+PHONY += test test.sh test.pylint test.pep8 test.unit test.robot
 
 # TODO: balance linting with pylint
-test: test.pep8 test.unit test.robot
+test: test.pep8 test.unit test.sh test.robot
 	- make pylint
 
+test.sh:
+
 test.pep8: pyenvinstall
 	$(PY_ENV_ACT); ./manage.sh pep8_check
 
diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index 5260da033..c1582cef2 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -35,8 +35,17 @@ processing additional packages are needed.  The XeTeX_ needed not only for PDF
 creation, its also needed for :ref:`math` when HTML output is build.
 
 To be able to do :ref:`sphinx:math-support` without CDNs, the math are rendered
-as images (``sphinx.ext.imgmath`` extension).  If your docs build (``make
-docs``) shows warnings like this::
+as images (``sphinx.ext.imgmath`` extension).
+
+Here is the extract from the :origin:`docs/conf.py` file, setting math renderer
+to ``imgmath``:
+
+.. literalinclude:: ../conf.py
+   :language: python
+   :start-after: # sphinx.ext.imgmath setup
+   :end-before: # sphinx.ext.imgmath setup END
+
+If your docs build (``make docs``) shows warnings like this::
 
    WARNING: dot(1) not found, for better output quality install \
             graphviz from http://www.graphviz.org
@@ -47,8 +56,6 @@ docs``) shows warnings like this::
 you need to install additional packages on your build host, to get better HTML
 output.
 
-.. _system requirements:
-
 .. tabs::
 
    .. group-tab:: Ubuntu / debian
@@ -94,10 +101,35 @@ For PDF output you also need:
 	        texlive-collection-fontsrecommended texlive-collection-latex \
 		dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts
 
-.. _system requirements END:
+.. _sh lint:
 
-.. literalinclude:: ../conf.py
-   :language: python
-   :start-after: # sphinx.ext.imgmath setup
-   :end-before: # sphinx.ext.imgmath setup END
+Lint shell scripts
+==================
 
+.. _ShellCheck: https://github.com/koalaman/shellcheck
+
+To lint shell scripts, we use ShellCheck_ - A shell script static analysis tool.
+
+.. SNIP sh lint requirements
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code-block:: sh
+
+         $ sudo apt install shellcheck
+
+   .. group-tab:: Arch Linux
+
+      .. code-block:: sh
+
+         $ sudo pacman -S shellcheck
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code-block:: sh
+
+         $ sudo dnf install ShellCheck
+
+.. SNAP sh lint requirements
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index f5957001c..8e54aef48 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -11,6 +11,8 @@ Makefile Targets
    Before looking deeper at the targets, first read about :ref:`makefile setup`
    and :ref:`make pyenv`.
 
+   To install system requirements follow :ref:`buildhosts`.
+
 With the aim to simplify development cycles, started with :pull:`1756` a
 ``Makefile`` based boilerplate was added.  If you are not familiar with
 Makefiles, we recommend to read gnu-make_ introduction.
@@ -170,7 +172,7 @@ e.g.:
 
 .. code:: sh
 
-  $ make test.pep8 test.unit
+  $ make test.pep8 test.unit test.sh
   . ./local/py3/bin/activate; ./manage.sh pep8_check
   [!] Running pep8 check
   . ./local/py3/bin/activate; ./manage.sh unit_tests
diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst
index 4dc1279f0..7c82ebe5a 100644
--- a/docs/dev/reST.rst
+++ b/docs/dev/reST.rst
@@ -1312,9 +1312,8 @@ others are basic-tabs_ and code-tabs_.  Below a *group-tab* example from
 
 .. literalinclude:: ../admin/buildhosts.rst
    :language: reST
-   :start-after: .. _system requirements:
-   :end-before: .. _system requirements END:
-
+   :start-after: .. SNIP sh lint requirements
+   :end-before: .. SNAP sh lint requirements
 
 .. _math:
 

From 28dacee28819ea2d24fd8ff6547fd3a175677084 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 8 Jan 2020 18:13:22 +0100
Subject: [PATCH 002/125] utils: add lib.sh containing common shell script

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .gitignore   |   1 +
 Makefile     |   1 +
 utils/lib.sh | 316 +++++++++++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 318 insertions(+)
 create mode 100755 utils/lib.sh

diff --git a/.gitignore b/.gitignore
index 069dfd35b..3c998afae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,7 @@
 # to sync with .dockerignore
 .coverage
 coverage/
+cache/
 .installed.cfg
 engines.cfg
 env
diff --git a/Makefile b/Makefile
index 6adba1037..a5265ff86 100644
--- a/Makefile
+++ b/Makefile
@@ -77,6 +77,7 @@ test: test.pep8 test.unit test.sh test.robot
 	- make pylint
 
 test.sh:
+	shellcheck -x utils/lib.sh
 
 test.pep8: pyenvinstall
 	$(PY_ENV_ACT); ./manage.sh pep8_check
diff --git a/utils/lib.sh b/utils/lib.sh
new file mode 100755
index 000000000..66790bc5f
--- /dev/null
+++ b/utils/lib.sh
@@ -0,0 +1,316 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh -*-
+# shellcheck disable=SC2059,SC1117,SC2162,SC2004
+
+if [[ -z "${REPO_ROOT}" ]]; then
+    REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")
+    while [ -h "${REPO_ROOT}" ] ; do
+        REPO_ROOT=$(readlink "${REPO_ROOT}")
+    done
+    REPO_ROOT=$(cd "${REPO_ROOT}/.." && pwd -P )
+fi
+
+if [[ -z ${TEMPLATES} ]]; then
+    TEMPLATES="${REPO_ROOT}/utils/templates"
+fi
+
+if [[ -z "$CACHE" ]]; then
+    CACHE="${REPO_ROOT}/cache"
+fi
+
+if [[ -z "$SYSTEMD_UNITS" ]]; then
+    SYSTEMD_UNITS="/lib/systemd/system/"
+fi
+
+sudo_or_exit() {
+    # usage: sudo_or_exit
+
+    if [ ! "$(id -u)" -eq 0 ];  then
+        err_msg "this command requires root (sudo) privilege!" >&2
+        exit 42
+    fi
+}
+
+rst_title() {
+    # usage: rst_title <header-text> [part|chapter|section]
+
+    case ${2-chapter} in
+        part)     printf "\n${1//?/=}\n$1\n${1//?/=}\n";;
+        chapter)  printf "\n${1}\n${1//?/=}\n";;
+        section)  printf "\n${1}\n${1//?/-}\n";;
+        *)
+            err_msg "invalid argument '${2}' in line $(caller)"
+            return 42
+            ;;
+    esac
+}
+
+if command -v fmt >/dev/null; then
+    export FMT="fmt -u"
+else
+    export FMT="cat"
+fi
+
+rst_para() {
+    # usage:  RST_INDENT=1 rst_para "lorem ipsum ..."
+    local prefix=''
+    if ! [[ -z $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
+	prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf "  "; done)"
+	echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
+    else
+	echo -en "\n$*\n" | $FMT
+    fi
+}
+
+err_msg()  { echo -e "ERROR: $*" >&2; }
+warn_msg() { echo -e "WARN:  $*" >&2; }
+info_msg() { echo -e "INFO:  $*"; }
+
+wait_key(){
+    # usage: waitKEY [<timeout in sec>]
+
+    local _t=$1
+    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ ! -z $_t ]] && _t="-t $_t"
+    # shellcheck disable=SC2086
+    read -n1 $_t -p "** press any [KEY] to continue **"
+    echo
+}
+
+ask_yn() {
+    # usage: ask_yn <prompt-text> [Ny|Yn] [<timeout in sec>]
+
+    local EXIT_YES=0 # exit status 0 --> successful
+    local EXIT_NO=1  # exit status 1 --> error code
+
+    local _t=$3
+    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ ! -z $_t ]] && _t="-t $_t"
+    case "${2}" in
+        Yn)
+            local exit_val=${EXIT_YES}
+            local choice="[YES/no]"
+            local default="Yes"
+            ;;
+        *)
+            local exit_val=${EXIT_NO}
+            local choice="[NO/yes]"
+            local default="No"
+            ;;
+    esac
+    echo
+    while true; do
+        printf "$1 ${choice} "
+        # shellcheck disable=SC2086
+        read -n1 $_t
+        if [[ -z $REPLY ]]; then
+            printf "$default\n"; break
+        elif [[ $REPLY =~ ^[Yy]$ ]]; then
+            exit_val=${EXIT_YES}
+            printf "\n"
+            break
+        elif [[ $REPLY =~ ^[Nn]$ ]]; then
+            exit_val=${EXIT_NO}
+            printf "\n"
+            break
+        fi
+        _t=""
+        err_msg "invalid choice"
+    done
+    return $exit_val
+}
+
+tee_stderr () {
+
+    # usage::
+    #   tee_stderr 1 <<EOF | python -i
+    #   print("hello")
+    #   EOF
+    #   ...
+    #   >>> print("hello")
+    #    hello
+
+    local _t="0";
+    if [[ ! -z $1 ]] ; then _t="$1"; fi
+
+    (while read line; do
+         # shellcheck disable=SC2086
+         sleep $_t
+         echo -e "$line" >&2
+         echo "$line"
+    done)
+}
+
+prefix_stdout () {
+    # usage: <cmd> | prefix_stdout [prefix]
+
+    local prefix="-->| "
+
+    if [[ ! -z $1 ]] ; then prefix="$1"; fi
+
+    (while IFS= read line; do
+        echo -e "${prefix}$line"
+    done)
+}
+
+append_line() {
+
+    # usage: append_line <line> <file>
+    #
+    # Append line if not exists, create file if not exists. E.g::
+    #
+    #     append_line 'source ~/.foo' ~/bashrc
+
+    local LINE=$1
+    local FILE=$2
+    grep -qFs -- "$LINE" "$FILE" || echo "$LINE" >> "$FILE"
+}
+
+cache_download() {
+
+    # usage: cache_download <url> <local-filename>
+
+    local exit_value=0
+
+    if [[ ! -z ${SUDO_USER} ]]; then
+        sudo -u "${SUDO_USER}" mkdir -p "${CACHE}"
+    else
+        mkdir -p "${CACHE}"
+    fi
+
+    if [[ -f "${CACHE}/$2" ]] ; then
+        info_msg "already cached: $1"
+        info_msg "  --> ${CACHE}/$2"
+    fi
+
+    if [[ ! -f "${CACHE}/$2" ]]; then
+        info_msg "caching: $1"
+        info_msg "  --> ${CACHE}/$2"
+        if [[ ! -z ${SUDO_USER} ]]; then
+            sudo -u "${SUDO_USER}" wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
+        else
+            wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
+        fi
+        if $exit_value; then
+            err_msg "failed to download: $1"
+        fi
+    fi
+}
+
+choose_one() {
+
+    # usage:
+    #
+    #   DEFAULT_SELECT= 2 \
+    #     choose_one <name> "your selection?" "Coffee" "Coffee with milk"
+
+    local default=${DEFAULT_SELECT-1}
+    local REPLY
+    local env_name=$1 && shift
+    local choice=$1;
+    local max="${#@}"
+    local _t
+    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ ! -z $_t ]] && _t="-t $_t"
+
+    list=("$@")
+    echo -e "Menu::"
+    for ((i=1; i<= $(($max -1)); i++)); do
+        if [[ "$i" == "$default" ]]; then
+            echo -e "  $i.) ${list[$i]} [default]"
+        else
+            echo -e "  $i.) ${list[$i]}"
+        fi
+    done
+    while true; do
+        printf "$1 [$default] "
+
+        if (( 10 > $max )); then
+            # shellcheck disable=SC2086
+            read -n1 $_t
+        else
+            # shellcheck disable=SC2086,SC2229
+            read $_t
+        fi
+        # selection fits
+        [[ $REPLY =~ ^-?[0-9]+$ ]] && (( $REPLY > 0 )) && (( $REPLY < $max )) && break
+
+        # take default
+        [[ -z $REPLY ]] && REPLY=$default && break
+
+        _t=""
+        err_msg "invalid choice"
+    done
+    echo
+    eval "$env_name"='${list[${REPLY}]}'
+}
+
+install_template() {
+
+    # usage:
+    #
+    #     install_template [--no-eval] {file} [{owner} [{group} [{chmod}]]]
+    #
+    #     install_template --no-eval /etc/updatedb.conf root root 644
+
+    local do_eval=0
+    if [[ "$1" == "--no-eval" ]]; then
+        do_eval=1; shift
+    fi
+    local dst="${1}"
+    local owner=${2-$(id -un)}
+    local group=${3-$(id -gn)}
+    local chmod=${4-644}
+    local _reply=""
+
+    info_msg "install: ${dst}"
+
+    if [[ ! -f "${TEMPLATES}${dst}" ]] ; then
+        err_msg "${TEMPLATES}${dst} does not exists"
+        err_msg "... can't install $dst / exit installation with error 42"
+        wait_key 30
+        return 42
+    fi
+
+    local template_file="${TEMPLATES}${dst}"
+    if [[ "$do_eval" == "1" ]]; then
+        info_msg "BUILD template ${template_file}"
+        if [[ -f "${TEMPLATES}${dst}" ]] ; then
+            template_file="${CACHE}${dst}"
+            mkdir -p "$(dirname "${template_file}")"
+            # shellcheck disable=SC2086
+            eval "echo \"$(cat ${TEMPLATES}${dst})\"" > "${template_file}"
+        else
+            err_msg "failed ${template_file}"
+            return 42
+        fi
+    fi
+
+    if [[ -f "${dst}" ]] ; then
+        info_msg "file ${dst} allready exists on this host"
+        choose_one _reply "choose next step with file $dst" \
+                   "replace file" \
+                   "leave file unchanged"
+
+        case $_reply in
+            "replace file")
+                info_msg "install: ${template_file}"
+                sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
+                     "${template_file}" "${dst}"
+                ;;
+            "leave file unchanged")
+                ;;
+            "interactiv shell")
+                echo "// exit with STRG-D"
+                sudo -H -u "${owner}" -i
+            ;;
+        esac
+
+    else
+        info_msg "install: ${template_file}"
+        sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
+             "${template_file}" "${dst}"
+    fi
+
+}
+

From 4139c63d23a1f4cc427eb428bcff0594c395c1c5 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 8 Jan 2020 19:21:07 +0100
Subject: [PATCH 003/125] utils/filtron.sh: add script to install filtron
 middleware

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile                                      |   1 +
 utils/filtron.sh                              | 214 ++++++++++++++++++
 utils/lib.sh                                  |  12 +-
 utils/templates/etc/filtron/rules.json        |  56 +++++
 .../lib/systemd/system/filtron.service        |  29 +++
 5 files changed, 307 insertions(+), 5 deletions(-)
 create mode 100755 utils/filtron.sh
 create mode 100644 utils/templates/etc/filtron/rules.json
 create mode 100644 utils/templates/lib/systemd/system/filtron.service

diff --git a/Makefile b/Makefile
index a5265ff86..fec004a5e 100644
--- a/Makefile
+++ b/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
diff --git a/utils/filtron.sh b/utils/filtron.sh
new file mode 100755
index 000000000..f310991a0
--- /dev/null
+++ b/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 "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/lib.sh b/utils/lib.sh
index 66790bc5f..b5e897549 100755
--- a/utils/lib.sh
+++ b/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
 
 }
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
new file mode 100644
index 000000000..4a232388e
--- /dev/null
+++ b/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"}
+                ]
+            }
+        ]
+    }
+]
diff --git a/utils/templates/lib/systemd/system/filtron.service b/utils/templates/lib/systemd/system/filtron.service
new file mode 100644
index 000000000..fdb67731a
--- /dev/null
+++ b/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

From 4990b07b4bc42b0caf0d890f8c81c3545bbb807b Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 9 Jan 2020 16:25:05 +0100
Subject: [PATCH 004/125] utils/filtron.sh: various fix from first installation
 test (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                              |  75 +++++++--
 utils/lib.sh                                  |  72 ++++++---
 utils/templates/etc/filtron/rules.json        | 142 ++++++++++++------
 .../lib/systemd/system/filtron.service        |   2 +-
 4 files changed, 206 insertions(+), 85 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index f310991a0..593c0fcf7 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -11,6 +11,11 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 
 FILTRON_ETC="/etc/filtron"
 
+FILTRON_RULES="$FILTRON_ETC/rules.json"
+FILTRON_API="127.0.0.1:4005"
+FILTRON_LISTEN="127.0.0.1:4004"
+FILTRON_TARGET="127.0.0.1:8888"
+
 SERVICE_NAME="filtron"
 SERVICE_USER="${SERVICE_NAME}"
 SERVICE_HOME="/home/${SERVICE_USER}"
@@ -23,6 +28,11 @@ 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")
 
+CONFIG_FILES=(
+    "${FILTRON_RULES}"
+    "${SERVICE_SYSTEMD_UNIT}"
+)
+
 # ----------------------------------------------------------------------------
 usage(){
 # ----------------------------------------------------------------------------
@@ -37,10 +47,16 @@ usage:
   $(basename "$0") remove     [all]
   $(basename "$0") activate   [server]
   $(basename "$0") deactivate [server]
+  $(basename "$0") show       [server]
 
-shell        - start interactive shell with user ${SERVICE_USER}
-install user - add service user '$SERVICE_USER' at $SERVICE_HOME
-
+shell
+  start interactive shell from user ${SERVICE_USER}
+show server
+  show server status and log
+install / remove
+  all  - complete setup of filtron server
+install user
+  add service user '$SERVICE_USER' at $SERVICE_HOME
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
@@ -58,6 +74,14 @@ main(){
 	    sudo_or_exit
 	    interactive_shell
 	    ;;
+        show)
+            case $2 in
+                server)
+		    sudo_or_exit
+		    show_server
+		    ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
         install)
             sudo_or_exit
             case $2 in
@@ -91,21 +115,27 @@ main(){
 install_all() {
     rst_title "Install $SERVICE_NAME (service)"
     assert_user
+    wait_key
     install_go
+    wait_key
     install_filtron
+    wait_key
     install_server
+    wait_key
 }
 
 remove_all() {
     rst_title "De-Install $SERVICE_NAME (service)"
     remove_server
+    wait_key
     remove_user
-    rm -rf "$FILTRON_ETC"
+    rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
     wait_key
 }
 
 install_server() {
-    rst_title "Install System-D Unit ${SERVICE_NAME}.service ..." section
+    rst_title "Install System-D Unit ${SERVICE_NAME}.service" section
+    echo
     install_template ${SERVICE_SYSTEMD_UNIT} root root 644
     wait_key
     activate_server
@@ -116,12 +146,12 @@ remove_server() {
         return
     fi
     deactivate_server
-    rm "${SERVICE_SYSTEMD_UNIT}"
+    rm "${SERVICE_SYSTEMD_UNIT}"  2>&1 | prefix_stdout
 }
 
-
 activate_server () {
     rst_title "Activate $SERVICE_NAME (service)" section
+    echo
     tee_stderr <<EOF | bash 2>&1 | prefix_stdout
 systemctl enable $SERVICE_NAME.service
 systemctl restart $SERVICE_NAME.service
@@ -129,7 +159,6 @@ EOF
     tee_stderr <<EOF | bash 2>&1 | prefix_stdout
 systemctl status $SERVICE_NAME.service
 EOF
-    wait_key
 }
 
 deactivate_server () {
@@ -139,7 +168,6 @@ deactivate_server () {
 systemctl stop $SERVICE_NAME.service
 systemctl disable $SERVICE_NAME.service
 EOF
-    wait_key
 }
 
 assert_user() {
@@ -168,18 +196,18 @@ 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"
+        userdel -r -f "$SERVICE_USER" 2>&1 | prefix_stdout
     else
         rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
     fi
 }
 
 interactive_shell(){
-    echo "// exit with STRG-D"
+    echo "// exit with CTRL-D"
     sudo -H -u ${SERVICE_USER} -i
 }
 
-_service_prefix="$SERVICE_USER@$(hostname) -->| "
+_service_prefix="  |$SERVICE_USER| "
 
 install_go(){
     rst_title "Install Go in user's HOME" section
@@ -199,14 +227,29 @@ EOF
 ! 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
+    rst_title "Install filtron in user's ~/go-apps" section
+    echo
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_service_prefix"
+go get -v -u github.com/asciimoo/filtron
 EOF
-    install_template "$FILTRON_ETC/rules.json" root root 644
+    install_template --no-eval "$FILTRON_RULES" root root 644
+}
+
+show_server () {
+    rst_title "server status & log"
+    echo
+    systemctl status filtron.service
+    echo
+    read -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
+    echo
+    while true;  do
+	trap break 2
+	journalctl -f -u filtron
+    done
+    return 0
 }
 
 # ----------------------------------------------------------------------------
diff --git a/utils/lib.sh b/utils/lib.sh
index b5e897549..d06cabf26 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -22,6 +22,13 @@ if [[ -z "$SYSTEMD_UNITS" ]]; then
     SYSTEMD_UNITS="/lib/systemd/system"
 fi
 
+if [[ -z ${DIFF_CMD} ]]; then
+    DIFF_CMD="diff -u"
+    if command -v colordiff >/dev/null;  then
+        DIFF_CMD="colordiff -u"
+    fi
+fi
+
 sudo_or_exit() {
     # usage: sudo_or_exit
 
@@ -55,10 +62,10 @@ rst_para() {
     # usage:  RST_INDENT=1 rst_para "lorem ipsum ..."
     local prefix=''
     if ! [[ -z $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
-	prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf "  "; done)"
-	echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
+        prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf "  "; done)"
+        echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
     else
-	echo -en "\n$*\n" | $FMT
+        echo -en "\n$*\n" | $FMT
     fi
 }
 
@@ -66,15 +73,23 @@ err_msg()  { echo -e "ERROR: $*" >&2; }
 warn_msg() { echo -e "WARN:  $*" >&2; }
 info_msg() { echo -e "INFO:  $*"; }
 
+clean_stdin() {
+    if [[ $(uname -s) != 'Darwin' ]]; then
+        while $(read -n1 -t 0.1); do : ; done
+    fi
+}
+
 wait_key(){
     # usage: waitKEY [<timeout in sec>]
 
+    clean_stdin
     local _t=$1
     [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
     [[ ! -z $_t ]] && _t="-t $_t"
     # shellcheck disable=SC2086
-    read -n1 $_t -p "** press any [KEY] to continue **"
+    read -s -n1 $_t -p "** press any [KEY] to continue **"
     echo
+    clean_stdin
 }
 
 ask_yn() {
@@ -100,6 +115,7 @@ ask_yn() {
     esac
     echo
     while true; do
+	clean_stdin
         printf "$1 ${choice} "
         # shellcheck disable=SC2086
         read -n1 $_t
@@ -117,6 +133,7 @@ ask_yn() {
         _t=""
         err_msg "invalid choice"
     done
+    clean_stdin
     return $exit_val
 }
 
@@ -144,7 +161,7 @@ tee_stderr () {
 prefix_stdout () {
     # usage: <cmd> | prefix_stdout [prefix]
 
-    local prefix="-->| "
+    local prefix="  | "
 
     if [[ ! -z $1 ]] ; then prefix="$1"; fi
 
@@ -223,6 +240,7 @@ choose_one() {
         fi
     done
     while true; do
+	clean_stdin
         printf "$1 [$default] "
 
         if (( 10 > $max )); then
@@ -242,6 +260,7 @@ choose_one() {
         err_msg "invalid choice"
     done
     echo
+    clean_stdin
     eval "$env_name"='${list[${REPLY}]}'
 }
 
@@ -288,31 +307,48 @@ install_template() {
 
     mkdir -p "$(dirname "${dst}")"
 
-    if [[ -f "${dst}" ]] ; then
-        info_msg "file ${dst} allready exists on this host"
+    if [[ ! -f "${dst}" ]]; then
+        info_msg "install: ${template_file}"
+        sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
+             "${template_file}" "${dst}" | prefix_stdout
+        return $?
+    fi
+
+    if [[ -f "${dst}" ]] && cmp --silent "${template_file}" "${dst}" ; then
+        info_msg "file ${dst} allready installed"
+        return 0
+    fi
+
+    info_msg "file ${dst} allready exists on this host"
+
+    while true; do
         choose_one _reply "choose next step with file $dst" \
                    "replace file" \
-                   "leave file unchanged"
+                   "leave file unchanged" \
+                   "interactiv shell" \
+                   "diff files"
 
         case $_reply in
             "replace file")
                 info_msg "install: ${template_file}"
                 sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
                      "${template_file}" "${dst}" | prefix_stdout
+		break
                 ;;
             "leave file unchanged")
+                break
                 ;;
             "interactiv shell")
-                echo "// exit with STRG-D"
+                echo "// edit ${dst} to your needs"
+                echo "// exit with CTRL-D"
                 sudo -H -u "${owner}" -i
-            ;;
+                $DIFF_CMD "${dst}" "${template_file}"
+		if ask_yn "did you edit ${template_file} to your needs?"; then
+		    break
+		fi
+                ;;
+            "diff files")
+                $DIFF_CMD "${dst}" "${template_file}" | prefix_stdout
         esac
-
-    else
-        info_msg "install: ${template_file}"
-        sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
-             "${template_file}" "${dst}" | prefix_stdout
-    fi
-
+    done
 }
-
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index 4a232388e..b54e097a5 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,56 +1,98 @@
-[
+[{
+  "name":"search request",
+  "filters":[
+    "Param:q",
+    "Path=^(/|/search)$"
+  ],
+  "interval":60,
+  "limit":15,
+  "subrules":[
     {
-        "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":"roboagent limit",
+      "interval":60,
+      "limit":15,
+      "filters":[
+        "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
+      ],
+      "actions":[
+	{"name": "log"},
+	{
+          "name":"block",
+          "params":{
+            "message":"Rate limit exceeded"
+          }
+        }
+      ]
     },
     {
-        "name": "log'n'block rss",
-        "interval": 300,
-        "limit": 2500,
-        "filters": ["Path=^/$", "GET:format=rss"],
-        "actions": [
-            {"name": "log"},
-            {"name": "block"}
-        ]
+      "name":"botlimit",
+      "limit":0,
+      "stop":true,
+      "filters":[
+        "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+      ],
+      "actions":[
+	{"name": "log"},
+        {
+          "name":"block",
+          "params":{
+            "message":"Rate limit exceeded"
+          }
+        }
+      ]
     },
     {
-        "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"}
-                ]
-            }
-        ]
+      "name":"IP limit",
+      "interval":60,
+      "limit":15,
+      "stop":true,
+      "aggregations":[
+        "Header:X-Forwarded-For"
+      ],
+      "actions":[
+	{"name": "log"},
+        {
+          "name":"block",
+          "params":{
+            "message":"Rate limit exceeded"
+          }
+        }
+      ]
+    },
+    {
+      "name":"rss/json limit",
+      "interval":60,
+      "limit":15,
+      "stop":true,
+      "filters":[
+        "Param:format=(csv|json|rss)"
+      ],
+      "actions":[
+	{"name": "log"},
+        {
+          "name":"block",
+          "params":{
+            "message":"Rate limit exceeded"
+          }
+        }
+      ]
+      },
+    {
+      "name":"useragent limit",
+      "interval":60,
+      "limit":15,
+      "aggregations":[
+        "Header:User-Agent"
+      ],
+      "actions":[
+	{"name": "log"},
+        {
+          "name":"block",
+          "params":{
+            "message":"Rate limit exceeded"
+          }
+        }
+      ]
     }
-]
+  ]
+}]
diff --git a/utils/templates/lib/systemd/system/filtron.service b/utils/templates/lib/systemd/system/filtron.service
index fdb67731a..3b0c6edcc 100644
--- a/utils/templates/lib/systemd/system/filtron.service
+++ b/utils/templates/lib/systemd/system/filtron.service
@@ -10,7 +10,7 @@ Type=simple
 User=${SERVICE_USER}
 Group=${SERVICE_GROUP}
 WorkingDirectory=${SERVICE_HOME}
-ExecStart=${SERVICE_HOME}/go-apps/bin/filtron -rules ${FILTRON_RULES}
+ExecStart=${SERVICE_HOME}/go-apps/bin/filtron -api '${FILTRON_API}' -listen '${FILTRON_LISTEN}' -rules '${FILTRON_RULES}' -target '${FILTRON_TARGET}'
 
 Restart=always
 Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME}

From 876988a34f76988fc7375caace382480f122f940 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 11 Jan 2020 12:49:02 +0100
Subject: [PATCH 005/125] docs: add Sphinx-doc extension
 sphinxcontrib.programoutput

- https://github.com/NextThought/sphinxcontrib-programoutput

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/conf.py         | 1 +
 requirements-dev.txt | 1 +
 2 files changed, 2 insertions(+)

diff --git a/docs/conf.py b/docs/conf.py
index af255e230..8c5f6b311 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -61,6 +61,7 @@ extensions = [
     "pallets_sphinx_themes",
     "sphinx_issues", # https://github.com/sloria/sphinx-issues/blob/master/README.rst
     "sphinxcontrib.jinja",  # https://github.com/tardyp/sphinx-jinja
+    "sphinxcontrib.programoutput",  # https://github.com/NextThought/sphinxcontrib-programoutput
     'linuxdoc.rstFlatTable',    # Implementation of the 'flat-table' reST-directive.
     'linuxdoc.kfigure',         # Sphinx extension which implements scalable image handling.
     "sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs
diff --git a/requirements-dev.txt b/requirements-dev.txt
index 3e8f617af..fe92bdce5 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -14,3 +14,4 @@ selenium==3.141.0
 linuxdoc @ git+http://github.com/return42/linuxdoc.git
 sphinx-jinja
 sphinx-tabs
+sphinxcontrib-programoutput

From 39feb141bc8361915b3d80a976852b38851e0419 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 11 Jan 2020 12:50:40 +0100
Subject: [PATCH 006/125] docs(admin): add description of the utils/filtron.sh
 script

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst | 34 +++++++++++++++++++++++++++---
 utils/filtron.sh       | 48 +++++++++++++++++++++++-------------------
 2 files changed, 57 insertions(+), 25 deletions(-)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 07dcb9bc5..009ca1c29 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -2,12 +2,40 @@
 How to protect an instance
 ==========================
 
+.. _filtron: https://github.com/asciimoo/filtron
+
 Searx depens on external search services.  To avoid the abuse of these services
 it is advised to limit the number of requests processed by searx.
 
-An application firewall, ``filtron`` solves exactly this problem.  Information
-on how to install it can be found at the `project page of filtron
-<https://github.com/asciimoo/filtron>`__.
+An application firewall, filtron_ solves exactly this problem.  Filtron is just
+a middleware between your web server (nginx, apache, ...) and searx.
+
+
+filtron & go
+============
+
+.. _Go: https://golang.org/
+.. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
+
+Filtron needs Go_ installed.  If Go_ is preinstalled, filtron_ is simply
+installed by ``go get`` package management (see `filtron README`_).  If you use
+filtron as middleware, a more isolated setup is recommended.
+
+#. Create a separated user account (``filtron``).
+#. Download and install Go_ binary in users $HOME (``~filtron``).
+#. Install filtron with the package management of Go_ (``go get -v -u
+   github.com/asciimoo/filtron``)
+#. Setup a proper rule configuration :origin:`[ref]
+   <utils/templates/etc/filtron/rules.json>` (``/etc/filtron/rules.json``).
+#. Setup a systemd service unit :origin:`[ref]
+   <utils/templates/lib/systemd/system/filtron.service>`
+   (``/lib/systemd/system/filtron.service``).
+
+To simplify such a installation and the maintenance of; use our script
+``utils/filtron.sh``:
+
+.. program-output:: ../utils/filtron.sh --help
+   :ellipsis: 0,5
 
 
 Sample configuration of filtron
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 593c0fcf7..597d0c634 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -45,18 +45,22 @@ usage:
   $(basename "$0") shell
   $(basename "$0") install    [all|user]
   $(basename "$0") remove     [all]
-  $(basename "$0") activate   [server]
-  $(basename "$0") deactivate [server]
-  $(basename "$0") show       [server]
+  $(basename "$0") activate   [service]
+  $(basename "$0") deactivate [service]
+  $(basename "$0") show       [service]
 
 shell
   start interactive shell from user ${SERVICE_USER}
-show server
-  show server status and log
-install / remove
-  all  - complete setup of filtron server
+install / remove all 
+  complete setup of filtron service
+activate
+  activate and start service daemon (systemd unit)
+deactivate service
+  stop and deactivate service daemon (systemd unit)
 install user
   add service user '$SERVICE_USER' at $SERVICE_HOME
+show service
+  show service status and log
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
@@ -68,7 +72,7 @@ main(){
 
     case $1 in
 	--source-only)  ;;
-        -h|--help) usage ;;
+        -h|--help) usage; exit 0;;
 
 	shell)
 	    sudo_or_exit
@@ -76,9 +80,9 @@ main(){
 	    ;;
         show)
             case $2 in
-                server)
+                service)
 		    sudo_or_exit
-		    show_server
+		    show_service
 		    ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
@@ -99,13 +103,13 @@ main(){
         activate)
             sudo_or_exit
             case $2 in
-                server)  activate_server ;;
+                service)  activate_service ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         deactivate)
             sudo_or_exit
             case $2 in
-                server)  deactivate_server ;;
+                service)  deactivate_service ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
@@ -120,36 +124,36 @@ install_all() {
     wait_key
     install_filtron
     wait_key
-    install_server
+    install_service
     wait_key
 }
 
 remove_all() {
     rst_title "De-Install $SERVICE_NAME (service)"
-    remove_server
+    remove_service
     wait_key
     remove_user
     rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
     wait_key
 }
 
-install_server() {
+install_service() {
     rst_title "Install System-D Unit ${SERVICE_NAME}.service" section
     echo
     install_template ${SERVICE_SYSTEMD_UNIT} root root 644
     wait_key
-    activate_server
+    activate_service
 }
 
-remove_server() {
+remove_service() {
     if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
         return
     fi
-    deactivate_server
+    deactivate_service
     rm "${SERVICE_SYSTEMD_UNIT}"  2>&1 | prefix_stdout
 }
 
-activate_server () {
+activate_service () {
     rst_title "Activate $SERVICE_NAME (service)" section
     echo
     tee_stderr <<EOF | bash 2>&1 | prefix_stdout
@@ -161,7 +165,7 @@ systemctl status $SERVICE_NAME.service
 EOF
 }
 
-deactivate_server () {
+deactivate_service () {
     rst_title "De-Activate $SERVICE_NAME (service)" section
     echo
     tee_stderr <<EOF | bash 2>&1 | prefix_stdout
@@ -238,8 +242,8 @@ EOF
     install_template --no-eval "$FILTRON_RULES" root root 644
 }
 
-show_server () {
-    rst_title "server status & log"
+show_service () {
+    rst_title "service status & log"
     echo
     systemctl status filtron.service
     echo

From 3ed82a97af4f0c65d59bbf748a19ac907db40753 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 12 Jan 2020 15:51:04 +0100
Subject: [PATCH 007/125] utils/filtron.sh: add command 'update filtron'

BTW: tidy up some code style and fix some issues reported by the linter.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh | 50 ++++++++++++++++++++++++++++++++++--------------
 utils/lib.sh     |  2 +-
 2 files changed, 37 insertions(+), 15 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 597d0c634..5c8a738b0 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -12,8 +12,12 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 FILTRON_ETC="/etc/filtron"
 
 FILTRON_RULES="$FILTRON_ETC/rules.json"
+
+# shellcheck disable=SC2034
 FILTRON_API="127.0.0.1:4005"
+# shellcheck disable=SC2034
 FILTRON_LISTEN="127.0.0.1:4004"
+# shellcheck disable=SC2034
 FILTRON_TARGET="127.0.0.1:8888"
 
 SERVICE_NAME="filtron"
@@ -28,6 +32,7 @@ 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")
 
+# shellcheck disable=SC2034
 CONFIG_FILES=(
     "${FILTRON_RULES}"
     "${SERVICE_SYSTEMD_UNIT}"
@@ -44,6 +49,7 @@ usage:
 
   $(basename "$0") shell
   $(basename "$0") install    [all|user]
+  $(basename "$0") update     [filtron]
   $(basename "$0") remove     [all]
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
@@ -51,8 +57,10 @@ usage:
 
 shell
   start interactive shell from user ${SERVICE_USER}
-install / remove all 
+install / remove all
   complete setup of filtron service
+update filtron
+  Update filtron installation of user ${SERVICE_USER}
 activate
   activate and start service daemon (systemd unit)
 deactivate service
@@ -71,33 +79,39 @@ main(){
     local _usage="ERROR: unknown or missing $1 command $2"
 
     case $1 in
-	--source-only)  ;;
+        --source-only)  ;;
         -h|--help) usage; exit 0;;
 
-	shell)
-	    sudo_or_exit
-	    interactive_shell
-	    ;;
+        shell)
+            sudo_or_exit
+            interactive_shell
+            ;;
         show)
             case $2 in
                 service)
-		    sudo_or_exit
-		    show_service
-		    ;;
+                    sudo_or_exit
+                    show_service
+                    ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         install)
             sudo_or_exit
             case $2 in
                 all) install_all ;;
-		user) assert_user ;;
+                user) assert_user ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        update)
+            sudo_or_exit
+            case $2 in
+                filtron) update_filtron ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         remove)
             sudo_or_exit
             case $2 in
                 all) remove_all;;
-		user) remove_user ;;
+                user) remove_user ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         activate)
@@ -242,16 +256,24 @@ EOF
     install_template --no-eval "$FILTRON_RULES" root root 644
 }
 
+update_filtron() {
+    rst_title "Update filtron" section
+    echo
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_service_prefix"
+go get -v -u github.com/asciimoo/filtron
+EOF
+}
+
 show_service () {
     rst_title "service status & log"
     echo
     systemctl status filtron.service
     echo
-    read -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
+    read -r -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
     echo
     while true;  do
-	trap break 2
-	journalctl -f -u filtron
+        trap break 2
+        journalctl -f -u filtron
     done
     return 0
 }
diff --git a/utils/lib.sh b/utils/lib.sh
index d06cabf26..fd6b92129 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -75,7 +75,7 @@ info_msg() { echo -e "INFO:  $*"; }
 
 clean_stdin() {
     if [[ $(uname -s) != 'Darwin' ]]; then
-        while $(read -n1 -t 0.1); do : ; done
+        while read -n1 -t 0.1; do : ; done
     fi
 }
 

From fe64274fb0405c47b3ba9039ab4e5b1440370a4d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 12 Jan 2020 15:53:35 +0100
Subject: [PATCH 008/125] docs: filtron.sh add remark about init systems

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst | 7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 009ca1c29..e8a2bfb15 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -17,6 +17,13 @@ filtron & go
 .. _Go: https://golang.org/
 .. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
 
+
+.. sidebar:: init system
+
+   ATM the ``filtron.sh`` supports only systemd init process used by debian,
+   ubuntu and many other dists.  If you have a working init.d file to start/stop
+   filtron service, please contribute.
+
 Filtron needs Go_ installed.  If Go_ is preinstalled, filtron_ is simply
 installed by ``go get`` package management (see `filtron README`_).  If you use
 filtron as middleware, a more isolated setup is recommended.

From b5449ec47cff805a05329a3e5f925cd661457530 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 13 Jan 2020 18:37:05 +0100
Subject: [PATCH 009/125] filtron: log suspiciously frequent queries (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/templates/etc/filtron/rules.json | 59 +++++++++++++++++---------
 1 file changed, 40 insertions(+), 19 deletions(-)

diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index b54e097a5..634f5f2d6 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,42 +1,63 @@
 [{
+  "name":"suspiciously frequent queries",
+  "filters":[
+    "Param:q",
+    "Path=^(/|/search)$"
+  ],
+  "interval":120,
+  "limit":9,
+  "actions":[
+    {"name":"log"}
+  ]
+ },
+ {
   "name":"search request",
   "filters":[
     "Param:q",
     "Path=^(/|/search)$"
   ],
-  "interval":60,
-  "limit":15,
+  "interval":120,
+  "limit":19,
+  "actions":[
+    {
+      "name":"block",
+      "params":{
+        "message":"common rate limit exceeded"
+      }
+    }
+  ],
   "subrules":[
     {
       "name":"roboagent limit",
       "interval":60,
-      "limit":15,
+      "limit":3,
       "filters":[
-        "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
+        "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby)"
       ],
       "actions":[
-	{"name": "log"},
-	{
+        {"name":"log"},
+        {
           "name":"block",
           "params":{
-            "message":"Rate limit exceeded"
+            "message":"rate limit exceeded"
           }
         }
       ]
     },
     {
       "name":"botlimit",
+      "interval":60,
       "limit":0,
       "stop":true,
       "filters":[
         "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
       ],
       "actions":[
-	{"name": "log"},
+        {"name":"log"},
         {
           "name":"block",
           "params":{
-            "message":"Rate limit exceeded"
+            "message":"rate limit exceeded"
           }
         }
       ]
@@ -44,17 +65,17 @@
     {
       "name":"IP limit",
       "interval":60,
-      "limit":15,
+      "limit":13,
       "stop":true,
       "aggregations":[
         "Header:X-Forwarded-For"
       ],
       "actions":[
-	{"name": "log"},
+        {"name":"log"},
         {
           "name":"block",
           "params":{
-            "message":"Rate limit exceeded"
+            "message":"rate limit exceeded"
           }
         }
       ]
@@ -62,34 +83,34 @@
     {
       "name":"rss/json limit",
       "interval":60,
-      "limit":15,
+      "limit":13,
       "stop":true,
       "filters":[
         "Param:format=(csv|json|rss)"
       ],
       "actions":[
-	{"name": "log"},
+        {"name":"log"},
         {
           "name":"block",
           "params":{
-            "message":"Rate limit exceeded"
+            "message":"rate limit exceeded"
           }
         }
       ]
-      },
+    },
     {
       "name":"useragent limit",
       "interval":60,
-      "limit":15,
+      "limit":13,
       "aggregations":[
         "Header:User-Agent"
       ],
       "actions":[
-	{"name": "log"},
+        {"name":"log"},
         {
           "name":"block",
           "params":{
-            "message":"Rate limit exceeded"
+            "message":"rate limit exceeded"
           }
         }
       ]

From 3cf31528f33c2a600cad21952b46a6ebe1e31420 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 14 Jan 2020 19:26:54 +0100
Subject: [PATCH 010/125] utils/searx.sh: add script to install isolated searx
 service (WIP)

WIP: written from scratch / linted but untested

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile         |   1 +
 utils/filtron.sh |   3 +-
 utils/lib.sh     | 186 ++++++++++++++++++++++++-
 utils/searx.sh   | 349 +++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 532 insertions(+), 7 deletions(-)
 create mode 100755 utils/searx.sh

diff --git a/Makefile b/Makefile
index fec004a5e..6577b374e 100644
--- a/Makefile
+++ b/Makefile
@@ -79,6 +79,7 @@ test: test.pep8 test.unit test.sh test.robot
 test.sh:
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
+	shellcheck -x utils/searx.sh
 
 test.pep8: pyenvinstall
 	$(PY_ENV_ACT); ./manage.sh pep8_check
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 5c8a738b0..50cce6e65 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -192,7 +192,8 @@ 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 adduser --shell /bin/bash --system --home $SERVICE_HOME \
+    --disabled-password --group --gecos 'Filtron' $SERVICE_USER
 sudo -H usermod -a -G shadow $SERVICE_USER
 groups $SERVICE_USER
 EOF
diff --git a/utils/lib.sh b/utils/lib.sh
index fd6b92129..f43a117cb 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -2,6 +2,12 @@
 # -*- coding: utf-8; mode: sh -*-
 # shellcheck disable=SC2059,SC1117,SC2162,SC2004
 
+ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
+ADMIN_NAME="${ADMIN_NAME:-$USER}"
+
+ADMIN_EMAIL="${ADMIN_EMAIL:-$(git config user.email)}"
+ADMIN_EMAIL="${ADMIN_EMAIL:-$USER@$(hostname)}"
+
 if [[ -z "${REPO_ROOT}" ]]; then
     REPO_ROOT=$(dirname "${BASH_SOURCE[0]}")
     while [ -h "${REPO_ROOT}" ] ; do
@@ -115,7 +121,7 @@ ask_yn() {
     esac
     echo
     while true; do
-	clean_stdin
+        clean_stdin
         printf "$1 ${choice} "
         # shellcheck disable=SC2086
         read -n1 $_t
@@ -240,7 +246,7 @@ choose_one() {
         fi
     done
     while true; do
-	clean_stdin
+        clean_stdin
         printf "$1 [$default] "
 
         if (( 10 > $max )); then
@@ -333,7 +339,7 @@ install_template() {
                 info_msg "install: ${template_file}"
                 sudo -H install -v -o "${owner}" -g "${group}" -m "${chmod}" \
                      "${template_file}" "${dst}" | prefix_stdout
-		break
+                break
                 ;;
             "leave file unchanged")
                 break
@@ -343,12 +349,180 @@ install_template() {
                 echo "// exit with CTRL-D"
                 sudo -H -u "${owner}" -i
                 $DIFF_CMD "${dst}" "${template_file}"
-		if ask_yn "did you edit ${template_file} to your needs?"; then
-		    break
-		fi
+                if ask_yn "did you edit ${template_file} to your needs?"; then
+                    break
+                fi
                 ;;
             "diff files")
                 $DIFF_CMD "${dst}" "${template_file}" | prefix_stdout
         esac
     done
 }
+
+
+# uWSGI
+# -----
+
+uWSGI_SETUP="${uWSGI_SETUP:=/etc/uwsgi}"
+
+uWSGI_restart() {
+
+    # usage:  uWSGI_restart()
+
+    info_msg "restart uWSGI service"
+    sudo -H systemctl restart uwsgi
+}
+
+uWSGI_install_app() {
+
+    # usage:  uWSGI_install_app [--no-eval] /etc/uwsgi/apps-available/myapp.ini ...
+
+    local do_eval=""
+    local CONF
+
+    if [[ "$1" == "--no-eval" ]]; then
+        no_eval=$1; shift
+    fi
+
+    for CONF in "$@"; do
+        install_template "$no_eval" "${CONF}" root root 644
+        uWSGI_enable_app "$(basename "${CONF}")"
+        info_msg "enabled uWSGI app: $(basename "${CONF}")"
+    done
+    uWSGI_restart
+}
+
+uWSGI_remove_app() {
+
+    # usage:  uWSGI_remove_app <path.ini> ...
+
+    local CONF
+    for CONF in "$@"; do
+        uWSGI_disable_app "$(basename "${CONF}")"
+        rm -f "$CONF"
+        info_msg "removed uWSGI app: $(basename "${CONF}")"
+    done
+    uWSGI_restart
+}
+
+# shellcheck disable=SC2164
+uWSGI_enable_app() {
+
+    # usage:   uWSGI_enable_app <path.ini>
+
+    local CONF=$1
+    if [[ -z $CONF ]]; then
+        err_msg "uWSGI_enable_app missing arguments"
+        return 42
+    fi
+    pushd "${uWSGI_SETUP}/apps-enabled" >/dev/null
+    # shellcheck disable=SC2226
+    ln -s "../apps-available/$(basename "${CONF}")"
+    info_msg "enabled uWSGI app: $(basename "${CONF}") (restart uWSGI required)"
+    popd >/dev/null
+}
+
+uWSGI_disable_app() {
+
+    # usage:   uWSGI_disable_app <path.ini>
+
+    local CONF=$1
+    if [[ -z $CONF ]]; then
+        err_msg "uWSGI_enable_app missing arguments"
+        return 42
+    fi
+
+    rm -f "${uWSGI_SETUP}/apps-enabled/$CONF"
+    info_msg "disabled uWSGI app: $(basename "${CONF}") (restart uWSGI required)"
+}
+
+# distro's package manager
+# ------------------------
+#
+# FIXME: Arch Linux & RHEL should be added
+#
+
+pkg_install() {
+
+    # usage: TITEL='install foobar' pkg_install foopkg barpkg
+
+    rst_title "${TITLE:-installation of packages}" section
+    echo -en "\npackage(s)::\n\n  $*\n" | $FMT
+
+    if ! ask_yn "Should packages be installed?" Yn 30; then
+        return 42
+    fi
+    # shellcheck disable=SC2068
+    apt-get install -y $@
+    wait_key 30
+}
+
+pkg_remove() {
+
+    # usage: TITEL='remove foobar' pkg_remove foopkg barpkg
+
+    rst_title "${TITLE:-remove packages}" section
+    echo -en "\npackage(s)::\n\n  $*\n" | $FMT
+
+    if ! ask_yn "Should packages be removed (purge)?" Yn 30; then
+        return 42
+    fi
+    apt-get purge --autoremove --ignore-missing -y "$@"
+    wait_key 30
+}
+
+pkg_is_installed() {
+
+    # usage: pkg_is_install foopkg || pkg_install foopkg
+
+    dpkg -l "$1" &> /dev/null
+    return $?
+}
+
+# git tooling
+# -----------
+
+# shellcheck disable=SC2164
+git_clone() {
+
+    # usage:
+    #
+    #    git_clone <url> <name> [<branch> [<user>]]
+    #    git_clone <url> <path> [<branch> [<user>]]
+    #
+    #  First form uses $CACHE/<name> as destination folder, second form clones
+    #  into <path>.  If repository is allready cloned, merge from origin and
+    #  update working tree (if needed, the caller has to stash local changes).
+    #
+    #    git clone https://github.com/asciimoo/searx searx-src origin/master searxlogin
+    #
+
+    local url="$1"
+    local dest="$2"
+    local branch="$3"
+    local user="$4"
+    local prefix=""
+
+    if [[ ! "${dest:0:1}" = "/" ]]; then
+        dest="$CACHE/$dest"
+    fi
+
+    [[ -z $branch ]] && branch=master
+    [[ -z $user ]] && [[ ! -z "${SUDO_USER}" ]] && user="${SUDO_USER}"
+    [[ -z $user ]] && prefix="sudo -H -u $user"
+
+    if [[ -d "${dest}" ]] ; then
+        info_msg "already cloned: $dest"
+        pushd "${dest}" > /dev/null
+        $prefix git checkout -b "$(basename "$branch")" --track "$branch"
+        $prefix git pull --all
+        popd > /dev/null
+
+    else
+        info_msg "clone into: $dest"
+        $prefix mkdir -p "$(dirname "$dest")"
+        pushd "${dest}" > /dev/null
+        git clone "$url" "$(basename "$dest")"
+        popd > /dev/null
+    fi
+}
diff --git a/utils/searx.sh b/utils/searx.sh
new file mode 100755
index 000000000..0d47820b0
--- /dev/null
+++ b/utils/searx.sh
@@ -0,0 +1,349 @@
+#!/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
+# ----------------------------------------------------------------------------
+
+SERVICE_NAME="searx"
+SERVICE_USER="${SERVICE_NAME}"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+SERVICE_HOME="/home/${SERVICE_USER}"
+
+SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
+SEARX_GIT_BRANCH="origin/master"
+
+# FIXME: Arch Linux & RHEL should be added
+
+SEARX_APT_PACKAGES="\
+libapache2-mod-uwsgi uwsgi uwsgi-plugin-python3 \
+  git build-essential libxslt-dev python3-dev python3-babel zlib1g-dev \
+  libffi-dev libssl-dev"
+
+SEARX_VENV="${SEARX_HOME}/searx-venv"
+SEARX_SRC="${SEARX_HOME}/searx-src"
+SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
+SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
+SEARX_UWSGI_APP="${uWSGI_SETUP}/apps-available/searx.ini"
+
+# shellcheck disable=SC2034
+CONFIG_FILES=(
+    "${SEARX_UWSGI_APP}"
+)
+
+# shellcheck disable=SC2034
+CONFIG_BACKUP_ENCRYPTED=(
+    "${SEARX_SETTINGS}"
+)
+
+# ----------------------------------------------------------------------------
+usage(){
+# ----------------------------------------------------------------------------
+
+    # shellcheck disable=SC1117
+    cat <<EOF
+
+usage:
+
+  $(basename "$0") shell
+  $(basename "$0") install    [all|user]
+  $(basename "$0") update     [searx]
+  $(basename "$0") remove     [all]
+  $(basename "$0") activate   [service]
+  $(basename "$0") deactivate [service]
+  $(basename "$0") show       [service]
+
+shell
+  start interactive shell from user ${SERVICE_USER}
+install / remove all
+  complete setup of searx service
+update searx
+  Update searx installation of user ${SERVICE_USER}
+activate
+  activate and start service daemon (systemd unit)
+deactivate service
+  stop and deactivate service daemon (systemd unit)
+install user
+  add service user '$SERVICE_USER' at $SERVICE_HOME
+show service
+  show service status and log
+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; exit 0;;
+
+        shell)
+            sudo_or_exit
+            interactive_shell
+            ;;
+        show)
+            case $2 in
+                service)
+                    sudo_or_exit
+                    show_service
+                    ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        install)
+            sudo_or_exit
+            case $2 in
+                all) install_all ;;
+                user) assert_user ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        update)
+            sudo_or_exit
+            case $2 in
+                searx) update_searx;;
+                *) 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
+                service)  activate_service ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        deactivate)
+            sudo_or_exit
+            case $2 in
+                service)  deactivate_service ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        *) usage "ERROR: unknown or missing command $1"; exit 42;;
+    esac
+}
+
+_service_prefix="  |$SERVICE_USER| "
+
+install_all() {
+    rst_title "Install $SERVICE_NAME (service)"
+    pkg_install "$SEARX_APT_PACKAGES"
+    wait_key
+    assert_user
+    wait_key
+    clone_searx
+    wait_key
+    create_venv
+    wait_key
+    configure_searx
+    wait_key
+    test_local_searx
+    wait_key
+    install_searx_uwsgi
+    wait_key
+
+    # ToDo ...
+    # install_apache_site
+    # test_public_searx
+    # info_msg "searX --> https://${SEARX_APACHE_DOMAIN}${SEARX_APACHE_URL}"
+
+}
+
+update_searx() {
+    rst_title "Update searx instance"
+
+    echo
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
+git stash push -m "BACKUP -- 'update server' at ($(date))"
+git checkout -b "$(basename "$SEARX_GIT_BRANCH")" --track "$SEARX_GIT_BRANCH"
+git pull "$SEARX_GIT_BRANCH"
+${SEARX_SRC}/manage.sh update_packages
+EOF
+    configure_searx
+
+    rst_title "${SEARX_SETTINGS}" section
+    rstBlock 'Diff between new setting file (<) and backup (>):'
+    echo
+    diff "$SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
+
+    local action
+    choose_one action "What should happen to the settings file? " \
+           "keep new configuration" \
+           "revert to the old configuration (backup file)" \
+           "start interactiv shell"
+    case $action in
+        "keep new configuration")
+	    info_msg "continue using new settings file"
+	    ;;
+        "revert to the old configuration (backup file)")
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
+EOF
+	    ;;
+	"start interactiv shell")
+	    interactive_shell
+	    ;;
+    esac
+    chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
+
+    # shellcheck disable=SC2016
+    rst_para 'Diff between local modified settings (<) and $SEARX_GIT_BRANCH branch (>):'
+    echo
+    git_diff
+    wait_key
+    uWSGI_restart
+}
+
+remove_all() {
+    rst_title "De-Install $SERVICE_NAME (service)"
+    remove_service
+    wait_key
+    remove_user
+}
+
+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" \
+  --disabled-password --group --gecos 'searx' $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"
+}
+
+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" 2>&1 | prefix_stdout
+    else
+        rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
+    fi
+}
+
+# shellcheck disable=SC2164
+clone_searx(){
+    rst_title "Clone searx sources" section
+    echo
+    git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
+	      "$SEARX_GIT_BRANCH" "$SERVICE_USER"
+
+    pushd "${SEARX_SRC}" > /dev/null
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
+cd "${SEARX_SRC}"
+git config user.email "$ADMIN_EMAIL"
+git config user.name "$ADMIN_NAME"
+git checkout "$SEARX_GIT_BRANCH"
+EOF
+    popd > /dev/null
+}
+
+create_venv(){
+    rst_title "Create virtualenv (python)" section
+
+    rst_para "Create venv in ${SEARX_VENV} and install needed python packages."
+    echo
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+rm -rf "${SEARX_VENV}"
+python3 -m venv "${SEARX_VENV}"
+. ${SEARX_VENV}/bin/activate
+${SEARX_SRC}/manage.sh update_packages
+EOF
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+grep -qFs -- 'source ${SEARX_VENV}/bin/activate' ~/.profile \
+  || echo 'source ${SEARX_VENV}/bin/activate' >> ~/.profile
+EOF
+
+}
+
+configure_searx(){
+    rst_title "Configure searx" section
+    rst_para "Setup searx config located at $SEARX_SETTINGS"
+    echo
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS"
+sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
+EOF
+}
+
+test_local_searx(){
+    rstHeading "Testing searx instance localy" section
+    echo
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
+timeout 5 python3 searx/webapp.py &
+sleep 1
+curl --location --verbose --head --insecure http://127.0.0.1:8888/
+sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
+EOF
+    waitKEY
+}
+
+install_searx_uwsgi() {
+    rst_title "Install searx's uWSGI app (searx.ini)" section
+    echo
+    uWSGI_install_app "$SEARX_UWSGI_APP"
+}
+
+remove_searx_uwsgi() {
+    rst_title "Remove searx's uWSGI app (searx.ini)" section
+    echo
+    uWSGI_remove_app "$SEARX_UWSGI_APP"
+}
+
+activate_service () {
+    rst_title "Activate $SERVICE_NAME (service)" section
+    uWSGI_enable_app "$SEARX_UWSGI_APP"
+}
+
+deactivate_service () {
+    rst_title "De-Activate $SERVICE_NAME (service)" section
+    uWSGI_disable_app "$SEARX_UWSGI_APP"
+}
+
+interactive_shell(){
+    echo "// exit with CTRL-D"
+    sudo -H -u "${SERVICE_USER}" -i
+}
+
+git_diff(){
+    sudo -H -u "${SERVICE_USER}" -i <<EOF
+cd ${SEARX_REPO_FOLDER}
+git --no-pager diff
+EOF
+}
+
+show_service () {
+    rst_title "service status & log"
+    echo
+    systemctl status uwsgi.service
+    echo
+    read -r -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
+    echo
+    while true;  do
+        trap break 2
+        journalctl -f -u uwsgi.service
+    done
+    return 0
+}
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------

From 89df9d91412c2ebb8817e72c0ca9c8cfc5852f66 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 16 Jan 2020 14:01:38 +0100
Subject: [PATCH 011/125] utils/searx.sh: add script to install isolated searx
 service (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                              |   2 +-
 utils/lib.sh                                  |  61 +++++-----
 utils/searx.sh                                | 112 ++++++++++++------
 .../etc/uwsgi/apps-available/searx.ini        |  62 ++++++++++
 4 files changed, 172 insertions(+), 65 deletions(-)
 create mode 100644 utils/templates/etc/uwsgi/apps-available/searx.ini

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 50cce6e65..bf6016b0f 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-# -*- coding: utf-8; mode: sh -*-
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
 # shellcheck disable=SC2119
 
 # shellcheck source=utils/lib.sh
diff --git a/utils/lib.sh b/utils/lib.sh
index f43a117cb..827862b7c 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-# -*- coding: utf-8; mode: sh -*-
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
 # shellcheck disable=SC2059,SC1117,SC2162,SC2004
 
 ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
@@ -265,9 +265,9 @@ choose_one() {
         _t=""
         err_msg "invalid choice"
     done
+    eval "$env_name"='${list[${REPLY}]}'
     echo
     clean_stdin
-    eval "$env_name"='${list[${REPLY}]}'
 }
 
 install_template() {
@@ -375,34 +375,32 @@ uWSGI_restart() {
 
 uWSGI_install_app() {
 
-    # usage:  uWSGI_install_app [--no-eval] /etc/uwsgi/apps-available/myapp.ini ...
+    # usage:  uWSGI_install_app [--no-eval] /etc/uwsgi/apps-available/myapp.ini
 
-    local do_eval=""
-    local CONF
+    local no_eval=""
+    local CONF=""
 
     if [[ "$1" == "--no-eval" ]]; then
         no_eval=$1; shift
     fi
 
-    for CONF in "$@"; do
-        install_template "$no_eval" "${CONF}" root root 644
-        uWSGI_enable_app "$(basename "${CONF}")"
-        info_msg "enabled uWSGI app: $(basename "${CONF}")"
-    done
+    CONF=$1
+    # shellcheck disable=SC2086
+    install_template $no_eval "${CONF}" root root 644
+    uWSGI_enable_app "$(basename "${CONF}")"
     uWSGI_restart
+    info_msg "installed uWSGI app: $(basename "${CONF}")"
 }
 
 uWSGI_remove_app() {
 
-    # usage:  uWSGI_remove_app <path.ini> ...
+    # usage:  uWSGI_remove_app <path.ini>
 
-    local CONF
-    for CONF in "$@"; do
-        uWSGI_disable_app "$(basename "${CONF}")"
-        rm -f "$CONF"
-        info_msg "removed uWSGI app: $(basename "${CONF}")"
-    done
+    local CONF=$1
+    uWSGI_disable_app "$(basename "${CONF}")"
     uWSGI_restart
+    rm -f "$CONF"
+    info_msg "removed uWSGI app: $(basename "${CONF}")"
 }
 
 # shellcheck disable=SC2164
@@ -416,6 +414,7 @@ uWSGI_enable_app() {
         return 42
     fi
     pushd "${uWSGI_SETUP}/apps-enabled" >/dev/null
+    rm -f "$(basename "${CONF}")"
     # shellcheck disable=SC2226
     ln -s "../apps-available/$(basename "${CONF}")"
     info_msg "enabled uWSGI app: $(basename "${CONF}") (restart uWSGI required)"
@@ -454,7 +453,6 @@ pkg_install() {
     fi
     # shellcheck disable=SC2068
     apt-get install -y $@
-    wait_key 30
 }
 
 pkg_remove() {
@@ -468,7 +466,6 @@ pkg_remove() {
         return 42
     fi
     apt-get purge --autoremove --ignore-missing -y "$@"
-    wait_key 30
 }
 
 pkg_is_installed() {
@@ -491,7 +488,7 @@ git_clone() {
     #    git_clone <url> <path> [<branch> [<user>]]
     #
     #  First form uses $CACHE/<name> as destination folder, second form clones
-    #  into <path>.  If repository is allready cloned, merge from origin and
+    #  into <path>.  If repository is allready cloned, pull from <branch> and
     #  update working tree (if needed, the caller has to stash local changes).
     #
     #    git clone https://github.com/asciimoo/searx searx-src origin/master searxlogin
@@ -501,7 +498,8 @@ git_clone() {
     local dest="$2"
     local branch="$3"
     local user="$4"
-    local prefix=""
+    local bash_cmd="bash"
+    local remote="origin"
 
     if [[ ! "${dest:0:1}" = "/" ]]; then
         dest="$CACHE/$dest"
@@ -509,20 +507,21 @@ git_clone() {
 
     [[ -z $branch ]] && branch=master
     [[ -z $user ]] && [[ ! -z "${SUDO_USER}" ]] && user="${SUDO_USER}"
-    [[ -z $user ]] && prefix="sudo -H -u $user"
+    [[ ! -z $user ]] && bash_cmd="sudo -H -u $user -i"
 
     if [[ -d "${dest}" ]] ; then
         info_msg "already cloned: $dest"
-        pushd "${dest}" > /dev/null
-        $prefix git checkout -b "$(basename "$branch")" --track "$branch"
-        $prefix git pull --all
-        popd > /dev/null
-
+	tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+cd "${dest}"
+git checkout -m -B "$branch" --track "$remote/$branch"
+git pull --all
+EOF
     else
         info_msg "clone into: $dest"
-        $prefix mkdir -p "$(dirname "$dest")"
-        pushd "${dest}" > /dev/null
-        git clone "$url" "$(basename "$dest")"
-        popd > /dev/null
+	tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+mkdir -p "$(dirname "$dest")"
+cd "$(dirname "$dest")"
+git clone --branch "$branch" --origin "$remote" "$url" "$(basename "$dest")"
+EOF
     fi
 }
diff --git a/utils/searx.sh b/utils/searx.sh
index 0d47820b0..6955f82a2 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -1,5 +1,5 @@
 #!/usr/bin/env bash
-# -*- coding: utf-8; mode: sh -*-
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
 # shellcheck disable=SC2119
 
 # shellcheck source=utils/lib.sh
@@ -15,8 +15,11 @@ SERVICE_USER="${SERVICE_NAME}"
 SERVICE_GROUP="${SERVICE_USER}"
 SERVICE_HOME="/home/${SERVICE_USER}"
 
+# shellcheck disable=SC2034
+SEARX_URL="127.0.0.1:8888"
+
 SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
-SEARX_GIT_BRANCH="origin/master"
+SEARX_GIT_BRANCH="master"
 
 # FIXME: Arch Linux & RHEL should be added
 
@@ -25,8 +28,8 @@ libapache2-mod-uwsgi uwsgi uwsgi-plugin-python3 \
   git build-essential libxslt-dev python3-dev python3-babel zlib1g-dev \
   libffi-dev libssl-dev"
 
-SEARX_VENV="${SEARX_HOME}/searx-venv"
-SEARX_SRC="${SEARX_HOME}/searx-src"
+SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
+SEARX_SRC="${SERVICE_HOME}/searx-src"
 SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
 SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
 SEARX_UWSGI_APP="${uWSGI_SETUP}/apps-available/searx.ini"
@@ -51,25 +54,26 @@ usage(){
 usage:
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user]
+  $(basename "$0") install    [all|user|pyenv|searx-src]
   $(basename "$0") update     [searx]
-  $(basename "$0") remove     [all]
+  $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
   $(basename "$0") show       [service]
 
 shell
   start interactive shell from user ${SERVICE_USER}
-install / remove all
-  complete setup of searx service
+install / remove
+  all:        complete (de-) installation of searx service
+  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  searx-src:  clone $SEARX_GIT_URL
+  pyenv:       create/remove virtualenv (python) in $SEARX_PYENV
 update searx
   Update searx installation of user ${SERVICE_USER}
 activate
   activate and start service daemon (systemd unit)
 deactivate service
   stop and deactivate service daemon (systemd unit)
-install user
-  add service user '$SERVICE_USER' at $SERVICE_HOME
 show service
   show service status and log
 EOF
@@ -102,6 +106,8 @@ main(){
             case $2 in
                 all) install_all ;;
                 user) assert_user ;;
+                pyenv) create_pyenv ;;
+                searx-src) clone_searx ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
@@ -115,6 +121,8 @@ main(){
             case $2 in
                 all) remove_all;;
                 user) remove_user ;;
+                pyenv) remove_pyenv ;;
+                searx-src) remove_searx ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         activate)
@@ -143,7 +151,7 @@ install_all() {
     wait_key
     clone_searx
     wait_key
-    create_venv
+    create_pyenv
     wait_key
     configure_searx
     wait_key
@@ -167,7 +175,7 @@ update_searx() {
 cd ${SEARX_SRC}
 cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
 git stash push -m "BACKUP -- 'update server' at ($(date))"
-git checkout -b "$(basename "$SEARX_GIT_BRANCH")" --track "$SEARX_GIT_BRANCH"
+git checkout -b $SEARX_GIT_BRANCH" --track "$SEARX_GIT_BRANCH"
 git pull "$SEARX_GIT_BRANCH"
 ${SEARX_SRC}/manage.sh update_packages
 EOF
@@ -185,16 +193,16 @@ EOF
            "start interactiv shell"
     case $action in
         "keep new configuration")
-	    info_msg "continue using new settings file"
-	    ;;
+            info_msg "continue using new settings file"
+            ;;
         "revert to the old configuration (backup file)")
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
 EOF
-	    ;;
-	"start interactiv shell")
-	    interactive_shell
-	    ;;
+            ;;
+        "start interactiv shell")
+            interactive_shell
+            ;;
     esac
     chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
 
@@ -208,7 +216,10 @@ EOF
 
 remove_all() {
     rst_title "De-Install $SERVICE_NAME (service)"
-    remove_service
+    if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
+        return
+    fi
+    remove_searx_uwsgi
     wait_key
     remove_user
 }
@@ -240,35 +251,71 @@ remove_user() {
 clone_searx(){
     rst_title "Clone searx sources" section
     echo
+    SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
+    if [[ ! "${SERVICE_HOME}" ]]; then
+        err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"
+        return 42
+    fi
+    export SERVICE_HOME
+
     git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
-	      "$SEARX_GIT_BRANCH" "$SERVICE_USER"
+              "$SEARX_GIT_BRANCH" "$SERVICE_USER"
 
     pushd "${SEARX_SRC}" > /dev/null
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
 cd "${SEARX_SRC}"
 git config user.email "$ADMIN_EMAIL"
 git config user.name "$ADMIN_NAME"
-git checkout "$SEARX_GIT_BRANCH"
+git config --list
 EOF
     popd > /dev/null
 }
 
-create_venv(){
-    rst_title "Create virtualenv (python)" section
+remove_searx() {
+    rst_title "Drop searx sources" section
+    if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
+        rm -rf "$SEARX_SRC"
+    else
+        rst_para "Leave searx sources unchanged."
+    fi
+}
 
-    rst_para "Create venv in ${SEARX_VENV} and install needed python packages."
+create_pyenv(){
+    rst_title "Create virtualenv (python)" section
     echo
+    if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
+        err_msg "to create pyenv for searx, searx has to be cloned first"
+        return 42
+    fi
+    info_msg "create pyenv in ${SEARX_PYENV}"
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+rm -rf "${SEARX_PYENV}"
+python3 -m venv "${SEARX_PYENV}"
+grep -qFs -- 'source ${SEARX_PYENV}/bin/activate' ~/.profile \
+  || echo 'source ${SEARX_PYENV}/bin/activate' >> ~/.profile
+EOF
+    info_msg "inspect python's virtual environment"
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+command -v python && python --version
+EOF
+    wait_key
+    info_msg "install needed python packages"
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
-rm -rf "${SEARX_VENV}"
-python3 -m venv "${SEARX_VENV}"
-. ${SEARX_VENV}/bin/activate
 ${SEARX_SRC}/manage.sh update_packages
 EOF
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
-grep -qFs -- 'source ${SEARX_VENV}/bin/activate' ~/.profile \
-  || echo 'source ${SEARX_VENV}/bin/activate' >> ~/.profile
-EOF
+}
 
+remove_pyenv(){
+    rst_title "Remove virtualenv (python)" section
+    if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
+        return
+    fi
+    info_msg "remove pyenv activation from ~/.profile"
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+grep -v 'source ${SEARX_PYENV}/bin/activate' ~/.profile > ~/.profile.##
+mv ~/.profile.## ~/.profile
+EOF
+    rm -rf "${SEARX_PYENV}"
 }
 
 configure_searx(){
@@ -283,7 +330,7 @@ EOF
 }
 
 test_local_searx(){
-    rstHeading "Testing searx instance localy" section
+    rst_title "Testing searx instance localy" section
     echo
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
@@ -293,7 +340,6 @@ sleep 1
 curl --location --verbose --head --insecure http://127.0.0.1:8888/
 sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
 EOF
-    waitKEY
 }
 
 install_searx_uwsgi() {
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
new file mode 100644
index 000000000..138a57384
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -0,0 +1,62 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}
+
+# disable logging for privacy
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python3
+
+# By default the Python plugin does not initialize the GIL.  This means your
+# app-generated threads will not run.  If you need threads, remember to enable
+# them with enable-threads.  Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SERVICE_HOME}
+
+
+# plugin http
+# -----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+http = ${SEARX_URL}

From 9b5a7f7559faf121e44ba3e7260290bd7efe74bf Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 20 Jan 2020 16:55:05 +0100
Subject: [PATCH 012/125] utils/searx.sh: add script to install isolated searx
 service

First version which serves searx over uwsgi at http://127.0.0.1:8888

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh                                  |  64 +++++---
 utils/searx.sh                                | 137 +++++++++++++++---
 .../etc/uwsgi/apps-available/searx.ini        |   6 +-
 3 files changed, 160 insertions(+), 47 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index 827862b7c..fadbcd648 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -373,66 +373,84 @@ uWSGI_restart() {
     sudo -H systemctl restart uwsgi
 }
 
+uWSGI_app_available() {
+    # usage:  uWSGI_app_available <myapp.ini>
+    local CONF="$1"
+    if [[ -z $CONF ]]; then
+        err_msg "uWSGI_app_available: missing arguments"
+        return 42
+    fi
+    [[ -f "${uWSGI_SETUP}/apps-available/${CONF}" ]]
+}
+
 uWSGI_install_app() {
 
-    # usage:  uWSGI_install_app [--no-eval] /etc/uwsgi/apps-available/myapp.ini
+    # usage:  uWSGI_install_app [--no-eval] <myapp.ini>
 
     local no_eval=""
-    local CONF=""
+    local CONF="$1"
 
     if [[ "$1" == "--no-eval" ]]; then
         no_eval=$1; shift
     fi
 
-    CONF=$1
     # shellcheck disable=SC2086
-    install_template $no_eval "${CONF}" root root 644
-    uWSGI_enable_app "$(basename "${CONF}")"
+    install_template $no_eval "${uWSGI_SETUP}/apps-available/${CONF}" root root 644
+    uWSGI_enable_app "${CONF}"
     uWSGI_restart
-    info_msg "installed uWSGI app: $(basename "${CONF}")"
+    info_msg "installed uWSGI app: ${CONF}"
 }
 
 uWSGI_remove_app() {
 
-    # usage:  uWSGI_remove_app <path.ini>
+    # usage:  uWSGI_remove_app <myapp.ini>
 
-    local CONF=$1
-    uWSGI_disable_app "$(basename "${CONF}")"
+    local CONF="$1"
+    uWSGI_disable_app "${CONF}"
     uWSGI_restart
-    rm -f "$CONF"
-    info_msg "removed uWSGI app: $(basename "${CONF}")"
+    rm -f "${uWSGI_SETUP}/apps-available/${CONF}"
+    info_msg "removed uWSGI app: ${CONF}"
+}
+
+uWSGI_app_enabled() {
+    # usage:  uWSGI_app_enabled <myapp.ini>
+    local CONF="$1"
+    if [[ -z $CONF ]]; then
+        err_msg "uWSGI_app_enabled: missing arguments"
+        return 42
+    fi
+    [[ -f "${uWSGI_SETUP}/apps-enabled/${CONF}" ]]
 }
 
 # shellcheck disable=SC2164
 uWSGI_enable_app() {
 
-    # usage:   uWSGI_enable_app <path.ini>
+    # usage:   uWSGI_enable_app <myapp.ini>
 
-    local CONF=$1
+    local CONF="$1"
     if [[ -z $CONF ]]; then
-        err_msg "uWSGI_enable_app missing arguments"
+        err_msg "uWSGI_enable_app: missing arguments"
         return 42
     fi
     pushd "${uWSGI_SETUP}/apps-enabled" >/dev/null
-    rm -f "$(basename "${CONF}")"
+    rm -f "$CONF"
     # shellcheck disable=SC2226
-    ln -s "../apps-available/$(basename "${CONF}")"
-    info_msg "enabled uWSGI app: $(basename "${CONF}") (restart uWSGI required)"
+    ln -s "../apps-available/${CONF}"
+    info_msg "enabled uWSGI app: ${CONF} (restart uWSGI required)"
     popd >/dev/null
 }
 
 uWSGI_disable_app() {
 
-    # usage:   uWSGI_disable_app <path.ini>
+    # usage:   uWSGI_disable_app <myapp.ini>
 
-    local CONF=$1
+    local CONF="$1"
     if [[ -z $CONF ]]; then
-        err_msg "uWSGI_enable_app missing arguments"
+        err_msg "uWSGI_enable_app: missing arguments"
         return 42
     fi
-
-    rm -f "${uWSGI_SETUP}/apps-enabled/$CONF"
-    info_msg "disabled uWSGI app: $(basename "${CONF}") (restart uWSGI required)"
+    rm -f "${uWSGI_SETUP}/apps-enabled/${CONF}"
+    info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)"
 }
 
 # distro's package manager
diff --git a/utils/searx.sh b/utils/searx.sh
index 6955f82a2..c9adecc8c 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -32,11 +32,11 @@ SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
 SEARX_SRC="${SERVICE_HOME}/searx-src"
 SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
 SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
-SEARX_UWSGI_APP="${uWSGI_SETUP}/apps-available/searx.ini"
+SEARX_UWSGI_APP="searx.ini"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
-    "${SEARX_UWSGI_APP}"
+    "${uWSGI_SETUP}/apps-available/${SEARX_UWSGI_APP}"
 )
 
 # shellcheck disable=SC2034
@@ -45,7 +45,7 @@ CONFIG_BACKUP_ENCRYPTED=(
 )
 
 # ----------------------------------------------------------------------------
-usage(){
+usage() {
 # ----------------------------------------------------------------------------
 
     # shellcheck disable=SC1117
@@ -60,6 +60,7 @@ usage:
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
   $(basename "$0") show       [service]
+  $(basename "$0") option     [debug-on|debug-off]
 
 shell
   start interactive shell from user ${SERVICE_USER}
@@ -75,12 +76,14 @@ activate
 deactivate service
   stop and deactivate service daemon (systemd unit)
 show service
-  show service status and log
+  run some small tests and show service's status and log
+option
+  set one of te available options
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
 
-main(){
+main() {
     rst_title "$SERVICE_NAME" part
 
     local _usage="ERROR: unknown or missing $1 command $2"
@@ -128,13 +131,21 @@ main(){
         activate)
             sudo_or_exit
             case $2 in
-                service)  activate_service ;;
+                service)
+                    activate_service; uWSGI_restart ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         deactivate)
             sudo_or_exit
             case $2 in
-                service)  deactivate_service ;;
+                service)  deactivate_service; uWSGI_restart ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        option)
+            sudo_or_exit
+            case $2 in
+                debug-on)  echo; enable_debug ;;
+                debug-off)  echo; disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
@@ -158,6 +169,11 @@ install_all() {
     test_local_searx
     wait_key
     install_searx_uwsgi
+    if service_is_available; then
+        info_msg "URL http://$SEARX_URL is available."
+    else
+        err_msg "URL http://$SEARX_URL not available, check searx & uwsgi setup!"
+    fi
     wait_key
 
     # ToDo ...
@@ -224,6 +240,10 @@ remove_all() {
     remove_user
 }
 
+user_is_available() {
+    sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
+}
+
 assert_user() {
     rst_title "user $SERVICE_USER" section
     echo
@@ -247,8 +267,12 @@ remove_user() {
     fi
 }
 
+clone_is_available() {
+    [[ -f "$SEARX_SETTINGS" ]]
+}
+
 # shellcheck disable=SC2164
-clone_searx(){
+clone_searx() {
     rst_title "Clone searx sources" section
     echo
     SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
@@ -280,7 +304,11 @@ remove_searx() {
     fi
 }
 
-create_pyenv(){
+pyenv_is_available() {
+    [[ -f "${SEARX_PYENV}/bin/activate" ]]
+}
+
+create_pyenv() {
     rst_title "Create virtualenv (python)" section
     echo
     if [[ ! -f "${SEARX_SRC}/manage.sh" ]]; then
@@ -305,7 +333,7 @@ ${SEARX_SRC}/manage.sh update_packages
 EOF
 }
 
-remove_pyenv(){
+remove_pyenv() {
     rst_title "Remove virtualenv (python)" section
     if ! ask_yn "Do you really want to drop ${SEARX_PYENV} ?"; then
         return
@@ -318,7 +346,7 @@ EOF
     rm -rf "${SEARX_PYENV}"
 }
 
-configure_searx(){
+configure_searx() {
     rst_title "Configure searx" section
     rst_para "Setup searx config located at $SEARX_SETTINGS"
     echo
@@ -329,7 +357,7 @@ sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
 EOF
 }
 
-test_local_searx(){
+test_local_searx() {
     rst_title "Testing searx instance localy" section
     echo
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
@@ -337,7 +365,7 @@ cd ${SEARX_SRC}
 sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
 timeout 5 python3 searx/webapp.py &
 sleep 1
-curl --location --verbose --head --insecure http://127.0.0.1:8888/
+curl --location --verbose --head --insecure $SEARX_URL
 sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
 EOF
 }
@@ -354,39 +382,106 @@ remove_searx_uwsgi() {
     uWSGI_remove_app "$SEARX_UWSGI_APP"
 }
 
-activate_service () {
+activate_service() {
     rst_title "Activate $SERVICE_NAME (service)" section
+    echo
     uWSGI_enable_app "$SEARX_UWSGI_APP"
 }
 
-deactivate_service () {
+deactivate_service() {
     rst_title "De-Activate $SERVICE_NAME (service)" section
+    echo
     uWSGI_disable_app "$SEARX_UWSGI_APP"
 }
 
-interactive_shell(){
+interactive_shell() {
     echo "// exit with CTRL-D"
     sudo -H -u "${SERVICE_USER}" -i
 }
 
-git_diff(){
+git_diff() {
     sudo -H -u "${SERVICE_USER}" -i <<EOF
 cd ${SEARX_REPO_FOLDER}
 git --no-pager diff
 EOF
 }
 
-show_service () {
+service_is_available() {
+    curl --insecure "http://$SEARX_URL" &>/dev/null
+}
+
+enable_debug() {
+    info_msg "try to enable debug mode ..."
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
+EOF
+    uWSGI_restart
+}
+
+disable_debug() {
+    info_msg "try to disable debug mode ..."
+    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
+EOF
+    uWSGI_restart
+}
+
+
+show_service() {
     rst_title "service status & log"
     echo
-    systemctl status uwsgi.service
+
+    if user_is_available; then
+        info_msg "service account $SERVICE_USER available."
+    else
+        err_msg "service account $SERVICE_USER not available!"
+    fi
+    if service_is_available; then
+        info_msg "URL http://$SEARX_URL is available."
+    else
+        err_msg "URL http://$SEARX_URL not available!"
+    fi
+    if pyenv_is_available; then
+        info_msg "${SEARX_PYENV}/bin/activate is available."
+    else
+        err_msg "${SEARX_PYENV}/bin/activate not available!"
+    fi
+    if clone_is_available; then
+        info_msg "Searx software is installed."
+    else
+        err_msg "Missing searx software!"
+    fi
+
+    uWSGI_app_available "$SEARX_UWSGI_APP" \
+        || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
+
+    if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
+        info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
+    else
+        err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
+    fi
+
+    local _debug_on
+    if ask_yn "Enable searx debug mode?"; then
+        enable_debug
+        _debug_on=1
+    fi
+    wait_key
     echo
-    read -r -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
+    systemctl status uwsgi.service
+    read -r -s -n1 -t 2  -p "// use CTRL-C to stop monitoring the log"
     echo
     while true;  do
         trap break 2
-        journalctl -f -u uwsgi.service
+        #journalctl -f -u uwsgi.service
+        tail -f /var/log/uwsgi/app/searx.log
     done
+
+    if [[ $_debug_on == 1 ]]; then
+        disable_debug
+    fi
     return 0
 }
 
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index 138a57384..d3893b3ad 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -10,7 +10,7 @@ uid = ${SERVICE_USER}
 gid = ${SERVICE_GROUP}
 
 # chdir to specified directory before apps loading
-chdir = ${SEARX_SRC}
+chdir = ${SEARX_SRC}/searx
 
 # disable logging for privacy
 disable-logging = true
@@ -28,7 +28,7 @@ master = true
 lazy-apps = true
 
 # load uWSGI plugins
-plugin = python3
+plugin = python3,http
 
 # By default the Python plugin does not initialize the GIL.  This means your
 # app-generated threads will not run.  If you need threads, remember to enable
@@ -50,7 +50,7 @@ module = searx.webapp
 virtualenv = ${SEARX_PYENV}
 
 # add directory (or glob) to pythonpath
-pythonpath = ${SERVICE_HOME}
+pythonpath = ${SEARX_SRC}
 
 
 # plugin http

From af2f58fc5847756d20741bb4c782f07943b0af60 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 20 Jan 2020 19:08:56 +0100
Subject: [PATCH 013/125] utils/filtron.sh: add script to install filtron
 middleware (apache)

Preparation for the installation of an apache site.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh | 98 +++++++++++++++++++++++++++++++++++++++++-------
 utils/lib.sh     | 53 ++++++++++++++++++++++++++
 utils/searx.sh   | 22 ++++++++---
 3 files changed, 154 insertions(+), 19 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index bf6016b0f..bb6597fa5 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -13,11 +13,8 @@ FILTRON_ETC="/etc/filtron"
 
 FILTRON_RULES="$FILTRON_ETC/rules.json"
 
-# shellcheck disable=SC2034
 FILTRON_API="127.0.0.1:4005"
-# shellcheck disable=SC2034
 FILTRON_LISTEN="127.0.0.1:4004"
-# shellcheck disable=SC2034
 FILTRON_TARGET="127.0.0.1:8888"
 
 SERVICE_NAME="filtron"
@@ -32,6 +29,8 @@ 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")
 
+APACHE_SITE="searx.conf"
+
 # shellcheck disable=SC2034
 CONFIG_FILES=(
     "${FILTRON_RULES}"
@@ -39,7 +38,7 @@ CONFIG_FILES=(
 )
 
 # ----------------------------------------------------------------------------
-usage(){
+usage() {
 # ----------------------------------------------------------------------------
 
     # shellcheck disable=SC1117
@@ -73,7 +72,7 @@ EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
 
-main(){
+main() {
     rst_title "$SERVICE_NAME" part
 
     local _usage="ERROR: unknown or missing $1 command $2"
@@ -140,6 +139,10 @@ install_all() {
     wait_key
     install_service
     wait_key
+    if apache_is_installed; then
+        install_apache_site
+        wait_key
+    fi
 }
 
 remove_all() {
@@ -151,10 +154,22 @@ remove_all() {
     wait_key
 }
 
+filtron_is_available() {
+    curl --insecure "http://${FILTRON_LISTEN}" &>/dev/null
+}
+
+api_is_available() {
+    curl --insecure "http://${FILTRON_API}" &>/dev/null
+}
+
+target_is_available() {
+    curl --insecure "http://${FILTRON_TARGET}" &>/dev/null
+}
+
 install_service() {
     rst_title "Install System-D Unit ${SERVICE_NAME}.service" section
     echo
-    install_template ${SERVICE_SYSTEMD_UNIT} root root 644
+    install_template "${SERVICE_SYSTEMD_UNIT}" root root 644
     wait_key
     activate_service
 }
@@ -167,19 +182,19 @@ remove_service() {
     rm "${SERVICE_SYSTEMD_UNIT}"  2>&1 | prefix_stdout
 }
 
-activate_service () {
+activate_service() {
     rst_title "Activate $SERVICE_NAME (service)" section
     echo
-    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+    tee_stderr <<EOF | bash 2>&1
 systemctl enable $SERVICE_NAME.service
 systemctl restart $SERVICE_NAME.service
 EOF
-    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+    tee_stderr <<EOF | bash 2>&1
 systemctl status $SERVICE_NAME.service
 EOF
 }
 
-deactivate_service () {
+deactivate_service() {
     rst_title "De-Activate $SERVICE_NAME (service)" section
     echo
     tee_stderr <<EOF | bash 2>&1 | prefix_stdout
@@ -188,6 +203,10 @@ systemctl disable $SERVICE_NAME.service
 EOF
 }
 
+user_is_available() {
+    sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
+}
+
 assert_user() {
     rst_title "user $SERVICE_USER" section
     echo
@@ -228,7 +247,11 @@ interactive_shell(){
 
 _service_prefix="  |$SERVICE_USER| "
 
-install_go(){
+go_is_available() {
+    sudo -i -u "$SERVICE_USER" which go &>/dev/null
+}
+
+install_go() {
     rst_title "Install Go in user's HOME" section
 
     rst_para "download and install go binary .."
@@ -248,6 +271,10 @@ which go >/dev/null &&  go version && echo "congratulations -- Go installation O
 EOF
 }
 
+filtron_is_installed() {
+    [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
+}
+
 install_filtron() {
     rst_title "Install filtron in user's ~/go-apps" section
     echo
@@ -265,12 +292,48 @@ go get -v -u github.com/asciimoo/filtron
 EOF
 }
 
-show_service () {
+show_service() {
     rst_title "service status & log"
     echo
-    systemctl status filtron.service
+
+    apache_is_installed && info_msg "Apache is installed."
+
+    if user_is_available; then
+        info_msg "service account $SERVICE_USER available."
+    else
+        err_msg "service account $SERVICE_USER not available!"
+    fi
+    if go_is_available; then
+        info_msg "~$SERVICE_USER: go is installed"
+    else
+        err_msg "~$SERVICE_USER: go is not installed"
+    fi
+    if filtron_is_installed; then
+        info_msg "~$SERVICE_USER: filtron app is installed"
+    else
+        err_msg "~$SERVICE_USER: filtron app is not installed!"
+    fi
+    if api_is_available; then
+        info_msg "API available at: http://${FILTRON_API}"
+    else
+        err_msg "API not available at: http://${FILTRON_API}"
+    fi
+    if filtron_is_available; then
+        info_msg "Filtron listening on: http://${FILTRON_LISTEN}"
+    else
+        err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
+    fi
+    if target_is_available; then
+        info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
+    else
+        err_msg "Filtron's target is not available at:  http://${FILTRON_TARGET}"
+    fi
+
+    wait_key
     echo
-    read -r -s -n1 -t 5  -p "// use CTRL-C to stop monitoring the log"
+    systemctl --no-pager -l status filtron.service
+    echo
+    read -r -s -n1 -t 2  -p "// use CTRL-C to stop monitoring the log"
     echo
     while true;  do
         trap break 2
@@ -279,6 +342,13 @@ show_service () {
     return 0
 }
 
+install_apache_site() {
+    rst_title "Install Apache site $APACHE_SITE" section
+    echo
+    err_msg "not yet implemented (${APACHE_SITE})"; return 42
+    # apache_install_site "${APACHE_SITE}"
+}
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
diff --git a/utils/lib.sh b/utils/lib.sh
index fadbcd648..40ecbb92e 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -359,6 +359,59 @@ install_template() {
     done
 }
 
+# Apache
+# ------
+
+# FIXME: Arch Linux & RHEL should be added
+
+if [[ -z "${APACHE_SITES_AVAILABE}" ]]; then
+    APACHE_SITES_AVAILABE="/etc/apache2/sites-available"
+fi
+
+apache_is_installed() {
+    (command -v apachectl \
+        && command -v a2ensite \
+        && command -v a2dissite ) &>/dev/null
+}
+
+apache_reload() {
+
+    info_msg "reload apache .."
+    echo
+    sudo -H apachectl configtest
+    sudo -H service apache2 force-reload
+}
+
+apache_install_site() {
+
+    # usage:  apache_install_site [--no-eval] <mysite.conf>
+
+    local no_eval=""
+    local CONF="$1"
+
+    if [[ "$1" == "--no-eval" ]]; then
+        no_eval=$1; shift
+    fi
+
+    # shellcheck disable=SC2086
+    install_template $no_eval "${APACHE_SITES_AVAILABE}/${CONF}" root root 644
+
+    apache_enable_site "${CONF}"
+    apache_reload
+    info_msg "installed apache site: ${CONF}"
+}
+
+apache_enable_site() {
+    info_msg "enable apache site $1 .."
+    sudo -H a2ensite -q "$1"
+    apache_reload
+}
+
+apache_dissable_site() {
+    info_msg "disable apache site $1 .."
+    sudo -H a2dissite -q "$1"
+    apache_reload
+}
 
 # uWSGI
 # -----
diff --git a/utils/searx.sh b/utils/searx.sh
index c9adecc8c..a1b1140ed 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -15,7 +15,6 @@ SERVICE_USER="${SERVICE_NAME}"
 SERVICE_GROUP="${SERVICE_USER}"
 SERVICE_HOME="/home/${SERVICE_USER}"
 
-# shellcheck disable=SC2034
 SEARX_URL="127.0.0.1:8888"
 
 SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
@@ -34,6 +33,8 @@ SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
 SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
 SEARX_UWSGI_APP="searx.ini"
 
+APACHE_SITE="searx.conf"
+
 # shellcheck disable=SC2034
 CONFIG_FILES=(
     "${uWSGI_SETUP}/apps-available/${SEARX_UWSGI_APP}"
@@ -175,9 +176,12 @@ install_all() {
         err_msg "URL http://$SEARX_URL not available, check searx & uwsgi setup!"
     fi
     wait_key
+    if apache_is_installed; then
+        install_apache_site
+        wait_key
+    fi
 
     # ToDo ...
-    # install_apache_site
     # test_public_searx
     # info_msg "searX --> https://${SEARX_APACHE_DOMAIN}${SEARX_APACHE_URL}"
 
@@ -428,11 +432,12 @@ EOF
     uWSGI_restart
 }
 
-
 show_service() {
     rst_title "service status & log"
     echo
 
+    apache_is_installed && info_msg "Apache is installed."
+
     if user_is_available; then
         info_msg "service account $SERVICE_USER available."
     else
@@ -468,9 +473,9 @@ show_service() {
         enable_debug
         _debug_on=1
     fi
-    wait_key
     echo
-    systemctl status uwsgi.service
+    systemctl --no-pager -l status uwsgi.service
+    echo
     read -r -s -n1 -t 2  -p "// use CTRL-C to stop monitoring the log"
     echo
     while true;  do
@@ -485,6 +490,13 @@ show_service() {
     return 0
 }
 
+install_apache_site() {
+    rst_title "Install Apache site $APACHE_SITE" section
+    echo
+    err_msg "not yet implemented (${APACHE_SITE})"; return 42
+    # apache_install_site "${APACHE_SITE}"
+}
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------

From d171fcd56ea0444598c6ae6d6d089dd2488bd64d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 21 Jan 2020 18:38:57 +0100
Subject: [PATCH 014/125] utils/searx.sh: add apache site searx.conf:uwsgi

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh                                  | 125 ++++++++++++------
 utils/searx.sh                                |  42 +++---
 .../apache2/sites-available/searx.conf:uwsgi  |  25 ++++
 3 files changed, 132 insertions(+), 60 deletions(-)
 create mode 100644 utils/templates/etc/apache2/sites-available/searx.conf:uwsgi

diff --git a/utils/lib.sh b/utils/lib.sh
index 40ecbb92e..b89d4d2f9 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -274,41 +274,65 @@ install_template() {
 
     # usage:
     #
-    #     install_template [--no-eval] {file} [{owner} [{group} [{chmod}]]]
+    #     install_template [--no-eval] [--variant=<name>] \
+    #                      {file} [{owner} [{group} [{chmod}]]]
     #
-    #     install_template --no-eval /etc/updatedb.conf root root 644
+    # E.g. the origin of variant 'raw' of /etc/updatedb.conf is::
+    #
+    #    ${TEMPLATES}/etc/updatedb.conf:raw
+    #
+    # To install variant 'raw' of /etc/updatedb.conf without evaluated
+    # replacements you can use::
+    #
+    #    install_template --variant=raw --no-eval \
+    #                     /etc/updatedb.conf root root 644
 
-    local do_eval=1
-    if [[ "$1" == "--no-eval" ]]; then
-        do_eval=0; shift
-    fi
-    local dst="${1}"
-    local owner=${2-$(id -un)}
-    local group=${3-$(id -gn)}
-    local chmod=${4-644}
     local _reply=""
+    local do_eval=1
+    local variant=""
+    local pos_args=("$0")
 
-    info_msg "install: ${dst}"
+    for i in "$@"; do
+        case $i in
+            --no-eval) do_eval=0; shift ;;
+            --variant=*) variant=":${i#*=}"; shift ;;
+            *) pos_args+=("$i") ;;
+        esac
+    done
 
-    if [[ ! -f "${TEMPLATES}${dst}" ]] ; then
-        err_msg "${TEMPLATES}${dst} does not exists"
-        err_msg "... can't install $dst / exit installation with error 42"
+    local dst="${pos_args[1]}"
+    local template_origin="${TEMPLATES}${dst}${variant}"
+    local template_file="${TEMPLATES}${dst}"
+
+    local owner="${pos_args[2]-$(id -un)}"
+    local group="${pos_args[3]-$(id -gn)}"
+    local chmod="${pos_args[4]-644}"
+
+    info_msg "install (eval=$do_eval): ${dst}"
+    [[ ! -z $variant ]] && info_msg "variant: ${variant}"
+
+    if [[ ! -f "${template_origin}" ]] ; then
+        err_msg "${template_origin} does not exists"
+        err_msg "... can't install $dst"
         wait_key 30
         return 42
     fi
 
-    local template_file="${TEMPLATES}${dst}"
     if [[ "$do_eval" == "1" ]]; then
+        template_file="${CACHE}${dst}${variant}"
         info_msg "BUILD template ${template_file}"
-        if [[ -f "${TEMPLATES}${dst}" ]] ; then
-            template_file="${CACHE}${dst}"
-            mkdir -p "$(dirname "${template_file}")"
-            # shellcheck disable=SC2086
-            eval "echo \"$(cat ${TEMPLATES}${dst})\"" > "${template_file}"
+        if [[ ! -z ${SUDO_USER} ]]; then
+            sudo -u "${SUDO_USER}" mkdir -p "$(dirname "${template_file}")"
         else
-            err_msg "failed ${template_file}"
-            return 42
+            mkdir -p "$(dirname "${template_file}")"
         fi
+        # shellcheck disable=SC2086
+        eval "echo \"$(cat ${template_origin})\"" > "${template_file}"
+        if [[ ! -z ${SUDO_USER} ]]; then
+            chown "${SUDO_USER}:${SUDO_USER}" "${template_file}"
+        fi
+    else
+        template_file=$template_origin
     fi
 
     mkdir -p "$(dirname "${dst}")"
@@ -325,7 +349,7 @@ install_template() {
         return 0
     fi
 
-    info_msg "file ${dst} allready exists on this host"
+    info_msg "diffrent file ${dst} allready exists on this host"
 
     while true; do
         choose_one _reply "choose next step with file $dst" \
@@ -349,7 +373,10 @@ install_template() {
                 echo "// exit with CTRL-D"
                 sudo -H -u "${owner}" -i
                 $DIFF_CMD "${dst}" "${template_file}"
-                if ask_yn "did you edit ${template_file} to your needs?"; then
+                echo
+                echo "did you edit file ..."
+                printf "  ${template_file}"
+                if ask_yn "... to your needs?"; then
                     break
                 fi
                 ;;
@@ -384,21 +411,27 @@ apache_reload() {
 
 apache_install_site() {
 
-    # usage:  apache_install_site [--no-eval] <mysite.conf>
+    # usage:  apache_install_site [<template option> ...] <mysite.conf>
+    #
+    # <template option>:   see install_template
 
-    local no_eval=""
-    local CONF="$1"
+    local template_opts=()
+    local pos_args=("$0")
 
-    if [[ "$1" == "--no-eval" ]]; then
-        no_eval=$1; shift
-    fi
+    for i in "$@"; do
+        case $i in
+            -*) template_opts+=("$i");;
+            *)  pos_args+=("$i");;
+        esac
+    done
 
-    # shellcheck disable=SC2086
-    install_template $no_eval "${APACHE_SITES_AVAILABE}/${CONF}" root root 644
+    install_template "${template_opts[@]}" \
+                     "${APACHE_SITES_AVAILABE}/${pos_args[1]}" \
+                     root root 644
 
-    apache_enable_site "${CONF}"
+    apache_enable_site "${pos_args[1]}"
     apache_reload
-    info_msg "installed apache site: ${CONF}"
+    info_msg "installed apache site: ${pos_args[1]}"
 }
 
 apache_enable_site() {
@@ -438,20 +471,24 @@ uWSGI_app_available() {
 
 uWSGI_install_app() {
 
-    # usage:  uWSGI_install_app [--no-eval] <myapp.ini>
+    # usage:  uWSGI_install_app [<template option> ...] <myapp.ini>
+    #
+    # <template option>:  see install_template
 
-    local no_eval=""
-    local CONF="$1"
+    for i in "$@"; do
+        case $i in
+            -*) template_opts+=("$i");;
+            *)  pos_args+=("$i");;
+        esac
+    done
 
-    if [[ "$1" == "--no-eval" ]]; then
-        no_eval=$1; shift
-    fi
+    install_template "${template_opts[@]}" \
+                     "${uWSGI_SETUP}/apps-available/${pos_args[1]}" \
+                     root root 644
 
-    # shellcheck disable=SC2086
-    install_template $no_eval "${uWSGI_SETUP}/apps-available/${CONF}" root root 644
-    uWSGI_enable_app "${CONF}"
+    uWSGI_enable_app "${pos_args[1]}"
     uWSGI_restart
-    info_msg "installed uWSGI app: ${CONF}"
+    info_msg "installed uWSGI app: ${pos_args[1]}"
 }
 
 uWSGI_remove_app() {
diff --git a/utils/searx.sh b/utils/searx.sh
index a1b1140ed..bd847e549 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -32,8 +32,11 @@ SEARX_SRC="${SERVICE_HOME}/searx-src"
 SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
 SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
 SEARX_UWSGI_APP="searx.ini"
+SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
-APACHE_SITE="searx.conf"
+# Apache Settings
+SEARX_APACHE_URL="/searx"
+SEARX_APACHE_SITE="searx.conf"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
@@ -55,7 +58,7 @@ usage() {
 usage:
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|pyenv|searx-src]
+  $(basename "$0") install    [all|user|pyenv|searx-src|apache]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
@@ -69,7 +72,8 @@ install / remove
   all:        complete (de-) installation of searx service
   user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
   searx-src:  clone $SEARX_GIT_URL
-  pyenv:       create/remove virtualenv (python) in $SEARX_PYENV
+  pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
+  apache:     install apache site for searx-uwsgi app
 update searx
   Update searx installation of user ${SERVICE_USER}
 activate
@@ -112,6 +116,7 @@ main() {
                 user) assert_user ;;
                 pyenv) create_pyenv ;;
                 searx-src) clone_searx ;;
+                apache) install_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
@@ -175,16 +180,6 @@ install_all() {
     else
         err_msg "URL http://$SEARX_URL not available, check searx & uwsgi setup!"
     fi
-    wait_key
-    if apache_is_installed; then
-        install_apache_site
-        wait_key
-    fi
-
-    # ToDo ...
-    # test_public_searx
-    # info_msg "searX --> https://${SEARX_APACHE_DOMAIN}${SEARX_APACHE_URL}"
-
 }
 
 update_searx() {
@@ -236,6 +231,11 @@ EOF
 
 remove_all() {
     rst_title "De-Install $SERVICE_NAME (service)"
+
+    rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
     if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
         return
     fi
@@ -491,10 +491,20 @@ show_service() {
 }
 
 install_apache_site() {
-    rst_title "Install Apache site $APACHE_SITE" section
+    rst_title "Install Apache site $SEARX_APACHE_SITE"
+
+    rst_para "\
+This installs the searx uwsgi app as apache site.  If your server ist public to
+the internet you should instead use a reverse proxy (filtron) to block
+excessively bot queries."
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
+        return
+    fi
     echo
-    err_msg "not yet implemented (${APACHE_SITE})"; return 42
-    # apache_install_site "${APACHE_SITE}"
+    apache_install_site --variant=uwsgi "${SEARX_APACHE_SITE}"
 }
 
 # ----------------------------------------------------------------------------
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
new file mode 100644
index 000000000..4147e8520
--- /dev/null
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
@@ -0,0 +1,25 @@
+# -*- coding: utf-8; mode: apache -*-
+
+<IfModule mod_uwsgi.c>
+
+    # SetEnvIf Request_URI "${SEARX_APACHE_URL}" dontlog
+    # CustomLog /dev/null combined env=dontlog
+
+    <Location ${SEARX_APACHE_URL}>
+
+        <IfModule mod_security2.c>
+            SecRuleEngine Off
+        </IfModule>
+
+        Options FollowSymLinks Indexes
+        SetHandler uwsgi-handler
+        uWSGISocket ${SEARX_UWSGI_SOCKET}
+
+        Order deny,allow
+        Deny from all
+        # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+        Allow from all
+
+    </Location>
+
+</IfModule>

From 971a8264b2e21dd844d2cc2b54686def8ba18c06 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 27 Jan 2020 19:08:40 +0100
Subject: [PATCH 015/125] utils/searx.sh: add apache site searx.conf:uwsgi
 (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                              |   1 +
 utils/lib.sh                                  |   2 +
 utils/searx.sh                                | 131 ++++++++++++------
 .../apache2/sites-available/searx.conf:uwsgi  |   4 +-
 .../etc/uwsgi/apps-available/searx.ini        |   2 +-
 5 files changed, 96 insertions(+), 44 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index bb6597fa5..7defe1ec5 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
 # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
 # shellcheck disable=SC2119
 
 # shellcheck source=utils/lib.sh
diff --git a/utils/lib.sh b/utils/lib.sh
index b89d4d2f9..2e272c284 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -475,6 +475,8 @@ uWSGI_install_app() {
     #
     # <template option>:  see install_template
 
+    local pos_args=("$0")
+
     for i in "$@"; do
         case $i in
             -*) template_opts+=("$i");;
diff --git a/utils/searx.sh b/utils/searx.sh
index bd847e549..3092d2fa9 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -1,5 +1,6 @@
 #!/usr/bin/env bash
 # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
 # shellcheck disable=SC2119
 
 # shellcheck source=utils/lib.sh
@@ -9,33 +10,41 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 # config
 # ----------------------------------------------------------------------------
 
-SERVICE_NAME="searx"
-SERVICE_USER="${SERVICE_NAME}"
+SEARX_PUBLIC_URL="${SEARX_PUBLIC_URL:-https://$(uname -n)/searx}"
+SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "$SEARX_PUBLIC_URL" \
+| sed -e 's,^.*://[^/]*\(/.*\),\1,g') }"
+SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$SEARX_PUBLIC_URL" \
+| sed -e 's,^.*://\([^\:/]*\).*,\1,g') }"
+
+SERVICE_USER="searx"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
 SERVICE_HOME="/home/${SERVICE_USER}"
 
-SEARX_URL="127.0.0.1:8888"
-
+SEARX_INTERNAL_URL="127.0.0.1:8888"
 SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
 SEARX_GIT_BRANCH="master"
+SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
+SEARX_SRC="${SERVICE_HOME}/searx-src"
+SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
+SEARX_UWSGI_APP="searx.ini"
+# shellcheck disable=SC2034
+SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
 # FIXME: Arch Linux & RHEL should be added
 
 SEARX_APT_PACKAGES="\
-libapache2-mod-uwsgi uwsgi uwsgi-plugin-python3 \
+  uwsgi uwsgi-plugin-python3 \
   git build-essential libxslt-dev python3-dev python3-babel zlib1g-dev \
-  libffi-dev libssl-dev"
-
-SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
-SEARX_SRC="${SERVICE_HOME}/searx-src"
-SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
-SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(uname -n)}"
-SEARX_UWSGI_APP="searx.ini"
-SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
+  libffi-dev libssl-dev \
+"
 
 # Apache Settings
-SEARX_APACHE_URL="/searx"
+
+APACHE_APT_PACKAGES="\
+  apache2 libapache2-mod-uwsgi \
+"
+
 SEARX_APACHE_SITE="searx.conf"
 
 # shellcheck disable=SC2034
@@ -63,7 +72,7 @@ usage:
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
-  $(basename "$0") show       [service]
+  $(basename "$0") inspect    [service]
   $(basename "$0") option     [debug-on|debug-off]
 
 shell
@@ -80,16 +89,23 @@ activate
   activate and start service daemon (systemd unit)
 deactivate service
   stop and deactivate service daemon (systemd unit)
-show service
-  run some small tests and show service's status and log
+inspect service
+  run some small tests and inspect service's status and log
 option
   set one of te available options
+
+Use environment SEARX_PUBLIC_URL to set public URL of your WEB-Server:
+
+  SEARX_PUBLIC_URL    :  ${SEARX_PUBLIC_URL}
+  SEARX_URL_PATH      :  ${SEARX_URL_PATH}
+  SEARX_INSTANCE_NAME :  ${SEARX_INSTANCE_NAME}
+
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
 
 main() {
-    rst_title "$SERVICE_NAME" part
+    rst_title "$SEARX_INSTANCE_NAME" part
 
     local _usage="ERROR: unknown or missing $1 command $2"
 
@@ -101,11 +117,11 @@ main() {
             sudo_or_exit
             interactive_shell
             ;;
-        show)
+        inspect)
             case $2 in
                 service)
                     sudo_or_exit
-                    show_service
+                    inspect_service
                     ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
@@ -161,7 +177,7 @@ main() {
 _service_prefix="  |$SERVICE_USER| "
 
 install_all() {
-    rst_title "Install $SERVICE_NAME (service)"
+    rst_title "Install $SEARX_INSTANCE_NAME (service)"
     pkg_install "$SEARX_APT_PACKAGES"
     wait_key
     assert_user
@@ -175,11 +191,15 @@ install_all() {
     test_local_searx
     wait_key
     install_searx_uwsgi
-    if service_is_available; then
-        info_msg "URL http://$SEARX_URL is available."
+    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
+        info_msg "URL http://$SEARX_INTERNAL_URL is available."
     else
-        err_msg "URL http://$SEARX_URL not available, check searx & uwsgi setup!"
+        err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
     fi
+    if ask_yn "Do you want to inspect the installation?" Yn; then
+        inspect_service
+    fi
+
 }
 
 update_searx() {
@@ -230,13 +250,13 @@ EOF
 }
 
 remove_all() {
-    rst_title "De-Install $SERVICE_NAME (service)"
+    rst_title "De-Install $SEARX_INSTANCE_NAME (service)"
 
     rst_para "\
 It goes without saying that this script can only be used to remove
 installations that were installed with this script."
 
-    if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
+    if ! ask_yn "Do you really want to deinstall $SEARX_INSTANCE_NAME?"; then
         return
     fi
     remove_searx_uwsgi
@@ -364,12 +384,20 @@ EOF
 test_local_searx() {
     rst_title "Testing searx instance localy" section
     echo
+
+    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
+        err_msg "URL/port http://$SEARX_INTERNAL_URL is already in use, you"
+        err_msg "should stop that service before starting local tests!"
+        if ! ask_yn "Continue with local tests?"; then
+            return
+        fi
+    fi
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
 sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
 timeout 5 python3 searx/webapp.py &
 sleep 1
-curl --location --verbose --head --insecure $SEARX_URL
+curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
 sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
 EOF
 }
@@ -387,13 +415,13 @@ remove_searx_uwsgi() {
 }
 
 activate_service() {
-    rst_title "Activate $SERVICE_NAME (service)" section
+    rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_enable_app "$SEARX_UWSGI_APP"
 }
 
 deactivate_service() {
-    rst_title "De-Activate $SERVICE_NAME (service)" section
+    rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_disable_app "$SEARX_UWSGI_APP"
 }
@@ -411,7 +439,9 @@ EOF
 }
 
 service_is_available() {
-    curl --insecure "http://$SEARX_URL" &>/dev/null
+    curl -H 'Cache-Control: no-cache' -o /dev/null \
+         --silent --head --write-out '%{http_code}' --insecure \
+         "${1?missing URL argument}"
 }
 
 enable_debug() {
@@ -432,42 +462,51 @@ EOF
     uWSGI_restart
 }
 
-show_service() {
+inspect_service() {
     rst_title "service status & log"
     echo
 
     apache_is_installed && info_msg "Apache is installed."
 
     if user_is_available; then
-        info_msg "service account $SERVICE_USER available."
+        info_msg "Service account $SERVICE_USER exists."
     else
-        err_msg "service account $SERVICE_USER not available!"
-    fi
-    if service_is_available; then
-        info_msg "URL http://$SEARX_URL is available."
-    else
-        err_msg "URL http://$SEARX_URL not available!"
+        err_msg "Service account $SERVICE_USER does not exists!"
     fi
+
     if pyenv_is_available; then
         info_msg "${SEARX_PYENV}/bin/activate is available."
     else
         err_msg "${SEARX_PYENV}/bin/activate not available!"
     fi
+
     if clone_is_available; then
         info_msg "Searx software is installed."
     else
         err_msg "Missing searx software!"
     fi
 
-    uWSGI_app_available "$SEARX_UWSGI_APP" \
-        || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
-
     if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
         info_msg "uWSGI app $SEARX_UWSGI_APP is enabled."
     else
         err_msg "uWSGI app $SEARX_UWSGI_APP not enabled!"
     fi
 
+    uWSGI_app_available "$SEARX_UWSGI_APP" \
+        || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
+
+    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
+        info_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is available"
+    else
+        err_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is not available!"
+    fi
+
+    if service_is_available "${SEARX_PUBLIC_URL}" &>/dev/null; then
+        info_msg "Public service at ${SEARX_PUBLIC_URL} is available"
+    else
+        err_msg "Public service at ${SEARX_PUBLIC_URL} is not available!"
+    fi
+
     local _debug_on
     if ask_yn "Enable searx debug mode?"; then
         enable_debug
@@ -503,8 +542,18 @@ excessively bot queries."
     if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
         return
     fi
+
+    pkg_install "$APACHE_APT_PACKAGES"
+    a2enmod uwsgi
+
     echo
     apache_install_site --variant=uwsgi "${SEARX_APACHE_SITE}"
+
+    if service_is_available "${SEARX_PUBLIC_URL}" &>/dev/null; then
+        info_msg "Public service at ${SEARX_PUBLIC_URL} is available"
+    else
+        err_msg "Public service at ${SEARX_PUBLIC_URL} is not available!"
+    fi
 }
 
 # ----------------------------------------------------------------------------
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
index 4147e8520..51cbdf41f 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
@@ -2,10 +2,10 @@
 
 <IfModule mod_uwsgi.c>
 
-    # SetEnvIf Request_URI "${SEARX_APACHE_URL}" dontlog
+    # SetEnvIf Request_URI "${SEARX_URL_PATH}" dontlog
     # CustomLog /dev/null combined env=dontlog
 
-    <Location ${SEARX_APACHE_URL}>
+    <Location ${SEARX_URL_PATH}>
 
         <IfModule mod_security2.c>
             SecRuleEngine Off
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index d3893b3ad..cff47f896 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -59,4 +59,4 @@ pythonpath = ${SEARX_SRC}
 # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
 
 # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
-http = ${SEARX_URL}
+http = ${SEARX_INTERNAL_URL}

From 924bf6551794e5962688c1e2962dc0c94869e2f1 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 29 Jan 2020 20:00:50 +0100
Subject: [PATCH 016/125] utils/searx.sh & filtron.sh: misc changes from first
 tests (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .gitignore                                    |   1 +
 utils/dot_config                              |   8 +
 utils/filtron.sh                              | 157 +++++++++++++-----
 utils/lib.sh                                  | 103 ++++++++++--
 utils/searx.sh                                | 116 ++++++++-----
 .../sites-available/searx.conf:filtron        |   3 +
 6 files changed, 286 insertions(+), 102 deletions(-)
 create mode 100644 utils/dot_config
 create mode 100644 utils/templates/etc/apache2/sites-available/searx.conf:filtron

diff --git a/.gitignore b/.gitignore
index 3c998afae..42b76ca9e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,3 +25,4 @@ dist/
 local/
 gh-pages/
 searx.egg-info/
+.config
diff --git a/utils/dot_config b/utils/dot_config
new file mode 100644
index 000000000..e4c283744
--- /dev/null
+++ b/utils/dot_config
@@ -0,0 +1,8 @@
+# -*- coding: utf-8; mode: sh -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+# Set environment used by ./utils scripts like filtron.sh or searx.sh
+#
+
+# Public URL of the searx instance
+PUBLIC_URL="${PUBLIC_URL:-https://$(uname -n)/searx}"
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 7defe1ec5..6ea001d42 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -5,6 +5,7 @@
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+source_dot_config
 
 # ----------------------------------------------------------------------------
 # config
@@ -30,7 +31,9 @@ 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")
 
-APACHE_SITE="searx.conf"
+# Apache Settings
+
+APACHE_FILTRON_SITE="searx.conf"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
@@ -53,22 +56,32 @@ usage:
   $(basename "$0") remove     [all]
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
-  $(basename "$0") show       [service]
+  $(basename "$0") inspect    [service]
+  $(basename "$0") apache     [install|remove]
+
 
 shell
   start interactive shell from user ${SERVICE_USER}
-install / remove all
-  complete setup of filtron service
+install / remove
+  all:        complete setup of filtron service
+  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
 update filtron
   Update filtron installation of user ${SERVICE_USER}
-activate
+activate service
   activate and start service daemon (systemd unit)
 deactivate service
   stop and deactivate service daemon (systemd unit)
-install user
-  add service user '$SERVICE_USER' at $SERVICE_HOME
-show service
+inspect service
   show service status and log
+apache
+  install: apache site with a reverse proxy (ProxyPass)
+  remove:  apache site ${APACHE_FILTRON_SITE}
+
+If needed change the environment variable PUBLIC_URL of your WEB service in the
+${DOT_CONFIG#"$REPO_ROOT/"} file:
+
+  PUBLIC_URL : ${PUBLIC_URL}
+
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
@@ -76,6 +89,10 @@ EOF
 main() {
     rst_title "$SERVICE_NAME" part
 
+    required_commands \
+        dpkg apt-get install git wget curl \
+        || exit
+
     local _usage="ERROR: unknown or missing $1 command $2"
 
     case $1 in
@@ -86,11 +103,11 @@ main() {
             sudo_or_exit
             interactive_shell
             ;;
-        show)
+        inspect)
             case $2 in
                 service)
                     sudo_or_exit
-                    show_service
+                    inspect_service
                     ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
@@ -126,6 +143,14 @@ main() {
                 service)  deactivate_service ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        apache)
+            sudo_or_exit
+            case $2 in
+                install) install_apache_site ;;
+                remove) remove_apache_site ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
     esac
 }
@@ -140,14 +165,29 @@ install_all() {
     wait_key
     install_service
     wait_key
-    if apache_is_installed; then
-        install_apache_site
-        wait_key
+    echo
+    if ! service_is_available "http://${FILTRON_LISTEN}" ; then
+        err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
     fi
+    if apache_is_installed; then
+        info_msg "Apache is installed on this host."
+        if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+            install_apache_site
+        fi
+    fi
+    if ask_yn "Do you want to inspect the installation?" Yn; then
+        inspect_service
+    fi
+
 }
 
 remove_all() {
     rst_title "De-Install $SERVICE_NAME (service)"
+
+    rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
     remove_service
     wait_key
     remove_user
@@ -155,18 +195,6 @@ remove_all() {
     wait_key
 }
 
-filtron_is_available() {
-    curl --insecure "http://${FILTRON_LISTEN}" &>/dev/null
-}
-
-api_is_available() {
-    curl --insecure "http://${FILTRON_API}" &>/dev/null
-}
-
-target_is_available() {
-    curl --insecure "http://${FILTRON_TARGET}" &>/dev/null
-}
-
 install_service() {
     rst_title "Install System-D Unit ${SERVICE_NAME}.service" section
     echo
@@ -191,7 +219,7 @@ systemctl enable $SERVICE_NAME.service
 systemctl restart $SERVICE_NAME.service
 EOF
     tee_stderr <<EOF | bash 2>&1
-systemctl status $SERVICE_NAME.service
+systemctl status --no-pager $SERVICE_NAME.service
 EOF
 }
 
@@ -265,9 +293,8 @@ 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 &&  echo "ERROR - Go Installation not found in PATH!?!"
 which go >/dev/null &&  go version && echo "congratulations -- Go installation OK :)"
 EOF
 }
@@ -293,9 +320,20 @@ go get -v -u github.com/asciimoo/filtron
 EOF
 }
 
-show_service() {
+inspect_service() {
+
     rst_title "service status & log"
-    echo
+
+    cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+  PUBLIC_URL          : ${PUBLIC_URL}
+  FILTRON_API         : ${FILTRON_API}
+  FILTRON_LISTEN      : ${FILTRON_LISTEN}
+  FILTRON_TARGET      : ${FILTRON_TARGET}
+
+EOF
 
     apache_is_installed && info_msg "Apache is installed."
 
@@ -314,20 +352,21 @@ show_service() {
     else
         err_msg "~$SERVICE_USER: filtron app is not installed!"
     fi
-    if api_is_available; then
-        info_msg "API available at: http://${FILTRON_API}"
-    else
+
+    if ! service_is_available "http://${FILTRON_API}"; then
         err_msg "API not available at: http://${FILTRON_API}"
     fi
-    if filtron_is_available; then
-        info_msg "Filtron listening on: http://${FILTRON_LISTEN}"
-    else
+
+    if ! service_is_available "http://${FILTRON_LISTEN}" ; then
         err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
     fi
-    if target_is_available; then
+
+    if ! service_is_available ""http://${FILTRON_TARGET}"" ; then
         info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
-    else
-        err_msg "Filtron's target is not available at:  http://${FILTRON_TARGET}"
+    fi
+
+    if ! service_is_available "${PUBLIC_URL}"; then
+        err_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
 
     wait_key
@@ -344,10 +383,44 @@ show_service() {
 }
 
 install_apache_site() {
-    rst_title "Install Apache site $APACHE_SITE" section
+
+    rst_title "Install Apache site $APACHE_FILTRON_SITE"
+
+    rst_para "\
+This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to continue?"; then
+        return
+    fi
+
+    a2enmod proxy
+    a2enmod proxy_http
+
     echo
-    err_msg "not yet implemented (${APACHE_SITE})"; return 42
-    # apache_install_site "${APACHE_SITE}"
+    apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
+
+    info_msg "testing public url .."
+    if ! service_is_available "${PUBLIC_URL}"; then
+        err_msg "Public service at ${PUBLIC_URL} is not available!"
+    fi
+}
+
+remove_apache_site() {
+
+    rst_title "Remove Apache site $APACHE_FILTRON_SITE"
+
+    rst_para "\
+This removes apache site ${APACHE_FILTRON_SITE}."
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to continue?"; then
+        return
+    fi
+
+    apache_remove_site "$APACHE_FILTRON_SITE"
 }
 
 # ----------------------------------------------------------------------------
diff --git a/utils/lib.sh b/utils/lib.sh
index 2e272c284..e2bcd8e9d 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1,6 +1,7 @@
 #!/usr/bin/env bash
 # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
-# shellcheck disable=SC2059,SC1117,SC2162,SC2004
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck disable=SC2059,SC1117
 
 ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
 ADMIN_NAME="${ADMIN_NAME:-$USER}"
@@ -35,6 +36,20 @@ if [[ -z ${DIFF_CMD} ]]; then
     fi
 fi
 
+DOT_CONFIG="${DOT_CONFIG:-${REPO_ROOT}/.config}"
+
+source_dot_config() {
+    if [[ ! -e "$DOT_CONFIG" ]]; then
+        info_msg "installing $DOT_CONFIG"
+        cp "$(dirname "${BASH_SOURCE[0]}")/dot_config" "$DOT_CONFIG"
+        if [[ ! -z ${SUDO_USER} ]]; then
+            chown "${SUDO_USER}:${SUDO_USER}" "$DOT_CONFIG"
+        fi
+    fi
+    # shellcheck disable=SC1090
+    source "${REPO_ROOT}/.config"
+}
+
 sudo_or_exit() {
     # usage: sudo_or_exit
 
@@ -44,6 +59,22 @@ sudo_or_exit() {
     fi
 }
 
+required_commands() {
+
+    # usage:  requires_commands [cmd1 ...]
+
+    local exit_val=0
+    while [ ! -z "$1" ]; do
+
+        if ! command -v "$1" &>/dev/null; then
+            err_msg "missing command $1"
+            exit_val=42
+        fi
+        shift
+    done
+    return $exit_val
+}
+
 rst_title() {
     # usage: rst_title <header-text> [part|chapter|section]
 
@@ -81,7 +112,7 @@ info_msg() { echo -e "INFO:  $*"; }
 
 clean_stdin() {
     if [[ $(uname -s) != 'Darwin' ]]; then
-        while read -n1 -t 0.1; do : ; done
+        while read -r -n1 -t 0.1; do : ; done
     fi
 }
 
@@ -93,7 +124,7 @@ wait_key(){
     [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
     [[ ! -z $_t ]] && _t="-t $_t"
     # shellcheck disable=SC2086
-    read -s -n1 $_t -p "** press any [KEY] to continue **"
+    read -r -s -n1 $_t -p "** press any [KEY] to continue **"
     echo
     clean_stdin
 }
@@ -124,7 +155,7 @@ ask_yn() {
         clean_stdin
         printf "$1 ${choice} "
         # shellcheck disable=SC2086
-        read -n1 $_t
+        read -r -n1 $_t
         if [[ -z $REPLY ]]; then
             printf "$default\n"; break
         elif [[ $REPLY =~ ^[Yy]$ ]]; then
@@ -156,7 +187,7 @@ tee_stderr () {
     local _t="0";
     if [[ ! -z $1 ]] ; then _t="$1"; fi
 
-    (while read line; do
+    (while read -r line; do
          # shellcheck disable=SC2086
          sleep $_t
          echo -e "$line" >&2
@@ -171,6 +202,7 @@ prefix_stdout () {
 
     if [[ ! -z $1 ]] ; then prefix="$1"; fi
 
+    # shellcheck disable=SC2162
     (while IFS= read line; do
         echo -e "${prefix}$line"
     done)
@@ -214,7 +246,7 @@ cache_download() {
         else
             wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
         fi
-        if $exit_value; then
+        if [[ $exit_value = 0 ]]; then
             err_msg "failed to download: $1"
         fi
     fi
@@ -238,7 +270,7 @@ choose_one() {
 
     list=("$@")
     echo -e "Menu::"
-    for ((i=1; i<= $(($max -1)); i++)); do
+    for ((i=1; i<= $((max -1)); i++)); do
         if [[ "$i" == "$default" ]]; then
             echo -e "  $i.) ${list[$i]} [default]"
         else
@@ -249,15 +281,15 @@ choose_one() {
         clean_stdin
         printf "$1 [$default] "
 
-        if (( 10 > $max )); then
+        if (( 10 > max )); then
             # shellcheck disable=SC2086
-            read -n1 $_t
+            read -r -n1 $_t
         else
             # shellcheck disable=SC2086,SC2229
-            read $_t
+            read -r $_t
         fi
         # selection fits
-        [[ $REPLY =~ ^-?[0-9]+$ ]] && (( $REPLY > 0 )) && (( $REPLY < $max )) && break
+        [[ $REPLY =~ ^-?[0-9]+$ ]] && (( REPLY > 0 )) && (( REPLY < max )) && break
 
         # take default
         [[ -z $REPLY ]] && REPLY=$default && break
@@ -386,6 +418,28 @@ install_template() {
     done
 }
 
+
+service_is_available() {
+
+    # usage:  service_is_available <URL>
+
+    local URL="$1"
+    if [[ -z $URL ]]; then
+        err_msg "service_is_available: missing arguments"
+        return 42
+    fi
+
+    http_code=$(curl -H 'Cache-Control: no-cache' \
+         --silent -o /dev/null --head --write-out '%{http_code}' --insecure \
+         "${URL}")
+    exit_val=$?
+    if [[ $exit_val = 0 ]]; then
+        info_msg "got $http_code from ${URL}"
+    fi
+    return $exit_val
+}
+
+
 # Apache
 # ------
 
@@ -430,18 +484,32 @@ apache_install_site() {
                      root root 644
 
     apache_enable_site "${pos_args[1]}"
-    apache_reload
     info_msg "installed apache site: ${pos_args[1]}"
 }
 
+apache_remove_site() {
+
+    # usage:  apache_remove_site <mysite.conf>
+
+    info_msg "remove apache site: $1"
+    apache_dissable_site "$1"
+    rm -f "${APACHE_SITES_AVAILABE}/$1"
+}
+
 apache_enable_site() {
-    info_msg "enable apache site $1 .."
+
+    # usage:  apache_enable_site <mysite.conf>
+
+    info_msg "enable apache site: $1"
     sudo -H a2ensite -q "$1"
     apache_reload
 }
 
 apache_dissable_site() {
-    info_msg "disable apache site $1 .."
+
+    # usage:  apache_disable_site <mysite.conf>
+
+    info_msg "disable apache site: $1"
     sudo -H a2dissite -q "$1"
     apache_reload
 }
@@ -456,7 +524,7 @@ uWSGI_restart() {
     # usage:  uWSGI_restart()
 
     info_msg "restart uWSGI service"
-    sudo -H systemctl restart uwsgi
+    systemctl restart uwsgi
 }
 
 uWSGI_app_available() {
@@ -498,10 +566,10 @@ uWSGI_remove_app() {
     # usage:  uWSGI_remove_app <myapp.ini>
 
     local CONF="$1"
+    info_msg "remove uWSGI app: ${CONF}"
     uWSGI_disable_app "${CONF}"
     uWSGI_restart
     rm -f "${uWSGI_SETUP}/apps-available/${CONF}"
-    info_msg "removed uWSGI app: ${CONF}"
 }
 
 uWSGI_app_enabled() {
@@ -542,6 +610,9 @@ uWSGI_disable_app() {
         return 42
     fi
     rm -f "${uWSGI_SETUP}/apps-enabled/${CONF}"
+    # FIXME: restart uwsgi service won't stop wsgi forked processes of user searx.
+    # I had to kill them manually here ...
+    pkill -f "${uWSGI_SETUP}/apps-enabled/${CONF}" -9
     info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)"
 }
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 3092d2fa9..a352c0439 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -1,19 +1,20 @@
 #!/usr/bin/env bash
 # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
 # SPDX-License-Identifier: AGPL-3.0-or-later
-# shellcheck disable=SC2119
+# shellcheck disable=SC2001
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+source_dot_config
 
 # ----------------------------------------------------------------------------
 # config
 # ----------------------------------------------------------------------------
 
-SEARX_PUBLIC_URL="${SEARX_PUBLIC_URL:-https://$(uname -n)/searx}"
-SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "$SEARX_PUBLIC_URL" \
-| sed -e 's,^.*://[^/]*\(/.*\),\1,g') }"
-SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$SEARX_PUBLIC_URL" \
+SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
+| sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
+[[ "${SEARX_URL_PATH}" == "${PUBLIC_URL}" ]] && SEARX_URL_PATH=/
+SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$PUBLIC_URL" \
 | sed -e 's,^.*://\([^\:/]*\).*,\1,g') }"
 
 SERVICE_USER="searx"
@@ -42,10 +43,10 @@ SEARX_APT_PACKAGES="\
 # Apache Settings
 
 APACHE_APT_PACKAGES="\
-  apache2 libapache2-mod-uwsgi \
+  libapache2-mod-uwsgi \
 "
 
-SEARX_APACHE_SITE="searx.conf"
+APACHE_SEARX_SITE="searx.conf"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
@@ -74,6 +75,7 @@ usage:
   $(basename "$0") deactivate [service]
   $(basename "$0") inspect    [service]
   $(basename "$0") option     [debug-on|debug-off]
+  $(basename "$0") apache     [install|remove]
 
 shell
   start interactive shell from user ${SERVICE_USER}
@@ -82,23 +84,25 @@ install / remove
   user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
   searx-src:  clone $SEARX_GIT_URL
   pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
-  apache:     install apache site for searx-uwsgi app
 update searx
   Update searx installation of user ${SERVICE_USER}
-activate
+activate service
   activate and start service daemon (systemd unit)
 deactivate service
   stop and deactivate service daemon (systemd unit)
 inspect service
   run some small tests and inspect service's status and log
 option
-  set one of te available options
+  set one of the available options
+apache
+  install: apache site with the searx uwsgi app
+  remove:  apache site ${APACHE_FILTRON_SITE}
 
-Use environment SEARX_PUBLIC_URL to set public URL of your WEB-Server:
+If needed change the environment variable PUBLIC_URL of your WEB service in the
+${DOT_CONFIG#"$REPO_ROOT/"} file:
 
-  SEARX_PUBLIC_URL    :  ${SEARX_PUBLIC_URL}
-  SEARX_URL_PATH      :  ${SEARX_URL_PATH}
-  SEARX_INSTANCE_NAME :  ${SEARX_INSTANCE_NAME}
+  PUBLIC_URL          : ${PUBLIC_URL}
+  SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
 
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
@@ -107,6 +111,10 @@ EOF
 main() {
     rst_title "$SEARX_INSTANCE_NAME" part
 
+    required_commands \
+        dpkg systemctl apt-get install git wget curl \
+        || exit
+
     local _usage="ERROR: unknown or missing $1 command $2"
 
     case $1 in
@@ -132,7 +140,6 @@ main() {
                 user) assert_user ;;
                 pyenv) create_pyenv ;;
                 searx-src) clone_searx ;;
-                apache) install_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
@@ -154,13 +161,13 @@ main() {
             sudo_or_exit
             case $2 in
                 service)
-                    activate_service; uWSGI_restart ;;
+                    activate_service ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         deactivate)
             sudo_or_exit
             case $2 in
-                service)  deactivate_service; uWSGI_restart ;;
+                service)  deactivate_service ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         option)
@@ -170,6 +177,14 @@ main() {
                 debug-off)  echo; disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        apache)
+            sudo_or_exit
+            case $2 in
+                install) install_apache_site ;;
+                remove) remove_apache_site ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
     esac
 }
@@ -191,9 +206,7 @@ install_all() {
     test_local_searx
     wait_key
     install_searx_uwsgi
-    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
-        info_msg "URL http://$SEARX_INTERNAL_URL is available."
-    else
+    if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
         err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
     fi
     if ask_yn "Do you want to inspect the installation?" Yn; then
@@ -418,12 +431,14 @@ activate_service() {
     rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_enable_app "$SEARX_UWSGI_APP"
+    uWSGI_restart
 }
 
 deactivate_service() {
     rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_disable_app "$SEARX_UWSGI_APP"
+    uWSGI_restart
 }
 
 interactive_shell() {
@@ -438,12 +453,6 @@ git --no-pager diff
 EOF
 }
 
-service_is_available() {
-    curl -H 'Cache-Control: no-cache' -o /dev/null \
-         --silent --head --write-out '%{http_code}' --insecure \
-         "${1?missing URL argument}"
-}
-
 enable_debug() {
     info_msg "try to enable debug mode ..."
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
@@ -464,7 +473,16 @@ EOF
 
 inspect_service() {
     rst_title "service status & log"
-    echo
+    cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+  PUBLIC_URL          : ${PUBLIC_URL}
+  SEARX_URL_PATH      : ${SEARX_URL_PATH}
+  SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
+  SEARX_INTERNAL_URL  : ${SEARX_INTERNAL_URL}
+
+EOF
 
     apache_is_installed && info_msg "Apache is installed."
 
@@ -475,15 +493,15 @@ inspect_service() {
     fi
 
     if pyenv_is_available; then
-        info_msg "${SEARX_PYENV}/bin/activate is available."
+        info_msg "~$SERVICE_USER: python environment is available."
     else
-        err_msg "${SEARX_PYENV}/bin/activate not available!"
+        err_msg "~$SERVICE_USER: python environment is not available!"
     fi
 
     if clone_is_available; then
-        info_msg "Searx software is installed."
+        info_msg "~$SERVICE_USER: Searx software is installed."
     else
-        err_msg "Missing searx software!"
+        err_msg "~$SERVICE_USER: Missing searx software!"
     fi
 
     if uWSGI_app_enabled "$SEARX_UWSGI_APP"; then
@@ -495,16 +513,12 @@ inspect_service() {
     uWSGI_app_available "$SEARX_UWSGI_APP" \
         || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
 
-    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
-        info_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is available"
-    else
+    if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
         err_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is not available!"
     fi
 
-    if service_is_available "${SEARX_PUBLIC_URL}" &>/dev/null; then
-        info_msg "Public service at ${SEARX_PUBLIC_URL} is available"
-    else
-        err_msg "Public service at ${SEARX_PUBLIC_URL} is not available!"
+    if ! service_is_available "${PUBLIC_URL}"; then
+        err_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
 
     local _debug_on
@@ -530,7 +544,7 @@ inspect_service() {
 }
 
 install_apache_site() {
-    rst_title "Install Apache site $SEARX_APACHE_SITE"
+    rst_title "Install Apache site $APACHE_SEARX_SITE"
 
     rst_para "\
 This installs the searx uwsgi app as apache site.  If your server ist public to
@@ -547,15 +561,29 @@ excessively bot queries."
     a2enmod uwsgi
 
     echo
-    apache_install_site --variant=uwsgi "${SEARX_APACHE_SITE}"
+    apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
 
-    if service_is_available "${SEARX_PUBLIC_URL}" &>/dev/null; then
-        info_msg "Public service at ${SEARX_PUBLIC_URL} is available"
-    else
-        err_msg "Public service at ${SEARX_PUBLIC_URL} is not available!"
+    if ! service_is_available "${PUBLIC_URL}"; then
+        err_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
 }
 
+remove_apache_site() {
+
+    rst_title "Remove Apache site ${APACHE_SEARX_SITE}"
+
+    rst_para "\
+This removes apache site ${APACHE_SEARX_SITE}."
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to continue?"; then
+        return
+    fi
+
+    apache_remove_site "${APACHE_SEARX_SITE}"
+}
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
new file mode 100644
index 000000000..d2f5431a7
--- /dev/null
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -0,0 +1,3 @@
+# -*- coding: utf-8; mode: apache -*-
+
+ProxyPass "/searx" "http://127.0.0.1:4004/"

From 6274a54746b4444f8850aecea0cb0734c6bdc29c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 30 Jan 2020 19:55:51 +0100
Subject: [PATCH 017/125] utils/searx.sh & filtron.sh: misc changes from first
 tests (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                                   | 10 +++++++++-
 utils/lib.sh                                       |  6 +++++-
 utils/searx.sh                                     |  3 +++
 .../etc/apache2/sites-available/searx.conf:filtron | 14 +++++++++++++-
 4 files changed, 30 insertions(+), 3 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 6ea001d42..d6e5b6a1b 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -11,6 +11,10 @@ source_dot_config
 # config
 # ----------------------------------------------------------------------------
 
+FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
+| sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
+[[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
+
 FILTRON_ETC="/etc/filtron"
 
 FILTRON_RULES="$FILTRON_ETC/rules.json"
@@ -192,7 +196,9 @@ installations that were installed with this script."
     wait_key
     remove_user
     rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
-    wait_key
+    if service_is_available "${PUBLIC_URL}"; then
+        MSG="** Don't forget to remove your public site! (${PUBLIC_URL}) **" wait_key 10
+    fi
 }
 
 install_service() {
@@ -329,6 +335,7 @@ inspect_service() {
 sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
 
   PUBLIC_URL          : ${PUBLIC_URL}
+  FILTRON_URL_PATH    : ${FILTRON_URL_PATH}
   FILTRON_API         : ${FILTRON_API}
   FILTRON_LISTEN      : ${FILTRON_LISTEN}
   FILTRON_TARGET      : ${FILTRON_TARGET}
@@ -395,6 +402,7 @@ This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SIT
         return
     fi
 
+    a2enmod headers
     a2enmod proxy
     a2enmod proxy_http
 
diff --git a/utils/lib.sh b/utils/lib.sh
index e2bcd8e9d..0586b2575 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -121,10 +121,11 @@ wait_key(){
 
     clean_stdin
     local _t=$1
+    local msg="${MSG:-** press any [KEY] to continue **}"
     [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
     [[ ! -z $_t ]] && _t="-t $_t"
     # shellcheck disable=SC2086
-    read -r -s -n1 $_t -p "** press any [KEY] to continue **"
+    read -r -s -n1 $_t -p "$msg"
     echo
     clean_stdin
 }
@@ -436,6 +437,9 @@ service_is_available() {
     if [[ $exit_val = 0 ]]; then
         info_msg "got $http_code from ${URL}"
     fi
+    case "$http_code" in
+        404|410|423) exit_val=$http_code;;
+    esac
     return $exit_val
 }
 
diff --git a/utils/searx.sh b/utils/searx.sh
index a352c0439..b036541cf 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -275,6 +275,9 @@ installations that were installed with this script."
     remove_searx_uwsgi
     wait_key
     remove_user
+    if service_is_available "${PUBLIC_URL}"; then
+        MSG="** Don't forgett to remove your public site! (${PUBLIC_URL}) **" wait_key 10
+    fi
 }
 
 user_is_available() {
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index d2f5431a7..f0914e9f9 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -1,3 +1,15 @@
 # -*- coding: utf-8; mode: apache -*-
 
-ProxyPass "/searx" "http://127.0.0.1:4004/"
+<Location ${FILTRON_URL_PATH} >
+
+    Require all granted
+    Allow from all
+
+    Order deny,allow
+    Deny from all
+    #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+
+    ProxyPass "http://127.0.0.1:4004"
+    RequestHeader set "X-Script-Name" "${FILTRON_URL_PATH}"
+
+</Location>

From cd9b877c8c186925e7eab5088554fcf5fc2ca5d5 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 31 Jan 2020 15:53:53 +0100
Subject: [PATCH 018/125] utils/searx.sh & filtron.sh: misc changes from first
 tests (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/dot_config | 7 ++++---
 utils/filtron.sh | 6 ++++--
 utils/lib.sh     | 2 +-
 utils/searx.sh   | 2 ++
 4 files changed, 11 insertions(+), 6 deletions(-)

diff --git a/utils/dot_config b/utils/dot_config
index e4c283744..0d6758a18 100644
--- a/utils/dot_config
+++ b/utils/dot_config
@@ -1,8 +1,9 @@
 # -*- coding: utf-8; mode: sh -*-
 # SPDX-License-Identifier: AGPL-3.0-or-later
 #
-# Set environment used by ./utils scripts like filtron.sh or searx.sh
+# environment used by utils scripts like filtron.sh or searx.sh
 #
 
-# Public URL of the searx instance
-PUBLIC_URL="${PUBLIC_URL:-https://$(uname -n)/searx}"
+# the public URL of the searx instance
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
diff --git a/utils/filtron.sh b/utils/filtron.sh
index d6e5b6a1b..9559902dd 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -1,7 +1,7 @@
 #!/usr/bin/env bash
 # -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
 # SPDX-License-Identifier: AGPL-3.0-or-later
-# shellcheck disable=SC2119
+# shellcheck disable=SC2119,SC2001
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
@@ -84,7 +84,8 @@ apache
 If needed change the environment variable PUBLIC_URL of your WEB service in the
 ${DOT_CONFIG#"$REPO_ROOT/"} file:
 
-  PUBLIC_URL : ${PUBLIC_URL}
+  PUBLIC_URL   : ${PUBLIC_URL}
+  PUBLIC_HOST  : ${PUBLIC_HOST}
 
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
@@ -335,6 +336,7 @@ inspect_service() {
 sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
 
   PUBLIC_URL          : ${PUBLIC_URL}
+  PUBLIC_HOST         : ${PUBLIC_HOST}
   FILTRON_URL_PATH    : ${FILTRON_URL_PATH}
   FILTRON_API         : ${FILTRON_API}
   FILTRON_LISTEN      : ${FILTRON_LISTEN}
diff --git a/utils/lib.sh b/utils/lib.sh
index 0586b2575..a3c3ee63d 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -440,7 +440,7 @@ service_is_available() {
     case "$http_code" in
         404|410|423) exit_val=$http_code;;
     esac
-    return $exit_val
+    return "$exit_val"
 }
 
 
diff --git a/utils/searx.sh b/utils/searx.sh
index b036541cf..359ae7124 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -102,6 +102,7 @@ If needed change the environment variable PUBLIC_URL of your WEB service in the
 ${DOT_CONFIG#"$REPO_ROOT/"} file:
 
   PUBLIC_URL          : ${PUBLIC_URL}
+  PUBLIC_HOST         : ${PUBLIC_HOST}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
 
 EOF
@@ -481,6 +482,7 @@ inspect_service() {
 sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
 
   PUBLIC_URL          : ${PUBLIC_URL}
+  PUBLIC_HOST         : ${PUBLIC_HOST}
   SEARX_URL_PATH      : ${SEARX_URL_PATH}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
   SEARX_INTERNAL_URL  : ${SEARX_INTERNAL_URL}

From 91a55e159e09450bb25206f42cf0189c2f5476ca Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 31 Jan 2020 15:54:07 +0100
Subject: [PATCH 019/125] apache: reverse proxy, set `ProxyPreserveHost On`

related discussions:

- https://github.com/asciimoo/searx/issues/1822
- https://github.com/asciimoo/searx/issues/1819#issuecomment-580400259

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .../apache2/sites-available/searx.conf:filtron   | 16 ++++++++++++----
 1 file changed, 12 insertions(+), 4 deletions(-)

diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index f0914e9f9..11bd70376 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -1,15 +1,23 @@
 # -*- coding: utf-8; mode: apache -*-
 
+ProxyPreserveHost On
+
 <Location ${FILTRON_URL_PATH} >
 
     Require all granted
-    Allow from all
-
     Order deny,allow
     Deny from all
     #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+    Allow from all
 
-    ProxyPass "http://127.0.0.1:4004"
-    RequestHeader set "X-Script-Name" "${FILTRON_URL_PATH}"
+    ProxyPass http://${FILTRON_LISTEN}
+    RequestHeader set X-Script-Name ${FILTRON_URL_PATH}
+
+    # In Apache it seems, that setting HTTP_HOST header direct here does have no
+    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST is
+    # needed by searx to render correct *Search URL* in the *Link* box and
+    # *saved preference*.
+
+    # RequestHeader set Host ${PUBLIC_HOST}
 
 </Location>

From 0bb884708745a708b35a3185afc6ad99f20c1838 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 31 Jan 2020 17:25:38 +0100
Subject: [PATCH 020/125] utils/filtron.sh: add option to debug filtron
 requests

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                       | 74 +++++++++++++++++++++++++-
 utils/templates/etc/filtron/rules.json | 12 -----
 2 files changed, 73 insertions(+), 13 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 9559902dd..528bedff2 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -61,6 +61,7 @@ usage:
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
   $(basename "$0") inspect    [service]
+  $(basename "$0") option     [debug-on|debug-off]
   $(basename "$0") apache     [install|remove]
 
 
@@ -77,6 +78,8 @@ deactivate service
   stop and deactivate service daemon (systemd unit)
 inspect service
   show service status and log
+option
+  set one of the available options
 apache
   install: apache site with a reverse proxy (ProxyPass)
   remove:  apache site ${APACHE_FILTRON_SITE}
@@ -155,6 +158,13 @@ main() {
                 remove) remove_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        option)
+            sudo_or_exit
+            case $2 in
+                debug-on)  echo; enable_debug ;;
+                debug-off)  echo; disable_debug ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
 
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
     esac
@@ -378,7 +388,12 @@ EOF
         err_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
 
-    wait_key
+    local _debug_on
+    if ask_yn "Enable filtron debug mode?"; then
+        enable_debug
+        _debug_on=1
+    fi
+
     echo
     systemctl --no-pager -l status filtron.service
     echo
@@ -388,9 +403,66 @@ EOF
         trap break 2
         journalctl -f -u filtron
     done
+
+    if [[ $_debug_on == 1 ]]; then
+        disable_debug
+    fi
     return 0
 }
 
+
+enable_debug() {
+    info_msg "try to enable debug mode ..."
+    python <<EOF
+import sys, json
+
+debug = {
+    u'name': u'debug request'
+    , u'filters': []
+    , u'interval': 0
+    , u'limit': 0
+    , u'actions': [{u'name': u'log'}]
+}
+
+with open('$FILTRON_RULES') as rules:
+    j = json.load(rules)
+
+pos = None
+for i in range(len(j)):
+    if j[i].get('name') == 'debug request':
+        pos = i
+        break
+if pos is not None:
+    j[pos] = debug
+else:
+    j.append(debug)
+with open('$FILTRON_RULES', 'w') as rules:
+    json.dump(j, rules, indent=2, sort_keys=True)
+
+EOF
+    systemctl restart "${SERVICE_NAME}.service"
+}
+
+disable_debug() {
+    info_msg "try to disable debug mode ..."
+    python <<EOF
+import sys, json
+with open('$FILTRON_RULES') as rules:
+    j = json.load(rules)
+
+pos = None
+for i in range(len(j)):
+    if j[i].get('name') == 'debug request':
+        pos = i
+        break
+if pos is not None:
+    del j[pos]
+    with open('$FILTRON_RULES', 'w') as rules:
+         json.dump(j, rules, indent=2, sort_keys=True)
+EOF
+    systemctl restart "${SERVICE_NAME}.service"
+}
+
 install_apache_site() {
 
     rst_title "Install Apache site $APACHE_FILTRON_SITE"
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index 634f5f2d6..f333306e1 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,16 +1,4 @@
 [{
-  "name":"suspiciously frequent queries",
-  "filters":[
-    "Param:q",
-    "Path=^(/|/search)$"
-  ],
-  "interval":120,
-  "limit":9,
-  "actions":[
-    {"name":"log"}
-  ]
- },
- {
   "name":"search request",
   "filters":[
     "Param:q",

From 56a93ee77006499b869b2fa067b72b0171710c61 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 31 Jan 2020 18:24:31 +0100
Subject: [PATCH 021/125] utils/lib.sh: colorize messages

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh |  6 ++--
 utils/lib.sh     | 82 ++++++++++++++++++++++++++++++++++--------------
 utils/searx.sh   | 10 +++---
 3 files changed, 68 insertions(+), 30 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 528bedff2..8283c9664 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -287,7 +287,7 @@ remove_user() {
 }
 
 interactive_shell(){
-    echo "// exit with CTRL-D"
+    echo "// exit with ${_BCyan}CTRL-D${_creset}"
     sudo -H -u ${SERVICE_USER} -i
 }
 
@@ -397,7 +397,9 @@ EOF
     echo
     systemctl --no-pager -l status filtron.service
     echo
-    read -r -s -n1 -t 2  -p "// use CTRL-C to stop monitoring the log"
+    # shellcheck disable=SC2059
+    printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+    read -r -s -n1 -t 2
     echo
     while true;  do
         trap break 2
diff --git a/utils/lib.sh b/utils/lib.sh
index a3c3ee63d..91335fbb8 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -75,19 +75,36 @@ required_commands() {
     return $exit_val
 }
 
-rst_title() {
-    # usage: rst_title <header-text> [part|chapter|section]
+# colors
+# ------
 
-    case ${2-chapter} in
-        part)     printf "\n${1//?/=}\n$1\n${1//?/=}\n";;
-        chapter)  printf "\n${1}\n${1//?/=}\n";;
-        section)  printf "\n${1}\n${1//?/-}\n";;
-        *)
-            err_msg "invalid argument '${2}' in line $(caller)"
-            return 42
-            ;;
-    esac
+# shellcheck disable=SC2034
+set_terminal_colors() {
+    _colors=8
+    _creset='\e[0m'  # reset all attributes
+
+    _Black='\e[0;30m'
+    _White='\e[1;37m'
+    _Red='\e[0;31m'
+    _Green='\e[0;32m'
+    _Yellow='\e[0;33m'
+    _Blue='\e[0;34m'
+    _Violet='\e[0;35m'
+    _Cyan='\e[0;36m'
+
+    _BBlack='\e[1;30m'
+    _BWhite='\e[1;37m'
+    _BRed='\e[1;31m'
+    _BGreen='\e[1;32m'
+    _BYellow='\e[1;33m'
+    _BBlue='\e[1;34m'
+    _BPurple='\e[1;35m'
+    _BCyan='\e[1;36m'
 }
+set_terminal_colors
+
+# reST
+# ----
 
 if command -v fmt >/dev/null; then
     export FMT="fmt -u"
@@ -95,6 +112,20 @@ else
     export FMT="cat"
 fi
 
+rst_title() {
+    # usage: rst_title <header-text> [part|chapter|section]
+
+    case ${2-chapter} in
+        part)     printf "\n${_BGreen}${1//?/=}\n$1\n${1//?/=}${_creset}\n";;
+        chapter)  printf "\n${_BGreen}${1}\n${1//?/=}${_creset}\n";;
+        section)  printf "\n${_BGreen}${1}\n${1//?/-}${_creset}\n";;
+        *)
+            err_msg "invalid argument '${2}' in line $(caller)"
+            return 42
+            ;;
+    esac
+}
+
 rst_para() {
     # usage:  RST_INDENT=1 rst_para "lorem ipsum ..."
     local prefix=''
@@ -106,9 +137,9 @@ rst_para() {
     fi
 }
 
-err_msg()  { echo -e "ERROR: $*" >&2; }
-warn_msg() { echo -e "WARN:  $*" >&2; }
-info_msg() { echo -e "INFO:  $*"; }
+err_msg()  { echo -e "${_BRed}ERROR:${_creset} $*" >&2; }
+warn_msg() { echo -e "${_BBlue}WARN:${_creset}  $*" >&2; }
+info_msg() { echo -e "${_BYellow}INFO:${_creset}  $*" >&2; }
 
 clean_stdin() {
     if [[ $(uname -s) != 'Darwin' ]]; then
@@ -121,11 +152,14 @@ wait_key(){
 
     clean_stdin
     local _t=$1
-    local msg="${MSG:-** press any [KEY] to continue **}"
+    local msg="${MSG}"
+    [[ -z "$msg" ]] && msg="${_Green}** press any [${_BCyan}KEY${_Green}] to continue **${_creset}"
+
     [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
     [[ ! -z $_t ]] && _t="-t $_t"
+    printf "$msg"
     # shellcheck disable=SC2086
-    read -r -s -n1 $_t -p "$msg"
+    read -r -s -n1 $_t
     echo
     clean_stdin
 }
@@ -142,12 +176,12 @@ ask_yn() {
     case "${2}" in
         Yn)
             local exit_val=${EXIT_YES}
-            local choice="[YES/no]"
+            local choice="[${_BGreen}YES${_creset}/no]"
             local default="Yes"
             ;;
         *)
             local exit_val=${EXIT_NO}
-            local choice="[NO/yes]"
+            local choice="[${_BGreen}NO${_creset}/yes]"
             local default="No"
             ;;
     esac
@@ -201,7 +235,7 @@ prefix_stdout () {
 
     local prefix="  | "
 
-    if [[ ! -z $1 ]] ; then prefix="$1"; fi
+    if [[ ! -z $1 ]] ; then prefix="${_BYellow}$1${_creset}"; fi
 
     # shellcheck disable=SC2162
     (while IFS= read line; do
@@ -270,17 +304,17 @@ choose_one() {
     [[ ! -z $_t ]] && _t="-t $_t"
 
     list=("$@")
-    echo -e "Menu::"
+    echo -e "${_BGreen}Menu::${_creset}"
     for ((i=1; i<= $((max -1)); i++)); do
         if [[ "$i" == "$default" ]]; then
-            echo -e "  $i.) ${list[$i]} [default]"
+            echo -e "  ${_BGreen}$i.${_creset}) ${list[$i]} [default]"
         else
             echo -e "  $i.) ${list[$i]}"
         fi
     done
     while true; do
         clean_stdin
-        printf "$1 [$default] "
+        printf "$1 [${_BGreen}$default${_creset}] "
 
         if (( 10 > max )); then
             # shellcheck disable=SC2086
@@ -403,11 +437,11 @@ install_template() {
                 ;;
             "interactiv shell")
                 echo "// edit ${dst} to your needs"
-                echo "// exit with CTRL-D"
+                echo "// exit with ${_BCyan}CTRL-D${_creset}"
                 sudo -H -u "${owner}" -i
                 $DIFF_CMD "${dst}" "${template_file}"
                 echo
-                echo "did you edit file ..."
+                echo "${_BBlack}did you edit file ...${_creset}"
                 printf "  ${template_file}"
                 if ask_yn "... to your needs?"; then
                     break
diff --git a/utils/searx.sh b/utils/searx.sh
index 359ae7124..ad8515d4f 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -445,9 +445,9 @@ deactivate_service() {
     uWSGI_restart
 }
 
-interactive_shell() {
-    echo "// exit with CTRL-D"
-    sudo -H -u "${SERVICE_USER}" -i
+interactive_shell(){
+    echo "// exit with ${_BCyan}CTRL-D${_creset}"
+    sudo -H -u ${SERVICE_USER} -i
 }
 
 git_diff() {
@@ -534,7 +534,9 @@ EOF
     echo
     systemctl --no-pager -l status uwsgi.service
     echo
-    read -r -s -n1 -t 2  -p "// use CTRL-C to stop monitoring the log"
+    # shellcheck disable=SC2059
+    printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+    read -r -s -n1 -t 2
     echo
     while true;  do
         trap break 2

From 709ac51d331853ad29c4f6de26c695a7aeeca125 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 1 Feb 2020 16:59:27 +0100
Subject: [PATCH 022/125] utils/filtron.sh: generalize systemd, accounts and
 golang tasks

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh |  99 ++++----------------------------------
 utils/lib.sh     | 123 +++++++++++++++++++++++++++++++++++++++++++++--
 utils/searx.sh   |  28 ++---------
 3 files changed, 134 insertions(+), 116 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 8283c9664..2aa8cf4f5 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -109,7 +109,7 @@ main() {
 
         shell)
             sudo_or_exit
-            interactive_shell
+            interactive_shell "${SERVICE_USER}"
             ;;
         inspect)
             case $2 in
@@ -136,19 +136,19 @@ main() {
             sudo_or_exit
             case $2 in
                 all) remove_all;;
-                user) remove_user ;;
+                user) drop_service_account "${SERVICE_USER}" ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         activate)
             sudo_or_exit
             case $2 in
-                service)  activate_service ;;
+                service)  systemd_activate_service "${SERVICE_NAME}" ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         deactivate)
             sudo_or_exit
             case $2 in
-                service)  deactivate_service ;;
+                service)  systemd_deactivate_service "${SERVICE_NAME}" ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         apache)
@@ -174,11 +174,11 @@ install_all() {
     rst_title "Install $SERVICE_NAME (service)"
     assert_user
     wait_key
-    install_go
+    install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
     wait_key
     install_filtron
     wait_key
-    install_service
+    systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
     wait_key
     echo
     if ! service_is_available "http://${FILTRON_LISTEN}" ; then
@@ -203,56 +203,15 @@ remove_all() {
 It goes without saying that this script can only be used to remove
 installations that were installed with this script."
 
-    remove_service
+    systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
     wait_key
-    remove_user
+    drop_service_account "${SERVICE_USER}"
     rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
     if service_is_available "${PUBLIC_URL}"; then
         MSG="** Don't forget to remove your public site! (${PUBLIC_URL}) **" wait_key 10
     fi
 }
 
-install_service() {
-    rst_title "Install System-D Unit ${SERVICE_NAME}.service" section
-    echo
-    install_template "${SERVICE_SYSTEMD_UNIT}" root root 644
-    wait_key
-    activate_service
-}
-
-remove_service() {
-    if ! ask_yn "Do you really want to deinstall $SERVICE_NAME?"; then
-        return
-    fi
-    deactivate_service
-    rm "${SERVICE_SYSTEMD_UNIT}"  2>&1 | prefix_stdout
-}
-
-activate_service() {
-    rst_title "Activate $SERVICE_NAME (service)" section
-    echo
-    tee_stderr <<EOF | bash 2>&1
-systemctl enable $SERVICE_NAME.service
-systemctl restart $SERVICE_NAME.service
-EOF
-    tee_stderr <<EOF | bash 2>&1
-systemctl status --no-pager $SERVICE_NAME.service
-EOF
-}
-
-deactivate_service() {
-    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
-}
-
-user_is_available() {
-    sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
-}
-
 assert_user() {
     rst_title "user $SERVICE_USER" section
     echo
@@ -277,44 +236,6 @@ 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" 2>&1 | prefix_stdout
-    else
-        rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
-    fi
-}
-
-interactive_shell(){
-    echo "// exit with ${_BCyan}CTRL-D${_creset}"
-    sudo -H -u ${SERVICE_USER} -i
-}
-
-_service_prefix="  |$SERVICE_USER| "
-
-go_is_available() {
-    sudo -i -u "$SERVICE_USER" which go &>/dev/null
-}
-
-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
-    sudo -i -u "$SERVICE_USER" <<EOF | prefix_stdout
-! which go >/dev/null &&  echo "ERROR - Go Installation not found in PATH!?!"
-which go >/dev/null &&  go version && echo "congratulations -- Go installation OK :)"
-EOF
-}
 
 filtron_is_installed() {
     [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
@@ -356,12 +277,12 @@ EOF
 
     apache_is_installed && info_msg "Apache is installed."
 
-    if user_is_available; then
+    if service_account_is_available "$SERVICE_USER"; then
         info_msg "service account $SERVICE_USER available."
     else
         err_msg "service account $SERVICE_USER not available!"
     fi
-    if go_is_available; then
+    if go_is_available "$SERVICE_USER"; then
         info_msg "~$SERVICE_USER: go is installed"
     else
         err_msg "~$SERVICE_USER: go is not installed"
diff --git a/utils/lib.sh b/utils/lib.sh
index 91335fbb8..4a77671a1 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -25,10 +25,6 @@ if [[ -z "$CACHE" ]]; then
     CACHE="${REPO_ROOT}/cache"
 fi
 
-if [[ -z "$SYSTEMD_UNITS" ]]; then
-    SYSTEMD_UNITS="/lib/systemd/system"
-fi
-
 if [[ -z ${DIFF_CMD} ]]; then
     DIFF_CMD="diff -u"
     if command -v colordiff >/dev/null;  then
@@ -477,6 +473,125 @@ service_is_available() {
     return "$exit_val"
 }
 
+# golang
+# ------
+
+go_is_available() {
+
+    # usage:  go_is_available $SERVICE_USER && echo "go is installed!"
+
+    sudo -i -u "${1}" which go &>/dev/null
+}
+
+install_go() {
+
+    # usage:  install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
+
+    local _service_prefix="  |${3}| "
+
+    rst_title "Install Go in user's HOME" section
+
+    rst_para "download and install go binary .."
+    cache_download "${1}" "${2}"
+
+    tee_stderr 0.1 <<EOF | sudo -i -u "${3}" | prefix_stdout "$_service_prefix"
+echo \$PATH
+echo \$GOPATH
+mkdir -p \$HOME/local
+rm -rf \$HOME/local/go
+tar -C \$HOME/local -xzf ${CACHE}/${2}
+EOF
+    sudo -i -u "${3}" <<EOF | prefix_stdout
+! which go >/dev/null &&  echo "ERROR - Go Installation not found in PATH!?!"
+which go >/dev/null &&  go version && echo "congratulations -- Go installation OK :)"
+EOF
+}
+
+# system accounts
+# ---------------
+
+service_account_is_available() {
+
+    # usage:  service_account_is_available "$SERVICE_USER" && echo "OK"
+
+    sudo -i -u "$1" echo \$HOME &>/dev/null
+}
+
+drop_service_account() {
+
+    # usage:  drop_service_account "${SERVICE_USER}"
+
+    rst_title "Drop ${1} HOME" section
+    if ask_yn "Do you really want to drop ${1} home folder?"; then
+        userdel -r -f "${1}" 2>&1 | prefix_stdout
+    else
+        rst_para "Leave HOME folder $(du -sh "${1}") unchanged."
+    fi
+}
+
+interactive_shell(){
+
+    # usage:  interactive_shell "${SERVICE_USER}"
+
+    echo "// exit with ${_BCyan}CTRL-D${_creset}"
+    sudo -H -u "${1}" -i
+}
+
+
+# systemd
+# -------
+
+SYSTEMD_UNITS="${SYSTEMD_UNITS:-/lib/systemd/system}"
+
+systemd_install_service() {
+
+    # usage:  systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+
+    rst_title "Install System-D Unit ${1}" section
+    echo
+    install_template "${2}" root root 644
+    wait_key
+    systemd_activate_service "${1}"
+}
+
+systemd_remove_service() {
+
+    # usage:  systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+
+    if ! ask_yn "Do you really want to deinstall ${1}?"; then
+        return
+    fi
+    systemd_deactivate_service "${1}"
+    rm "${2}"  2>&1 | prefix_stdout
+}
+
+systemd_activate_service() {
+
+    # usage:  systemd_activate_service "${SERVICE_NAME}"w
+
+    rst_title "Activate ${1} (service)" section
+    echo
+    tee_stderr <<EOF | bash 2>&1
+systemctl enable  ${1}.service
+systemctl restart ${1}.service
+EOF
+    tee_stderr <<EOF | bash 2>&1
+systemctl status --no-pager ${1}.service
+EOF
+}
+
+systemd_deactivate_service() {
+
+    # usage:  systemd_deactivate_service "${SERVICE_NAME}"
+
+    rst_title "De-Activate ${1} (service)" section
+    echo
+    tee_stderr <<EOF | bash 2>&1 | prefix_stdout
+systemctl stop    ${1}.service
+systemctl disable ${1}.service
+EOF
+}
+
 
 # Apache
 # ------
diff --git a/utils/searx.sh b/utils/searx.sh
index ad8515d4f..47aad286b 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -124,7 +124,7 @@ main() {
 
         shell)
             sudo_or_exit
-            interactive_shell
+            interactive_shell "${SERVICE_USER}"
             ;;
         inspect)
             case $2 in
@@ -153,7 +153,7 @@ main() {
             sudo_or_exit
             case $2 in
                 all) remove_all;;
-                user) remove_user ;;
+                user) drop_service_account "${SERVICE_USER}";;
                 pyenv) remove_pyenv ;;
                 searx-src) remove_searx ;;
                 *) usage "$_usage"; exit 42;;
@@ -250,7 +250,7 @@ cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
 EOF
             ;;
         "start interactiv shell")
-            interactive_shell
+            interactive_shell "${SERVICE_USER}"
             ;;
     esac
     chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
@@ -275,16 +275,12 @@ installations that were installed with this script."
     fi
     remove_searx_uwsgi
     wait_key
-    remove_user
+    drop_service_account "${SERVICE_USER}"
     if service_is_available "${PUBLIC_URL}"; then
         MSG="** Don't forgett to remove your public site! (${PUBLIC_URL}) **" wait_key 10
     fi
 }
 
-user_is_available() {
-    sudo -i -u "$SERVICE_USER" echo \$HOME &>/dev/null
-}
-
 assert_user() {
     rst_title "user $SERVICE_USER" section
     echo
@@ -299,15 +295,6 @@ EOF
     #echo "export SERVICE_HOME=$SERVICE_HOME"
 }
 
-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" 2>&1 | prefix_stdout
-    else
-        rst_para "Leave HOME folder $(du -sh "$SERVICE_HOME") unchanged."
-    fi
-}
-
 clone_is_available() {
     [[ -f "$SEARX_SETTINGS" ]]
 }
@@ -445,11 +432,6 @@ deactivate_service() {
     uWSGI_restart
 }
 
-interactive_shell(){
-    echo "// exit with ${_BCyan}CTRL-D${_creset}"
-    sudo -H -u ${SERVICE_USER} -i
-}
-
 git_diff() {
     sudo -H -u "${SERVICE_USER}" -i <<EOF
 cd ${SEARX_REPO_FOLDER}
@@ -491,7 +473,7 @@ EOF
 
     apache_is_installed && info_msg "Apache is installed."
 
-    if user_is_available; then
+    if service_account_is_available "$SERVICE_USER"; then
         info_msg "Service account $SERVICE_USER exists."
     else
         err_msg "Service account $SERVICE_USER does not exists!"

From a4437c47ac0bd22cd7f5aaa8e7895cdd8e93a317 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 2 Feb 2020 18:14:10 +0100
Subject: [PATCH 023/125] utils/morty.sh: add script to install morty result
 proxy

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/dot_config                              |  14 +-
 utils/filtron.sh                              |  43 +-
 utils/lib.sh                                  |  28 +-
 utils/morty.sh                                | 382 ++++++++++++++++++
 utils/searx.sh                                |  36 +-
 .../etc/apache2/sites-available/morty.conf    |  23 ++
 .../lib/systemd/system/morty.service          |  29 ++
 7 files changed, 506 insertions(+), 49 deletions(-)
 create mode 100755 utils/morty.sh
 create mode 100644 utils/templates/etc/apache2/sites-available/morty.conf
 create mode 100644 utils/templates/lib/systemd/system/morty.service

diff --git a/utils/dot_config b/utils/dot_config
index 0d6758a18..1ddfa868c 100644
--- a/utils/dot_config
+++ b/utils/dot_config
@@ -1,9 +1,17 @@
 # -*- coding: utf-8; mode: sh -*-
 # SPDX-License-Identifier: AGPL-3.0-or-later
 #
-# environment used by utils scripts like filtron.sh or searx.sh
-#
+# This environment is used by ./utils scripts like filtron.sh or searx.sh.  The
+# default values are *most flexible* and *best maintained*, you normally not
+# need to change them.  Before you change any value here you have to uninstall
+# any previous installation.  It is recommended to backup your changes simply by
+# adding them to you local brand (git branch).
 
-# the public URL of the searx instance
+# The public URL of the searx instance
 PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
 PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
+
+# Run all services by one account, but be aware that removing discrete
+# components might conflict!   **experimental**
+#
+# SERVICE_USER=service_account42
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 2aa8cf4f5..dd49a3d1e 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -24,9 +24,11 @@ FILTRON_LISTEN="127.0.0.1:4004"
 FILTRON_TARGET="127.0.0.1:8888"
 
 SERVICE_NAME="filtron"
-SERVICE_USER="${SERVICE_NAME}"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
 SERVICE_HOME="/home/${SERVICE_USER}"
 SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
 
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
@@ -52,7 +54,7 @@ usage() {
     # shellcheck disable=SC1117
     cat <<EOF
 
-usage:
+usage::
 
   $(basename "$0") shell
   $(basename "$0") install    [all|user]
@@ -64,12 +66,11 @@ usage:
   $(basename "$0") option     [debug-on|debug-off]
   $(basename "$0") apache     [install|remove]
 
-
 shell
   start interactive shell from user ${SERVICE_USER}
 install / remove
-  all:        complete setup of filtron service
-  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  :all:        complete setup of filtron service
+  :user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
 update filtron
   Update filtron installation of user ${SERVICE_USER}
 activate service
@@ -80,12 +81,11 @@ inspect service
   show service status and log
 option
   set one of the available options
-apache
-  install: apache site with a reverse proxy (ProxyPass)
-  remove:  apache site ${APACHE_FILTRON_SITE}
+apache : ${PUBLIC_URL}
+  :install: apache site with a reverse proxy (ProxyPass)
+  :remove:  apache site ${APACHE_FILTRON_SITE}
 
-If needed change the environment variable PUBLIC_URL of your WEB service in the
-${DOT_CONFIG#"$REPO_ROOT/"} file:
+If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
   PUBLIC_URL   : ${PUBLIC_URL}
   PUBLIC_HOST  : ${PUBLIC_HOST}
@@ -203,8 +203,9 @@ remove_all() {
 It goes without saying that this script can only be used to remove
 installations that were installed with this script."
 
-    systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
-    wait_key
+    if ! systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+        return 42
+    fi
     drop_service_account "${SERVICE_USER}"
     rm -r "$FILTRON_ETC" 2>&1 | prefix_stdout
     if service_is_available "${PUBLIC_URL}"; then
@@ -231,7 +232,7 @@ 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
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER"
 grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
 EOF
 }
@@ -241,10 +242,12 @@ filtron_is_installed() {
     [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
 }
 
+_svcpr="  |${SERVICE_USER}| "
+
 install_filtron() {
     rst_title "Install filtron in user's ~/go-apps" section
     echo
-    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_service_prefix"
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
 go get -v -u github.com/asciimoo/filtron
 EOF
     install_template --no-eval "$FILTRON_RULES" root root 644
@@ -253,7 +256,7 @@ EOF
 update_filtron() {
     rst_title "Update filtron" section
     echo
-    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_service_prefix"
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
 go get -v -u github.com/asciimoo/filtron
 EOF
 }
@@ -301,12 +304,14 @@ EOF
         err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
     fi
 
-    if ! service_is_available ""http://${FILTRON_TARGET}"" ; then
+    if service_is_available ""http://${FILTRON_TARGET}"" ; then
         info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
     fi
 
     if ! service_is_available "${PUBLIC_URL}"; then
         err_msg "Public service at ${PUBLIC_URL} is not available!"
+        echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
+        wait_key
     fi
 
     local _debug_on
@@ -316,15 +321,17 @@ EOF
     fi
 
     echo
-    systemctl --no-pager -l status filtron.service
+    systemctl --no-pager -l status "${SERVICE_NAME}"
     echo
+
+    info_msg "public URL --> ${PUBLIC_URL}"
     # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
     read -r -s -n1 -t 2
     echo
     while true;  do
         trap break 2
-        journalctl -f -u filtron
+        journalctl -f -u "${SERVICE_NAME}"
     done
 
     if [[ $_debug_on == 1 ]]; then
diff --git a/utils/lib.sh b/utils/lib.sh
index 4a77671a1..818477fc3 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -112,9 +112,9 @@ rst_title() {
     # usage: rst_title <header-text> [part|chapter|section]
 
     case ${2-chapter} in
-        part)     printf "\n${_BGreen}${1//?/=}\n$1\n${1//?/=}${_creset}\n";;
-        chapter)  printf "\n${_BGreen}${1}\n${1//?/=}${_creset}\n";;
-        section)  printf "\n${_BGreen}${1}\n${1//?/-}${_creset}\n";;
+        part)     printf "\n${_BGreen}${1//?/=}\n${_BCyan}${1}${_BGreen}\n${1//?/=}${_creset}\n";;
+        chapter)  printf "\n${_BCyan}${1}\n${_BGreen}${1//?/=}${_creset}\n";;
+        section)  printf "\n${_BCyan}${1}\n${_BGreen}${1//?/-}${_creset}\n";;
         *)
             err_msg "invalid argument '${2}' in line $(caller)"
             return 42
@@ -169,7 +169,9 @@ ask_yn() {
     local _t=$3
     [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
     [[ ! -z $_t ]] && _t="-t $_t"
-    case "${2}" in
+    case "${FORCE_SELECTION:-${2}}" in
+        Y) return ${EXIT_YES} ;;
+        N) return ${EXIT_NO} ;;
         Yn)
             local exit_val=${EXIT_YES}
             local choice="[${_BGreen}YES${_creset}/no]"
@@ -229,7 +231,7 @@ tee_stderr () {
 prefix_stdout () {
     # usage: <cmd> | prefix_stdout [prefix]
 
-    local prefix="  | "
+    local prefix="${_BYellow}-->|${_creset}"
 
     if [[ ! -z $1 ]] ; then prefix="${_BYellow}$1${_creset}"; fi
 
@@ -433,7 +435,7 @@ install_template() {
                 ;;
             "interactiv shell")
                 echo "// edit ${dst} to your needs"
-                echo "// exit with ${_BCyan}CTRL-D${_creset}"
+                echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
                 sudo -H -u "${owner}" -i
                 $DIFF_CMD "${dst}" "${template_file}"
                 echo
@@ -487,14 +489,14 @@ install_go() {
 
     # usage:  install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
 
-    local _service_prefix="  |${3}| "
+    local _svcpr="  |${3}| "
 
     rst_title "Install Go in user's HOME" section
 
     rst_para "download and install go binary .."
     cache_download "${1}" "${2}"
 
-    tee_stderr 0.1 <<EOF | sudo -i -u "${3}" | prefix_stdout "$_service_prefix"
+    tee_stderr 0.1 <<EOF | sudo -i -u "${3}" | prefix_stdout "$_svcpr"
 echo \$PATH
 echo \$GOPATH
 mkdir -p \$HOME/local
@@ -533,7 +535,7 @@ interactive_shell(){
 
     # usage:  interactive_shell "${SERVICE_USER}"
 
-    echo "// exit with ${_BCyan}CTRL-D${_creset}"
+    echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
     sudo -H -u "${1}" -i
 }
 
@@ -558,8 +560,8 @@ systemd_remove_service() {
 
     # usage:  systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
 
-    if ! ask_yn "Do you really want to deinstall ${1}?"; then
-        return
+    if ! ask_yn "Do you really want to deinstall systemd unit ${1}?"; then
+        return 42
     fi
     systemd_deactivate_service "${1}"
     rm "${2}"  2>&1 | prefix_stdout
@@ -845,14 +847,14 @@ git_clone() {
 
     if [[ -d "${dest}" ]] ; then
         info_msg "already cloned: $dest"
-	tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
 cd "${dest}"
 git checkout -m -B "$branch" --track "$remote/$branch"
 git pull --all
 EOF
     else
         info_msg "clone into: $dest"
-	tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
 mkdir -p "$(dirname "$dest")"
 cd "$(dirname "$dest")"
 git clone --branch "$branch" --origin "$remote" "$url" "$(basename "$dest")"
diff --git a/utils/morty.sh b/utils/morty.sh
new file mode 100755
index 000000000..49f7e2724
--- /dev/null
+++ b/utils/morty.sh
@@ -0,0 +1,382 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+source_dot_config
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+PUBLIC_URL_PATH_MORTY="/morty"
+PUBLIC_URL_MORTY="$(dirname ${PUBLIC_URL})${PUBLIC_URL_PATH_MORTY}"
+
+MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+MORTY_TIMEOUT=5
+
+SERVICE_NAME="morty"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
+SERVICE_HOME="/home/${SERVICE_USER}"
+SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
+# shellcheck disable=SC2034
+SERVICE_GROUP="${SERVICE_USER}"
+SERVICE_ENV_DEBUG=false
+
+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")
+
+# shellcheck disable=SC2034
+CONFIG_FILES=()
+
+# Apache Settings
+
+APACHE_MORTY_SITE="morty.conf"
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+
+    # shellcheck disable=SC1117
+    cat <<EOF
+
+usage::
+
+  $(basename "$0") shell
+  $(basename "$0") install    [all|user]
+  $(basename "$0") update     [morty]
+  $(basename "$0") remove     [all]
+  $(basename "$0") activate   [service]
+  $(basename "$0") deactivate [service]
+  $(basename "$0") inspect    [service]
+  $(basename "$0") option     [debug-on|debug-off]
+  $(basename "$0") apache     [install|remove]
+
+shell
+  start interactive shell from user ${SERVICE_USER}
+install / remove
+  all:        complete setup of morty service
+  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+update morty
+  Update morty installation of user ${SERVICE_USER}
+activate service
+  activate and start service daemon (systemd unit)
+deactivate service
+  stop and deactivate service daemon (systemd unit)
+inspect service
+  show service status and log
+option
+  set one of the available options
+apache : ${PUBLIC_URL_MORTY}
+  :install: apache site with a reverse proxy (ProxyPass)
+  :remove:  apache site ${APACHE_MORTY_SITE}
+
+If needed, set the environment variable MORTY_LISTEN in the
+${DOT_CONFIG#"$REPO_ROOT/"} file::
+
+  MORTY_LISTEN :   ${MORTY_LISTEN}
+
+To activate morty in searx, add result_proxy to your settings.yml::
+
+  result_proxy:
+      url : ${PUBLIC_URL_MORTY}
+
+further read: https://asciimoo.github.io/searx/admin/morty.html
+
+EOF
+    [ ! -z ${1+x} ] &&  echo -e "$1"
+}
+
+main() {
+    rst_title "$SERVICE_NAME" part
+
+    required_commands \
+        dpkg apt-get install git wget curl \
+        || exit
+
+    local _usage="ERROR: unknown or missing $1 command $2"
+
+    case $1 in
+        --source-only)  ;;
+        -h|--help) usage; exit 0;;
+
+        shell)
+            sudo_or_exit
+            interactive_shell "${SERVICE_USER}"
+            ;;
+        inspect)
+            case $2 in
+                service)
+                    sudo_or_exit
+                    inspect_service
+                    ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        install)
+            sudo_or_exit
+            case $2 in
+                all) install_all ;;
+                user) assert_user ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        update)
+            sudo_or_exit
+            case $2 in
+                morty) update_morty ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        remove)
+            sudo_or_exit
+            case $2 in
+                all) remove_all;;
+                user) drop_service_account "${SERVICE_USER}" ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        activate)
+            sudo_or_exit
+            case $2 in
+                service)  systemd_activate_service "${SERVICE_NAME}" ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        deactivate)
+            sudo_or_exit
+            case $2 in
+                service)  systemd_deactivate_service "${SERVICE_NAME}" ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        apache)
+            sudo_or_exit
+            case $2 in
+                install) install_apache_site ;;
+                remove) remove_apache_site ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        option)
+            sudo_or_exit
+            case $2 in
+                debug-on)  enable_debug ;;
+                debug-off)  disable_debug ;;
+                *) 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
+    wait_key
+    install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
+    wait_key
+    install_morty
+    wait_key
+    systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+    wait_key
+    echo
+    if ! service_is_available "http://${MORTY_LISTEN}" ; then
+        err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
+    fi
+    if ask_yn "Do you want to inspect the installation?" Yn; then
+        inspect_service
+    fi
+
+}
+
+remove_all() {
+    rst_title "De-Install $SERVICE_NAME (service)"
+
+    rst_para "\
+It goes without saying that this script can only be used to remove
+installations that were installed with this script."
+
+    if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+        drop_service_account "${SERVICE_USER}"
+    fi
+}
+
+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 \
+    --disabled-password --group --gecos 'Morty' $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
+}
+
+morty_is_installed() {
+    [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
+}
+
+_svcpr="  |${SERVICE_USER}| "
+
+install_morty() {
+    rst_title "Install morty in user's ~/go-apps" section
+    echo
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/morty
+EOF
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+cd \$GOPATH/src/github.com/asciimoo/morty
+go test
+go test -benchmem -bench .
+EOF
+}
+
+update_morty() {
+    rst_title "Update morty" section
+    echo
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+go get -v -u github.com/asciimoo/morty
+EOF
+    tee_stderr <<EOF | sudo -i -u "$SERVICE_USER" 2>&1 | prefix_stdout "$_svcpr"
+cd \$GOPATH/src/github.com/asciimoo/morty
+go test
+go test -benchmem -bench .
+EOF
+}
+
+set_service_env_debug() {
+
+    # usage:  set_service_env_debug [false|true]
+
+    local SERVICE_ENV_DEBUG="${1:-false}"
+    if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
+        systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+    fi
+}
+
+inspect_service() {
+
+    rst_title "service status & log"
+
+    cat <<EOF
+
+sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
+
+  MORTY_LISTEN :   ${MORTY_LISTEN}
+
+EOF
+
+    if service_account_is_available "$SERVICE_USER"; then
+        info_msg "service account $SERVICE_USER available."
+    else
+        err_msg "service account $SERVICE_USER not available!"
+    fi
+    if go_is_available "$SERVICE_USER"; then
+        info_msg "~$SERVICE_USER: go is installed"
+    else
+        err_msg "~$SERVICE_USER: go is not installed"
+    fi
+    if morty_is_installed; then
+        info_msg "~$SERVICE_USER: morty app is installed"
+    else
+        err_msg "~$SERVICE_USER: morty app is not installed!"
+    fi
+
+    if ! service_is_available "http://${MORTY_LISTEN}" ; then
+        err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
+        echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
+        wait_key
+    fi
+
+    local _debug_on
+    if ask_yn "Enable filtron debug mode?"; then
+        enable_debug
+        _debug_on=1
+    fi
+
+    echo
+    systemctl --no-pager -l status "${SERVICE_NAME}"
+    echo
+
+    # shellcheck disable=SC2059
+    info_msg "morty URL --> http://${MORTY_LISTEN}"
+    info_msg "public URL --> ${PUBLIC_URL_MORTY}"
+    printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
+    read -r -s -n1 -t 2
+    echo
+    while true;  do
+        trap break 2
+        journalctl -f -u "${SERVICE_NAME}"
+    done
+
+    if [[ $_debug_on == 1 ]]; then
+        FORCE_SELECTION=Y disable_debug
+    fi
+    return 0
+}
+
+
+enable_debug() {
+    warn_msg "Do not enable debug in production enviroments!!"
+    info_msg "Enabling debug option needs to reinstall systemd service!"
+    set_service_env_debug true
+}
+
+disable_debug() {
+    info_msg "Disabling debug option needs to reinstall systemd service!"
+    set_service_env_debug false
+}
+
+install_apache_site() {
+
+    rst_title "Install Apache site $APACHE_MORTY_SITE"
+
+    rst_para "\
+This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE})"
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to continue?"; then
+        return
+    fi
+
+    a2enmod headers
+    a2enmod proxy
+    a2enmod proxy_http
+
+    echo
+    apache_install_site "${APACHE_MORTY_SITE}"
+
+    info_msg "testing public url .."
+    if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+        err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
+    fi
+}
+
+remove_apache_site() {
+
+    rst_title "Remove Apache site $APACHE_MORTY_SITE"
+
+    rst_para "\
+This removes apache site ${APACHE_MORTY_SITE}."
+
+    ! apache_is_installed && err_msg "Apache is not installed."
+
+    if ! ask_yn "Do you really want to continue?"; then
+        return
+    fi
+
+    apache_remove_site "$APACHE_MORTY_SITE"
+}
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------
diff --git a/utils/searx.sh b/utils/searx.sh
index 47aad286b..204386fa8 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -17,10 +17,11 @@ SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
 SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$PUBLIC_URL" \
 | sed -e 's,^.*://\([^\:/]*\).*,\1,g') }"
 
-SERVICE_USER="searx"
+SERVICE_NAME="searx"
+SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
+SERVICE_HOME="/home/${SERVICE_USER}"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
-SERVICE_HOME="/home/${SERVICE_USER}"
 
 SEARX_INTERNAL_URL="127.0.0.1:8888"
 SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
@@ -65,7 +66,7 @@ usage() {
     # shellcheck disable=SC1117
     cat <<EOF
 
-usage:
+usage::
 
   $(basename "$0") shell
   $(basename "$0") install    [all|user|pyenv|searx-src|apache]
@@ -80,10 +81,10 @@ usage:
 shell
   start interactive shell from user ${SERVICE_USER}
 install / remove
-  all:        complete (de-) installation of searx service
-  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
-  searx-src:  clone $SEARX_GIT_URL
-  pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
+  :all:        complete (de-) installation of searx service
+  :user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  :searx-src:  clone $SEARX_GIT_URL
+  :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
 update searx
   Update searx installation of user ${SERVICE_USER}
 activate service
@@ -95,11 +96,10 @@ inspect service
 option
   set one of the available options
 apache
-  install: apache site with the searx uwsgi app
-  remove:  apache site ${APACHE_FILTRON_SITE}
+  :install: apache site with the searx uwsgi app
+  :remove:  apache site ${APACHE_FILTRON_SITE}
 
-If needed change the environment variable PUBLIC_URL of your WEB service in the
-${DOT_CONFIG#"$REPO_ROOT/"} file:
+If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
   PUBLIC_URL          : ${PUBLIC_URL}
   PUBLIC_HOST         : ${PUBLIC_HOST}
@@ -440,6 +440,7 @@ EOF
 }
 
 enable_debug() {
+    warn_msg "Do not enable debug in production enviroments!!"
     info_msg "try to enable debug mode ..."
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
@@ -500,8 +501,10 @@ EOF
     uWSGI_app_available "$SEARX_UWSGI_APP" \
         || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
 
-    if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
-        err_msg "uWSGI app (service) at http://$SEARX_INTERNAL_URL is not available!"
+    if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
+        err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
+        echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
+        wait_key
     fi
 
     if ! service_is_available "${PUBLIC_URL}"; then
@@ -514,15 +517,18 @@ EOF
         _debug_on=1
     fi
     echo
-    systemctl --no-pager -l status uwsgi.service
+    systemctl --no-pager -l status "${SERVICE_NAME}"
     echo
+
     # shellcheck disable=SC2059
+    info_msg "public URL   --> ${PUBLIC_URL}"
+    info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
     read -r -s -n1 -t 2
     echo
     while true;  do
         trap break 2
-        #journalctl -f -u uwsgi.service
+        #journalctl -f -u "${SERVICE_NAME}"
         tail -f /var/log/uwsgi/app/searx.log
     done
 
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/apache2/sites-available/morty.conf
new file mode 100644
index 000000000..231b3fb79
--- /dev/null
+++ b/utils/templates/etc/apache2/sites-available/morty.conf
@@ -0,0 +1,23 @@
+# -*- coding: utf-8; mode: apache -*-
+
+ProxyPreserveHost On
+
+<Location ${PUBLIC_URL_PATH_MORTY} >
+
+    Require all granted
+    Order deny,allow
+    Deny from all
+    #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+    Allow from all
+
+    ProxyPass http://${MORTY_LISTEN}
+    RequestHeader set X-Script-Name ${PUBLIC_URL_PATH_MORTY}
+
+    # In Apache it seems, that setting HTTP_HOST header direct here does have no
+    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST is
+    # needed by searx to render correct *Search URL* in the *Link* box and
+    # *saved preference*.
+
+    # RequestHeader set Host ${PUBLIC_URL_PATH_MORTY}
+
+</Location>
diff --git a/utils/templates/lib/systemd/system/morty.service b/utils/templates/lib/systemd/system/morty.service
new file mode 100644
index 000000000..d463c5097
--- /dev/null
+++ b/utils/templates/lib/systemd/system/morty.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/morty -key '' -listen '${MORTY_LISTEN}' -timeout ${MORTY_TIMEOUT}
+
+Restart=always
+Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME} DEBUG=${SERVICE_ENV_DEBUG}
+
+# 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

From e7401796f652c946f52faf12b1b7c871ceaa7211 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 2 Feb 2020 18:20:20 +0100
Subject: [PATCH 024/125] utils/morty.sh: fixed public url

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/morty.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/morty.sh b/utils/morty.sh
index 49f7e2724..4f689e2b7 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -81,7 +81,7 @@ ${DOT_CONFIG#"$REPO_ROOT/"} file::
 To activate morty in searx, add result_proxy to your settings.yml::
 
   result_proxy:
-      url : ${PUBLIC_URL_MORTY}
+      url : ${PUBLIC_URL_MORTY}/
 
 further read: https://asciimoo.github.io/searx/admin/morty.html
 

From 94ac560dcc85e2e75d3b14706a65eb9488643eb4 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 3 Feb 2020 13:25:51 +0100
Subject: [PATCH 025/125] doc: add descriptions of our tooling box in ./utils

Initial started with the scripts for searx, filtron and morty installations.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst    | 36 ++++++++++----------------------
 docs/admin/morty.rst      |  3 +++
 docs/index.rst            |  1 +
 docs/utils/filtron.sh.rst | 44 +++++++++++++++++++++++++++++++++++++++
 docs/utils/index.rst      | 23 ++++++++++++++++++++
 docs/utils/morty.sh.rst   | 31 +++++++++++++++++++++++++++
 docs/utils/searx.sh.rst   | 21 +++++++++++++++++++
 utils/filtron.sh          |  1 +
 utils/lib.sh              |  5 ++++-
 utils/morty.sh            |  1 +
 utils/searx.sh            |  1 +
 11 files changed, 141 insertions(+), 26 deletions(-)
 create mode 100644 docs/utils/filtron.sh.rst
 create mode 100644 docs/utils/index.rst
 create mode 100644 docs/utils/morty.sh.rst
 create mode 100644 docs/utils/searx.sh.rst

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index e8a2bfb15..332c27203 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -1,14 +1,22 @@
+
+.. _searx_filtron:
+
 ==========================
 How to protect an instance
 ==========================
 
+.. sidebar:: further reading
+
+   - :ref:`filtron.sh`
+
 .. _filtron: https://github.com/asciimoo/filtron
 
 Searx depens on external search services.  To avoid the abuse of these services
 it is advised to limit the number of requests processed by searx.
 
 An application firewall, filtron_ solves exactly this problem.  Filtron is just
-a middleware between your web server (nginx, apache, ...) and searx.
+a middleware between your web server (nginx, apache, ...) and searx, we describe
+such infratructures in chapter: :ref:`architecture`.
 
 
 filtron & go
@@ -17,32 +25,10 @@ filtron & go
 .. _Go: https://golang.org/
 .. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
 
-
-.. sidebar:: init system
-
-   ATM the ``filtron.sh`` supports only systemd init process used by debian,
-   ubuntu and many other dists.  If you have a working init.d file to start/stop
-   filtron service, please contribute.
-
 Filtron needs Go_ installed.  If Go_ is preinstalled, filtron_ is simply
 installed by ``go get`` package management (see `filtron README`_).  If you use
-filtron as middleware, a more isolated setup is recommended.
-
-#. Create a separated user account (``filtron``).
-#. Download and install Go_ binary in users $HOME (``~filtron``).
-#. Install filtron with the package management of Go_ (``go get -v -u
-   github.com/asciimoo/filtron``)
-#. Setup a proper rule configuration :origin:`[ref]
-   <utils/templates/etc/filtron/rules.json>` (``/etc/filtron/rules.json``).
-#. Setup a systemd service unit :origin:`[ref]
-   <utils/templates/lib/systemd/system/filtron.service>`
-   (``/lib/systemd/system/filtron.service``).
-
-To simplify such a installation and the maintenance of; use our script
-``utils/filtron.sh``:
-
-.. program-output:: ../utils/filtron.sh --help
-   :ellipsis: 0,5
+filtron as middleware, a more isolated setup is recommended.  To simplify such
+an installation and the maintenance of, use our script :ref:`filtron.sh`.
 
 
 Sample configuration of filtron
diff --git a/docs/admin/morty.rst b/docs/admin/morty.rst
index 7d7b34492..9af9b6ae9 100644
--- a/docs/admin/morty.rst
+++ b/docs/admin/morty.rst
@@ -1,3 +1,6 @@
+
+.. _searx_morty:
+
 =========================
 How to setup result proxy
 =========================
diff --git a/docs/index.rst b/docs/index.rst
index d9503fef6..95202f39c 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -29,4 +29,5 @@ don't trust anyone, you can set up your own, see :ref:`installation`.
    user/index
    admin/index
    dev/index
+   utils/index
    blog/index
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
new file mode 100644
index 000000000..d93e38105
--- /dev/null
+++ b/docs/utils/filtron.sh.rst
@@ -0,0 +1,44 @@
+
+.. _filtron.sh:
+
+====================
+``utils/filtron.sh``
+====================
+
+.. sidebar:: further reading
+
+   - :ref:`searx_filtron`
+
+.. _Go: https://golang.org/
+.. _filtron: https://github.com/asciimoo/filtron
+.. _filtron README: https://github.com/asciimoo/filtron/blob/master/README.md
+
+To simplify installation and maintenance of a filtron instance you can use the
+script :origin:`utils/filtron.sh`.  In most cases you will install filtron_
+simply by running the command:
+
+.. code::  bash
+
+   sudo -H ./utils/filtron.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``filtron``) and installs filtron_
+into this user account:
+
+#. Create a separated user account (``filtron``).
+#. Download and install Go_ binary in users $HOME (``~filtron``).
+#. Install filtron with the package management of Go_ (``go get -v -u
+   github.com/asciimoo/filtron``)
+#. Setup a proper rule configuration :origin:`[ref]
+   <utils/templates/etc/filtron/rules.json>` (``/etc/filtron/rules.json``).
+#. Setup a systemd service unit :origin:`[ref]
+   <utils/templates/lib/systemd/system/filtron.service>`
+   (``/lib/systemd/system/filtron.service``).
+
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory:
+
+.. program-output:: ../utils/filtron.sh --help
+
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
new file mode 100644
index 000000000..4836ac652
--- /dev/null
+++ b/docs/utils/index.rst
@@ -0,0 +1,23 @@
+
+.. _searx_utils:
+
+======================
+Tooling box ``utils/``
+======================
+
+In the folder :origin:`utils/` we maintain some tools useful for admins and
+developers.
+
+.. toctree::
+   :maxdepth: 1
+
+   searx.sh
+   filtron.sh
+   morty.sh
+
+.. hint::
+
+   ATM :ref:`filtron.sh` and :ref:`searx.sh` do supports only systemd init
+   process used by debian, ubuntu and many other dists.  If you have working
+   init.d files to start/stop services, please contribute / thanks!
+
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
new file mode 100644
index 000000000..64ebda574
--- /dev/null
+++ b/docs/utils/morty.sh.rst
@@ -0,0 +1,31 @@
+
+.. _morty.sh:
+
+.. _morty: https://github.com/asciimoo/morty
+.. _morty's README: https://github.com/asciimoo/morty
+
+==================
+``utils/morty.sh``
+==================
+
+To simplify installation and maintenance of a morty_ instance you can use the
+script :origin:`utils/morty.sh`.  In most cases you will install morty_ simply by
+running the command:
+
+.. code::  bash
+
+   sudo -H ./utils/morty.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``morty``) and installs morty_
+into this user account.
+
+.. hint::
+
+   To add morty to your searx instance read chapter :reF:`searx_morty`.
+
+
+The ``--help`` output of the script is largely
+self-explanatory:
+
+.. program-output:: ../utils/morty.sh --help
+
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
new file mode 100644
index 000000000..fb606c0b0
--- /dev/null
+++ b/docs/utils/searx.sh.rst
@@ -0,0 +1,21 @@
+
+.. _searx.sh:
+
+==================
+``utils/searx.sh``
+==================
+
+To simplify installation and maintenance of a searx instance you can use the
+script :origin:`utils/searx.sh`.  In most cases you will install searx simply by
+running the command:
+
+.. code::  bash
+
+   sudo -H ./utils/searx.sh install all
+
+The script adds a ``${SERVICE_USER}`` (default:``searx``) and installs searx
+into this user account.  The ``--help`` output of the script is largely
+self-explanatory:
+
+.. program-output:: ../utils/searx.sh --help
+
diff --git a/utils/filtron.sh b/utils/filtron.sh
index dd49a3d1e..2a68f1728 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -89,6 +89,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
 
   PUBLIC_URL   : ${PUBLIC_URL}
   PUBLIC_HOST  : ${PUBLIC_HOST}
+  SERVICE_USER : ${SERVICE_USER}
 
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
diff --git a/utils/lib.sh b/utils/lib.sh
index 818477fc3..78641cc0b 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -97,7 +97,10 @@ set_terminal_colors() {
     _BPurple='\e[1;35m'
     _BCyan='\e[1;36m'
 }
-set_terminal_colors
+
+if [ ! -p /dev/stdout ]; then
+    set_terminal_colors
+fi
 
 # reST
 # ----
diff --git a/utils/morty.sh b/utils/morty.sh
index 4f689e2b7..be741c2a4 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -77,6 +77,7 @@ If needed, set the environment variable MORTY_LISTEN in the
 ${DOT_CONFIG#"$REPO_ROOT/"} file::
 
   MORTY_LISTEN :   ${MORTY_LISTEN}
+  SERVICE_USER : ${SERVICE_USER}
 
 To activate morty in searx, add result_proxy to your settings.yml::
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 204386fa8..ec4eaacce 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -104,6 +104,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   PUBLIC_URL          : ${PUBLIC_URL}
   PUBLIC_HOST         : ${PUBLIC_HOST}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
+  SERVICE_USER        : ${SERVICE_USER}
 
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"

From 79e1f837ef7e7c402fd6e9dbb167aac695f2aee7 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 10:39:42 +0100
Subject: [PATCH 026/125] utils & Makefile: add .config.mk & .config.sh for
 searx brands

By isolating the environment of makefiles and bash scripts into .config.mk and
.config.sh it is simple to maintain searx brands by setting some central
environments.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.mk       | 13 ++++++++++++
 .config.sh       | 54 ++++++++++++++++++++++++++++++++++++++++++++++++
 .gitignore       |  1 -
 Makefile         |  6 +++---
 utils/dot_config | 17 ---------------
 utils/filtron.sh | 25 ++++++++++++----------
 utils/lib.sh     | 13 +++++-------
 utils/morty.sh   | 35 +++++++++++++++++++++++--------
 utils/searx.sh   | 16 +++++++-------
 9 files changed, 124 insertions(+), 56 deletions(-)
 create mode 100644 .config.mk
 create mode 100644 .config.sh
 delete mode 100644 utils/dot_config

diff --git a/.config.mk b/.config.mk
new file mode 100644
index 000000000..3349a7602
--- /dev/null
+++ b/.config.mk
@@ -0,0 +1,13 @@
+# -*- coding: utf-8; mode: makefile-gmake -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+# This environment is used by Makefile targets.  If you not maintain your own
+# searx brand, you normally not need to change the defaults (except SEARX_URL).
+# Compare your settings here with file .config.sh used by the toolboxing in
+# utils.
+
+export SEARX_URL=https://searx.me
+
+export GIT_URL=https://github.com/asciimoo/searx
+export DOCS_URL=https://asciimoo.github.io/searx
+
diff --git a/.config.sh b/.config.sh
new file mode 100644
index 000000000..346cb9018
--- /dev/null
+++ b/.config.sh
@@ -0,0 +1,54 @@
+# -*- coding: utf-8; mode: sh -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck shell=bash
+#
+# This environment is used by ./utils scripts like filtron.sh or searx.sh.  The
+# default values are *most flexible* and *best maintained*, you normally not
+# need to change the defaults (except PUBLIC_URL).
+#
+# Before you change any value here you have to uninstall any previous
+# installation.  Further is it recommended to backup your changes simply by
+# adding them to you local brand (git branch)::
+#
+#     git add .config
+#
+# Compare your settings here with file .config.mk used by the Makefile targets.
+
+# The public URL of the searx instance: PUBLIC_URL="https://mydomain.xy/searx"
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
+
+# searx.sh
+# ---------
+
+SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
+
+# Only change, if you maintain a searx brand in your searx fork
+SEARX_DOCS_URL="${SEARX_DOCS_URL:-https://asciimoo.github.io/searx}"
+SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
+SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
+
+# filtron.sh
+# ----------
+
+FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
+FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
+FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
+
+# morty.sh
+# --------
+
+# morty listen address
+MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+
+# system services
+# ---------------
+
+# **experimental**: Set SERVICE_USER to run all services by one account, but be
+# aware that removing discrete components might conflict!
+#
+# SERVICE_USER=searx
+
+# Common $HOME folder of the service accounts
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+
diff --git a/.gitignore b/.gitignore
index 42b76ca9e..3c998afae 100644
--- a/.gitignore
+++ b/.gitignore
@@ -25,4 +25,3 @@ dist/
 local/
 gh-pages/
 searx.egg-info/
-.config
diff --git a/Makefile b/Makefile
index 6577b374e..500910d8e 100644
--- a/Makefile
+++ b/Makefile
@@ -1,8 +1,6 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 
-export GIT_URL=https://github.com/asciimoo/searx
-export SEARX_URL=https://searx.me
-export DOCS_URL=https://asciimoo.github.io/searx
+include ./.config.mk
 
 PYOBJECTS = searx
 DOC       = docs
@@ -80,6 +78,8 @@ test.sh:
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
 	shellcheck -x utils/searx.sh
+	shellcheck -x utils/morty.sh
+	shellcheck -x .config.sh
 
 test.pep8: pyenvinstall
 	$(PY_ENV_ACT); ./manage.sh pep8_check
diff --git a/utils/dot_config b/utils/dot_config
deleted file mode 100644
index 1ddfa868c..000000000
--- a/utils/dot_config
+++ /dev/null
@@ -1,17 +0,0 @@
-# -*- coding: utf-8; mode: sh -*-
-# SPDX-License-Identifier: AGPL-3.0-or-later
-#
-# This environment is used by ./utils scripts like filtron.sh or searx.sh.  The
-# default values are *most flexible* and *best maintained*, you normally not
-# need to change them.  Before you change any value here you have to uninstall
-# any previous installation.  It is recommended to backup your changes simply by
-# adding them to you local brand (git branch).
-
-# The public URL of the searx instance
-PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
-PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
-
-# Run all services by one account, but be aware that removing discrete
-# components might conflict!   **experimental**
-#
-# SERVICE_USER=service_account42
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 2a68f1728..3c545ff14 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -19,13 +19,14 @@ FILTRON_ETC="/etc/filtron"
 
 FILTRON_RULES="$FILTRON_ETC/rules.json"
 
-FILTRON_API="127.0.0.1:4005"
-FILTRON_LISTEN="127.0.0.1:4004"
-FILTRON_TARGET="127.0.0.1:8888"
+FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
+FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
+FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
 
 SERVICE_NAME="filtron"
 SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
-SERVICE_HOME="/home/${SERVICE_USER}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
 SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
@@ -70,9 +71,9 @@ shell
   start interactive shell from user ${SERVICE_USER}
 install / remove
   :all:        complete setup of filtron service
-  :user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
 update filtron
-  Update filtron installation of user ${SERVICE_USER}
+  Update filtron installation ($SERVICE_HOME)
 activate service
   activate and start service daemon (systemd unit)
 deactivate service
@@ -87,10 +88,12 @@ apache : ${PUBLIC_URL}
 
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
-  PUBLIC_URL   : ${PUBLIC_URL}
-  PUBLIC_HOST  : ${PUBLIC_HOST}
-  SERVICE_USER : ${SERVICE_USER}
-
+  PUBLIC_URL     : ${PUBLIC_URL}
+  PUBLIC_HOST    : ${PUBLIC_HOST}
+  SERVICE_USER   : ${SERVICE_USER}
+  FILTRON_API    : ${FILTRON_API}
+  FILTRON_LISTEN : ${FILTRON_LISTEN}
+  FILTRON_TARGET : ${FILTRON_TARGET}
 EOF
     [ ! -z ${1+x} ] &&  echo -e "$1"
 }
@@ -305,7 +308,7 @@ EOF
         err_msg "Filtron does not listening on: http://${FILTRON_LISTEN}"
     fi
 
-    if service_is_available ""http://${FILTRON_TARGET}"" ; then
+    if service_is_available "http://${FILTRON_TARGET}" ; then
         info_msg "Filtron's target is available at: http://${FILTRON_TARGET}"
     fi
 
diff --git a/utils/lib.sh b/utils/lib.sh
index 78641cc0b..05ff5e594 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -32,18 +32,15 @@ if [[ -z ${DIFF_CMD} ]]; then
     fi
 fi
 
-DOT_CONFIG="${DOT_CONFIG:-${REPO_ROOT}/.config}"
+DOT_CONFIG="${DOT_CONFIG:-${REPO_ROOT}/.config.sh}"
 
 source_dot_config() {
-    if [[ ! -e "$DOT_CONFIG" ]]; then
-        info_msg "installing $DOT_CONFIG"
-        cp "$(dirname "${BASH_SOURCE[0]}")/dot_config" "$DOT_CONFIG"
-        if [[ ! -z ${SUDO_USER} ]]; then
-            chown "${SUDO_USER}:${SUDO_USER}" "$DOT_CONFIG"
-        fi
+    if [[ ! -e "${DOT_CONFIG}" ]]; then
+        err_msg "configuration does not extsts at: ${DOT_CONFIG}"
+        return 42
     fi
     # shellcheck disable=SC1090
-    source "${REPO_ROOT}/.config"
+    source "${DOT_CONFIG}"
 }
 
 sudo_or_exit() {
diff --git a/utils/morty.sh b/utils/morty.sh
index be741c2a4..ba4e7dd2e 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -11,17 +11,20 @@ source_dot_config
 # ----------------------------------------------------------------------------
 
 PUBLIC_URL_PATH_MORTY="/morty"
-PUBLIC_URL_MORTY="$(dirname ${PUBLIC_URL})${PUBLIC_URL_PATH_MORTY}"
+PUBLIC_URL_MORTY="$(dirname "${PUBLIC_URL}")${PUBLIC_URL_PATH_MORTY}"
 
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+# shellcheck disable=SC2034
 MORTY_TIMEOUT=5
 
 SERVICE_NAME="morty"
 SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
-SERVICE_HOME="/home/${SERVICE_USER}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
 SERVICE_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${SERVICE_NAME}.service"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
+# shellcheck disable=SC2034
 SERVICE_ENV_DEBUG=false
 
 GO_ENV="${SERVICE_HOME}/.go_env"
@@ -53,14 +56,15 @@ usage::
   $(basename "$0") inspect    [service]
   $(basename "$0") option     [debug-on|debug-off]
   $(basename "$0") apache     [install|remove]
+  $(basename "$0") info       [searx]
 
 shell
   start interactive shell from user ${SERVICE_USER}
 install / remove
   all:        complete setup of morty service
-  user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
 update morty
-  Update morty installation of user ${SERVICE_USER}
+  Update morty installation ($SERVICE_HOME)
 activate service
   activate and start service daemon (systemd unit)
 deactivate service
@@ -77,17 +81,24 @@ If needed, set the environment variable MORTY_LISTEN in the
 ${DOT_CONFIG#"$REPO_ROOT/"} file::
 
   MORTY_LISTEN :   ${MORTY_LISTEN}
-  SERVICE_USER : ${SERVICE_USER}
+  SERVICE_USER :   ${SERVICE_USER}
+EOF
+    info_searx
+    [ ! -z ${1+x} ] &&  echo -e "$1"
+}
+
+info_searx() {
+    # shellcheck disable=SC1117
+    cat <<EOF
 
 To activate morty in searx, add result_proxy to your settings.yml::
 
   result_proxy:
       url : ${PUBLIC_URL_MORTY}/
 
-further read: https://asciimoo.github.io/searx/admin/morty.html
+further read: ${DOCS_URL}/admin/morty.html
 
 EOF
-    [ ! -z ${1+x} ] &&  echo -e "$1"
 }
 
 main() {
@@ -154,6 +165,11 @@ main() {
                 remove) remove_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        info)
+            case $2 in
+                searx) info_searx ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
         option)
             sudo_or_exit
             case $2 in
@@ -176,7 +192,7 @@ install_all() {
     wait_key
     systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
     wait_key
-    echo
+    info_searx
     if ! service_is_available "http://${MORTY_LISTEN}" ; then
         err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
     fi
@@ -258,6 +274,7 @@ set_service_env_debug() {
 
     # usage:  set_service_env_debug [false|true]
 
+    # shellcheck disable=SC2034
     local SERVICE_ENV_DEBUG="${1:-false}"
     if systemd_remove_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"; then
         systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
@@ -308,9 +325,9 @@ EOF
     systemctl --no-pager -l status "${SERVICE_NAME}"
     echo
 
-    # shellcheck disable=SC2059
     info_msg "morty URL --> http://${MORTY_LISTEN}"
     info_msg "public URL --> ${PUBLIC_URL_MORTY}"
+    # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
     read -r -s -n1 -t 2
     echo
diff --git a/utils/searx.sh b/utils/searx.sh
index ec4eaacce..2b5e92c40 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -11,6 +11,8 @@ source_dot_config
 # config
 # ----------------------------------------------------------------------------
 
+SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
+
 SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
 | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
 [[ "${SEARX_URL_PATH}" == "${PUBLIC_URL}" ]] && SEARX_URL_PATH=/
@@ -19,13 +21,13 @@ SEARX_INSTANCE_NAME="${SEARX_INSTANCE_NAME:-searx@$(echo "$PUBLIC_URL" \
 
 SERVICE_NAME="searx"
 SERVICE_USER="${SERVICE_USER:-${SERVICE_NAME}}"
-SERVICE_HOME="/home/${SERVICE_USER}"
+SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
+SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
 
-SEARX_INTERNAL_URL="127.0.0.1:8888"
-SEARX_GIT_URL="https://github.com/asciimoo/searx.git"
-SEARX_GIT_BRANCH="master"
+SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
+SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
 SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
 SEARX_SRC="${SERVICE_HOME}/searx-src"
 SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
@@ -82,11 +84,11 @@ shell
   start interactive shell from user ${SERVICE_USER}
 install / remove
   :all:        complete (de-) installation of searx service
-  :user:       add/remove service user '$SERVICE_USER' at $SERVICE_HOME
+  :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
   :searx-src:  clone $SEARX_GIT_URL
   :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
 update searx
-  Update searx installation of user ${SERVICE_USER}
+  Update searx installation ($SERVICE_HOME)
 activate service
   activate and start service daemon (systemd unit)
 deactivate service
@@ -521,9 +523,9 @@ EOF
     systemctl --no-pager -l status "${SERVICE_NAME}"
     echo
 
-    # shellcheck disable=SC2059
     info_msg "public URL   --> ${PUBLIC_URL}"
     info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
+    # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
     read -r -s -n1 -t 2
     echo

From 8ca86f46d3dfd81a69d2c2d1515bb163f327826c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 10:43:48 +0100
Subject: [PATCH 027/125] .config.sh: wrap up to use environment from Makefile
 as best defaults

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.sh | 10 ++++++----
 1 file changed, 6 insertions(+), 4 deletions(-)

diff --git a/.config.sh b/.config.sh
index 346cb9018..e4c5f9928 100644
--- a/.config.sh
+++ b/.config.sh
@@ -15,7 +15,8 @@
 # Compare your settings here with file .config.mk used by the Makefile targets.
 
 # The public URL of the searx instance: PUBLIC_URL="https://mydomain.xy/searx"
-PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+# The default is taken from the Makefile environment (SEARX_URL).
+PUBLIC_URL="${SEARX_URL:-http://$(uname -n)/searx}"
 PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
 
 # searx.sh
@@ -23,9 +24,10 @@ PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)
 
 SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
 
-# Only change, if you maintain a searx brand in your searx fork
-SEARX_DOCS_URL="${SEARX_DOCS_URL:-https://asciimoo.github.io/searx}"
-SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
+# Only change, if you maintain a searx brand in your searx fork.  The default is
+# taken from the Makefile environment (DOCS_URL, GIT_URL).
+SEARX_DOCS_URL="${DOCS_URL:-https://asciimoo.github.io/searx}"
+SEARX_GIT_URL="${GIT_URL:-https://github.com/asciimoo/searx.git}"
 SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
 
 # filtron.sh

From 9278f0fb45bab21247085a709c0e4bef743752db Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 13:13:17 +0100
Subject: [PATCH 028/125] docs: add some documentation about the tooling box
 ./utils/*

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/arch_public.dot  |  8 +++---
 docs/admin/architecture.rst | 12 +++++----
 docs/admin/installation.rst |  6 +++++
 docs/dev/makefile.rst       | 10 ++++---
 docs/utils/filtron.sh.rst   | 34 +++++++++++++++++++++++-
 docs/utils/index.rst        | 43 +++++++++++++++++++++++++-----
 docs/utils/morty.sh.rst     | 15 ++++++++---
 docs/utils/searx.sh.rst     | 53 ++++++++++++++++++++++++++++++++++---
 8 files changed, 153 insertions(+), 28 deletions(-)

diff --git a/docs/admin/arch_public.dot b/docs/admin/arch_public.dot
index a46c96de3..5018225c3 100644
--- a/docs/admin/arch_public.dot
+++ b/docs/admin/arch_public.dot
@@ -4,11 +4,11 @@ digraph G {
   edge [fontname="Sans"];
 
   browser [label="Browser", shape=Mdiamond];
-  rp      [label="Reverse Proxy", href="url to configure reverse proxy"];
-  filtron [label="Filtron",       href="https://github.com/asciimoo/filtron"];
-  morty   [label="Morty",         href="https://github.com/asciimoo/morty"];
+  rp      [label="Reverse Proxy", href="https://asciimoo.github.io/searx/utils/filtron.sh.html#public-reverse-proxy"];
+  filtron [label="Filtron",       href="https://asciimoo.github.io/searx/utils/filtron.sh.html"];
+  morty   [label="Morty",         href="https://asciimoo.github.io/searx/utils/morty.sh.html"];
   static  [label="Static files",  href="url to configure static files"];
-  uwsgi   [label="uwsgi",         href="url to configure uwsgi"]
+  uwsgi   [label="uwsgi",         href="https://asciimoo.github.io/searx/utils/searx.sh.html"]
   searx1  [label="Searx #1"];
   searx2  [label="Searx #2"];
   searx3  [label="Searx #3"];
diff --git a/docs/admin/architecture.rst b/docs/admin/architecture.rst
index 7064a294b..fbc3bf483 100644
--- a/docs/admin/architecture.rst
+++ b/docs/admin/architecture.rst
@@ -4,17 +4,19 @@
 Architecture
 ============
 
-.. sidebar:: Needs work!
+.. sidebar:: Tooling box
 
-   This article needs some work / Searx is a collaborative effort.  If you have
-   any contribution, feel welcome to send us your :pull:`PR <../pulls>`, see
-   :ref:`how to contribute`.
+   - :ref:`searx & uwsgi <searx.sh>`
+   - :ref:`filtron <filtron.sh>`
+   - :ref:`reverse proxy`
+   - :ref:`morty <morty.sh>`
 
 Herein you will find some hints and suggestions about typical architectures of
 searx infrastructures.
 
 We start with a contribution from :pull:`@dalf <1776#issuecomment-567917320>`.
-It shows a *reference* setup for public searx instances.
+It shows a *reference* setup for public searx instances which can build up and
+maintained by the scripts from our :ref:`toolboxing`.
 
 .. _arch public:
 
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 15800fc01..bff3e44d9 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -7,9 +7,15 @@ Installation
 .. contents::
    :depth: 3
 
+.. _installation basic:
+
 Basic installation
 ==================
 
+.. sidebar:: further reading
+
+   - :ref:`searx.sh`
+
 Step by step installation for Debian/Ubuntu with virtualenv. For Ubuntu, be sure
 to have enable universe repository.
 
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index 8e54aef48..ed9942f4c 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -39,15 +39,17 @@ Calling the ``help`` target gives a first overview::
 
 .. _makefile setup:
 
-Setup
-=====
+Makefile setup
+==============
 
 .. _git stash: https://git-scm.com/docs/git-stash
 
-The main setup is done in the :origin:`Makefile`::
+The main setup is done in the :origin:`.config.sh` (read :ref:`toolboxing
+setup`)::
+
+  export SEARX_URL=https://searx.me
 
   export GIT_URL=https://github.com/asciimoo/searx
-  export SEARX_URL=https://searx.me
   export DOCS_URL=https://asciimoo.github.io/searx
 
 .. sidebar:: fork & upstream
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index d93e38105..356359753 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -8,6 +8,7 @@
 .. sidebar:: further reading
 
    - :ref:`searx_filtron`
+   - :ref:`architecture`
 
 .. _Go: https://golang.org/
 .. _filtron: https://github.com/asciimoo/filtron
@@ -34,11 +35,42 @@ into this user account:
    <utils/templates/lib/systemd/system/filtron.service>`
    (``/lib/systemd/system/filtron.service``).
 
+.. _reverse proxy:
+
+Public Reverse Proxy
+====================
+
+.. tabs::
+
+To install searx in your public HTTP server use:
+
+.. code::  bash
+
+   sudo -H ./utils/filtron.sh apache install
+
+.. tabs::
+
+   .. group-tab:: apache
+
+      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:filtron
+	 :language: apache
+
+      .. tabs::
+
+	 .. group-tab:: Ubuntu / debian
+
+	       .. code-block:: sh
+
+		  $ sudo -H a2enmod headers
+		  $ sudo -H a2enmod proxy
+		  $ sudo -H a2enmod proxy_http
+
 
 Overview
 ========
 
-The ``--help`` output of the script is largely self-explanatory:
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
 
 .. program-output:: ../utils/filtron.sh --help
 
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 4836ac652..9905d755f 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -1,9 +1,10 @@
 
 .. _searx_utils:
+.. _toolboxing:
 
-======================
-Tooling box ``utils/``
-======================
+=======================
+Tooling box ``utils/*``
+=======================
 
 In the folder :origin:`utils/` we maintain some tools useful for admins and
 developers.
@@ -15,9 +16,37 @@ developers.
    filtron.sh
    morty.sh
 
-.. hint::
+.. admonition:: Work needed!
 
-   ATM :ref:`filtron.sh` and :ref:`searx.sh` do supports only systemd init
-   process used by debian, ubuntu and many other dists.  If you have working
-   init.d files to start/stop services, please contribute / thanks!
+   Our scripts to maintain services do most support only systemd init process
+   used by debian, ubuntu and many other dists.  In general our scripts are only
+   partially usable on debian systems.  We are working on this limitation, if
+   you have any contribution, please send us your :pull:`PR <../pulls>`, see
+   :ref:`how to contribute`.
 
+.. _toolboxing common:
+
+Common commands
+===============
+
+Scripts to maintain services often dispose of common commands and environments.
+
+``shell``:
+  Opens a shell from the service user ``${SERVICE_USSR}``, very helpful for
+  troubleshooting.
+
+``inspect service``:
+  Shows status and log of the service, most often you have a option to enable
+  more verbose debug logs.  Very helpful for debugging, but be careful not to
+  enable debugging in a production environment!
+
+.. _toolboxing setup:
+
+Tooling box setup
+=================
+
+The main setup is done in the :origin:`.config.sh` (read also :ref:`makefile
+setup`).
+
+.. literalinclude:: ../../.config.sh
+   :language: bash
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 64ebda574..988b16935 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -1,13 +1,17 @@
 
-.. _morty.sh:
-
 .. _morty: https://github.com/asciimoo/morty
 .. _morty's README: https://github.com/asciimoo/morty
 
+.. _morty.sh:
+
 ==================
 ``utils/morty.sh``
 ==================
 
+.. sidebar:: further reading
+
+   - :ref:`architecture`
+
 To simplify installation and maintenance of a morty_ instance you can use the
 script :origin:`utils/morty.sh`.  In most cases you will install morty_ simply by
 running the command:
@@ -24,8 +28,11 @@ into this user account.
    To add morty to your searx instance read chapter :reF:`searx_morty`.
 
 
-The ``--help`` output of the script is largely
-self-explanatory:
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
 
 .. program-output:: ../utils/morty.sh --help
 
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
index fb606c0b0..1e3f954bb 100644
--- a/docs/utils/searx.sh.rst
+++ b/docs/utils/searx.sh.rst
@@ -5,6 +5,11 @@
 ``utils/searx.sh``
 ==================
 
+.. sidebar:: further reading
+
+   - :ref:`installation`
+   - :ref:`architecture`
+
 To simplify installation and maintenance of a searx instance you can use the
 script :origin:`utils/searx.sh`.  In most cases you will install searx simply by
 running the command:
@@ -14,8 +19,50 @@ running the command:
    sudo -H ./utils/searx.sh install all
 
 The script adds a ``${SERVICE_USER}`` (default:``searx``) and installs searx
-into this user account.  The ``--help`` output of the script is largely
-self-explanatory:
+into this user account.  The installation is described in chapter
+:ref:`installation basic`.
+
+.. _intranet reverse proxy:
+
+Intranet Reverse Proxy
+======================
+
+.. warning::
+
+   This setup is **not** suitable **for public instances**, go on with
+   :ref:`reverse proxy`!
+
+To install searx in your intranet HTTP server use:
+
+.. code::  bash
+
+   sudo -H ./utils/searx.sh apache install
+
+.. tabs::
+
+   .. group-tab:: apache
+
+      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
+	 :language: apache
+
+      .. tabs::
+
+	 .. group-tab:: Ubuntu / debian
+
+	       .. code-block:: sh
+
+		  $ sudo -H apt install libapache2-mod-uwsgi
+
+	 .. group-tab:: Arch Linux
+
+	    .. code-block:: sh
+
+	       $ sudo pacman -S uwsgi
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
 
 .. program-output:: ../utils/searx.sh --help
-

From a5eefea61d8a2287a0065f79cbe12d4e415be743 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 13:26:43 +0100
Subject: [PATCH 029/125] docs: fix extlink 'origin' to use GIT_BRANCH env

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/conf.py | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/docs/conf.py b/docs/conf.py
index 8c5f6b311..8b4276c18 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -5,6 +5,7 @@ from searx.version import VERSION_STRING
 from pallets_sphinx_themes import ProjectLink
 
 GIT_URL = os.environ.get("GIT_URL", "https://github.com/asciimoo/searx")
+GIT_BRANCH =os.environ.get("GIT_BRANCH", "master")
 SEARX_URL = os.environ.get("SEARX_URL", "https://searx.me")
 DOCS_URL = os.environ.get("DOCS_URL", "https://asciimoo.github.io/searx/")
 
@@ -35,7 +36,7 @@ extlinks['wiki'] = ('https://github.com/asciimoo/searx/wiki/%s', ' ')
 extlinks['pull'] = ('https://github.com/asciimoo/searx/pull/%s', 'PR ')
 
 # links to custom brand
-extlinks['origin'] = (GIT_URL + '/blob/master/%s', 'git://')
+extlinks['origin'] = (GIT_URL + '/blob/' + GIT_BRANCH + '/%s', 'git://')
 extlinks['patch'] = (GIT_URL + '/commit/%s', '#')
 extlinks['search'] = (SEARX_URL + '/%s', '#')
 extlinks['docs'] = (DOCS_URL + '/%s', 'docs: ')

From eedd63ccd504099836d9f528d02daffa6bc826ad Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 16:42:13 +0100
Subject: [PATCH 030/125] docs: revision of the installation instructions

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/buildhosts.rst          |   9 +-
 docs/admin/filtron.rst             |   5 +
 docs/admin/index.rst               |   5 +-
 docs/admin/installation-apache.rst |  94 ++++++++
 docs/admin/installation-nginx.rst  | 141 ++++++++++++
 docs/admin/installation.rst        | 338 ++++++++++-------------------
 docs/admin/settings.rst            |   8 +-
 docs/blog/index.rst                |   3 +-
 docs/dev/contribution_guide.rst    |   5 +
 docs/dev/index.rst                 |   3 +-
 docs/index.rst                     |  17 +-
 docs/user/index.rst                |   3 +-
 docs/user/own-instance.rst         |   6 +-
 docs/user/public_instances.rst     |   8 +-
 docs/utils/filtron.sh.rst          |   3 +-
 docs/utils/index.rst               |  23 +-
 docs/utils/morty.sh.rst            |   1 +
 docs/utils/searx.sh.rst            |  19 +-
 utils/searx.sh                     |   5 +-
 19 files changed, 427 insertions(+), 269 deletions(-)
 create mode 100644 docs/admin/installation-apache.rst
 create mode 100644 docs/admin/installation-nginx.rst

diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index c1582cef2..27784764f 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -4,13 +4,18 @@
 Buildhosts
 ==========
 
+To get best results from build, its recommend to install additional packages
+on build hosts.
+
 .. sidebar:: This article needs some work
 
    If you have any contribution send us your :pull:`PR <../pulls>`, see
    :ref:`how to contribute`.
 
-To get best results from build, its recommend to install additional packages
-on build hosts.
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
 
 .. _docs build:
 
diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 332c27203..205269973 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -9,6 +9,11 @@ How to protect an instance
 
    - :ref:`filtron.sh`
 
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
 .. _filtron: https://github.com/asciimoo/filtron
 
 Searx depens on external search services.  To avoid the abuse of these services
diff --git a/docs/admin/index.rst b/docs/admin/index.rst
index b3c7f5119..4190bf24b 100644
--- a/docs/admin/index.rst
+++ b/docs/admin/index.rst
@@ -3,9 +3,12 @@ Administrator documentation
 ===========================
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
+   :caption: Contents
 
    installation
+   installation-nginx
+   installation-apache
    settings
    api
    architecture
diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
new file mode 100644
index 000000000..7f92e47d5
--- /dev/null
+++ b/docs/admin/installation-apache.rst
@@ -0,0 +1,94 @@
+.. _installation apache:
+
+===================
+Install with apache
+===================
+
+.. sidebar:: public HTTP servers
+
+   On public searx instances use an application firewall (:ref:`filtron
+   <filtron.sh>`).
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+Add wsgi mod
+============
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H apt-get install libapache2-mod-uwsgi
+         sudo -H a2enmod uwsgi
+
+Add this configuration in the file ``/etc/apache2/apache2.conf``.  To limit
+acces to your intranet replace ``Allow from all`` directive and replace
+``192.168.0.0/16`` with your subnet IP/class.
+
+.. _inranet apache site:
+
+Note that if your instance of searx is not at the root, you should change
+``<Location />`` by the location of your instance, like ``<Location /searx>``:
+
+.. code:: apache
+
+   # CustomLog /dev/null combined
+
+   <IfModule mod_uwsgi.c>
+
+     <Location />
+
+          Options FollowSymLinks Indexes
+          SetHandler uwsgi-handler
+          uWSGISocket /run/uwsgi/app/searx/socket
+
+          Order deny,allow
+          Deny from all
+          # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+          Allow from all
+
+     </Location>
+
+   </IfModule>
+
+Enable apache mod_uwsgi and restart apache:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         a2enmod uwsgi
+         sudo -H systemctl restart apache2
+
+disable logs
+============
+
+For better privacy you can disable Apache logs.  Go back to
+``/etc/apache2/apache2.conf`` :ref:`[example] <inranet apache site>` and above
+``<Location />`` activate directive:
+
+.. code:: apache
+
+    CustomLog /dev/null combined
+
+Restart apache:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart apache2
+
+.. warning::
+
+   You can only disable logs for the whole (virtual) server not for a specific
+   path.
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
new file mode 100644
index 000000000..37d3e7532
--- /dev/null
+++ b/docs/admin/installation-nginx.rst
@@ -0,0 +1,141 @@
+.. _installation nginx:
+
+==================
+Install with nginx
+==================
+
+.. sidebar:: public HTTP servers
+
+   On public searx instances use an application firewall (:ref:`filtron
+   <filtron.sh>`).
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+If nginx is not installed (uwsgi will not work with the package
+nginx-light):
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H apt-get install nginx
+
+Hosted at ``/``
+===============
+
+Create the configuration file ``/etc/nginx/sites-available/searx`` with this
+content:
+
+.. code:: nginx
+
+    server {
+        listen 80;
+        server_name searx.example.com;
+        root /usr/local/searx/searx;
+
+        location /static {
+        }
+
+        location / {
+                include uwsgi_params;
+                uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+        }
+    }
+
+Create a symlink to sites-enabled:
+
+.. code:: sh
+
+   sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
+
+Restart service:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart nginx
+         sudo -H systemctl restart uwsgi
+
+from subdirectory URL (``/searx``)
+==================================
+
+Add this configuration in the server config file
+``/etc/nginx/sites-enabled/default``:
+
+.. code:: nginx
+
+    location /searx/static {
+            alias /usr/local/searx/searx/static;
+    }
+
+    location /searx {
+            uwsgi_param SCRIPT_NAME /searx;
+            include uwsgi_params;
+            uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+    }
+
+
+**OR** using reverse proxy (Please, note that reverse proxy advised to be used
+in case of single-user or low-traffic instances.)
+
+.. code:: nginx
+
+    location /searx/static {
+            alias /usr/local/searx/searx/static;
+    }
+
+    location /searx {
+        proxy_pass http://127.0.0.1:8888;
+        proxy_set_header Host $host;
+        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+        proxy_set_header X-Scheme $scheme;
+        proxy_set_header X-Script-Name /searx;
+        proxy_buffering off;
+    }
+
+Enable ``base_url`` in ``searx/settings.yml``
+
+.. code:: yaml
+
+    base_url : http://your.domain.tld/searx/
+
+Restart service:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart nginx
+         sudo -H systemctl restart uwsgi
+
+
+disable logs
+============
+
+For better privacy you can disable nginx logs about searx.  How to proceed:
+below ``uwsgi_pass`` in ``/etc/nginx/sites-available/default`` add:
+
+.. code:: nginx
+
+    access_log /dev/null;
+    error_log /dev/null;
+
+Restart service:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart nginx
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index bff3e44d9..a901fee1c 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -4,50 +4,64 @@
 Installation
 ============
 
-.. contents::
-   :depth: 3
+.. sidebar:: Searx server setup
+
+   - :ref:`installation nginx`
+   - :ref:`installation apache`
+
+   If you do not have any special preferences, it is recommend to use
+   :ref:`searx.sh`.
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
 
 .. _installation basic:
 
 Basic installation
 ==================
 
-.. sidebar:: further reading
-
-   - :ref:`searx.sh`
-
-Step by step installation for Debian/Ubuntu with virtualenv. For Ubuntu, be sure
-to have enable universe repository.
+Step by step installation with virtualenv.  For Ubuntu, be sure to have enable
+universe repository.
 
 Install packages:
 
-.. code:: sh
+.. tabs::
 
-    $ sudo -H apt-get install \
-           git build-essential libxslt-dev \
-	   python-dev python-virtualenv python-babel \
-	   zlib1g-dev libffi-dev libssl-dev
+   .. group-tab:: Ubuntu / debian
+
+      .. code-block:: sh
+
+         $ sudo -H apt-get install \
+                   git build-essential
+                   libxslt-dev python3-dev python3-babel \
+                   zlib1g-dev libffi-dev libssl-dev
 
 Install searx:
 
 .. code:: sh
 
-    cd /usr/local
-    sudo -H git clone https://github.com/asciimoo/searx.git
-    sudo -H useradd searx -d /usr/local/searx
+    sudo -H useradd searx --system --disabled-password -d /usr/local/searx
+    sudo -H usermod -a -G shadow $SERVICE_USER
+    cd /usr/local/searx
+    sudo -H git clone https://github.com/asciimoo/searx.git searx-src
     sudo -H chown searx:searx -R /usr/local/searx
 
-Install dependencies in a virtualenv:
+Install virtualenv:
 
 .. code:: sh
 
-    cd /usr/local/searx
     sudo -H -u searx -i
+    (searx)$ python3 -m venv searx-pyenv
+    (searx)$ echo 'source ~/searx-pyenv/bin/activate' > ~/.profile
+
+Exit the searx bash and restart a new to install the searx dependencies:
 
 .. code:: sh
 
-    (searx)$ virtualenv searx-ve
-    (searx)$ . ./searx-ve/bin/activate
+    sudo -H -u searx -i
+    (searx)$ cd searx-src
     (searx)$ ./manage.sh update_packages
 
 Configuration
@@ -55,7 +69,9 @@ Configuration
 
 .. code:: sh
 
-    sed -i -e "s/ultrasecretkey/`openssl rand -hex 16`/g" searx/settings.yml
+    sudo -H -u searx -i
+    (searx)$ cd searx-src
+    (searx)$ sed -i -e "s/ultrasecretkey/`openssl rand -hex 16`/g" searx/settings.yml
 
 Edit searx/settings.yml if necessary.
 
@@ -66,7 +82,9 @@ Start searx:
 
 .. code:: sh
 
-    python searx/webapp.py
+    sudo -H -u searx -i
+    (searx)$ cd searx-src
+    (searx)$ python3 searx/webapp.py
 
 Go to http://localhost:8888
 
@@ -76,57 +94,88 @@ If everything works fine, disable the debug option in settings.yml:
 
     sed -i -e "s/debug : True/debug : False/g" searx/settings.yml
 
-At this point searx is not demonized ; uwsgi allows this.
-
-You can exit the virtualenv and the searx user bash (enter exit command
-twice).
+At this point searx is not demonized ; uwsgi allows this.  You can exit the
+virtualenv and the searx user bash (enter exit command twice).
 
 uwsgi
 =====
 
 Install packages:
 
-.. code:: sh
+.. tabs::
 
-    sudo -H apt-get install \
-         uwsgi uwsgi-plugin-python
+   .. group-tab:: Ubuntu / debian
+
+      .. code-block:: bash
+
+         sudo -H apt-get install uwsgi uwsgi-plugin-python3
 
 Create the configuration file ``/etc/uwsgi/apps-available/searx.ini`` with this
 content:
 
 .. code:: ini
 
-    [uwsgi]
-    # Who will run the code
-    uid = searx
-    gid = searx
+   [uwsgi]
 
-    # disable logging for privacy
-    disable-logging = true
+   # uWSGI core
+   # ----------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
 
-    # Number of workers (usually CPU count)
-    workers = 4
+   # Who will run the code
+   uid = searx
+   gid = searx
 
-    # The right granted on the created socket
-    chmod-socket = 666
+   # chdir to specified directory before apps loading
+   chdir = /usr/local/searx/searx-src/searx
 
-    # Plugin to use and interpretor config
-    single-interpreter = true
-    master = true
-    plugin = python
-    lazy-apps = true
-    enable-threads = true
+   # disable logging for privacy
+   disable-logging = true
 
-    # Module to import
-    module = searx.webapp
+   # The right granted on the created socket
+   chmod-socket = 666
 
-    # Support running the module from a webserver subdirectory.
-    route-run = fixpathinfo:
+   # Plugin to use and interpretor config
+   single-interpreter = true
 
-    # Virtualenv and python path
-    virtualenv = /usr/local/searx/searx-ve/
-    pythonpath = /usr/local/searx/
-    chdir = /usr/local/searx/searx/
+   # enable master process
+   master = true
+
+   # load apps in each worker instead of the master
+   lazy-apps = true
+
+   # load uWSGI plugins
+   plugin = python3,http
+
+   # By default the Python plugin does not initialize the GIL.  This means your
+   # app-generated threads will not run.  If you need threads, remember to enable
+   # them with enable-threads.  Running uWSGI in multithreading mode (with the
+   # threads options) will automatically enable threading support. This *strange*
+   # default behaviour is for performance reasons.
+   enable-threads = true
+
+   # plugin: python
+   # --------------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+   # load a WSGI module
+   module = searx.webapp
+
+   # set PYTHONHOME/virtualenv
+   virtualenv = /usr/local/searx/searx-pyenv
+
+   # add directory (or glob) to pythonpath
+   pythonpath = /usr/local/searx/searx-src
+
+
+   # plugin http
+   # -----------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+
+   # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+   http = 127.0.0.1:8888
 
 Activate the uwsgi application and restart:
 
@@ -136,194 +185,27 @@ Activate the uwsgi application and restart:
     ln -s ../apps-available/searx.ini
     /etc/init.d/uwsgi restart
 
-Web server
-==========
-
-with nginx
-----------
-
-If nginx is not installed (uwsgi will not work with the package
-nginx-light):
-
-.. code:: sh
-
-    sudo -H apt-get install nginx
-
-Hosted at /
-~~~~~~~~~~~
-
-Create the configuration file ``/etc/nginx/sites-available/searx`` with this
-content:
-
-.. code:: nginx
-
-    server {
-        listen 80;
-        server_name searx.example.com;
-        root /usr/local/searx/searx;
-
-        location /static {
-        }
-
-        location / {
-                include uwsgi_params;
-                uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-        }
-    }
-
-Create a symlink to sites-enabled:
-
-.. code:: sh
-
-   sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
-
-Restart service:
-
-.. code:: sh
-
-    sudo -H service nginx restart
-    sudo -H service uwsgi restart
-
-from subdirectory URL (/searx)
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-Add this configuration in the server config file
-``/etc/nginx/sites-enabled/default``:
-
-.. code:: nginx
-
-    location /searx/static {
-            alias /usr/local/searx/searx/static;
-    }
-
-    location /searx {
-            uwsgi_param SCRIPT_NAME /searx;
-            include uwsgi_params;
-            uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-    }
-
-
-**OR** using reverse proxy (Please, note that reverse proxy advised to be used
-in case of single-user or low-traffic instances.)
-
-.. code:: nginx
-
-    location /searx/static {
-            alias /usr/local/searx/searx/static;
-    }
-
-    location /searx {
-        proxy_pass http://127.0.0.1:8888;
-        proxy_set_header Host $host;
-        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header X-Scheme $scheme;
-        proxy_set_header X-Script-Name /searx;
-        proxy_buffering off;
-    }
-
-
-Enable ``base_url`` in ``searx/settings.yml``
-
-.. code:: yaml
-
-    base_url : http://your.domain.tld/searx/
-
-Restart service:
-
-.. code:: sh
-
-    sudo -H service nginx restart
-    sudo -H service uwsgi restart
-
-disable logs
-^^^^^^^^^^^^
-
-for better privacy you can disable nginx logs about searx.
-
-how to proceed: below ``uwsgi_pass`` in ``/etc/nginx/sites-available/default``
-add:
-
-.. code:: nginx
-
-    access_log /dev/null;
-    error_log /dev/null;
-
-Restart service:
-
-.. code:: sh
-
-    sudo -H service nginx restart
-
-with apache
------------
-
-Add wsgi mod:
-
-.. code:: sh
-
-    sudo -H apt-get install libapache2-mod-uwsgi
-    sudo -H a2enmod uwsgi
-
-Add this configuration in the file ``/etc/apache2/apache2.conf``:
-
-.. code:: apache
-
-    <Location />
-        Options FollowSymLinks Indexes
-        SetHandler uwsgi-handler
-        uWSGISocket /run/uwsgi/app/searx/socket
-    </Location>
-
-Note that if your instance of searx is not at the root, you should change
-``<Location />`` by the location of your instance, like ``<Location /searx>``.
-
-Restart Apache:
-
-.. code:: sh
-
-    sudo -H /etc/init.d/apache2 restart
-
-disable logs
-~~~~~~~~~~~~
-
-For better privacy you can disable Apache logs.
-
-.. warning::
-
-   You can only disable logs for the whole (virtual) server not for a specific
-   path.
-
-Go back to ``/etc/apache2/apache2.conf`` and above ``<Location />`` add:
-
-.. code:: apache
-
-    CustomLog /dev/null combined
-
-Restart Apache:
-
-.. code:: sh
-
-    sudo -H /etc/init.d/apache2 restart
 
 How to update
 =============
 
 .. code:: sh
 
-    cd /usr/local/searx
     sudo -H -u searx -i
-
-.. code:: sh
-
-    (searx)$ . ./searx-ve/bin/activate
     (searx)$ git stash
     (searx)$ git pull origin master
     (searx)$ git stash apply
     (searx)$ ./manage.sh update_packages
 
-.. code:: sh
+Restart uwsgi:
 
-    sudo -H service uwsgi restart
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart uwsgi
 
 Docker
 ======
diff --git a/docs/admin/settings.rst b/docs/admin/settings.rst
index 0bfdcc6cb..59a406a28 100644
--- a/docs/admin/settings.rst
+++ b/docs/admin/settings.rst
@@ -4,11 +4,17 @@
 ``settings.yml``
 ================
 
+This page describe the options possibilities of the :origin:`searx/settings.yml`
+file.
+
 .. sidebar:: Further reading ..
 
    - :ref:`search API`
 
-This page describe the options possibilities of the settings.yml file.
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
 
 .. _settings global:
 
diff --git a/docs/blog/index.rst b/docs/blog/index.rst
index 52fa3f126..bc90f4502 100644
--- a/docs/blog/index.rst
+++ b/docs/blog/index.rst
@@ -3,7 +3,8 @@ Blog
 ====
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
+   :caption: Contents
 
    python3
    admin
diff --git a/docs/dev/contribution_guide.rst b/docs/dev/contribution_guide.rst
index 459dfb448..f9e9569f9 100644
--- a/docs/dev/contribution_guide.rst
+++ b/docs/dev/contribution_guide.rst
@@ -4,6 +4,11 @@
 How to contribute
 =================
 
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
 Prime directives: Privacy, Hackability
 ======================================
 
diff --git a/docs/dev/index.rst b/docs/dev/index.rst
index cb913a82b..ba0a25a9c 100644
--- a/docs/dev/index.rst
+++ b/docs/dev/index.rst
@@ -3,7 +3,8 @@ Developer documentation
 =======================
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
+   :caption: Contents
 
    quickstart
    contribution_guide
diff --git a/docs/index.rst b/docs/index.rst
index 95202f39c..c6f5eee96 100644
--- a/docs/index.rst
+++ b/docs/index.rst
@@ -2,7 +2,14 @@
 Welcome to searx
 ================
 
-Search without being tracked.
+    *Search without being tracked.*
+
+Searx is a free internet metasearch engine which aggregates results from more
+than 70 search services.  Users are neither tracked nor profiled.  Additionally,
+searx can be used over Tor for online anonymity.
+
+Get started with searx by using one of the :wiki:`Searx-instances`.  If you
+don't trust anyone, you can set up your own, see :ref:`installation`.
 
 .. sidebar::  Features
 
@@ -16,15 +23,9 @@ Search without being tracked.
    - Hosted by organizations, such as *La Quadrature du Net*, which promote
      digital rights
 
-Searx is a free internet metasearch engine which aggregates results from more
-than 70 search services.  Users are neither tracked nor profiled.  Additionally,
-searx can be used over Tor for online anonymity.
-
-Get started with searx by using one of the :wiki:`Searx-instances`.  If you
-don't trust anyone, you can set up your own, see :ref:`installation`.
-
 .. toctree::
    :maxdepth: 2
+   :caption: Contents
 
    user/index
    admin/index
diff --git a/docs/user/index.rst b/docs/user/index.rst
index 329be3b10..ac8731a69 100644
--- a/docs/user/index.rst
+++ b/docs/user/index.rst
@@ -3,7 +3,8 @@ User documentation
 ==================
 
 .. toctree::
-   :maxdepth: 1
+   :maxdepth: 2
+   :caption: Contents
 
    public_instances
    search_syntax
diff --git a/docs/user/own-instance.rst b/docs/user/own-instance.rst
index a2f736562..4876abe99 100644
--- a/docs/user/own-instance.rst
+++ b/docs/user/own-instance.rst
@@ -2,8 +2,10 @@
 Why use a private instance?
 ===========================
 
-"Is it worth to run my own instance?" is a common question among searx users.
-Before answering this question, see what options a searx user has.
+  *"Is it worth to run my own instance?"*
+
+\.\. is a common question among searx users.  Before answering this question,
+see what options a searx user has.
 
 Public instances are open to everyone who has access to its URL.  Usually, these
 are operated by unknown parties (from the users' point of view).  Private
diff --git a/docs/user/public_instances.rst b/docs/user/public_instances.rst
index 9665c58b7..a5a7b307d 100644
--- a/docs/user/public_instances.rst
+++ b/docs/user/public_instances.rst
@@ -1,16 +1,20 @@
-.. _public instances:
-
 ..
    links has been ported from markdown to reST by::
 
       regexpr:        \[([^\]]*)\]\(([^)]*)\)
       substitution:  `\1 <\2>`__
 
+.. _public instances:
 
 ======================
 Public Searx instances
 ======================
 
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
 .. _mailing list: mailto:searx-instances@autistici.org
 .. _subscription page: https://www.autistici.org/mailman/listinfo/searx-instances
 
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index 356359753..1bba1b3e4 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -7,6 +7,7 @@
 
 .. sidebar:: further reading
 
+   - :ref:`installation`
    - :ref:`searx_filtron`
    - :ref:`architecture`
 
@@ -40,8 +41,6 @@ into this user account:
 Public Reverse Proxy
 ====================
 
-.. tabs::
-
 To install searx in your public HTTP server use:
 
 .. code::  bash
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 9905d755f..97e2c875f 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -6,17 +6,7 @@
 Tooling box ``utils/*``
 =======================
 
-In the folder :origin:`utils/` we maintain some tools useful for admins and
-developers.
-
-.. toctree::
-   :maxdepth: 1
-
-   searx.sh
-   filtron.sh
-   morty.sh
-
-.. admonition:: Work needed!
+.. sidebar:: Work needed!
 
    Our scripts to maintain services do most support only systemd init process
    used by debian, ubuntu and many other dists.  In general our scripts are only
@@ -24,6 +14,17 @@ developers.
    you have any contribution, please send us your :pull:`PR <../pulls>`, see
    :ref:`how to contribute`.
 
+In the folder :origin:`utils/` we maintain some tools useful for admins and
+developers.
+
+.. toctree::
+   :maxdepth: 2
+   :caption: Contents
+
+   searx.sh
+   filtron.sh
+   morty.sh
+
 .. _toolboxing common:
 
 Common commands
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 988b16935..9997ffec5 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -10,6 +10,7 @@
 
 .. sidebar:: further reading
 
+   - :ref:`installation`
    - :ref:`architecture`
 
 To simplify installation and maintenance of a morty_ instance you can use the
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
index 1e3f954bb..988c08438 100644
--- a/docs/utils/searx.sh.rst
+++ b/docs/utils/searx.sh.rst
@@ -9,10 +9,15 @@
 
    - :ref:`installation`
    - :ref:`architecture`
+   - :ref:`filtron.sh`
 
 To simplify installation and maintenance of a searx instance you can use the
-script :origin:`utils/searx.sh`.  In most cases you will install searx simply by
-running the command:
+script :origin:`utils/searx.sh`.
+
+Install
+=======
+
+In most cases you will install searx simply by running the command:
 
 .. code::  bash
 
@@ -27,17 +32,17 @@ into this user account.  The installation is described in chapter
 Intranet Reverse Proxy
 ======================
 
-.. warning::
-
-   This setup is **not** suitable **for public instances**, go on with
-   :ref:`reverse proxy`!
-
 To install searx in your intranet HTTP server use:
 
 .. code::  bash
 
    sudo -H ./utils/searx.sh apache install
 
+.. warning::
+
+   This setup is **not** suitable **for public instances**, go on with
+   :ref:`reverse proxy`!
+
 .. tabs::
 
    .. group-tab:: apache
diff --git a/utils/searx.sh b/utils/searx.sh
index 2b5e92c40..707009a11 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -39,8 +39,9 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
 SEARX_APT_PACKAGES="\
   uwsgi uwsgi-plugin-python3 \
-  git build-essential libxslt-dev python3-dev python3-babel zlib1g-dev \
-  libffi-dev libssl-dev \
+  git build-essential \
+  libxslt-dev python3-dev python3-babel\
+  zlib1g-dev libffi-dev libssl-dev \
 "
 
 # Apache Settings

From d07119ca490eb01b5b4506fb02433a59deb0ac19 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 16:43:07 +0100
Subject: [PATCH 031/125] docs(css): render ..content and ..toctree directive
 same

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/_themes/searx/static/searx.css | 12 +++++++++++-
 1 file changed, 11 insertions(+), 1 deletion(-)

diff --git a/docs/_themes/searx/static/searx.css b/docs/_themes/searx/static/searx.css
index d6a664f0f..ae742a4e0 100644
--- a/docs/_themes/searx/static/searx.css
+++ b/docs/_themes/searx/static/searx.css
@@ -33,7 +33,7 @@ p.sidebar-title, .sidebar p {
 /* admonitions
 */
 
-div.admonition, div.topic {
+div.admonition, div.topic, div.toctree-wrapper {
   background-color: #fafafa;
   margin: 8px 0px;
   padding: 1em;
@@ -42,6 +42,16 @@ div.admonition, div.topic {
   border-right: none;
   border-bottom: none;
   border-left: 5pt solid #ccc;
+  list-style-type: disclosure-closed;
+}
+
+div.toctree-wrapper p.caption {
+  font-weight: normal;
+  font-size: 24px;
+  margin: 0 0 10px 0;
+  padding: 0;
+  line-height: 1;
+  display: inline;
 }
 
 p.admonition-title:after {

From 2f40f61f83afb34f0c4b95019a53050a504ce43a Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 17:59:58 +0100
Subject: [PATCH 032/125] /etc/filtron/rules.json: normalize rules from docs &
 tooling box

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst                 | 192 +++++++++++-----------
 utils/filtron.sh                       |   7 +-
 utils/morty.sh                         |   2 +-
 utils/searx.sh                         |   6 +-
 utils/templates/etc/filtron/rules.json | 210 ++++++++++++-------------
 5 files changed, 210 insertions(+), 207 deletions(-)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 205269973..2bc663411 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -39,6 +39,10 @@ an installation and the maintenance of, use our script :ref:`filtron.sh`.
 Sample configuration of filtron
 ===============================
 
+.. sidebar:: Tooling box
+
+   - :origin:`/etc/filtron/rules.json <utils/templates/etc/filtron/rules.json>`
+
 An example configuration can be find below. This configuration limits the access
 of:
 
@@ -50,100 +54,100 @@ of:
 
 .. code:: json
 
-   [{
-      "name":"search request",
-      "filters":[
-         "Param:q",
-         "Path=^(/|/search)$"
-      ],
-      "interval":"<time-interval-in-sec (int)>",
-      "limit":"<max-request-number-in-interval (int)>",
-      "subrules":[
-         {
-            "name":"roboagent limit",
-            "interval":"<time-interval-in-sec (int)>",
-            "limit":"<max-request-number-in-interval (int)>",
-            "filters":[
-               "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
-            ],
-            "actions":[
-               {
-                  "name":"block",
-                  "params":{
-                     "message":"Rate limit exceeded"
-                  }
-               }
-            ]
-         },
-         {
-            "name":"botlimit",
-            "limit":0,
-            "stop":true,
-            "filters":[
-               "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
-            ],
-            "actions":[
-               {
-                  "name":"block",
-                  "params":{
-                     "message":"Rate limit exceeded"
-                  }
-               }
-            ]
-         },
-         {
-            "name":"IP limit",
-            "interval":"<time-interval-in-sec (int)>",
-            "limit":"<max-request-number-in-interval (int)>",
-            "stop":true,
-            "aggregations":[
-               "Header:X-Forwarded-For"
-            ],
-            "actions":[
-               {
-                  "name":"block",
-                  "params":{
-                     "message":"Rate limit exceeded"
-                  }
-               }
-            ]
-         },
-         {
-            "name":"rss/json limit",
-            "interval":"<time-interval-in-sec (int)>",
-            "limit":"<max-request-number-in-interval (int)>",
-            "stop":true,
-            "filters":[
-               "Param:format=(csv|json|rss)"
-            ],
-            "actions":[
-               {
-                  "name":"block",
-                  "params":{
-                     "message":"Rate limit exceeded"
-                  }
-               }
-            ]
-         },
-         {
-            "name":"useragent limit",
-            "interval":"<time-interval-in-sec (int)>",
-            "limit":"<max-request-number-in-interval (int)>",
-            "aggregations":[
-               "Header:User-Agent"
-            ],
-            "actions":[
-               {
-                  "name":"block",
-                  "params":{
-                     "message":"Rate limit exceeded"
-                  }
-               }
-            ]
-         }
-      ]
-   }]
-
+   [
+     { "name": "search request",
+       "filters": [
+	 "Param:q",
+	 "Path=^(/|/search)$"
+       ],
+       "interval": "<time-interval-in-sec (int)>",
+       "limit": "<max-request-number-in-interval (int)>",
+       "subrules": [
+	 {
+	   "name": "roboagent limit",
+	   "interval": "<time-interval-in-sec (int)>",
+	   "limit": "<max-request-number-in-interval (int)>",
+	   "filters": [
+	     "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
+	   ],
+	   "actions": [
+	     { "name": "log"},
+	     { "name": "block",
+	       "params": {
+		 "message": "Rate limit exceeded"
+	       }
+	     }
+	   ]
+	 },
+	 {
+	   "name": "botlimit",
+	   "limit": 0,
+	   "stop": true,
+	   "filters": [
+	     "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+	   ],
+	   "actions": [
+	     { "name": "log"},
+	     { "name": "block",
+	       "params": {
+		 "message": "Rate limit exceeded"
+	       }
+	     }
+	   ]
+	 },
+	 {
+	   "name": "IP limit",
+	   "interval": "<time-interval-in-sec (int)>",
+	   "limit": "<max-request-number-in-interval (int)>",
+	   "stop": true,
+	   "aggregations": [
+	     "Header:X-Forwarded-For"
+	   ],
+	   "actions": [
+	     { "name": "log"},
+	     { "name": "block",
+	       "params": {
+		 "message": "Rate limit exceeded"
+	       }
+	     }
+	   ]
+	 },
+	 {
+	   "name": "rss/json limit",
+	   "interval": "<time-interval-in-sec (int)>",
+	   "limit": "<max-request-number-in-interval (int)>",
+	   "stop": true,
+	   "filters": [
+	     "Param:format=(csv|json|rss)"
+	   ],
+	   "actions": [
+	     { "name": "log"},
+	     { "name": "block",
+	       "params": {
+		 "message": "Rate limit exceeded"
+	       }
+	     }
+	   ]
+	 },
+	 {
+	   "name": "useragent limit",
+	   "interval": "<time-interval-in-sec (int)>",
+	   "limit": "<max-request-number-in-interval (int)>",
+	   "aggregations": [
+	     "Header:User-Agent"
+	   ],
+	   "actions": [
+	     { "name": "log"},
+	     { "name": "block",
+	       "params": {
+		 "message": "Rate limit exceeded"
+	       }
+	     }
+	   ]
+	 }
+       ]
+     }
+   ]
 
 
 Route request through filtron
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 3c545ff14..c1e360067 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -94,8 +94,9 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   FILTRON_API    : ${FILTRON_API}
   FILTRON_LISTEN : ${FILTRON_LISTEN}
   FILTRON_TARGET : ${FILTRON_TARGET}
+
 EOF
-    [ ! -z ${1+x} ] &&  echo -e "$1"
+    [ ! -z ${1+x} ] &&  err_msg "$1"
 }
 
 main() {
@@ -105,7 +106,7 @@ main() {
         dpkg apt-get install git wget curl \
         || exit
 
-    local _usage="ERROR: unknown or missing $1 command $2"
+    local _usage="unknown or missing $1 command $2"
 
     case $1 in
         --source-only)  ;;
@@ -170,7 +171,7 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
 
-        *) usage "ERROR: unknown or missing command $1"; exit 42;;
+        *) usage "unknown or missing command $1"; exit 42;;
     esac
 }
 
diff --git a/utils/morty.sh b/utils/morty.sh
index ba4e7dd2e..6c63351cf 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -84,7 +84,7 @@ ${DOT_CONFIG#"$REPO_ROOT/"} file::
   SERVICE_USER :   ${SERVICE_USER}
 EOF
     info_searx
-    [ ! -z ${1+x} ] &&  echo -e "$1"
+    [ ! -z ${1+x} ] &&  err_msg "$1"
 }
 
 info_searx() {
diff --git a/utils/searx.sh b/utils/searx.sh
index 707009a11..7609a4acf 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -110,7 +110,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   SERVICE_USER        : ${SERVICE_USER}
 
 EOF
-    [ ! -z ${1+x} ] &&  echo -e "$1"
+    [ ! -z ${1+x} ] &&  err_msg "$1"
 }
 
 main() {
@@ -120,7 +120,7 @@ main() {
         dpkg systemctl apt-get install git wget curl \
         || exit
 
-    local _usage="ERROR: unknown or missing $1 command $2"
+    local _usage="unknown or missing $1 command $2"
 
     case $1 in
         --source-only)  ;;
@@ -190,7 +190,7 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
 
-        *) usage "ERROR: unknown or missing command $1"; exit 42;;
+        *) usage "unknown or missing command $1"; exit 42;;
     esac
 }
 
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index f333306e1..1c7005ae5 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,107 +1,105 @@
-[{
-  "name":"search request",
-  "filters":[
-    "Param:q",
-    "Path=^(/|/search)$"
-  ],
-  "interval":120,
-  "limit":19,
-  "actions":[
-    {
-      "name":"block",
-      "params":{
-        "message":"common rate limit exceeded"
+[
+  { "name": "suspiciously frequent IP",
+    "filters": [],
+    "interval": 600,
+    "limit": 30,
+    "aggregations": [
+      "Header:X-Forwarded-For"
+    ],
+    "actions":[
+      {"name":"log"}
+    ]
+  },
+  { "name": "search request",
+    "filters": [
+      "Param:q",
+      "Path=^(/|/search)$"
+    ],
+    "interval": 61,
+    "limit": 999,
+    "subrules": [
+      {
+        "name": "roboagent limit",
+        "interval": 61,
+        "limit": 1,
+        "filters": [
+          "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
+        ],
+        "actions": [
+          { "name": "log"},
+          { "name": "block",
+            "params": {
+              "message": "Rate limit exceeded"
+            }
+          }
+        ]
+      },
+      {
+        "name": "botlimit",
+        "limit": 0,
+        "stop": true,
+        "filters": [
+          "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+        ],
+        "actions": [
+          { "name": "log"},
+          { "name": "block",
+            "params": {
+              "message": "Rate limit exceeded"
+            }
+          }
+        ]
+      },
+      {
+        "name": "IP limit",
+        "interval": 61,
+        "limit": 9,
+        "stop": true,
+        "aggregations": [
+          "Header:X-Forwarded-For"
+        ],
+        "actions": [
+          { "name": "log"},
+          { "name": "block",
+            "params": {
+              "message": "Rate limit exceeded"
+            }
+          }
+        ]
+      },
+      {
+        "name": "rss/json limit",
+        "interval": 121,
+        "limit": 2,
+        "stop": true,
+        "filters": [
+          "Param:format=(csv|json|rss)"
+        ],
+        "actions": [
+          { "name": "log"},
+          { "name": "block",
+            "params": {
+              "message": "Rate limit exceeded"
+            }
+          }
+        ]
+      },
+      {
+        "name": "useragent limit",
+        "interval": 61,
+        "limit": 199,
+        "aggregations": [
+          "Header:User-Agent"
+        ],
+        "actions": [
+          { "name": "log"},
+          { "name": "block",
+            "params": {
+              "message": "Rate limit exceeded"
+            }
+          }
+        ]
       }
-    }
-  ],
-  "subrules":[
-    {
-      "name":"roboagent limit",
-      "interval":60,
-      "limit":3,
-      "filters":[
-        "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby)"
-      ],
-      "actions":[
-        {"name":"log"},
-        {
-          "name":"block",
-          "params":{
-            "message":"rate limit exceeded"
-          }
-        }
-      ]
-    },
-    {
-      "name":"botlimit",
-      "interval":60,
-      "limit":0,
-      "stop":true,
-      "filters":[
-        "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
-      ],
-      "actions":[
-        {"name":"log"},
-        {
-          "name":"block",
-          "params":{
-            "message":"rate limit exceeded"
-          }
-        }
-      ]
-    },
-    {
-      "name":"IP limit",
-      "interval":60,
-      "limit":13,
-      "stop":true,
-      "aggregations":[
-        "Header:X-Forwarded-For"
-      ],
-      "actions":[
-        {"name":"log"},
-        {
-          "name":"block",
-          "params":{
-            "message":"rate limit exceeded"
-          }
-        }
-      ]
-    },
-    {
-      "name":"rss/json limit",
-      "interval":60,
-      "limit":13,
-      "stop":true,
-      "filters":[
-        "Param:format=(csv|json|rss)"
-      ],
-      "actions":[
-        {"name":"log"},
-        {
-          "name":"block",
-          "params":{
-            "message":"rate limit exceeded"
-          }
-        }
-      ]
-    },
-    {
-      "name":"useragent limit",
-      "interval":60,
-      "limit":13,
-      "aggregations":[
-        "Header:User-Agent"
-      ],
-      "actions":[
-        {"name":"log"},
-        {
-          "name":"block",
-          "params":{
-            "message":"rate limit exceeded"
-          }
-        }
-      ]
-    }
-  ]
-}]
+    ]
+  }
+]

From 71d7550dbe750f35f0dd7b5c513bc9e8db9512ee Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 4 Feb 2020 19:47:33 +0100
Subject: [PATCH 033/125] tooling box ./utils/*:  minor fix from production
 test

---
 .config.sh                                               | 2 +-
 utils/filtron.sh                                         | 2 +-
 utils/lib.sh                                             | 2 +-
 utils/morty.sh                                           | 8 +++++++-
 utils/searx.sh                                           | 6 +++---
 utils/templates/etc/apache2/sites-available/morty.conf   | 5 +++++
 .../etc/apache2/sites-available/searx.conf:filtron       | 9 +++++++++
 .../etc/apache2/sites-available/searx.conf:uwsgi         | 2 ++
 8 files changed, 29 insertions(+), 7 deletions(-)

diff --git a/.config.sh b/.config.sh
index e4c5f9928..0fdc0ca12 100644
--- a/.config.sh
+++ b/.config.sh
@@ -1,6 +1,6 @@
 # -*- coding: utf-8; mode: sh -*-
 # SPDX-License-Identifier: AGPL-3.0-or-later
-# shellcheck shell=bash
+# shellcheck shell=bash disable=SC2034
 #
 # This environment is used by ./utils scripts like filtron.sh or searx.sh.  The
 # default values are *most flexible* and *best maintained*, you normally not
diff --git a/utils/filtron.sh b/utils/filtron.sh
index c1e360067..e97b9f014 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -96,7 +96,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   FILTRON_TARGET : ${FILTRON_TARGET}
 
 EOF
-    [ ! -z ${1+x} ] &&  err_msg "$1"
+    [ ! -z "${1+x}" ] &&  err_msg "$1"
 }
 
 main() {
diff --git a/utils/lib.sh b/utils/lib.sh
index 05ff5e594..59ad12229 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -279,7 +279,7 @@ cache_download() {
         else
             wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
         fi
-        if [[ $exit_value = 0 ]]; then
+        if [[ ! $exit_value = 0 ]]; then
             err_msg "failed to download: $1"
         fi
     fi
diff --git a/utils/morty.sh b/utils/morty.sh
index 6c63351cf..7a9db08a8 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -84,7 +84,7 @@ ${DOT_CONFIG#"$REPO_ROOT/"} file::
   SERVICE_USER :   ${SERVICE_USER}
 EOF
     info_searx
-    [ ! -z ${1+x} ] &&  err_msg "$1"
+    [ ! -z "${1+x}" ] &&  err_msg "$1"
 }
 
 info_searx() {
@@ -196,6 +196,12 @@ install_all() {
     if ! service_is_available "http://${MORTY_LISTEN}" ; then
         err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
     fi
+    if apache_is_installed; then
+        info_msg "Apache is installed on this host."
+        if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+            install_apache_site
+        fi
+    fi
     if ask_yn "Do you want to inspect the installation?" Yn; then
         inspect_service
     fi
diff --git a/utils/searx.sh b/utils/searx.sh
index 7609a4acf..ea6cb1e96 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -110,7 +110,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   SERVICE_USER        : ${SERVICE_USER}
 
 EOF
-    [ ! -z ${1+x} ] &&  err_msg "$1"
+    [ ! -z "${1+x}" ] &&  err_msg "$1"
 }
 
 main() {
@@ -403,8 +403,8 @@ test_local_searx() {
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
 sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
-timeout 5 python3 searx/webapp.py &
-sleep 1
+timeout 10 python3 searx/webapp.py &
+sleep 3
 curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
 sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
 EOF
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/apache2/sites-available/morty.conf
index 231b3fb79..6bcc77b78 100644
--- a/utils/templates/etc/apache2/sites-available/morty.conf
+++ b/utils/templates/etc/apache2/sites-available/morty.conf
@@ -4,7 +4,12 @@ ProxyPreserveHost On
 
 <Location ${PUBLIC_URL_PATH_MORTY} >
 
+    <IfModule mod_security2.c>
+        SecRuleEngine Off
+    </IfModule>
+
     Require all granted
+
     Order deny,allow
     Deny from all
     #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index 11bd70376..3f03dfadd 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -2,9 +2,18 @@
 
 ProxyPreserveHost On
 
+# SecRuleRemoveById 981054
+# SecRuleRemoveById 981059
+# SecRuleRemoveById 981060
+
 <Location ${FILTRON_URL_PATH} >
 
+    <IfModule mod_security2.c>
+        SecRuleEngine Off
+    </IfModule>
+
     Require all granted
+
     Order deny,allow
     Deny from all
     #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
index 51cbdf41f..21e01ac4e 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
@@ -11,6 +11,8 @@
             SecRuleEngine Off
         </IfModule>
 
+	Require all granted
+
         Options FollowSymLinks Indexes
         SetHandler uwsgi-handler
         uWSGISocket ${SEARX_UWSGI_SOCKET}

From a99c65dd018f092a8b7adc6ff7a12961334b40f3 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 5 Feb 2020 07:37:26 +0100
Subject: [PATCH 034/125] docs: misc marginal fixes

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/dev/reST.rst    | 2 +-
 docs/utils/index.rst | 6 +++---
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst
index 7c82ebe5a..7cf9b4be8 100644
--- a/docs/dev/reST.rst
+++ b/docs/dev/reST.rst
@@ -326,7 +326,7 @@ Literal blocks
 The simplest form of :duref:`literal-blocks` is a indented block introduced by
 two colons (``::``).  For highlighting use :dudir:`highlight` or :ref:`reST
 code` directive.  To include literals from external files use directive
-:dudir:`literalinclude`.
+:rst:dir:`literalinclude`.
 
 .. _reST literal:
 
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 97e2c875f..338914715 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -6,6 +6,9 @@
 Tooling box ``utils/*``
 =======================
 
+In the folder :origin:`utils/` we maintain some tools useful for admins and
+developers.
+
 .. sidebar:: Work needed!
 
    Our scripts to maintain services do most support only systemd init process
@@ -14,9 +17,6 @@ Tooling box ``utils/*``
    you have any contribution, please send us your :pull:`PR <../pulls>`, see
    :ref:`how to contribute`.
 
-In the folder :origin:`utils/` we maintain some tools useful for admins and
-developers.
-
 .. toctree::
    :maxdepth: 2
    :caption: Contents

From a014fa0ccde01dbdbe314a45b176e932657b19e4 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 5 Feb 2020 15:26:57 +0100
Subject: [PATCH 035/125] upload-pypi: twine is needed to upload to PyPi 'make
 upload-pypi'

To build & deploy packages on PyPi call make target:

  make upload-pypi

- https://github.com/asciimoo/searx/issues/1829

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 requirements-dev.txt  | 1 +
 utils/makefile.python | 4 ++--
 2 files changed, 3 insertions(+), 2 deletions(-)

diff --git a/requirements-dev.txt b/requirements-dev.txt
index fe92bdce5..a794c3e10 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -15,3 +15,4 @@ linuxdoc @ git+http://github.com/return42/linuxdoc.git
 sphinx-jinja
 sphinx-tabs
 sphinxcontrib-programoutput
+twine
diff --git a/utils/makefile.python b/utils/makefile.python
index 4aa9d6b49..789cc4c90 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -192,7 +192,7 @@ quiet_cmd_pytest      = TEST      $@
 # .. _installing: https://packaging.python.org/tutorials/installing-packages/
 #
 quiet_cmd_pybuild     = BUILD     $@
-      cmd_pybuild     = $(PY_ENV_BIN)/$(PYTHON) setup.py \
+      cmd_pybuild     = $(PY_ENV_BIN)/python setup.py \
 			sdist -d $(PYDIST)  \
 			bdist_wheel --bdist-dir $(PYBUILD) -d $(PYDIST)
 
@@ -284,7 +284,7 @@ pyenv-python: pyenv-install
 
 # https://github.com/pypa/twine
 PHONY += upload-pypi
-upload-pypi: pyclean pybuild
+upload-pypi: pyclean pyenvinstall pybuild
 	@$(PY_ENV_BIN)/twine upload $(PYDIST)/*
 
 .PHONY: $(PHONY)

From ed4cb4f1603dc519aa42cc626874f23d2e9db84e Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 8 Feb 2020 13:24:08 +0100
Subject: [PATCH 036/125] tooling box: varius fix from tests

---
 docs/admin/installation.rst                                 | 2 +-
 utils/searx.sh                                              | 6 +++---
 .../etc/apache2/sites-available/searx.conf:filtron          | 1 +
 3 files changed, 5 insertions(+), 4 deletions(-)

diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index a901fee1c..67358e996 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -43,7 +43,7 @@ Install searx:
 .. code:: sh
 
     sudo -H useradd searx --system --disabled-password -d /usr/local/searx
-    sudo -H usermod -a -G shadow $SERVICE_USER
+    sudo -H usermod -a -G shadow searx
     cd /usr/local/searx
     sudo -H git clone https://github.com/asciimoo/searx.git searx-src
     sudo -H chown searx:searx -R /usr/local/searx
diff --git a/utils/searx.sh b/utils/searx.sh
index ea6cb1e96..f02066ea0 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -235,9 +235,9 @@ EOF
     configure_searx
 
     rst_title "${SEARX_SETTINGS}" section
-    rstBlock 'Diff between new setting file (<) and backup (>):'
+    rst_para 'Diff between new setting file (<) and backup (>):'
     echo
-    diff "$SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
+    $DIFF_CMD "${SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
 
     local action
     choose_one action "What should happen to the settings file? " \
@@ -438,7 +438,7 @@ deactivate_service() {
 
 git_diff() {
     sudo -H -u "${SERVICE_USER}" -i <<EOF
-cd ${SEARX_REPO_FOLDER}
+cd ${SEARX_SRC}
 git --no-pager diff
 EOF
 }
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index 3f03dfadd..5ede66301 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -5,6 +5,7 @@ ProxyPreserveHost On
 # SecRuleRemoveById 981054
 # SecRuleRemoveById 981059
 # SecRuleRemoveById 981060
+# SecRuleRemoveById 950907
 
 <Location ${FILTRON_URL_PATH} >
 

From 5c0f9c8c79f8eca72c206e2f9bb7c6d26bf80fa4 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 8 Feb 2020 17:12:01 +0100
Subject: [PATCH 037/125] tooling box: varius fix from tests

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh | 5 +++++
 utils/searx.sh   | 7 ++++---
 2 files changed, 9 insertions(+), 3 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index e97b9f014..1bea7a717 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -129,6 +129,11 @@ main() {
             case $2 in
                 all) install_all ;;
                 user) assert_user ;;
+                rules)
+                    rst_title "Re-Install filtron rules"
+                    echo
+                    install_template --no-eval "$FILTRON_RULES" root root 644
+                    ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
diff --git a/utils/searx.sh b/utils/searx.sh
index f02066ea0..5eb16700b 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -224,12 +224,13 @@ update_searx() {
     rst_title "Update searx instance"
 
     echo
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+    tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
 cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
 git stash push -m "BACKUP -- 'update server' at ($(date))"
-git checkout -b $SEARX_GIT_BRANCH" --track "$SEARX_GIT_BRANCH"
-git pull "$SEARX_GIT_BRANCH"
+git checkout -B "$SEARX_GIT_BRANCH"
+git pull
+cd ${SEARX_SRC}
 ${SEARX_SRC}/manage.sh update_packages
 EOF
     configure_searx

From 7751b2955948664076409abaff4d4e4161a31c6f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 8 Feb 2020 19:12:28 +0100
Subject: [PATCH 038/125] LXC: add LXC tooling box (initial, WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile     |   1 +
 utils/lxc.sh | 152 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 153 insertions(+)
 create mode 100755 utils/lxc.sh

diff --git a/Makefile b/Makefile
index 500910d8e..0e1816eb5 100644
--- a/Makefile
+++ b/Makefile
@@ -75,6 +75,7 @@ test: test.pep8 test.unit test.sh test.robot
 	- make pylint
 
 test.sh:
+	shellcheck -x utils/lxc.sh
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
 	shellcheck -x utils/searx.sh
diff --git a/utils/lxc.sh b/utils/lxc.sh
new file mode 100755
index 000000000..d79262534
--- /dev/null
+++ b/utils/lxc.sh
@@ -0,0 +1,152 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# shellcheck source=utils/lib.sh
+source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+source_dot_config
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+# name of https://images.linuxcontainers.org
+LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
+HOST_PREFIX="${HOST_PREFIX:-searx}"
+
+TEST_IMAGES=(
+    "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
+    "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
+    "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
+    #"$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
+    #"ubuntu-minimal:18.04"  "ubu1804"
+    #"ubuntu-minimal:19.10"  "ubu1910"
+)
+
+REMOTE_IMAGES=()
+LOCAL_IMAGES=()
+
+for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+    REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${TEST_IMAGES[i]}")
+    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${TEST_IMAGES[i+1]}")
+done
+
+# ----------------------------------------------------------------------------
+usage() {
+# ----------------------------------------------------------------------------
+
+    cat <<EOF
+
+usage::
+
+  $(basename "$0") build [hosts]
+  $(basename "$0") delete [hosts]
+
+build / delete
+  build and/or delete all LXC hosts
+
+all LXC hosts:
+  ${LOCAL_IMAGES[@]}
+
+EOF
+    [ ! -z "${1+x}" ] &&  err_msg "$1"
+}
+
+main() {
+    rst_title "LXC tooling box" part
+
+    required_commands lxc || exit
+
+    local _usage="unknown or missing $1 command $2"
+
+    case $1 in
+        --source-only)  ;;
+        -h|--help) usage; exit 0;;
+
+        build)
+            sudo_or_exit
+            case $2 in
+                hosts) build_instances ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        delete)
+            sudo_or_exit
+            case $2 in
+                hosts) delete_instances ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        *)
+            usage "unknown or missing command $1"; exit 42;;
+    esac
+}
+
+build_instances() {
+    rst_title "Build LXC instances"
+    lxc_copy_images_localy
+    lxc_init_containers
+
+    err_msg "WIP / sorry, not implemented yet :o"
+}
+
+delete_instances() {
+    rst_title "Delete LXC instances"
+    echo -en "\\nLXC hosts(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
+    if ask_yn "Do you really want to delete all images"; then
+        lxc_delete_containers
+    fi
+}
+
+lxc_copy_images_localy() {
+    echo
+    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+        if lxc image info "local:${TEST_IMAGES[i+1]}" &>/dev/null; then
+            info_msg "image ${TEST_IMAGES[i]} already copied --> ${TEST_IMAGES[i+1]}"
+        else
+            info_msg "copy image locally ${TEST_IMAGES[i]} --> ${TEST_IMAGES[i+1]}"
+            lxc image copy "${TEST_IMAGES[i]}" local: \
+                --alias  "${TEST_IMAGES[i+1]}" prefix_stdout
+        fi
+    done
+    #lxc image list local:
+}
+
+lxc_delete_images_localy() {
+    echo
+    for i in "${LOCAL_IMAGES[@]}"; do
+        info_msg "delete image 'local:$i'"
+        lxc image delete "local:$i"
+    done
+    #lxc image list local:
+}
+
+lxc_init_containers() {
+    echo
+    for i in "${LOCAL_IMAGES[@]}"; do
+        if lxc info "$HOST_PREFIX-$i" &>/dev/null; then
+            info_msg "conatiner '$HOST_PREFIX-$i' already exists"
+        else
+            info_msg "create conatiner instance: $HOST_PREFIX-$i"
+            lxc init "local:$i" "$HOST_PREFIX-$i"
+        fi
+    done
+    #lxc list "$HOST_PREFIX"
+}
+
+lxc_delete_containers() {
+    echo
+    for i in "${LOCAL_IMAGES[@]}"; do
+        if lxc info "$HOST_PREFIX-$i" &>/dev/null; then
+            info_msg "stop & delete instance '$HOST_PREFIX-$i'"
+            lxc stop "$HOST_PREFIX-$i" &>/dev/null
+            lxc delete "$HOST_PREFIX-$i" | prefix_stdout
+        else
+            warn_msg "instance '$HOST_PREFIX-$i' does not exist / can't delete :o"
+        fi
+    done
+    #lxc list "$HOST_PREFIX"
+}
+
+
+# ----------------------------------------------------------------------------
+main "$@"
+# ----------------------------------------------------------------------------

From 0d6153db127133e59137f4359975bafd705e0f4b Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 11 Feb 2020 15:57:42 +0100
Subject: [PATCH 039/125] filtron.sh: updated rules from production

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/templates/etc/filtron/rules.json | 226 ++++++++++++++-----------
 1 file changed, 123 insertions(+), 103 deletions(-)

diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index 1c7005ae5..f4a99b3e8 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,105 +1,125 @@
 [
-  { "name": "suspiciously frequent IP",
-    "filters": [],
-    "interval": 600,
-    "limit": 30,
-    "aggregations": [
-      "Header:X-Forwarded-For"
-    ],
-    "actions":[
-      {"name":"log"}
-    ]
-  },
-  { "name": "search request",
-    "filters": [
-      "Param:q",
-      "Path=^(/|/search)$"
-    ],
-    "interval": 61,
-    "limit": 999,
-    "subrules": [
-      {
-        "name": "roboagent limit",
-        "interval": 61,
-        "limit": 1,
-        "filters": [
-          "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
-        ],
-        "actions": [
-          { "name": "log"},
-          { "name": "block",
-            "params": {
-              "message": "Rate limit exceeded"
-            }
-          }
-        ]
-      },
-      {
-        "name": "botlimit",
-        "limit": 0,
-        "stop": true,
-        "filters": [
-          "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
-        ],
-        "actions": [
-          { "name": "log"},
-          { "name": "block",
-            "params": {
-              "message": "Rate limit exceeded"
-            }
-          }
-        ]
-      },
-      {
-        "name": "IP limit",
-        "interval": 61,
-        "limit": 9,
-        "stop": true,
-        "aggregations": [
-          "Header:X-Forwarded-For"
-        ],
-        "actions": [
-          { "name": "log"},
-          { "name": "block",
-            "params": {
-              "message": "Rate limit exceeded"
-            }
-          }
-        ]
-      },
-      {
-        "name": "rss/json limit",
-        "interval": 121,
-        "limit": 2,
-        "stop": true,
-        "filters": [
-          "Param:format=(csv|json|rss)"
-        ],
-        "actions": [
-          { "name": "log"},
-          { "name": "block",
-            "params": {
-              "message": "Rate limit exceeded"
-            }
-          }
-        ]
-      },
-      {
-        "name": "useragent limit",
-        "interval": 61,
-        "limit": 199,
-        "aggregations": [
-          "Header:User-Agent"
-        ],
-        "actions": [
-          { "name": "log"},
-          { "name": "block",
-            "params": {
-              "message": "Rate limit exceeded"
-            }
-          }
-        ]
-      }
-    ]
-  }
+    {
+	"name": "roboagent limit",
+	"filters": [
+	    "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)"
+	],
+	"limit": 0,
+	"stop": true,
+	"actions": [
+	    { "name": "log"},
+	    { "name": "block",
+              "params": {
+		  "message": "Rate limit exceeded"
+              }
+	    }
+	]
+    },
+    {
+	"name": "botlimit",
+	"filters": [
+	    "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+	],
+	"limit": 0,
+	"stop": true,
+	"actions": [
+	    { "name": "log"},
+	    { "name": "block",
+              "params": {
+		  "message": "Rate limit exceeded"
+              }
+	    }
+	]
+    },
+    { "name": "suspiciously frequent IP",
+      "filters": [],
+      "interval": 600,
+      "limit": 30,
+      "aggregations": [
+	  "Header:X-Forwarded-For"
+      ],
+      "actions":[
+	  {"name":"log"}
+      ]
+    },
+    { "name": "search request",
+      "filters": [
+	  "Param:q",
+	  "Path=^(/|/search)$"
+      ],
+      "interval": 61,
+      "limit": 999,
+      "subrules": [
+	  {
+	      "name": "missing Accept-Language",
+	      "filters": ["!Header:Accept-Language"],
+	      "limit": 0,
+	      "stop": true,
+	      "actions": [
+		  {"name": "block",
+		   "params": {"message": "Rate limit exceeded"}}
+	      ]
+	  },
+	  {
+              "name": "suspiciously Connection=close header",
+              "filters": ["Header:Connection=close"],
+              "limit": 0,
+              "stop": true,
+              "actions": [
+		  {"name": "block",
+		   "params": {"message": "Rate limit exceeded"}}
+              ]
+	  },
+	  {
+              "name": "IP limit",
+              "interval": 61,
+              "limit": 9,
+              "stop": true,
+              "aggregations": [
+		  "Header:X-Forwarded-For"
+              ],
+              "actions": [
+		  { "name": "log"},
+		  { "name": "block",
+		    "params": {
+			"message": "Rate limit exceeded"
+		    }
+		  }
+              ]
+	  },
+	  {
+              "name": "rss/json limit",
+              "filters": [
+		  "Param:format=(csv|json|rss)"
+              ],
+              "interval": 121,
+              "limit": 2,
+              "stop": true,
+              "actions": [
+		  { "name": "log"},
+		  { "name": "block",
+		    "params": {
+			"message": "Rate limit exceeded"
+		    }
+		  }
+              ]
+	  },
+	  {
+              "name": "useragent limit",
+              "interval": 61,
+              "limit": 199,
+              "aggregations": [
+		  "Header:User-Agent"
+              ],
+              "actions": [
+		  { "name": "log"},
+		  { "name": "block",
+		    "params": {
+			"message": "Rate limit exceeded"
+		    }
+		  }
+              ]
+	  }
+      ]
+    }
 ]

From e6bf2038c38599a278ca110276c942e7e111d207 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 11 Feb 2020 15:58:35 +0100
Subject: [PATCH 040/125] filtron.sh: add 'install rules' command

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh |  4 +++-
 utils/lib.sh     | 16 +++++++++++++++-
 2 files changed, 18 insertions(+), 2 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 1bea7a717..df6932723 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -58,7 +58,7 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user]
+  $(basename "$0") install    [all|user|rules]
   $(basename "$0") update     [filtron]
   $(basename "$0") remove     [all]
   $(basename "$0") activate   [service]
@@ -72,6 +72,7 @@ shell
 install / remove
   :all:        complete setup of filtron service
   :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
+  :rules:      reinstall filtron rules $FILTRON_RULES
 update filtron
   Update filtron installation ($SERVICE_HOME)
 activate service
@@ -133,6 +134,7 @@ main() {
                     rst_title "Re-Install filtron rules"
                     echo
                     install_template --no-eval "$FILTRON_RULES" root root 644
+                    systemd_restart_service "${SERVICE_NAME}"
                     ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
diff --git a/utils/lib.sh b/utils/lib.sh
index 59ad12229..991fe77fa 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -569,7 +569,7 @@ systemd_remove_service() {
 
 systemd_activate_service() {
 
-    # usage:  systemd_activate_service "${SERVICE_NAME}"w
+    # usage:  systemd_activate_service "${SERVICE_NAME}"
 
     rst_title "Activate ${1} (service)" section
     echo
@@ -594,6 +594,20 @@ systemctl disable ${1}.service
 EOF
 }
 
+systemd_restart_service() {
+
+    # usage:  systemd_restart_service "${SERVICE_NAME}"
+
+    rst_title "Restart ${1} (service)" section
+    echo
+    tee_stderr <<EOF | bash 2>&1
+systemctl restart ${1}.service
+EOF
+    tee_stderr <<EOF | bash 2>&1
+systemctl status --no-pager ${1}.service
+EOF
+}
+
 
 # Apache
 # ------

From 0b2b3255526110770bd419f11dfc4857c699ce77 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 13 Feb 2020 18:25:03 +0100
Subject: [PATCH 041/125] LXC: tooling box - add HOST shares and commandline to
 containers (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 152 +++++++++++++++++++++++++++++++++++++++++++++------
 1 file changed, 136 insertions(+), 16 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index d79262534..1762abd7c 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -9,6 +9,9 @@ source_dot_config
 # ----------------------------------------------------------------------------
 # config
 # ----------------------------------------------------------------------------
+#
+# read also:
+# - https://lxd.readthedocs.io/en/latest/
 
 # name of https://images.linuxcontainers.org
 LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
@@ -17,10 +20,11 @@ HOST_PREFIX="${HOST_PREFIX:-searx}"
 TEST_IMAGES=(
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
-    "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
+
+    # TODO: installation of searx & filtron not yet implemented ..
+    #
+    #"$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
     #"$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
-    #"ubuntu-minimal:18.04"  "ubu1804"
-    #"ubuntu-minimal:19.10"  "ubu1910"
 )
 
 REMOTE_IMAGES=()
@@ -31,6 +35,10 @@ for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
     LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${TEST_IMAGES[i+1]}")
 done
 
+HOST_USER="${SUDO_USER:-$USER}"
+HOST_USER_ID=$(id -u "${HOST_USER}")
+HOST_GROUP_ID=$(id -g "${HOST_USER}")
+
 # ----------------------------------------------------------------------------
 usage() {
 # ----------------------------------------------------------------------------
@@ -39,13 +47,25 @@ usage() {
 
 usage::
 
-  $(basename "$0") build [hosts]
-  $(basename "$0") delete [hosts]
+  $(basename "$0") build        [containers]
+  $(basename "$0") delete       [containers|subordinate]
+  $(basename "$0") [start|stop] [containers]
+  $(basename "$0") inspect      [info|config]
+  $(basename "$0") cmd          ...
 
 build / delete
-  build and/or delete all LXC hosts
+  :containers:   build and delete all LXC containers
+add / delete
+  :subordinate:  lxd permission to map ${HOST_USER}'s user/group id through
+start/stop
+  :containers:   start/stop of all containers
+inspect
+  :info:    show info of all containers
+  :config:  show config of all containers
+cmd ...
+  run commandline ... in all containers
 
-all LXC hosts:
+all LXC containers:
   ${LOCAL_IMAGES[@]}
 
 EOF
@@ -53,7 +73,6 @@ EOF
 }
 
 main() {
-    rst_title "LXC tooling box" part
 
     required_commands lxc || exit
 
@@ -66,15 +85,43 @@ main() {
         build)
             sudo_or_exit
             case $2 in
-                hosts) build_instances ;;
+                containers) build_instances ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         delete)
             sudo_or_exit
             case $2 in
-                hosts) delete_instances ;;
+                containers) delete_instances ;;
+                subordinate) echo; del_subordinate_ids ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        add)
+            sudo_or_exit
+            case $2 in
+                subordinate) echo; add_subordinate_ids ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        start|stop)
+            sudo_or_exit
+            case $2 in
+                containers)  lxc_cmd "$1" ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        inspect)
+            sudo_or_exit
+            case $2 in
+                config) lxc_cmd config show;;
+                info) lxc_cmd info;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
+        cmd)
+            sudo_or_exit
+            shift
+            for i in "${LOCAL_IMAGES[@]}"; do
+                info_msg "lxc exec ${_BBlue}${HOST_PREFIX}-${i}${_creset} -- ${_BGreen}${*}${_creset}"
+                lxc exec "${HOST_PREFIX}-${i}" -- "$@"
+            done
+            ;;
         *)
             usage "unknown or missing command $1"; exit 42;;
     esac
@@ -83,19 +130,25 @@ main() {
 build_instances() {
     rst_title "Build LXC instances"
     lxc_copy_images_localy
+    #lxc image list local: && wait_key
     lxc_init_containers
-
-    err_msg "WIP / sorry, not implemented yet :o"
+    lxc_config_containers
+    lxc list "$HOST_PREFIX"
 }
 
 delete_instances() {
     rst_title "Delete LXC instances"
-    echo -en "\\nLXC hosts(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
+    echo -en "\\nLXC containers(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
     if ask_yn "Do you really want to delete all images"; then
         lxc_delete_containers
     fi
+    # lxc list "$HOST_PREFIX"
+    # lxc image list local: && wait_key
 }
 
+# images
+# ------
+
 lxc_copy_images_localy() {
     echo
     for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
@@ -107,7 +160,6 @@ lxc_copy_images_localy() {
                 --alias  "${TEST_IMAGES[i+1]}" prefix_stdout
         fi
     done
-    #lxc image list local:
 }
 
 lxc_delete_images_localy() {
@@ -119,6 +171,17 @@ lxc_delete_images_localy() {
     #lxc image list local:
 }
 
+# container
+# ---------
+
+lxc_cmd() {
+    echo
+    for i in "${LOCAL_IMAGES[@]}"; do
+        info_msg "lxc $* $HOST_PREFIX-$i"
+        lxc "$@" "$HOST_PREFIX-$i"
+    done
+}
+
 lxc_init_containers() {
     echo
     for i in "${LOCAL_IMAGES[@]}"; do
@@ -129,7 +192,25 @@ lxc_init_containers() {
             lxc init "local:$i" "$HOST_PREFIX-$i"
         fi
     done
-    #lxc list "$HOST_PREFIX"
+}
+
+lxc_config_containers() {
+    echo
+    for i in "${LOCAL_IMAGES[@]}"; do
+
+        info_msg "map uid/gid from host to conatiner: $HOST_PREFIX-$i"
+        # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
+        echo -e -n "uid $HOST_USER_ID 1000\\ngid $HOST_GROUP_ID 1000"\
+            | lxc config set "$HOST_PREFIX-$i" raw.idmap -
+
+        info_msg "share ${REPO_ROOT} (repo_share) from HOST into container: $HOST_PREFIX-$i"
+        # https://lxd.readthedocs.io/en/latest/instances/#type-disk
+        lxc config device add "$HOST_PREFIX-$i" repo_share disk \
+            source="${REPO_ROOT}" \
+            path="/share/$(basename "${REPO_ROOT}")"
+
+        # lxc config show "$HOST_PREFIX-$i" && wait_key
+    done
 }
 
 lxc_delete_containers() {
@@ -143,7 +224,46 @@ lxc_delete_containers() {
             warn_msg "instance '$HOST_PREFIX-$i' does not exist / can't delete :o"
         fi
     done
-    #lxc list "$HOST_PREFIX"
+}
+
+# subordinates
+# ------------
+#
+# see man: subgid(5), subuid(5), https://lxd.readthedocs.io/en/latest/userns-idmap
+#
+# E.g. in the HOST you have uid=1001(user) and/or gid=1001(user) ::
+#
+#   root:1001:1
+#
+# in the CONTAINER::
+#
+#   config:
+#     raw.idmap: |
+#       uid 1001 1000
+#       gid 1001 1000
+
+add_subordinate_ids() {
+    if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
+        info_msg "lxd already has permission to map ${HOST_USER_ID}'s user/group id through"
+    else
+        info_msg "add lxd permission to map ${HOST_USER_ID}'s user/group id through"
+        usermod --add-subuids "${HOST_USER_ID}-${HOST_USER_ID}" \
+                --add-subgids "${HOST_GROUP_ID}-${HOST_GROUP_ID}" root
+    fi
+}
+
+del_subordinate_ids() {
+    local out
+    if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
+        # TODO: root user is always in use by process 1, how can we remove subordinates?
+        info_msg "remove lxd permission to map ${HOST_USER_ID}'s user/group id through"
+        out=$(usermod --del-subuids "${HOST_USER_ID}-${HOST_USER_ID}" --del-subgids "${HOST_GROUP_ID}-${HOST_GROUP_ID}" root 2>&1)
+        if [ ! -z $? ]; then
+            err_msg "$out"
+        fi
+    else
+        info_msg "lxd does not have permission to map ${HOST_USER_ID}'s user/group id through"
+    fi
 }
 
 

From e8cf22504650f742da247bc923abebccec869676 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 16 Feb 2020 18:18:15 +0100
Subject: [PATCH 042/125] LXC: tooling box - add boilerplate to containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 109 +++++++++++++++++++++++++++++++++++++--------------
 1 file changed, 80 insertions(+), 29 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index 1762abd7c..d0ad49bd1 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -27,12 +27,18 @@ TEST_IMAGES=(
     #"$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
 )
 
+ubu1804_boilerplate="
+export DEBIAN_FRONTEND=noninteractive
+apt-get install -y git curl wget
+"
+ubu1904_boilerplate="$ubu1804_boilerplate"
+
 REMOTE_IMAGES=()
 LOCAL_IMAGES=()
 
 for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
     REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${TEST_IMAGES[i]}")
-    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${TEST_IMAGES[i+1]}")
+    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${HOST_PREFIX}-${TEST_IMAGES[i+1]}")
 done
 
 HOST_USER="${SUDO_USER:-$USER}"
@@ -72,9 +78,26 @@ EOF
     [ ! -z "${1+x}" ] &&  err_msg "$1"
 }
 
+lxd_info() {
+
+    cat <<EOF
+
+LXD is needed, to install run::
+
+  snap install lxd
+  lxc init --auto
+
+EOF
+}
+
 main() {
 
-    required_commands lxc || exit
+    local exit_val
+
+    if ! required_commands lxc; then
+        lxd_info
+        exit 42
+    fi
 
     local _usage="unknown or missing $1 command $2"
 
@@ -118,8 +141,13 @@ main() {
             sudo_or_exit
             shift
             for i in "${LOCAL_IMAGES[@]}"; do
-                info_msg "lxc exec ${_BBlue}${HOST_PREFIX}-${i}${_creset} -- ${_BGreen}${*}${_creset}"
-                lxc exec "${HOST_PREFIX}-${i}" -- "$@"
+                info_msg "call ${_BBlue}${i}${_creset} -- ${_BGreen}${*}${_creset}"
+                wait_key 3
+                lxc exec "${i}" -- "$@"
+                exit_val=$?
+                if [ $exit_val -ne 0 ]; then
+                    err_msg "$exit_val ${_BBlue}${i}${_creset} -- ${_BGreen}${*}${_creset}"
+                fi
             done
             ;;
         *)
@@ -129,10 +157,18 @@ main() {
 
 build_instances() {
     rst_title "Build LXC instances"
+
+    rst_title "copy images" section
+    echo
     lxc_copy_images_localy
-    #lxc image list local: && wait_key
+    lxc image list local: && wait_key
+    echo
+    rst_title "build containers" section
+    echo
     lxc_init_containers
     lxc_config_containers
+    lxc_boilerplate_containers
+    echo
     lxc list "$HOST_PREFIX"
 }
 
@@ -142,7 +178,8 @@ delete_instances() {
     if ask_yn "Do you really want to delete all images"; then
         lxc_delete_containers
     fi
-    # lxc list "$HOST_PREFIX"
+    echo
+    lxc list "$HOST_PREFIX"
     # lxc image list local: && wait_key
 }
 
@@ -150,14 +187,13 @@ delete_instances() {
 # ------
 
 lxc_copy_images_localy() {
-    echo
     for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
         if lxc image info "local:${TEST_IMAGES[i+1]}" &>/dev/null; then
             info_msg "image ${TEST_IMAGES[i]} already copied --> ${TEST_IMAGES[i+1]}"
         else
             info_msg "copy image locally ${TEST_IMAGES[i]} --> ${TEST_IMAGES[i+1]}"
             lxc image copy "${TEST_IMAGES[i]}" local: \
-                --alias  "${TEST_IMAGES[i+1]}" prefix_stdout
+                --alias  "${TEST_IMAGES[i+1]}" | prefix_stdout
         fi
     done
 }
@@ -175,53 +211,68 @@ lxc_delete_images_localy() {
 # ---------
 
 lxc_cmd() {
-    echo
     for i in "${LOCAL_IMAGES[@]}"; do
-        info_msg "lxc $* $HOST_PREFIX-$i"
-        lxc "$@" "$HOST_PREFIX-$i"
+        info_msg "lxc $* $i"
+        lxc "$@" "$i"
     done
 }
 
 lxc_init_containers() {
-    echo
     for i in "${LOCAL_IMAGES[@]}"; do
-        if lxc info "$HOST_PREFIX-$i" &>/dev/null; then
-            info_msg "conatiner '$HOST_PREFIX-$i' already exists"
+        if lxc info "$i" &>/dev/null; then
+            info_msg "conatiner '$i' already exists"
         else
-            info_msg "create conatiner instance: $HOST_PREFIX-$i"
-            lxc init "local:$i" "$HOST_PREFIX-$i"
+            info_msg "create conatiner instance: $i"
+            lxc init "local:$i" "$i"
         fi
     done
 }
 
 lxc_config_containers() {
-    echo
     for i in "${LOCAL_IMAGES[@]}"; do
+        info_msg "configure container: ${_BBlue}${i}${_creset}"
 
-        info_msg "map uid/gid from host to conatiner: $HOST_PREFIX-$i"
+        info_msg "map uid/gid from host to container"
         # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
         echo -e -n "uid $HOST_USER_ID 1000\\ngid $HOST_GROUP_ID 1000"\
-            | lxc config set "$HOST_PREFIX-$i" raw.idmap -
+            | lxc config set "$i" raw.idmap -
 
-        info_msg "share ${REPO_ROOT} (repo_share) from HOST into container: $HOST_PREFIX-$i"
+        info_msg "share ${REPO_ROOT} (repo_share) from HOST into container"
         # https://lxd.readthedocs.io/en/latest/instances/#type-disk
-        lxc config device add "$HOST_PREFIX-$i" repo_share disk \
+        lxc config device add "$i" repo_share disk \
             source="${REPO_ROOT}" \
-            path="/share/$(basename "${REPO_ROOT}")"
+            path="/share/$(basename "${REPO_ROOT}")" &>/dev/null
+        # lxc config show "$i" && wait_key
+    done
+}
 
-        # lxc config show "$HOST_PREFIX-$i" && wait_key
+lxc_boilerplate_containers() {
+    local shortname
+    local boilerplate_script
+    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+        shortname="${TEST_IMAGES[i+1]}"
+        info_msg "install boilerplate: ${_BBlue}${HOST_PREFIX}-${shortname}${_creset}"
+        lxc start -q "${HOST_PREFIX}-${shortname}" &>/dev/null
+        boilerplate_script="${shortname}_boilerplate"
+        boilerplate_script="${!boilerplate_script}"
+        if [[ ! -z "${boilerplate_script}" ]]; then
+            echo "$boilerplate_script" \
+                | lxc exec "${HOST_PREFIX}-${shortname}" -- bash \
+                | prefix_stdout " ${HOST_PREFIX}-${shortname} | "
+        else
+            warn_msg "no boilerplate for instance '$i'"
+        fi
     done
 }
 
 lxc_delete_containers() {
-    echo
     for i in "${LOCAL_IMAGES[@]}"; do
-        if lxc info "$HOST_PREFIX-$i" &>/dev/null; then
-            info_msg "stop & delete instance '$HOST_PREFIX-$i'"
-            lxc stop "$HOST_PREFIX-$i" &>/dev/null
-            lxc delete "$HOST_PREFIX-$i" | prefix_stdout
+        if lxc info "$i" &>/dev/null; then
+            info_msg "stop & delete instance '$i'"
+            lxc stop "$i" &>/dev/null
+            lxc delete "$i" | prefix_stdout
         else
-            warn_msg "instance '$HOST_PREFIX-$i' does not exist / can't delete :o"
+            warn_msg "instance '$i' does not exist / can't delete :o"
         fi
     done
 }

From ad3273986024c80cfe067d1b77983901a41b6d01 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 16 Feb 2020 20:07:37 +0100
Subject: [PATCH 043/125] shellcheck: fix usse -n instead of ! -z (SC2236
 SC2237)

 - https://www.shellcheck.net/wiki/SC2236 -- Use -n instead of ! -z.
 - https://www.shellcheck.net/wiki/SC2237 -- Use [ -n .. ] instead of ! [ -z ....

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh |  2 +-
 utils/lib.sh     | 34 +++++++++++++++++-----------------
 utils/lxc.sh     |  9 ++++++---
 utils/morty.sh   |  2 +-
 utils/searx.sh   |  2 +-
 5 files changed, 26 insertions(+), 23 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index df6932723..2cbea4023 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -97,7 +97,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   FILTRON_TARGET : ${FILTRON_TARGET}
 
 EOF
-    [ ! -z "${1+x}" ] &&  err_msg "$1"
+    [[ -n ${1} ]] &&  err_msg "$1"
 }
 
 main() {
diff --git a/utils/lib.sh b/utils/lib.sh
index 991fe77fa..a7efb78ea 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -57,7 +57,7 @@ required_commands() {
     # usage:  requires_commands [cmd1 ...]
 
     local exit_val=0
-    while [ ! -z "$1" ]; do
+    while [ -n "$1" ]; do
 
         if ! command -v "$1" &>/dev/null; then
             err_msg "missing command $1"
@@ -125,7 +125,7 @@ rst_title() {
 rst_para() {
     # usage:  RST_INDENT=1 rst_para "lorem ipsum ..."
     local prefix=''
-    if ! [[ -z $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
+    if [[ -n $RST_INDENT ]] && [[ $RST_INDENT -gt 0 ]]; then
         prefix="$(for i in $(seq 1 "$RST_INDENT"); do printf "  "; done)"
         echo -en "\n$*\n" | $FMT | prefix_stdout "$prefix"
     else
@@ -151,8 +151,8 @@ wait_key(){
     local msg="${MSG}"
     [[ -z "$msg" ]] && msg="${_Green}** press any [${_BCyan}KEY${_Green}] to continue **${_creset}"
 
-    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
-    [[ ! -z $_t ]] && _t="-t $_t"
+    [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ -n $_t ]] && _t="-t $_t"
     printf "$msg"
     # shellcheck disable=SC2086
     read -r -s -n1 $_t
@@ -167,8 +167,8 @@ ask_yn() {
     local EXIT_NO=1  # exit status 1 --> error code
 
     local _t=$3
-    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
-    [[ ! -z $_t ]] && _t="-t $_t"
+    [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ -n $_t ]] && _t="-t $_t"
     case "${FORCE_SELECTION:-${2}}" in
         Y) return ${EXIT_YES} ;;
         N) return ${EXIT_NO} ;;
@@ -218,7 +218,7 @@ tee_stderr () {
     #    hello
 
     local _t="0";
-    if [[ ! -z $1 ]] ; then _t="$1"; fi
+    if [[ -n $1 ]] ; then _t="$1"; fi
 
     (while read -r line; do
          # shellcheck disable=SC2086
@@ -233,7 +233,7 @@ prefix_stdout () {
 
     local prefix="${_BYellow}-->|${_creset}"
 
-    if [[ ! -z $1 ]] ; then prefix="${_BYellow}$1${_creset}"; fi
+    if [[ -n $1 ]] ; then prefix="${_BYellow}$1${_creset}"; fi
 
     # shellcheck disable=SC2162
     (while IFS= read line; do
@@ -260,7 +260,7 @@ cache_download() {
 
     local exit_value=0
 
-    if [[ ! -z ${SUDO_USER} ]]; then
+    if [[ -n ${SUDO_USER} ]]; then
         sudo -u "${SUDO_USER}" mkdir -p "${CACHE}"
     else
         mkdir -p "${CACHE}"
@@ -274,7 +274,7 @@ cache_download() {
     if [[ ! -f "${CACHE}/$2" ]]; then
         info_msg "caching: $1"
         info_msg "  --> ${CACHE}/$2"
-        if [[ ! -z ${SUDO_USER} ]]; then
+        if [[ -n ${SUDO_USER} ]]; then
             sudo -u "${SUDO_USER}" wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
         else
             wget --progress=bar -O "${CACHE}/$2" "$1" ; exit_value=$?
@@ -298,8 +298,8 @@ choose_one() {
     local choice=$1;
     local max="${#@}"
     local _t
-    [[ ! -z $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
-    [[ ! -z $_t ]] && _t="-t $_t"
+    [[ -n $FORCE_TIMEOUT ]] && _t=$FORCE_TIMEOUT
+    [[ -n $_t ]] && _t="-t $_t"
 
     list=("$@")
     echo -e "${_BGreen}Menu::${_creset}"
@@ -374,7 +374,7 @@ install_template() {
     local chmod="${pos_args[4]-644}"
 
     info_msg "install (eval=$do_eval): ${dst}"
-    [[ ! -z $variant ]] && info_msg "variant: ${variant}"
+    [[ -n $variant ]] && info_msg "variant: ${variant}"
 
     if [[ ! -f "${template_origin}" ]] ; then
         err_msg "${template_origin} does not exists"
@@ -386,14 +386,14 @@ install_template() {
     if [[ "$do_eval" == "1" ]]; then
         template_file="${CACHE}${dst}${variant}"
         info_msg "BUILD template ${template_file}"
-        if [[ ! -z ${SUDO_USER} ]]; then
+        if [[ -n ${SUDO_USER} ]]; then
             sudo -u "${SUDO_USER}" mkdir -p "$(dirname "${template_file}")"
         else
             mkdir -p "$(dirname "${template_file}")"
         fi
         # shellcheck disable=SC2086
         eval "echo \"$(cat ${template_origin})\"" > "${template_file}"
-        if [[ ! -z ${SUDO_USER} ]]; then
+        if [[ -n ${SUDO_USER} ]]; then
             chown "${SUDO_USER}:${SUDO_USER}" "${template_file}"
         fi
     else
@@ -856,8 +856,8 @@ git_clone() {
     fi
 
     [[ -z $branch ]] && branch=master
-    [[ -z $user ]] && [[ ! -z "${SUDO_USER}" ]] && user="${SUDO_USER}"
-    [[ ! -z $user ]] && bash_cmd="sudo -H -u $user -i"
+    [[ -z $user ]] && [[ -n "${SUDO_USER}" ]] && user="${SUDO_USER}"
+    [[ -n $user ]] && bash_cmd="sudo -H -u $user -i"
 
     if [[ -d "${dest}" ]] ; then
         info_msg "already cloned: $dest"
diff --git a/utils/lxc.sh b/utils/lxc.sh
index d0ad49bd1..0c198babc 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -31,6 +31,7 @@ ubu1804_boilerplate="
 export DEBIAN_FRONTEND=noninteractive
 apt-get install -y git curl wget
 "
+# shellcheck disable=SC2034
 ubu1904_boilerplate="$ubu1804_boilerplate"
 
 REMOTE_IMAGES=()
@@ -75,7 +76,7 @@ all LXC containers:
   ${LOCAL_IMAGES[@]}
 
 EOF
-    [ ! -z "${1+x}" ] &&  err_msg "$1"
+    [ -n "${1+x}" ] &&  err_msg "$1"
 }
 
 lxd_info() {
@@ -255,7 +256,7 @@ lxc_boilerplate_containers() {
         lxc start -q "${HOST_PREFIX}-${shortname}" &>/dev/null
         boilerplate_script="${shortname}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
-        if [[ ! -z "${boilerplate_script}" ]]; then
+        if [[ -n "${boilerplate_script}" ]]; then
             echo "$boilerplate_script" \
                 | lxc exec "${HOST_PREFIX}-${shortname}" -- bash \
                 | prefix_stdout " ${HOST_PREFIX}-${shortname} | "
@@ -305,11 +306,13 @@ add_subordinate_ids() {
 
 del_subordinate_ids() {
     local out
+    local exit_value
     if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
         # TODO: root user is always in use by process 1, how can we remove subordinates?
         info_msg "remove lxd permission to map ${HOST_USER_ID}'s user/group id through"
         out=$(usermod --del-subuids "${HOST_USER_ID}-${HOST_USER_ID}" --del-subgids "${HOST_GROUP_ID}-${HOST_GROUP_ID}" root 2>&1)
-        if [ ! -z $? ]; then
+        exit_val=$?
+        if [ $exit_val -ne 0 ]; then
             err_msg "$out"
         fi
     else
diff --git a/utils/morty.sh b/utils/morty.sh
index 7a9db08a8..bd4ad5901 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -84,7 +84,7 @@ ${DOT_CONFIG#"$REPO_ROOT/"} file::
   SERVICE_USER :   ${SERVICE_USER}
 EOF
     info_searx
-    [ ! -z "${1+x}" ] &&  err_msg "$1"
+    [[ -n ${1} ]] &&  err_msg "$1"
 }
 
 info_searx() {
diff --git a/utils/searx.sh b/utils/searx.sh
index 5eb16700b..73bdd5510 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -110,7 +110,7 @@ If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"
   SERVICE_USER        : ${SERVICE_USER}
 
 EOF
-    [ ! -z "${1+x}" ] &&  err_msg "$1"
+    [[ -n ${1} ]] &&  err_msg "$1"
 }
 
 main() {

From a1f5f2ced805fad8941586fab66b16fcb11e948e Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 16 Feb 2020 22:26:03 +0100
Subject: [PATCH 044/125] LXC: minor fixes and renaming

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 26 ++++++++++++++------------
 1 file changed, 14 insertions(+), 12 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index 0c198babc..3d627e8b7 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -55,14 +55,14 @@ usage() {
 usage::
 
   $(basename "$0") build        [containers]
-  $(basename "$0") delete       [containers|subordinate]
+  $(basename "$0") remove       [containers|subordinate]
   $(basename "$0") [start|stop] [containers]
   $(basename "$0") inspect      [info|config]
   $(basename "$0") cmd          ...
 
-build / delete
-  :containers:   build and delete all LXC containers
-add / delete
+build / remove
+  :containers:   build and remove all LXC containers
+add / remove
   :subordinate:  lxd permission to map ${HOST_USER}'s user/group id through
 start/stop
   :containers:   start/stop of all containers
@@ -86,7 +86,7 @@ lxd_info() {
 LXD is needed, to install run::
 
   snap install lxd
-  lxc init --auto
+  lxd init --auto
 
 EOF
 }
@@ -112,10 +112,10 @@ main() {
                 containers) build_instances ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
-        delete)
+        remove)
             sudo_or_exit
             case $2 in
-                containers) delete_instances ;;
+                containers) remove_instances ;;
                 subordinate) echo; del_subordinate_ids ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
@@ -173,8 +173,8 @@ build_instances() {
     lxc list "$HOST_PREFIX"
 }
 
-delete_instances() {
-    rst_title "Delete LXC instances"
+remove_instances() {
+    rst_title "Remove LXC instances"
     echo -en "\\nLXC containers(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
     if ask_yn "Do you really want to delete all images"; then
         lxc_delete_containers
@@ -219,12 +219,14 @@ lxc_cmd() {
 }
 
 lxc_init_containers() {
-    for i in "${LOCAL_IMAGES[@]}"; do
-        if lxc info "$i" &>/dev/null; then
+    local shortname
+    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+        shortname="${TEST_IMAGES[i+1]}"
+        if lxc info "${HOST_PREFIX}-${shortname}" &>/dev/null; then
             info_msg "conatiner '$i' already exists"
         else
             info_msg "create conatiner instance: $i"
-            lxc init "local:$i" "$i"
+            lxc init "local:${shortname}" "${HOST_PREFIX}-${shortname}"
         fi
     done
 }

From 0bfc61dbe9a7b54b1053f9c92f9ff04d8ff7e1bc Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 16 Feb 2020 22:29:06 +0100
Subject: [PATCH 045/125] tooling box: misc fixes from lxc tests

Tested by:

  sudo -H ./utils/lxc.sh build containers
  sudo -H ./utils/lxc.sh cmd /share/searx/utils/searx.sh    install all
  sudo -H ./utils/lxc.sh cmd /share/searx/utils/filtron.sh  install all
  sudo -H ./utils/lxc.sh cmd /share/searx/utils/morty.sh    install all

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation.rst | 4 ++--
 utils/filtron.sh            | 1 -
 utils/lib.sh                | 2 +-
 utils/searx.sh              | 2 +-
 4 files changed, 4 insertions(+), 5 deletions(-)

diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 67358e996..a4c7eb8b3 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -33,9 +33,9 @@ Install packages:
 
       .. code-block:: sh
 
-         $ sudo -H apt-get install \
+         $ sudo -H apt-get install -m \
                    git build-essential
-                   libxslt-dev python3-dev python3-babel \
+                   libxslt-dev python3-dev python3-babel python3-venv \
                    zlib1g-dev libffi-dev libssl-dev
 
 Install searx:
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 2cbea4023..13bb76751 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -322,7 +322,6 @@ EOF
 
     if ! service_is_available "${PUBLIC_URL}"; then
         err_msg "Public service at ${PUBLIC_URL} is not available!"
-        echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
         wait_key
     fi
 
diff --git a/utils/lib.sh b/utils/lib.sh
index a7efb78ea..a045c91a8 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -802,7 +802,7 @@ pkg_install() {
         return 42
     fi
     # shellcheck disable=SC2068
-    apt-get install -y $@
+    apt-get install -m -y $@
 }
 
 pkg_remove() {
diff --git a/utils/searx.sh b/utils/searx.sh
index 73bdd5510..942aa07ce 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -40,7 +40,7 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 SEARX_APT_PACKAGES="\
   uwsgi uwsgi-plugin-python3 \
   git build-essential \
-  libxslt-dev python3-dev python3-babel\
+  libxslt-dev python3-dev python3-babel python3-venv \
   zlib1g-dev libffi-dev libssl-dev \
 "
 

From de58f02f6b502f05a86df50e3e6fa77d0b666ec9 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 17 Feb 2020 15:36:10 +0100
Subject: [PATCH 046/125] filtron: add missing log action to the filtron rules

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/templates/etc/filtron/rules.json | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index f4a99b3e8..8fbffa937 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -56,6 +56,7 @@
 	      "limit": 0,
 	      "stop": true,
 	      "actions": [
+		  {"name":"log"},
 		  {"name": "block",
 		   "params": {"message": "Rate limit exceeded"}}
 	      ]
@@ -66,6 +67,7 @@
               "limit": 0,
               "stop": true,
               "actions": [
+		  {"name":"log"},
 		  {"name": "block",
 		   "params": {"message": "Rate limit exceeded"}}
               ]

From 59e4026762a809fe2a5b5a5a949d7d671f4d989b Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 17 Feb 2020 18:58:59 +0100
Subject: [PATCH 047/125] searx.sh: install settings at /etc/searx/settings.yml

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.mk                                    |   6 +-
 Makefile                                      |   5 +
 utils/filtron.sh                              |   2 +
 utils/searx.sh                                | 132 ++++++++++--------
 .../etc/uwsgi/apps-available/searx.ini        |   3 +
 5 files changed, 84 insertions(+), 64 deletions(-)

diff --git a/.config.mk b/.config.mk
index 3349a7602..e03d29056 100644
--- a/.config.mk
+++ b/.config.mk
@@ -6,8 +6,8 @@
 # Compare your settings here with file .config.sh used by the toolboxing in
 # utils.
 
-export SEARX_URL=https://searx.me
+export SEARX_URL:=$(or ${SEARX_URL},https://searx.me)
 
-export GIT_URL=https://github.com/asciimoo/searx
-export DOCS_URL=https://asciimoo.github.io/searx
+export DOCS_URL:=$(or ${DOCS_URL},https://asciimoo.github.io/searx)
+export GIT_URL:=$(or ${GIT_URL},https://github.com/asciimoo/searx)
 
diff --git a/Makefile b/Makefile
index 0e1816eb5..6cc799f2e 100644
--- a/Makefile
+++ b/Makefile
@@ -26,6 +26,11 @@ help:
 	@echo  '  gh-pages  - build docs & deploy on gh-pages branch'
 	@echo  '  clean     - drop builds and environments'
 	@echo  ''
+	@echo  'environment'
+	@echo  '  SEARX_URL = $(SEARX_URL)'
+	@echo  '  GIT_URL   = $(GIT_URL)'
+	@echo  '  DOCS_URL  = $(DOCS_URL)'
+	@echo  ''
 	@$(MAKE) -s -f utils/makefile.include make-help
 	@echo  ''
 	@$(MAKE) -s -f utils/makefile.python python-help
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 13bb76751..2d2270812 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -87,6 +87,8 @@ apache : ${PUBLIC_URL}
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_FILTRON_SITE}
 
+filtron rules: ${FILTRON_RULES}
+
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
   PUBLIC_URL     : ${PUBLIC_URL}
diff --git a/utils/searx.sh b/utils/searx.sh
index 942aa07ce..1b9bfd42c 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -30,7 +30,7 @@ SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
 SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
 SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
 SEARX_SRC="${SERVICE_HOME}/searx-src"
-SEARX_SETTINGS="${SEARX_SRC}/searx/settings.yml"
+SEARX_SETTINGS_PATH="/etc/searx/settings.yml"
 SEARX_UWSGI_APP="searx.ini"
 # shellcheck disable=SC2034
 SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
@@ -59,7 +59,7 @@ CONFIG_FILES=(
 
 # shellcheck disable=SC2034
 CONFIG_BACKUP_ENCRYPTED=(
-    "${SEARX_SETTINGS}"
+    "${SEARX_SETTINGS_PATH}"
 )
 
 # ----------------------------------------------------------------------------
@@ -88,6 +88,7 @@ install / remove
   :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
   :searx-src:  clone $SEARX_GIT_URL
   :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
+  :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
 update searx
   Update searx installation ($SERVICE_HOME)
 activate service
@@ -102,6 +103,8 @@ apache
   :install: apache site with the searx uwsgi app
   :remove:  apache site ${APACHE_FILTRON_SITE}
 
+searx settings: ${SEARX_SETTINGS_PATH}
+
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
   PUBLIC_URL          : ${PUBLIC_URL}
@@ -145,6 +148,7 @@ main() {
                 user) assert_user ;;
                 pyenv) create_pyenv ;;
                 searx-src) clone_searx ;;
+                settings) install_settings ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
@@ -206,7 +210,7 @@ install_all() {
     wait_key
     create_pyenv
     wait_key
-    configure_searx
+    install_settings
     wait_key
     test_local_searx
     wait_key
@@ -217,7 +221,6 @@ install_all() {
     if ask_yn "Do you want to inspect the installation?" Yn; then
         inspect_service
     fi
-
 }
 
 update_searx() {
@@ -226,45 +229,11 @@ update_searx() {
     echo
     tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
-cp -f ${SEARX_SETTINGS} ${SEARX_SETTINGS}.backup
-git stash push -m "BACKUP -- 'update server' at ($(date))"
 git checkout -B "$SEARX_GIT_BRANCH"
 git pull
-cd ${SEARX_SRC}
 ${SEARX_SRC}/manage.sh update_packages
 EOF
-    configure_searx
-
-    rst_title "${SEARX_SETTINGS}" section
-    rst_para 'Diff between new setting file (<) and backup (>):'
-    echo
-    $DIFF_CMD "${SEARX_SETTINGS}" "${SEARX_SETTINGS}.backup"
-
-    local action
-    choose_one action "What should happen to the settings file? " \
-           "keep new configuration" \
-           "revert to the old configuration (backup file)" \
-           "start interactiv shell"
-    case $action in
-        "keep new configuration")
-            info_msg "continue using new settings file"
-            ;;
-        "revert to the old configuration (backup file)")
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
-cp -f ${SEARX_SETTINGS}.backup ${SEARX_SETTINGS}
-EOF
-            ;;
-        "start interactiv shell")
-            interactive_shell "${SERVICE_USER}"
-            ;;
-    esac
-    chown "${SERVICE_USER}:${SERVICE_USER}" "${SEARX_SETTINGS}"
-
-    # shellcheck disable=SC2016
-    rst_para 'Diff between local modified settings (<) and $SEARX_GIT_BRANCH branch (>):'
-    echo
-    git_diff
-    wait_key
+    install_settings
     uWSGI_restart
 }
 
@@ -279,8 +248,9 @@ installations that were installed with this script."
         return
     fi
     remove_searx_uwsgi
-    wait_key
     drop_service_account "${SERVICE_USER}"
+    remove_settings
+    wait_key
     if service_is_available "${PUBLIC_URL}"; then
         MSG="** Don't forgett to remove your public site! (${PUBLIC_URL}) **" wait_key 10
     fi
@@ -301,7 +271,7 @@ EOF
 }
 
 clone_is_available() {
-    [[ -f "$SEARX_SETTINGS" ]]
+    [[ -f "$SEARX_SRC/.git/config" ]]
 }
 
 # shellcheck disable=SC2164
@@ -314,13 +284,13 @@ clone_searx() {
         return 42
     fi
     export SERVICE_HOME
-
-    git_clone "$SEARX_GIT_URL" "$SEARX_SRC" \
+    git_clone "$REPO_ROOT" "$SEARX_SRC" \
               "$SEARX_GIT_BRANCH" "$SERVICE_USER"
 
     pushd "${SEARX_SRC}" > /dev/null
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
 cd "${SEARX_SRC}"
+git remote set-url origin ${SEARX_GIT_URL}
 git config user.email "$ADMIN_EMAIL"
 git config user.name "$ADMIN_NAME"
 git config --list
@@ -328,6 +298,52 @@ EOF
     popd > /dev/null
 }
 
+install_settings() {
+    rst_title "${SEARX_SETTINGS_PATH}" section
+    if ! clone_is_available; then
+        err_msg "you have to install searx first"
+        exit 42
+    fi
+    mkdir -p "$(dirname ${SEARX_SETTINGS_PATH})"
+
+    if [[ ! -f ${SEARX_SETTINGS_PATH} ]]; then
+        info_msg "install settings ${REPO_ROOT}/searx/settings.yml"
+        info_msg "  --> ${SEARX_SETTINGS_PATH}"
+        cp "${REPO_ROOT}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+        configure_searx
+        return
+    fi
+
+    rst_para "Diff between origin's setting file (-) and current (+):"
+    echo
+    $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+
+    local action
+    choose_one action "What should happen to the settings file? " \
+           "keep new configuration" \
+           "start interactiv shell"
+    case $action in
+        "keep new configuration")
+            info_msg "continue using new settings file"
+            ;;
+        "start interactiv shell")
+            echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
+            sudo -H -i
+            rst_para 'Diff between new setting file (-) and current (+):'
+            echo
+            $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}" 
+            wait_key
+            ;;
+    esac
+}
+
+remove_settings() {
+    rst_title "remove searx settings" section
+    echo
+    info_msg "delete ${SEARX_SETTINGS_PATH}"
+    rm -f "${SEARX_SETTINGS_PATH}"
+}
+
 remove_searx() {
     rst_title "Drop searx sources" section
     if ask_yn "Do you really want to drop searx sources ($SEARX_SRC)?"; then
@@ -381,12 +397,12 @@ EOF
 
 configure_searx() {
     rst_title "Configure searx" section
-    rst_para "Setup searx config located at $SEARX_SETTINGS"
+    rst_para "Setup searx config located at $SEARX_SETTINGS_PATH"
     echo
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
-sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS"
-sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS"
+sed -i -e "s/ultrasecretkey/$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
+sed -i -e "s/{instance_name}/${SEARX_INSTANCE_NAME}/g" "$SEARX_SETTINGS_PATH"
 EOF
 }
 
@@ -401,14 +417,15 @@ test_local_searx() {
             return
         fi
     fi
+    sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}" 
 cd ${SEARX_SRC}
-sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
 timeout 10 python3 searx/webapp.py &
 sleep 3
 curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
-sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
 EOF
+    sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
 }
 
 install_searx_uwsgi() {
@@ -437,28 +454,21 @@ deactivate_service() {
     uWSGI_restart
 }
 
-git_diff() {
-    sudo -H -u "${SERVICE_USER}" -i <<EOF
-cd ${SEARX_SRC}
-git --no-pager diff
-EOF
-}
-
 enable_debug() {
     warn_msg "Do not enable debug in production enviroments!!"
     info_msg "try to enable debug mode ..."
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
-sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS"
+sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
 EOF
     uWSGI_restart
 }
 
 disable_debug() {
     info_msg "try to disable debug mode ..."
-    tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
-sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS"
+sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
 EOF
     uWSGI_restart
 }
@@ -513,7 +523,7 @@ EOF
     fi
 
     if ! service_is_available "${PUBLIC_URL}"; then
-        err_msg "Public service at ${PUBLIC_URL} is not available!"
+        warn_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
 
     local _debug_on
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index cff47f896..bc62e5864 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -12,6 +12,9 @@ gid = ${SERVICE_GROUP}
 # chdir to specified directory before apps loading
 chdir = ${SEARX_SRC}/searx
 
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
 # disable logging for privacy
 disable-logging = true
 

From ccb96544f77814273bc0fbe2342633df69433669 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 18 Feb 2020 18:20:03 +0100
Subject: [PATCH 048/125] LXC: improved console messages & prompt

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 81 +++++++++++++++++++++++++++++++++-------------------
 1 file changed, 51 insertions(+), 30 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index 3d627e8b7..89c839cf1 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -17,6 +17,9 @@ source_dot_config
 LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
 HOST_PREFIX="${HOST_PREFIX:-searx}"
 
+# where all folders from HOST are mounted
+LXC_SHARE_FOLDER="/share"
+
 TEST_IMAGES=(
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
@@ -61,7 +64,7 @@ usage::
   $(basename "$0") cmd          ...
 
 build / remove
-  :containers:   build and remove all LXC containers
+  :containers:   build & launch (or remove) all LXC containers
 add / remove
   :subordinate:  lxd permission to map ${HOST_USER}'s user/group id through
 start/stop
@@ -129,7 +132,10 @@ main() {
             sudo_or_exit
             case $2 in
                 containers)  lxc_cmd "$1" ;;
-                *) usage "$_usage"; exit 42;;
+                *)
+                    info_msg "lxc $1 $2"
+                    lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+                    ;;
             esac ;;
         inspect)
             sudo_or_exit
@@ -142,12 +148,11 @@ main() {
             sudo_or_exit
             shift
             for i in "${LOCAL_IMAGES[@]}"; do
-                info_msg "call ${_BBlue}${i}${_creset} -- ${_BGreen}${*}${_creset}"
-                wait_key 3
+                info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${*}${_creset}"
                 lxc exec "${i}" -- "$@"
                 exit_val=$?
-                if [ $exit_val -ne 0 ]; then
-                    err_msg "$exit_val ${_BBlue}${i}${_creset} -- ${_BGreen}${*}${_creset}"
+                if [[ $exit_val -ne 0 ]]; then
+                    err_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
                 fi
             done
             ;;
@@ -162,7 +167,7 @@ build_instances() {
     rst_title "copy images" section
     echo
     lxc_copy_images_localy
-    lxc image list local: && wait_key
+    # lxc image list local: && wait_key
     echo
     rst_title "build containers" section
     echo
@@ -175,6 +180,7 @@ build_instances() {
 
 remove_instances() {
     rst_title "Remove LXC instances"
+    lxc list "$HOST_PREFIX"
     echo -en "\\nLXC containers(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
     if ask_yn "Do you really want to delete all images"; then
         lxc_delete_containers
@@ -214,64 +220,79 @@ lxc_delete_images_localy() {
 lxc_cmd() {
     for i in "${LOCAL_IMAGES[@]}"; do
         info_msg "lxc $* $i"
-        lxc "$@" "$i"
+        lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
     done
 }
 
 lxc_init_containers() {
-    local shortname
+
+    local image_name
+    local container_name
+
     for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
-        shortname="${TEST_IMAGES[i+1]}"
-        if lxc info "${HOST_PREFIX}-${shortname}" &>/dev/null; then
-            info_msg "conatiner '$i' already exists"
+
+        image_name="${TEST_IMAGES[i+1]}"
+        container_name="${HOST_PREFIX}-${image_name}"
+
+        if lxc info "${container_name}" &>/dev/null; then
+            info_msg "container '${container_name}' already exists"
         else
-            info_msg "create conatiner instance: $i"
-            lxc init "local:${shortname}" "${HOST_PREFIX}-${shortname}"
+            info_msg "create conatiner instance: ${container_name}"
+            lxc init "local:${image_name}" "${container_name}"
         fi
     done
 }
 
 lxc_config_containers() {
     for i in "${LOCAL_IMAGES[@]}"; do
-        info_msg "configure container: ${_BBlue}${i}${_creset}"
+        info_msg "[${_BBlue}${i}${_creset}] configure container ..."
 
-        info_msg "map uid/gid from host to container"
+        info_msg "[${_BBlue}${i}${_creset}] map uid/gid from host to container"
         # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
         echo -e -n "uid $HOST_USER_ID 1000\\ngid $HOST_GROUP_ID 1000"\
             | lxc config set "$i" raw.idmap -
 
-        info_msg "share ${REPO_ROOT} (repo_share) from HOST into container"
+        info_msg "[${_BBlue}${i}${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"
         # https://lxd.readthedocs.io/en/latest/instances/#type-disk
         lxc config device add "$i" repo_share disk \
             source="${REPO_ROOT}" \
-            path="/share/$(basename "${REPO_ROOT}")" &>/dev/null
+            path="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")" &>/dev/null
         # lxc config show "$i" && wait_key
     done
 }
 
 lxc_boilerplate_containers() {
-    local shortname
+
+    local image_name
+    local container_name
     local boilerplate_script
+
     for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
-        shortname="${TEST_IMAGES[i+1]}"
-        info_msg "install boilerplate: ${_BBlue}${HOST_PREFIX}-${shortname}${_creset}"
-        lxc start -q "${HOST_PREFIX}-${shortname}" &>/dev/null
-        boilerplate_script="${shortname}_boilerplate"
+
+        image_name="${TEST_IMAGES[i+1]}"
+        container_name="${HOST_PREFIX}-${image_name}"
+        boilerplate_script="${image_name}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
-        if [[ -n "${boilerplate_script}" ]]; then
-            echo "$boilerplate_script" \
-                | lxc exec "${HOST_PREFIX}-${shortname}" -- bash \
-                | prefix_stdout " ${HOST_PREFIX}-${shortname} | "
-        else
-            warn_msg "no boilerplate for instance '$i'"
+
+        info_msg "[${_BBlue}${container_name}${_creset}] install boilerplate"
+        if lxc start -q "${container_name}" &>/dev/null; then
+            sleep 5 # guest needs some time to come up and get an IP
         fi
+        if [[ -n "${boilerplate_script}" ]]; then
+            echo "${boilerplate_script}" \
+                | lxc exec "${container_name}" -- bash \
+                | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+        else
+            err_msg "[${_BBlue}${container_name}${_creset}] no boilerplate for image '${image_name}'"
+        fi
+
     done
 }
 
 lxc_delete_containers() {
     for i in "${LOCAL_IMAGES[@]}"; do
         if lxc info "$i" &>/dev/null; then
-            info_msg "stop & delete instance '$i'"
+            info_msg "stop & delete instance ${_BBlue}${i}${_creset}"
             lxc stop "$i" &>/dev/null
             lxc delete "$i" | prefix_stdout
         else

From e36e0f80aeec5d513b0bf8d4e4dc94c9fa4d98b4 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 18 Feb 2020 18:40:34 +0100
Subject: [PATCH 049/125] LXC: added archlinux and fedora31 containers

BTW: update all packages when installing conatiner's boilerplate

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 20 +++++++++++++++-----
 1 file changed, 15 insertions(+), 5 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index 89c839cf1..08205d374 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -23,20 +23,30 @@ LXC_SHARE_FOLDER="/share"
 TEST_IMAGES=(
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
-
-    # TODO: installation of searx & filtron not yet implemented ..
-    #
-    #"$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
-    #"$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
+    "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
+    "$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
 )
 
 ubu1804_boilerplate="
 export DEBIAN_FRONTEND=noninteractive
+apt-get update -y
+apt-get upgrade -y
 apt-get install -y git curl wget
 "
 # shellcheck disable=SC2034
 ubu1904_boilerplate="$ubu1804_boilerplate"
 
+# shellcheck disable=SC2034
+archlinux_boilerplate="
+pacman -Syu --noconfirm
+pacman -S --noconfirm git curl wget
+"
+
+fedora31_boilerplate="
+dnf update -y
+dnf install -y git curl wget
+"
+
 REMOTE_IMAGES=()
 LOCAL_IMAGES=()
 

From 5fb6d4f508d9744a8d82160ca184729514bc18c8 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 23 Feb 2020 12:10:45 +0100
Subject: [PATCH 050/125] LXC: normalize package installation & user creation.

utils/lib.sh:
- get DIST_ID & DIST_VERSION from /etc/os-release
- pkg_[install|remove|...] supports ubuntu, debian, archlinux & fedora

utils/lxc.sh
- Workaround for the "setrlimit(RLIMIT_CORE): Operation not permitted" error::
    'Set disable_coredump false' >> /etc/sudo.conf

utils/[searx.sh|filtron.sh|morty.sh]
- switched user creation from 'adduser' perl script to 'useradd' built-in
  command

utils/searx.sh
- install packages for ubuntu, debian, archlinux & fedora

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh | 10 +++++----
 utils/lib.sh     | 57 ++++++++++++++++++++++++++++++++++++++++--------
 utils/lxc.sh     | 11 +++++++---
 utils/morty.sh   | 10 +++++----
 utils/searx.sh   | 42 +++++++++++++++++++++++------------
 5 files changed, 96 insertions(+), 34 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 2d2270812..ba284787e 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -106,7 +106,7 @@ main() {
     rst_title "$SERVICE_NAME" part
 
     required_commands \
-        dpkg apt-get install git wget curl \
+        sudo install git wget curl \
         || exit
 
     local _usage="unknown or missing $1 command $2"
@@ -231,9 +231,11 @@ 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 \
-    --disabled-password --group --gecos 'Filtron' $SERVICE_USER
-sudo -H usermod -a -G shadow $SERVICE_USER
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Reverse HTTP proxy to filter requests' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
 groups $SERVICE_USER
 EOF
     SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
diff --git a/utils/lib.sh b/utils/lib.sh
index a045c91a8..c3707d580 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -3,6 +3,11 @@
 # SPDX-License-Identifier: AGPL-3.0-or-later
 # shellcheck disable=SC2059,SC1117
 
+# ubuntu, debian, arch, fedora ...
+DIST_ID=$(source /etc/os-release; echo $ID);
+# shellcheck disable=SC2034
+DIST_VERS=$(source /etc/os-release; echo $VERSION_ID);
+
 ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
 ADMIN_NAME="${ADMIN_NAME:-$USER}"
 
@@ -54,7 +59,7 @@ sudo_or_exit() {
 
 required_commands() {
 
-    # usage:  requires_commands [cmd1 ...]
+    # usage:  required_commands [cmd1 ...]
 
     local exit_val=0
     while [ -n "$1" ]; do
@@ -787,9 +792,6 @@ uWSGI_disable_app() {
 
 # distro's package manager
 # ------------------------
-#
-# FIXME: Arch Linux & RHEL should be added
-#
 
 pkg_install() {
 
@@ -801,8 +803,20 @@ pkg_install() {
     if ! ask_yn "Should packages be installed?" Yn 30; then
         return 42
     fi
-    # shellcheck disable=SC2068
-    apt-get install -m -y $@
+    case $DIST_ID in
+        ubuntu|debian)
+            # shellcheck disable=SC2068
+            apt-get install -m -y $@
+            ;;
+        arch)
+            # shellcheck disable=SC2068
+            pacman -S --noconfirm $@
+            ;;
+        fedora)
+            # shellcheck disable=SC2068
+            dnf install -y $@
+            ;;
+    esac
 }
 
 pkg_remove() {
@@ -815,15 +829,40 @@ pkg_remove() {
     if ! ask_yn "Should packages be removed (purge)?" Yn 30; then
         return 42
     fi
-    apt-get purge --autoremove --ignore-missing -y "$@"
+    case $DIST_ID in
+        ubuntu|debian)
+            # shellcheck disable=SC2068
+            apt-get purge --autoremove --ignore-missing -y $@
+            ;;
+        arch)
+            # shellcheck disable=SC2068
+            pacman -R --noconfirm $@
+            ;;
+        fedora)
+            # shellcheck disable=SC2068
+            dnf remove -y $@
+            ;;
+    esac
 }
 
 pkg_is_installed() {
 
     # usage: pkg_is_install foopkg || pkg_install foopkg
 
-    dpkg -l "$1" &> /dev/null
-    return $?
+    case $DIST_ID in
+        ubuntu|debian)
+            dpkg -l "$1" &> /dev/null
+            return $?
+            ;;
+        arch)
+            pacman -Qsq "$1" &> /dev/null
+            return $?
+            ;;
+        fedora)
+            dnf list -q --installed "$1" &> /dev/null
+            return $?
+            ;;
+    esac
 }
 
 # git tooling
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 08205d374..8020b1346 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -39,12 +39,15 @@ ubu1904_boilerplate="$ubu1804_boilerplate"
 # shellcheck disable=SC2034
 archlinux_boilerplate="
 pacman -Syu --noconfirm
-pacman -S --noconfirm git curl wget
+pacman -S --noconfirm git curl wget sudo
+echo 'Set disable_coredump false' >> /etc/sudo.conf
 "
 
+# shellcheck disable=SC2034
 fedora31_boilerplate="
 dnf update -y
-dnf install -y git curl wget
+dnf install -y git curl wget hostname
+echo 'Set disable_coredump false' >> /etc/sudo.conf
 "
 
 REMOTE_IMAGES=()
@@ -162,7 +165,9 @@ main() {
                 lxc exec "${i}" -- "$@"
                 exit_val=$?
                 if [[ $exit_val -ne 0 ]]; then
-                    err_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+                    warn_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+                else
+                    info_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
                 fi
             done
             ;;
diff --git a/utils/morty.sh b/utils/morty.sh
index bd4ad5901..37ee87edf 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -105,7 +105,7 @@ main() {
     rst_title "$SERVICE_NAME" part
 
     required_commands \
-        dpkg apt-get install git wget curl \
+        sudo install git wget curl \
         || exit
 
     local _usage="ERROR: unknown or missing $1 command $2"
@@ -224,9 +224,11 @@ 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 \
-    --disabled-password --group --gecos 'Morty' $SERVICE_USER
-sudo -H usermod -a -G shadow $SERVICE_USER
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Web content sanitizer proxy' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
 groups $SERVICE_USER
 EOF
     SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"
diff --git a/utils/searx.sh b/utils/searx.sh
index 1b9bfd42c..0d011ef0a 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -35,14 +35,26 @@ SEARX_UWSGI_APP="searx.ini"
 # shellcheck disable=SC2034
 SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
-# FIXME: Arch Linux & RHEL should be added
-
-SEARX_APT_PACKAGES="\
-  uwsgi uwsgi-plugin-python3 \
-  git build-essential \
-  libxslt-dev python3-dev python3-babel python3-venv \
-  zlib1g-dev libffi-dev libssl-dev \
-"
+case $DIST_ID in
+    ubuntu|debian)  # apt packages
+        SEARX_PACKAGES="\
+ python3-dev python3-babel python3-venv \
+ uwsgi uwsgi-plugin-python3 \
+ git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev "
+        ;;
+    arch)           # pacman packages
+        SEARX_PACKAGES="\
+ python python-pip python-lxml python-babel \
+ uwsgi uwsgi-plugin-python \
+ git base-devel libxml2 "
+        ;;
+    fedora)          # dnf packages
+        SEARX_PACKAGES="\
+ python python-pip python-lxml python-babel \
+ uwsgi uwsgi-plugin-python3 \
+ git @development-tools libxml2 "
+        ;;
+esac
 
 # Apache Settings
 
@@ -72,7 +84,7 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|pyenv|searx-src|apache]
+  $(basename "$0") install    [all|user|searx-src|pyenv|apache]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
@@ -120,7 +132,7 @@ main() {
     rst_title "$SEARX_INSTANCE_NAME" part
 
     required_commands \
-        dpkg systemctl apt-get install git wget curl \
+        sudo systemctl install git wget curl \
         || exit
 
     local _usage="unknown or missing $1 command $2"
@@ -202,7 +214,7 @@ _service_prefix="  |$SERVICE_USER| "
 
 install_all() {
     rst_title "Install $SEARX_INSTANCE_NAME (service)"
-    pkg_install "$SEARX_APT_PACKAGES"
+    pkg_install "$SEARX_PACKAGES"
     wait_key
     assert_user
     wait_key
@@ -260,9 +272,11 @@ 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" \
-  --disabled-password --group --gecos 'searx' $SERVICE_USER
-sudo -H usermod -a -G shadow $SERVICE_USER
+useradd --shell /bin/bash --system \
+ --home-dir "$SERVICE_HOME" \
+ --comment 'Privacy-respecting metasearch engine' $SERVICE_USER
+mkdir "$SERVICE_HOME"
+chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
 groups $SERVICE_USER
 EOF
     #SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME)"

From f17b28743a084a5ee36b4d29e672202b09ffc84d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 23 Feb 2020 21:05:22 +0100
Subject: [PATCH 051/125] utils/serx.sh: make uWSGI installation available for
 all distros (WIP)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh   | 116 ++++++++++++++++++++++++++++++++++++++++---------
 utils/searx.sh |  12 ++---
 2 files changed, 101 insertions(+), 27 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index c3707d580..f74187f47 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -693,12 +693,46 @@ apache_dissable_site() {
 
 uWSGI_SETUP="${uWSGI_SETUP:=/etc/uwsgi}"
 
+case $DIST_ID-$DIST_VERS in
+    ubuntu-*|debian-*)
+        # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+        uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
+        ;;
+    arch-*)
+        # systemd --> /usr/lib/systemd/system/uwsgi@.service
+        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}"
+        uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
+        ;;
+    *)
+        info_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+        ;;
+esac
+
 uWSGI_restart() {
 
-    # usage:  uWSGI_restart()
+    # usage:  uWSGI_restart() <myapp.ini>
+
+    local CONF="$1"
+    if [[ -z $CONF ]]; then
+        err_msg "uWSGI_restart: missing arguments"
+        return 42
+    fi
 
     info_msg "restart uWSGI service"
-    systemctl restart uwsgi
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            service uwsgi restart "${CONF%.*}"
+            ;;
+        arch-*)
+            systemctl restart "uwsgi@${CONF%.*}"
+            ;;
+        *)
+            err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            return 42
+            ;;
+    esac
 }
 
 uWSGI_app_available() {
@@ -708,7 +742,7 @@ uWSGI_app_available() {
         err_msg "uWSGI_app_available: missing arguments"
         return 42
     fi
-    [[ -f "${uWSGI_SETUP}/apps-available/${CONF}" ]]
+    [[ -f "${uWSGI_APPS_AVAILABLE}/${CONF}" ]]
 }
 
 uWSGI_install_app() {
@@ -725,13 +759,12 @@ uWSGI_install_app() {
             *)  pos_args+=("$i");;
         esac
     done
-
+    mkdir -p "${uWSGI_APPS_AVAILABLE}"
     install_template "${template_opts[@]}" \
-                     "${uWSGI_SETUP}/apps-available/${pos_args[1]}" \
+                     "${uWSGI_APPS_AVAILABLE}/${pos_args[1]}" \
                      root root 644
-
     uWSGI_enable_app "${pos_args[1]}"
-    uWSGI_restart
+    uWSGI_restart "${pos_args[1]}"
     info_msg "installed uWSGI app: ${pos_args[1]}"
 }
 
@@ -742,18 +775,35 @@ uWSGI_remove_app() {
     local CONF="$1"
     info_msg "remove uWSGI app: ${CONF}"
     uWSGI_disable_app "${CONF}"
-    uWSGI_restart
-    rm -f "${uWSGI_SETUP}/apps-available/${CONF}"
+    uWSGI_restart "${CONF}"
+    rm -f "${uWSGI_APPS_AVAILABLE}/${CONF}"
 }
 
 uWSGI_app_enabled() {
     # usage:  uWSGI_app_enabled <myapp.ini>
+
     local CONF="$1"
+    local exit_val=0
     if [[ -z $CONF ]]; then
         err_msg "uWSGI_app_enabled: missing arguments"
         return 42
     fi
-    [[ -f "${uWSGI_SETUP}/apps-enabled/${CONF}" ]]
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
+            exit_val=$?
+            ;;
+        arch-*)
+            systemctl -q is-enabled "uwsgi@${CONF%.*}"
+            exit_val=$?
+            ;;
+        *)
+            # FIXME
+            err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            exit_val=1
+            ;;
+    esac
+    return $exit_val
 }
 
 # shellcheck disable=SC2164
@@ -762,16 +812,29 @@ uWSGI_enable_app() {
     # usage:   uWSGI_enable_app <myapp.ini>
 
     local CONF="$1"
+
     if [[ -z $CONF ]]; then
         err_msg "uWSGI_enable_app: missing arguments"
         return 42
     fi
-    pushd "${uWSGI_SETUP}/apps-enabled" >/dev/null
-    rm -f "$CONF"
-    # shellcheck disable=SC2226
-    ln -s "../apps-available/${CONF}"
-    info_msg "enabled uWSGI app: ${CONF} (restart uWSGI required)"
-    popd >/dev/null
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            mkdir -p "${uWSGI_APPS_ENABLED}"
+            pushd "${uWSGI_APPS_ENABLED}" >/dev/null
+            rm -f "$CONF"
+            ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" .
+            popd >/dev/null
+            info_msg "enabled uWSGI app: ${CONF} (restart required)"
+            ;;
+        arch-*)
+            systemctl enable "uwsgi@${CONF%.*}"
+            ;;
+        *)
+            # FIXME
+            err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            ;;
+    esac
 }
 
 uWSGI_disable_app() {
@@ -783,11 +846,22 @@ uWSGI_disable_app() {
         err_msg "uWSGI_enable_app: missing arguments"
         return 42
     fi
-    rm -f "${uWSGI_SETUP}/apps-enabled/${CONF}"
-    # FIXME: restart uwsgi service won't stop wsgi forked processes of user searx.
-    # I had to kill them manually here ...
-    pkill -f "${uWSGI_SETUP}/apps-enabled/${CONF}" -9
-    info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)"
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            service uwsgi stop "${CONF%.*}"
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+            info_msg "disabled uWSGI app: ${CONF} (restart uWSGI required)"
+            ;;
+        arch-*)
+            systemctl stop "uwsgi@${CONF%.*}"
+            systemctl disable "uwsgi@${CONF%.*}"
+            ;;
+        *)
+            # FIXME
+            err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            ;;
+    esac
 }
 
 # distro's package manager
diff --git a/utils/searx.sh b/utils/searx.sh
index 0d011ef0a..2bf26bba4 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -66,7 +66,7 @@ APACHE_SEARX_SITE="searx.conf"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
-    "${uWSGI_SETUP}/apps-available/${SEARX_UWSGI_APP}"
+    "${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}"
 )
 
 # shellcheck disable=SC2034
@@ -246,7 +246,7 @@ git pull
 ${SEARX_SRC}/manage.sh update_packages
 EOF
     install_settings
-    uWSGI_restart
+    uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
 remove_all() {
@@ -458,14 +458,14 @@ activate_service() {
     rst_title "Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_enable_app "$SEARX_UWSGI_APP"
-    uWSGI_restart
+    uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
 deactivate_service() {
     rst_title "De-Activate $SEARX_INSTANCE_NAME (service)" section
     echo
     uWSGI_disable_app "$SEARX_UWSGI_APP"
-    uWSGI_restart
+    uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
 enable_debug() {
@@ -475,7 +475,7 @@ enable_debug() {
 cd ${SEARX_SRC}
 sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
 EOF
-    uWSGI_restart
+    uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
 disable_debug() {
@@ -484,7 +484,7 @@ disable_debug() {
 cd ${SEARX_SRC}
 sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
 EOF
-    uWSGI_restart
+    uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
 inspect_service() {

From d5917cc029e2736b11412a570470c666af093ec9 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 25 Feb 2020 20:20:17 +0100
Subject: [PATCH 052/125] utils/lib.sh: make uWSGI installation available for
 all distros

support: ubuntu, debin, fedora, archlinux

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile                                      |  2 +-
 utils/lib.sh                                  | 69 ++++++++++++++++---
 utils/lxc.sh                                  |  2 +-
 utils/morty.sh                                |  5 ++
 utils/searx.sh                                |  3 +
 .../etc/uwsgi/apps-archlinux/searx.ini        | 66 ++++++++++++++++++
 6 files changed, 136 insertions(+), 11 deletions(-)
 create mode 100644 utils/templates/etc/uwsgi/apps-archlinux/searx.ini

diff --git a/Makefile b/Makefile
index 1504072a5..f62db2f03 100644
--- a/Makefile
+++ b/Makefile
@@ -84,11 +84,11 @@ test.pylint: pyenvinstall
 	$(call cmd,pylint,searx/testing.py)
 
 test.sh:
-	shellcheck -x utils/lxc.sh
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
 	shellcheck -x utils/searx.sh
 	shellcheck -x utils/morty.sh
+	shellcheck -x utils/lxc.sh
 	shellcheck -x .config.sh
 
 test.pep8: pyenvinstall
diff --git a/utils/lib.sh b/utils/lib.sh
index f74187f47..39b9134d0 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -692,18 +692,37 @@ apache_dissable_site() {
 # -----
 
 uWSGI_SETUP="${uWSGI_SETUP:=/etc/uwsgi}"
+uWSGI_USER=
+uWSGI_GROUP=
+
+# How distros manage uWSGI apps is very different.  From uWSGI POV read:
+# - https://uwsgi-docs.readthedocs.io/en/latest/Management.html
 
 case $DIST_ID-$DIST_VERS in
     ubuntu-*|debian-*)
         # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+        # For uWSGI debian uses the LSB init process, this might be changed
+        # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
         uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
         uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
         ;;
     arch-*)
         # systemd --> /usr/lib/systemd/system/uwsgi@.service
-        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}"
+        # For uWSGI archlinux uses systemd template units, see
+        # - http://0pointer.de/blog/projects/instances.html
+        # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux"
         uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
         ;;
+    fedora-*)
+        # systemd --> /usr/lib/systemd/system/uwsgi.service
+        # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+        # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+        uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d"
+        uWSGI_USER="uwsgi"
+        uWSGI_GROUP="uwsgi"
+        ;;
     *)
         info_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
         ;;
@@ -723,10 +742,25 @@ uWSGI_restart() {
 
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
+            # the 'service' method seems broken in that way, that it (re-)starts
+            # the whole uwsgi process.
             service uwsgi restart "${CONF%.*}"
             ;;
         arch-*)
-            systemctl restart "uwsgi@${CONF%.*}"
+            # restart systemd template instance
+            if uWSGI_app_available "${CONF}"; then
+                systemctl restart "uwsgi@${CONF%.*}"
+            else
+                info_msg "in systemd template mode: ${CONF} not installed (nothing to restart)"
+            fi
+            ;;
+        fedora-*)
+            # in emperor mode, just touch the file to restart
+            if uWSGI_app_enabled "${CONF}"; then
+                touch "${uWSGI_APPS_ENABLED}/${CONF}"
+            else
+                info_msg "in uWSGI emperor mode: ${CONF} not installed (nothing to restart)"
+            fi
             ;;
         *)
             err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
@@ -765,7 +799,7 @@ uWSGI_install_app() {
                      root root 644
     uWSGI_enable_app "${pos_args[1]}"
     uWSGI_restart "${pos_args[1]}"
-    info_msg "installed uWSGI app: ${pos_args[1]}"
+    info_msg "uWSGI app: ${pos_args[1]} is installed"
 }
 
 uWSGI_remove_app() {
@@ -797,6 +831,10 @@ uWSGI_app_enabled() {
             systemctl -q is-enabled "uwsgi@${CONF%.*}"
             exit_val=$?
             ;;
+        fedora-*)
+            [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
+            exit_val=$?
+            ;;
         *)
             # FIXME
             err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
@@ -821,14 +859,22 @@ uWSGI_enable_app() {
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
             mkdir -p "${uWSGI_APPS_ENABLED}"
-            pushd "${uWSGI_APPS_ENABLED}" >/dev/null
-            rm -f "$CONF"
-            ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" .
-            popd >/dev/null
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+            ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
             info_msg "enabled uWSGI app: ${CONF} (restart required)"
             ;;
         arch-*)
-            systemctl enable "uwsgi@${CONF%.*}"
+            mkdir -p "${uWSGI_APPS_ENABLED}"
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+            ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+            info_msg "enabled uWSGI app: ${CONF} (restart required)"
+            ;;
+        fedora-*)
+            mkdir -p "${uWSGI_APPS_ENABLED}"
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+            ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+            chown "${uWSGI_USER}:${uWSGI_GROUP}" "${uWSGI_APPS_ENABLED}/${CONF}"
+            info_msg "enabled uWSGI app: ${CONF}"
             ;;
         *)
             # FIXME
@@ -843,7 +889,7 @@ uWSGI_disable_app() {
 
     local CONF="$1"
     if [[ -z $CONF ]]; then
-        err_msg "uWSGI_enable_app: missing arguments"
+        err_msg "uWSGI_disable_app: missing arguments"
         return 42
     fi
 
@@ -856,6 +902,11 @@ uWSGI_disable_app() {
         arch-*)
             systemctl stop "uwsgi@${CONF%.*}"
             systemctl disable "uwsgi@${CONF%.*}"
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
+            ;;
+        fedora-*)
+            # in emperor mode, just remove the app.ini file
+            rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
             ;;
         *)
             # FIXME
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 8020b1346..502f25366 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -344,7 +344,7 @@ add_subordinate_ids() {
 
 del_subordinate_ids() {
     local out
-    local exit_value
+    local exit_val
     if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
         # TODO: root user is always in use by process 1, how can we remove subordinates?
         info_msg "remove lxd permission to map ${HOST_USER_ID}'s user/group id through"
diff --git a/utils/morty.sh b/utils/morty.sh
index 37ee87edf..1ce15608f 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -96,6 +96,11 @@ To activate morty in searx, add result_proxy to your settings.yml::
   result_proxy:
       url : ${PUBLIC_URL_MORTY}/
 
+  server:
+      ...
+      image_proxy : True # Proxying image results through searx
+      ...
+
 further read: ${DOCS_URL}/admin/morty.html
 
 EOF
diff --git a/utils/searx.sh b/utils/searx.sh
index 2bf26bba4..c6f58b6a9 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -43,6 +43,9 @@ case $DIST_ID in
  git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev "
         ;;
     arch)           # pacman packages
+        # FIXME:
+        # - /usr/lib/uwsgi/http_plugin.so: cannot open shared object file: No such file or directory !!!
+        # - /usr/lib/uwsgi/systemd_logger_plugin.so: cannot open shared object file: No such file or directory !!!
         SEARX_PACKAGES="\
  python python-pip python-lxml python-babel \
  uwsgi uwsgi-plugin-python \
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
new file mode 100644
index 000000000..08873cf0f
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -0,0 +1,66 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+logger = systemd
+disable-logging = false
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python,http,systemd_logger
+
+# By default the Python plugin does not initialize the GIL.  This means your
+# app-generated threads will not run.  If you need threads, remember to enable
+# them with enable-threads.  Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# plugin http
+# -----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+http = ${SEARX_INTERNAL_URL}

From af6acd3417bf53c151b9ba6068186c1e472a2776 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 26 Feb 2020 19:07:55 +0100
Subject: [PATCH 053/125] LXC: install searx-suite installs searx, filtron &
 morty on all containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                              |  3 +-
 utils/lib.sh                                  |  9 +++
 utils/lxc.sh                                  | 55 ++++++++++++++++---
 utils/morty.sh                                |  3 +-
 utils/searx.sh                                | 22 +++++---
 .../etc/uwsgi/apps-archlinux/searx.ini        |  2 +-
 6 files changed, 74 insertions(+), 20 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index ba284787e..56b27dc78 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -204,7 +204,7 @@ install_all() {
             install_apache_site
         fi
     fi
-    if ask_yn "Do you want to inspect the installation?" Yn; then
+    if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
     fi
 
@@ -253,7 +253,6 @@ grep -qFs -- 'source $GO_ENV' ~/.profile || echo 'source $GO_ENV' >> ~/.profile
 EOF
 }
 
-
 filtron_is_installed() {
     [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
 }
diff --git a/utils/lib.sh b/utils/lib.sh
index 39b9134d0..aeb38e150 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -290,6 +290,15 @@ cache_download() {
     fi
 }
 
+backup_file() {
+
+    # usage: backup_file /path/to/file.foo
+
+    local stamp=$(date +"_%Y%m%d_%H%M%S")
+    info_msg "create backup: ${1}${stamp}"
+    cp -a "${1}" "${1}${stamp}"
+}
+
 choose_one() {
 
     # usage:
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 502f25366..58528d591 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -19,6 +19,7 @@ HOST_PREFIX="${HOST_PREFIX:-searx}"
 
 # where all folders from HOST are mounted
 LXC_SHARE_FOLDER="/share"
+LXC_REPO_ROOT="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")"
 
 TEST_IMAGES=(
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
@@ -71,6 +72,7 @@ usage() {
 usage::
 
   $(basename "$0") build        [containers]
+  $(basename "$0") install      [searx-suite]
   $(basename "$0") remove       [containers|subordinate]
   $(basename "$0") [start|stop] [containers]
   $(basename "$0") inspect      [info|config]
@@ -87,6 +89,8 @@ inspect
   :config:  show config of all containers
 cmd ...
   run commandline ... in all containers
+install
+  :searx-suite:  install searx suite, includes morty & filtron
 
 all LXC containers:
   ${LOCAL_IMAGES[@]}
@@ -110,14 +114,24 @@ EOF
 main() {
 
     local exit_val
-
-    if ! required_commands lxc; then
-        lxd_info
-        exit 42
-    fi
-
     local _usage="unknown or missing $1 command $2"
 
+    case $1 in
+        __install)
+            sudo_or_exit
+            case $2 in
+                searx-suite)  install_searx_suite ;;
+            esac
+            exit
+            ;;
+        *)
+            if ! required_commands lxc; then
+                lxd_info
+                exit 42
+            fi
+            ;;
+    esac
+
     case $1 in
         --source-only)  ;;
         -h|--help) usage; exit 0;;
@@ -161,6 +175,7 @@ main() {
             sudo_or_exit
             shift
             for i in "${LOCAL_IMAGES[@]}"; do
+                exit_val=
                 info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${*}${_creset}"
                 lxc exec "${i}" -- "$@"
                 exit_val=$?
@@ -171,11 +186,37 @@ main() {
                 fi
             done
             ;;
+        install)
+            sudo_or_exit
+            case $2 in
+                searx-suite)
+                    for i in "${LOCAL_IMAGES[@]}"; do
+                        info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install $2${_creset}"
+                        lxc exec "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
+                    done
+                    ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
         *)
             usage "unknown or missing command $1"; exit 42;;
     esac
 }
 
+install_searx_suite() {
+    export FILTRON_API="0.0.0.0:4005"
+    export FILTRON_LISTEN="0.0.0.0:4004"
+    export MORTY_LISTEN="0.0.0.0:3000"
+    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/searx.sh" install all
+    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/morty.sh" install all
+    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/filtron.sh" install all
+    rst_title "[$(hostname)] searx-suite installation finished" part
+    rst_para "IPs of the container ..."
+    echo
+    ip addr show | grep "inet\s*[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"
+    echo
+}
+
+
 build_instances() {
     rst_title "Build LXC instances"
 
@@ -271,7 +312,7 @@ lxc_config_containers() {
         # https://lxd.readthedocs.io/en/latest/instances/#type-disk
         lxc config device add "$i" repo_share disk \
             source="${REPO_ROOT}" \
-            path="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")" &>/dev/null
+            path="${LXC_REPO_ROOT}" &>/dev/null
         # lxc config show "$i" && wait_key
     done
 }
diff --git a/utils/morty.sh b/utils/morty.sh
index 1ce15608f..fcc9dc728 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -207,7 +207,7 @@ install_all() {
             install_apache_site
         fi
     fi
-    if ask_yn "Do you want to inspect the installation?" Yn; then
+    if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
     fi
 
@@ -408,6 +408,7 @@ This removes apache site ${APACHE_MORTY_SITE}."
 
     apache_remove_site "$APACHE_MORTY_SITE"
 }
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
diff --git a/utils/searx.sh b/utils/searx.sh
index c6f58b6a9..270c30ace 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -43,9 +43,6 @@ case $DIST_ID in
  git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev "
         ;;
     arch)           # pacman packages
-        # FIXME:
-        # - /usr/lib/uwsgi/http_plugin.so: cannot open shared object file: No such file or directory !!!
-        # - /usr/lib/uwsgi/systemd_logger_plugin.so: cannot open shared object file: No such file or directory !!!
         SEARX_PACKAGES="\
  python python-pip python-lxml python-babel \
  uwsgi uwsgi-plugin-python \
@@ -233,7 +230,7 @@ install_all() {
     if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
         err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
     fi
-    if ask_yn "Do you want to inspect the installation?" Yn; then
+    if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
     fi
 }
@@ -331,19 +328,26 @@ install_settings() {
         return
     fi
 
-    rst_para "Diff between origin's setting file (-) and current (+):"
+    rst_para "Diff between origin's setting file (+) and current (-):"
     echo
-    $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+    $DIFF_CMD "${SEARX_SETTINGS_PATH}" "${SEARX_SRC}/searx/settings.yml"
 
     local action
     choose_one action "What should happen to the settings file? " \
-           "keep new configuration" \
+           "keep configuration unchanged" \
+           "use origin settings" \
            "start interactiv shell"
     case $action in
-        "keep new configuration")
-            info_msg "continue using new settings file"
+        "keep configuration unchanged")
+            info_msg "leave settings file unchanged"
+            ;;
+        "use origin settings")
+            backup_file "${SEARX_SETTINGS_PATH}"
+            info_msg "install origin settings"
+            cp "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
             ;;
         "start interactiv shell")
+            backup_file "${SEARX_SETTINGS_PATH}"
             echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
             sudo -H -i
             rst_para 'Diff between new setting file (-) and current (+):'
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index 08873cf0f..78ad50443 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -32,7 +32,7 @@ master = true
 lazy-apps = true
 
 # load uWSGI plugins
-plugin = python,http,systemd_logger
+plugin = python
 
 # By default the Python plugin does not initialize the GIL.  This means your
 # app-generated threads will not run.  If you need threads, remember to enable

From 37c135f2cec6113aa2baaf1b52fe46fd64265975 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 27 Feb 2020 19:13:03 +0100
Subject: [PATCH 054/125] LXC: improved UX when working with a bunch of
 containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh |   2 +-
 utils/lib.sh     |  32 +++++++++----
 utils/lxc.sh     | 120 ++++++++++++++++++++++++++++++-----------------
 utils/morty.sh   |   2 +-
 utils/searx.sh   |  21 +++++++--
 5 files changed, 122 insertions(+), 55 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 56b27dc78..663c2b1dc 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -257,7 +257,7 @@ filtron_is_installed() {
     [[ -f $SERVICE_HOME/go-apps/bin/filtron ]]
 }
 
-_svcpr="  |${SERVICE_USER}| "
+_svcpr="  ${_Yellow}|${SERVICE_USER}|${_creset} "
 
 install_filtron() {
     rst_title "Install filtron in user's ~/go-apps" section
diff --git a/utils/lib.sh b/utils/lib.sh
index aeb38e150..47e9e8030 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -117,9 +117,9 @@ rst_title() {
     # usage: rst_title <header-text> [part|chapter|section]
 
     case ${2-chapter} in
-        part)     printf "\n${_BGreen}${1//?/=}\n${_BCyan}${1}${_BGreen}\n${1//?/=}${_creset}\n";;
-        chapter)  printf "\n${_BCyan}${1}\n${_BGreen}${1//?/=}${_creset}\n";;
-        section)  printf "\n${_BCyan}${1}\n${_BGreen}${1//?/-}${_creset}\n";;
+        part)     printf "\n${_BGreen}${1//?/=}${_creset}\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";;
+        chapter)  printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/=}${_creset}\n";;
+        section)  printf "\n${_BCyan}${1}${_creset}\n${_BGreen}${1//?/-}${_creset}\n";;
         *)
             err_msg "invalid argument '${2}' in line $(caller)"
             return 42
@@ -238,7 +238,7 @@ prefix_stdout () {
 
     local prefix="${_BYellow}-->|${_creset}"
 
-    if [[ -n $1 ]] ; then prefix="${_BYellow}$1${_creset}"; fi
+    if [[ -n $1 ]] ; then prefix="$1"; fi
 
     # shellcheck disable=SC2162
     (while IFS= read line; do
@@ -294,7 +294,8 @@ backup_file() {
 
     # usage: backup_file /path/to/file.foo
 
-    local stamp=$(date +"_%Y%m%d_%H%M%S")
+    local stamp
+    stamp=$(date +"_%Y%m%d_%H%M%S")
     info_msg "create backup: ${1}${stamp}"
     cp -a "${1}" "${1}${stamp}"
 }
@@ -503,7 +504,7 @@ install_go() {
 
     # usage:  install_go "${GO_PKG_URL}" "${GO_TAR}" "${SERVICE_USER}"
 
-    local _svcpr="  |${3}| "
+    local _svcpr="  ${_Yellow}|${3}|${_creset} "
 
     rst_title "Install Go in user's HOME" section
 
@@ -1034,17 +1035,32 @@ git_clone() {
 
     if [[ -d "${dest}" ]] ; then
         info_msg "already cloned: $dest"
-        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  ${_Yellow}|$user|${_creset} "
 cd "${dest}"
 git checkout -m -B "$branch" --track "$remote/$branch"
 git pull --all
 EOF
     else
         info_msg "clone into: $dest"
-        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  |$user| "
+        tee_stderr 0.1 <<EOF | $bash_cmd 2>&1 |  prefix_stdout "  ${_Yellow}|$user|${_creset} "
 mkdir -p "$(dirname "$dest")"
 cd "$(dirname "$dest")"
 git clone --branch "$branch" --origin "$remote" "$url" "$(basename "$dest")"
 EOF
     fi
 }
+
+# containers
+# ----------
+
+is_container() {
+    sudo_or_exit
+
+    # usage:  is_container && echo "process running inside a LXC container"
+    #         is_container || echo "process is not running inside a LXC container"
+    #
+    # hint:   Reads init process environment, therefore root access is required!
+
+    # to be safe, take a look at the environment of process 1 (/sbin/init)
+    grep -qa 'container=lxc' /proc/1/environ
+}
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 58528d591..ab9afcbb4 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -63,6 +63,12 @@ HOST_USER="${SUDO_USER:-$USER}"
 HOST_USER_ID=$(id -u "${HOST_USER}")
 HOST_GROUP_ID=$(id -g "${HOST_USER}")
 
+searx_suite_set_env() {
+    export FILTRON_API="0.0.0.0:4005"
+    export FILTRON_LISTEN="0.0.0.0:4004"
+    export MORTY_LISTEN="0.0.0.0:3000"
+}
+
 # ----------------------------------------------------------------------------
 usage() {
 # ----------------------------------------------------------------------------
@@ -74,8 +80,8 @@ usage::
   $(basename "$0") build        [containers]
   $(basename "$0") install      [searx-suite]
   $(basename "$0") remove       [containers|subordinate]
-  $(basename "$0") [start|stop] [containers]
-  $(basename "$0") inspect      [info|config]
+  $(basename "$0") [start|stop] [containers|<container-name>]
+  $(basename "$0") show         [info|config|searx-suite]
   $(basename "$0") cmd          ...
 
 build / remove
@@ -83,10 +89,11 @@ build / remove
 add / remove
   :subordinate:  lxd permission to map ${HOST_USER}'s user/group id through
 start/stop
-  :containers:   start/stop of all containers
-inspect
-  :info:    show info of all containers
-  :config:  show config of all containers
+  :containers:   start/stop of all 'containers' or only <container-name>
+show
+  :info:         show info of all containers
+  :config:       show config of all containers
+  :searx-suite:  show searx-suite services of all containers
 cmd ...
   run commandline ... in all containers
 install
@@ -116,22 +123,10 @@ main() {
     local exit_val
     local _usage="unknown or missing $1 command $2"
 
-    case $1 in
-        __install)
-            sudo_or_exit
-            case $2 in
-                searx-suite)  install_searx_suite ;;
-            esac
-            exit
-            ;;
-        *)
-            if ! required_commands lxc; then
-                lxd_info
-                exit 42
-            fi
-            ;;
-    esac
-
+    if [[ ! $1 == __* ]] && ! required_commands lxc; then
+        lxd_info
+        exit 42
+    fi
     case $1 in
         --source-only)  ;;
         -h|--help) usage; exit 0;;
@@ -141,20 +136,23 @@ main() {
             case $2 in
                 containers) build_instances ;;
                 *) usage "$_usage"; exit 42;;
-            esac ;;
+            esac
+            ;;
         remove)
             sudo_or_exit
             case $2 in
                 containers) remove_instances ;;
                 subordinate) echo; del_subordinate_ids ;;
                 *) usage "$_usage"; exit 42;;
-            esac ;;
+            esac
+            ;;
         add)
             sudo_or_exit
             case $2 in
                 subordinate) echo; add_subordinate_ids ;;
                 *) usage "$_usage"; exit 42;;
-            esac ;;
+            esac
+            ;;
         start|stop)
             sudo_or_exit
             case $2 in
@@ -163,14 +161,27 @@ main() {
                     info_msg "lxc $1 $2"
                     lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "
                     ;;
-            esac ;;
-        inspect)
+            esac
+            ;;
+        show)
             sudo_or_exit
             case $2 in
                 config) lxc_cmd config show;;
                 info) lxc_cmd info;;
+                searx-suite)
+                    for i in "${LOCAL_IMAGES[@]}"; do
+                        info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install $2${_creset}"
+                        lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show "$2"  | prefix_stdout "[${i}] "
+                    done
+                    ;;
                 *) usage "$_usage"; exit 42;;
-            esac ;;
+            esac
+            ;;
+        __show)
+            case $2 in
+                searx-suite) searx_suite_info ;;
+            esac
+            ;;
         cmd)
             sudo_or_exit
             shift
@@ -192,30 +203,55 @@ main() {
                 searx-suite)
                     for i in "${LOCAL_IMAGES[@]}"; do
                         info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install $2${_creset}"
-                        lxc exec "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
+                        lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"  | prefix_stdout "[${i}] "
                     done
                     ;;
                 *) usage "$_usage"; exit 42;;
-            esac ;;
+            esac
+            ;;
+        __install)
+            case $2 in
+                searx-suite) searx_suite_install ;;
+            esac
+            ;;
         *)
             usage "unknown or missing command $1"; exit 42;;
     esac
 }
 
-install_searx_suite() {
-    export FILTRON_API="0.0.0.0:4005"
-    export FILTRON_LISTEN="0.0.0.0:4004"
-    export MORTY_LISTEN="0.0.0.0:3000"
-    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/searx.sh" install all
-    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/morty.sh" install all
-    FORCE_TIMEOUT=0 "${LXC_REPO_ROOT}/utils/filtron.sh" install all
-    rst_title "[$(hostname)] searx-suite installation finished" part
-    rst_para "IPs of the container ..."
-    echo
-    ip addr show | grep "inet\s*[0-9]*\.[0-9]*\.[0-9]*\.[0-9]*"
-    echo
+searx_suite_install() {
+    (
+        searx_suite_set_env
+        export FORCE_TIMEOUT=0
+        "${LXC_REPO_ROOT}/utils/searx.sh"   install all
+        "${LXC_REPO_ROOT}/utils/morty.sh"   install all
+        "${LXC_REPO_ROOT}/utils/filtron.sh" install all
+
+        rst_title "searx-suite installation finished ($(hostname))" part
+        searx_suite_info
+        echo
+    )
 }
 
+searx_suite_info() {
+    (
+        searx_suite_set_env
+        rst_para "Services of the container $(hostname)"
+        for ip in $(hostname -I); do
+            echo
+            if [[ $ip =~ .*:.* ]]; then
+                :
+                # IPv6: not yet implemented / tested
+                # echo " searx (filtron) --> http://[$ip]:4004/"
+                # echo " morty           --> http://[$ip]:3000/"
+            else
+                # IPv4:
+                echo " searx (filtron) --> http://$ip:4004/"
+                echo " morty           --> http://$ip:3000/"
+            fi
+        done
+    )
+}
 
 build_instances() {
     rst_title "Build LXC instances"
diff --git a/utils/morty.sh b/utils/morty.sh
index fcc9dc728..e7e2f8651 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -255,7 +255,7 @@ morty_is_installed() {
     [[ -f $SERVICE_HOME/go-apps/bin/morty ]]
 }
 
-_svcpr="  |${SERVICE_USER}| "
+_svcpr="  ${_Yellow}|${SERVICE_USER}|${_creset} "
 
 install_morty() {
     rst_title "Install morty in user's ~/go-apps" section
diff --git a/utils/searx.sh b/utils/searx.sh
index 270c30ace..c435b5c00 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -210,7 +210,7 @@ main() {
     esac
 }
 
-_service_prefix="  |$SERVICE_USER| "
+_service_prefix="  ${_Yellow}|$SERVICE_USER|${_creset} "
 
 install_all() {
     rst_title "Install $SEARX_INSTANCE_NAME (service)"
@@ -352,7 +352,7 @@ install_settings() {
             sudo -H -i
             rst_para 'Diff between new setting file (-) and current (+):'
             echo
-            $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}" 
+            $DIFF_CMD "${SEARX_SRC}/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
             wait_key
             ;;
     esac
@@ -440,7 +440,7 @@ test_local_searx() {
     fi
     sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
-export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}" 
+export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
 cd ${SEARX_SRC}
 timeout 10 python3 searx/webapp.py &
 sleep 3
@@ -537,6 +537,18 @@ EOF
     uWSGI_app_available "$SEARX_UWSGI_APP" \
         || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
 
+    if is_container; then
+        warn_msg "runnning inside container ..."
+        for ip in $(hostname -I); do
+            if [[ $ip =~ .*:.* ]]; then
+                info_msg "  public HTTP service (IPv6) --> http://[$ip]"
+            else
+                info_msg "  public HTTP service (IPv4) --> http://$ip"
+            fi
+        done
+        warn_msg "SEARX_INTERNAL_URL not available from outside"
+    fi
+
     if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
         err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
         echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
@@ -545,6 +557,9 @@ EOF
 
     if ! service_is_available "${PUBLIC_URL}"; then
         warn_msg "Public service at ${PUBLIC_URL} is not available!"
+        if is_container; then
+            warn_msg "Check if public name is correct and routed or use the public IP from above."
+        fi
     fi
 
     local _debug_on

From 491cb95a1f910e17ac022ddfa73b68aae2fbc6e2 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 1 Mar 2020 18:28:10 +0100
Subject: [PATCH 055/125] utils/lxc.env: separate environment that is used in
 containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile         |  1 +
 utils/filtron.sh |  1 +
 utils/lxc.env    | 14 ++++++++++++++
 utils/lxc.sh     |  7 +------
 utils/morty.sh   |  1 +
 utils/searx.sh   |  1 +
 6 files changed, 19 insertions(+), 6 deletions(-)
 create mode 100644 utils/lxc.env

diff --git a/Makefile b/Makefile
index f62db2f03..1d26ef187 100644
--- a/Makefile
+++ b/Makefile
@@ -89,6 +89,7 @@ test.sh:
 	shellcheck -x utils/searx.sh
 	shellcheck -x utils/morty.sh
 	shellcheck -x utils/lxc.sh
+	shellcheck -x utils/lxc.env
 	shellcheck -x .config.sh
 
 test.pep8: pyenvinstall
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 663c2b1dc..c53acc607 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -6,6 +6,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
+source "${REPO_ROOT}/utils/lxc.env"
 
 # ----------------------------------------------------------------------------
 # config
diff --git a/utils/lxc.env b/utils/lxc.env
new file mode 100644
index 000000000..d470f256c
--- /dev/null
+++ b/utils/lxc.env
@@ -0,0 +1,14 @@
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+
+# Herein we place all, what is needed to know when inside containers.  Its
+# sourced from different context --> do not manipulate the environment directly,
+# implement functions!
+
+# shellcheck shell=sh
+
+searx_suite_set_env() {
+    export FILTRON_API="0.0.0.0:4005"
+    export FILTRON_LISTEN="0.0.0.0:4004"
+    export MORTY_LISTEN="0.0.0.0:3000"
+}
diff --git a/utils/lxc.sh b/utils/lxc.sh
index ab9afcbb4..f950cdb1b 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -5,6 +5,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
+source "${REPO_ROOT}/utils/lxc.env"
 
 # ----------------------------------------------------------------------------
 # config
@@ -63,12 +64,6 @@ HOST_USER="${SUDO_USER:-$USER}"
 HOST_USER_ID=$(id -u "${HOST_USER}")
 HOST_GROUP_ID=$(id -g "${HOST_USER}")
 
-searx_suite_set_env() {
-    export FILTRON_API="0.0.0.0:4005"
-    export FILTRON_LISTEN="0.0.0.0:4004"
-    export MORTY_LISTEN="0.0.0.0:3000"
-}
-
 # ----------------------------------------------------------------------------
 usage() {
 # ----------------------------------------------------------------------------
diff --git a/utils/morty.sh b/utils/morty.sh
index e7e2f8651..c3531f735 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -5,6 +5,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
+source "${REPO_ROOT}/utils/lxc.env"
 
 # ----------------------------------------------------------------------------
 # config
diff --git a/utils/searx.sh b/utils/searx.sh
index c435b5c00..1089f30d7 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -6,6 +6,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
+source "${REPO_ROOT}/utils/lxc.env"
 
 # ----------------------------------------------------------------------------
 # config

From c3e4753ce951f759844db13d98f9ad5b226f84b6 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 2 Mar 2020 19:00:19 +0100
Subject: [PATCH 056/125] docs: generic documentation from the installation
 scripts

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile                           |  12 +-
 docs/admin/index.rst               |   4 +
 docs/admin/installation-docker.rst |  28 ++++
 docs/admin/installation-searx.rst  |  85 ++++++++++
 docs/admin/installation-uwsgi.rst  |  82 ++++++++++
 docs/admin/installation.rst        | 247 +++++------------------------
 docs/admin/update-searx.rst        |  23 +++
 docs/utils/index.rst               |   9 --
 utils/filtron.sh                   |   4 +
 utils/lib.sh                       |   8 +-
 utils/lxc.sh                       |   5 +
 utils/morty.sh                     |   4 +
 utils/searx.sh                     | 214 ++++++++++++++++++++++---
 13 files changed, 484 insertions(+), 241 deletions(-)
 create mode 100644 docs/admin/installation-docker.rst
 create mode 100644 docs/admin/installation-searx.rst
 create mode 100644 docs/admin/installation-uwsgi.rst
 create mode 100644 docs/admin/update-searx.rst

diff --git a/Makefile b/Makefile
index 1d26ef187..fadf80cdc 100644
--- a/Makefile
+++ b/Makefile
@@ -60,13 +60,21 @@ run:  pyenvinstall
 # ----
 
 PHONY += docs
-docs:  pyenvinstall sphinx-doc
+
+docs:  pyenvinstall sphinx-doc prebuild-includes
 	$(call cmd,sphinx,html,docs,docs)
 
 PHONY += docs-live
-docs-live:  pyenvinstall sphinx-live
+docs-live:  pyenvinstall sphinx-live prebuild-includes
 	$(call cmd,sphinx_autobuild,html,docs,docs)
 
+prebuild-includes:
+	@mkdir -p $(DOCS_BUILD)/includes
+	@./utils/searx.sh doc | cat > $(DOCS_BUILD)/includes/searx.rst
+	@./utils/filtron.sh doc | cat > $(DOCS_BUILD)/includes/filtron.rst
+	@./utils/morty.sh doc | cat > $(DOCS_BUILD)/includes/morty.rst
+
+
 $(GH_PAGES)::
 	@echo "doc available at --> $(DOCS_URL)"
 
diff --git a/docs/admin/index.rst b/docs/admin/index.rst
index 4190bf24b..c708c4ffa 100644
--- a/docs/admin/index.rst
+++ b/docs/admin/index.rst
@@ -7,8 +7,12 @@ Administrator documentation
    :caption: Contents
 
    installation
+   installation-searx
+   installation-uwsgi
    installation-nginx
    installation-apache
+   installation-docker
+   update-searx
    settings
    api
    architecture
diff --git a/docs/admin/installation-docker.rst b/docs/admin/installation-docker.rst
new file mode 100644
index 000000000..340e66319
--- /dev/null
+++ b/docs/admin/installation-docker.rst
@@ -0,0 +1,28 @@
+.. _installation docker:
+
+===================
+Docker installation
+===================
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+Make sure you have installed Docker.  For instance, you can deploy searx like this:
+
+.. code:: sh
+
+    docker pull wonderfall/searx
+    docker run -d --name searx -p $PORT:8888 wonderfall/searx
+
+Go to ``http://localhost:$PORT``.
+
+See https://hub.docker.com/r/wonderfall/searx/ for more informations.  It's also
+possible to build searx from the embedded Dockerfile.
+
+.. code:: sh
+
+   git clone https://github.com/asciimoo/searx.git
+   cd searx
+   docker build -t whatever/searx .
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
new file mode 100644
index 000000000..11018bb3b
--- /dev/null
+++ b/docs/admin/installation-searx.rst
@@ -0,0 +1,85 @@
+.. _installation basic:
+
+=========================
+Step by step installation
+=========================
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+Step by step installation with virtualenv.  For Ubuntu, be sure to have enable
+universe repository.
+
+Install packages
+================
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START distro-packages
+   :end-before: END distro-packages
+
+.. hint::
+
+   This installs also the packages needed by :ref:`searx uwsgi`
+
+Create user
+===========
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START create user
+   :end-before: END create user
+
+Start a interactive shell from new created user and clone searx:
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START clone searx
+   :end-before: END clone searx
+
+In the same shell create *virtualenv*:
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START create virtualenv
+   :end-before: END create virtualenv
+
+To install searx's dependencies, exit the searx *bash* session you opened above
+and restart a new.  Before install, first check if your *virualenv* was sourced
+from the login (*~/.profile*):
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START manage.sh update_packages
+   :end-before: END manage.sh update_packages
+
+.. tip::
+
+   Open a second terminal for the configuration tasks and left the ``(searx)$``
+   terminal open for the tasks below.
+
+
+Configuration
+==============
+
+Create a copy of the :origin:`searx/settings.yml` configuration file in system's
+*/etc* folder.  Configure like shown below -- replace ``searx@\$(uname -n)`` with
+a name of your choice -- *and/or* edit ``/etc/searx/settings.yml`` if necessary.
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START searx config
+   :end-before: END searx config
+
+
+Check
+=====
+
+To check your searx setup, optional enable debugging and start the *webapp*.
+Searx looks at the exported environment ``$SEARX_SETTINGS_PATH`` for a
+configuration file.
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START check searx installation
+   :end-before: END check searx installation
+
+If everything works fine, hit ``[CTRL-C]`` to stop the *webapp* and disable the
+debug option in ``settings.yml``. You can now exit searx user bash (enter exit
+command twice).  At this point searx is not demonized; uwsgi allows this.
+
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
new file mode 100644
index 000000000..0ee4800ad
--- /dev/null
+++ b/docs/admin/installation-uwsgi.rst
@@ -0,0 +1,82 @@
+.. _searx uwsgi:
+
+=====
+uwsgi
+=====
+
+Create the configuration file ``/etc/uwsgi/apps-available/searx.ini`` with this
+content:
+
+.. code:: ini
+
+   [uwsgi]
+
+   # uWSGI core
+   # ----------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+   # Who will run the code
+   uid = searx
+   gid = searx
+
+   # chdir to specified directory before apps loading
+   chdir = /usr/local/searx/searx-src/searx
+
+   # disable logging for privacy
+   disable-logging = true
+
+   # The right granted on the created socket
+   chmod-socket = 666
+
+   # Plugin to use and interpretor config
+   single-interpreter = true
+
+   # enable master process
+   master = true
+
+   # load apps in each worker instead of the master
+   lazy-apps = true
+
+   # load uWSGI plugins
+   plugin = python3,http
+
+   # By default the Python plugin does not initialize the GIL.  This means your
+   # app-generated threads will not run.  If you need threads, remember to enable
+   # them with enable-threads.  Running uWSGI in multithreading mode (with the
+   # threads options) will automatically enable threading support. This *strange*
+   # default behaviour is for performance reasons.
+   enable-threads = true
+
+   # plugin: python
+   # --------------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+   # load a WSGI module
+   module = searx.webapp
+
+   # set PYTHONHOME/virtualenv
+   virtualenv = /usr/local/searx/searx-pyenv
+
+   # add directory (or glob) to pythonpath
+   pythonpath = /usr/local/searx/searx-src
+
+
+   # plugin http
+   # -----------
+   #
+   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+
+   # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+   http = 127.0.0.1:8888
+
+Activate the uwsgi application and restart:
+
+.. code:: sh
+
+    cd /etc/uwsgi/apps-enabled
+    ln -s ../apps-available/searx.ini
+    /etc/init.d/uwsgi restart
+
+
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index a4c7eb8b3..330a51fd1 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -4,234 +4,63 @@
 Installation
 ============
 
-.. sidebar:: Searx server setup
+*You're spoilt for choice*, choose your preferred method of installation.
 
-   - :ref:`installation nginx`
-   - :ref:`installation apache`
+- :ref:`installation docker`
+- `Installation scripts`_
+- :ref:`installation basic`
 
-   If you do not have any special preferences, it is recommend to use
-   :ref:`searx.sh`.
+The :ref:`installation basic` is good enough for intranet usage and it is a
+excellent illustration of *how a searx instance is build up*.  If you place your
+instance public to the internet you should really consider to install a
+:ref:`filtron reverse proxy <filtron.sh>` and for privacy a :ref:`result proxy
+<morty.sh>` is mandatory.
 
-.. contents:: Contents
-   :depth: 2
-   :local:
-   :backlinks: entry
+Therefore, if you do not have any special preferences, its recommend to use the
+:ref:`installation docker` or the `Installation scripts`_ from our :ref:`tooling
+box <toolboxing>` as described below.
 
-.. _installation basic:
 
-Basic installation
-==================
+Installation scripts
+====================
 
-Step by step installation with virtualenv.  For Ubuntu, be sure to have enable
-universe repository.
+The following will install a setup as shown in :ref:`architecture`.  First you
+need to get a clone.  The clone is only needed for the installation procedure
+and some maintenance tasks (alternatively you can create your own fork).
 
-Install packages:
+.. code:: bash
 
-.. tabs::
+   $ cd ~/Download
+   $ git clone https://github.com/asciimoo/searx searx
+   $ cd searx
 
-   .. group-tab:: Ubuntu / debian
+.. hint::
 
-      .. code-block:: sh
+   The *tooling box* is not yet merged into `asciimoo/searx master
+   <https://github.com/asciimoo/searx>`_.  As long as PR is not merged, you need
+   to merge the PR into your local clone (see below).  The discussion takes
+   place in :pull:`1803`.  To merge the :pull:`1803` in your local branch use:
 
-         $ sudo -H apt-get install -m \
-                   git build-essential
-                   libxslt-dev python3-dev python3-babel python3-venv \
-                   zlib1g-dev libffi-dev libssl-dev
+   .. code:: bash
 
-Install searx:
+      $ git pull origin refs/pull/1803/head
 
-.. code:: sh
+**Install** :ref:`searx service <searx.sh>`
 
-    sudo -H useradd searx --system --disabled-password -d /usr/local/searx
-    sudo -H usermod -a -G shadow searx
-    cd /usr/local/searx
-    sudo -H git clone https://github.com/asciimoo/searx.git searx-src
-    sudo -H chown searx:searx -R /usr/local/searx
+This installs searx as described in :ref:`installation basic`.
 
-Install virtualenv:
+.. code:: bash
 
-.. code:: sh
+   $ sudo -H ./utils/searx.sh install all
 
-    sudo -H -u searx -i
-    (searx)$ python3 -m venv searx-pyenv
-    (searx)$ echo 'source ~/searx-pyenv/bin/activate' > ~/.profile
+**Install** :ref:`filtron reverse proxy <filtron.sh>`
 
-Exit the searx bash and restart a new to install the searx dependencies:
+.. code:: bash
 
-.. code:: sh
+   $ sudo -H ./utils/filtron.sh install all
 
-    sudo -H -u searx -i
-    (searx)$ cd searx-src
-    (searx)$ ./manage.sh update_packages
+**Install** :ref:`result proxy <morty.sh>`
 
-Configuration
-==============
+.. code:: bash
 
-.. code:: sh
-
-    sudo -H -u searx -i
-    (searx)$ cd searx-src
-    (searx)$ sed -i -e "s/ultrasecretkey/`openssl rand -hex 16`/g" searx/settings.yml
-
-Edit searx/settings.yml if necessary.
-
-Check
-=====
-
-Start searx:
-
-.. code:: sh
-
-    sudo -H -u searx -i
-    (searx)$ cd searx-src
-    (searx)$ python3 searx/webapp.py
-
-Go to http://localhost:8888
-
-If everything works fine, disable the debug option in settings.yml:
-
-.. code:: sh
-
-    sed -i -e "s/debug : True/debug : False/g" searx/settings.yml
-
-At this point searx is not demonized ; uwsgi allows this.  You can exit the
-virtualenv and the searx user bash (enter exit command twice).
-
-uwsgi
-=====
-
-Install packages:
-
-.. tabs::
-
-   .. group-tab:: Ubuntu / debian
-
-      .. code-block:: bash
-
-         sudo -H apt-get install uwsgi uwsgi-plugin-python3
-
-Create the configuration file ``/etc/uwsgi/apps-available/searx.ini`` with this
-content:
-
-.. code:: ini
-
-   [uwsgi]
-
-   # uWSGI core
-   # ----------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
-
-   # Who will run the code
-   uid = searx
-   gid = searx
-
-   # chdir to specified directory before apps loading
-   chdir = /usr/local/searx/searx-src/searx
-
-   # disable logging for privacy
-   disable-logging = true
-
-   # The right granted on the created socket
-   chmod-socket = 666
-
-   # Plugin to use and interpretor config
-   single-interpreter = true
-
-   # enable master process
-   master = true
-
-   # load apps in each worker instead of the master
-   lazy-apps = true
-
-   # load uWSGI plugins
-   plugin = python3,http
-
-   # By default the Python plugin does not initialize the GIL.  This means your
-   # app-generated threads will not run.  If you need threads, remember to enable
-   # them with enable-threads.  Running uWSGI in multithreading mode (with the
-   # threads options) will automatically enable threading support. This *strange*
-   # default behaviour is for performance reasons.
-   enable-threads = true
-
-   # plugin: python
-   # --------------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
-
-   # load a WSGI module
-   module = searx.webapp
-
-   # set PYTHONHOME/virtualenv
-   virtualenv = /usr/local/searx/searx-pyenv
-
-   # add directory (or glob) to pythonpath
-   pythonpath = /usr/local/searx/searx-src
-
-
-   # plugin http
-   # -----------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
-
-   # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
-   http = 127.0.0.1:8888
-
-Activate the uwsgi application and restart:
-
-.. code:: sh
-
-    cd /etc/uwsgi/apps-enabled
-    ln -s ../apps-available/searx.ini
-    /etc/init.d/uwsgi restart
-
-
-How to update
-=============
-
-.. code:: sh
-
-    sudo -H -u searx -i
-    (searx)$ git stash
-    (searx)$ git pull origin master
-    (searx)$ git stash apply
-    (searx)$ ./manage.sh update_packages
-
-Restart uwsgi:
-
-.. tabs::
-
-   .. group-tab:: Ubuntu / debian
-
-      .. code:: sh
-
-         sudo -H systemctl restart uwsgi
-
-Docker
-======
-
-Make sure you have installed Docker. For instance, you can deploy searx like this:
-
-.. code:: sh
-
-    docker pull wonderfall/searx
-    docker run -d --name searx -p $PORT:8888 wonderfall/searx
-
-Go to ``http://localhost:$PORT``.
-
-See https://hub.docker.com/r/wonderfall/searx/ for more informations.  It's also
-possible to build searx from the embedded Dockerfile.
-
-.. code:: sh
-
-   git clone https://github.com/asciimoo/searx.git
-   cd searx
-   docker build -t whatever/searx .
-
-References
-==========
-
-* https://about.okhin.fr/posts/Searx/ with some additions
-
-* How to: `Setup searx in a couple of hours with a free SSL certificate
-  <https://www.reddit.com/r/privacytoolsIO/comments/366kvn/how_to_setup_your_own_privacy_respecting_search/>`__
+   $ sudo -H ./utils/morty.sh install all
diff --git a/docs/admin/update-searx.rst b/docs/admin/update-searx.rst
new file mode 100644
index 000000000..c74c9568a
--- /dev/null
+++ b/docs/admin/update-searx.rst
@@ -0,0 +1,23 @@
+.. _update searx:
+
+=============
+How to update
+=============
+
+.. code:: sh
+
+    sudo -H -u searx -i
+    (searx)$ git stash
+    (searx)$ git pull origin master
+    (searx)$ git stash apply
+    (searx)$ ./manage.sh update_packages
+
+Restart uwsgi:
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H systemctl restart uwsgi
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 338914715..1c77cbfcd 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -1,4 +1,3 @@
-
 .. _searx_utils:
 .. _toolboxing:
 
@@ -9,14 +8,6 @@ Tooling box ``utils/*``
 In the folder :origin:`utils/` we maintain some tools useful for admins and
 developers.
 
-.. sidebar:: Work needed!
-
-   Our scripts to maintain services do most support only systemd init process
-   used by debian, ubuntu and many other dists.  In general our scripts are only
-   partially usable on debian systems.  We are working on this limitation, if
-   you have any contribution, please send us your :pull:`PR <../pulls>`, see
-   :ref:`how to contribute`.
-
 .. toctree::
    :maxdepth: 2
    :caption: Contents
diff --git a/utils/filtron.sh b/utils/filtron.sh
index c53acc607..89971d61d 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -180,6 +180,10 @@ main() {
                 debug-off)  echo; disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        doc)
+            echo
+            echo ".. generic utils/filtron.sh documentation"
+            ;;
 
         *) usage "unknown or missing command $1"; exit 42;;
     esac
diff --git a/utils/lib.sh b/utils/lib.sh
index 47e9e8030..6de66bcaf 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -933,7 +933,9 @@ pkg_install() {
     # usage: TITEL='install foobar' pkg_install foopkg barpkg
 
     rst_title "${TITLE:-installation of packages}" section
-    echo -en "\npackage(s)::\n\n  $*\n" | $FMT
+    echo -e "\npackage(s)::\n"
+    # shellcheck disable=SC2068
+    echo "  " $@ | $FMT
 
     if ! ask_yn "Should packages be installed?" Yn 30; then
         return 42
@@ -959,7 +961,9 @@ pkg_remove() {
     # usage: TITEL='remove foobar' pkg_remove foopkg barpkg
 
     rst_title "${TITLE:-remove packages}" section
-    echo -en "\npackage(s)::\n\n  $*\n" | $FMT
+    echo -e "\npackage(s)::\n"
+    # shellcheck disable=SC2068
+    echo "  " $@ | $FMT
 
     if ! ask_yn "Should packages be removed (purge)?" Yn 30; then
         return 42
diff --git a/utils/lxc.sh b/utils/lxc.sh
index f950cdb1b..674eab4a4 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -209,6 +209,11 @@ main() {
                 searx-suite) searx_suite_install ;;
             esac
             ;;
+        doc)
+            echo
+            echo ".. generic utils/lxc.sh documentation"
+            ;;
+
         *)
             usage "unknown or missing command $1"; exit 42;;
     esac
diff --git a/utils/morty.sh b/utils/morty.sh
index c3531f735..143e7e0c8 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -183,6 +183,10 @@ main() {
                 debug-off)  disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        doc)
+            echo
+            echo ".. generic utils/morty.sh documentation"
+            ;;
 
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
     esac
diff --git a/utils/searx.sh b/utils/searx.sh
index 1089f30d7..de903bc09 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -36,25 +36,28 @@ SEARX_UWSGI_APP="searx.ini"
 # shellcheck disable=SC2034
 SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
+# apt packages
+SEARX_PACKAGES_debian="\
+python3-dev python3-babel python3-venv
+uwsgi uwsgi-plugin-python3
+git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
+
+# pacman packages
+SEARX_PACKAGES_arch="\
+python python-pip python-lxml python-babel
+uwsgi uwsgi-plugin-python
+git base-devel libxml2"
+
+# dnf packages
+SEARX_PACKAGES_fedora="\
+python python-pip python-lxml python-babel
+uwsgi uwsgi-plugin-python3
+git @development-tools libxml2"
+
 case $DIST_ID in
-    ubuntu|debian)  # apt packages
-        SEARX_PACKAGES="\
- python3-dev python3-babel python3-venv \
- uwsgi uwsgi-plugin-python3 \
- git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev "
-        ;;
-    arch)           # pacman packages
-        SEARX_PACKAGES="\
- python python-pip python-lxml python-babel \
- uwsgi uwsgi-plugin-python \
- git base-devel libxml2 "
-        ;;
-    fedora)          # dnf packages
-        SEARX_PACKAGES="\
- python python-pip python-lxml python-babel \
- uwsgi uwsgi-plugin-python3 \
- git @development-tools libxml2 "
-        ;;
+    ubuntu|debian) SEARX_PACKAGES="${SEARX_PACKAGES_debian}" ;;
+    arch) SEARX_PACKAGES="${SEARX_PACKAGES_arch}" ;;
+    fedora) SEARX_PACKAGES="${SEARX_PACKAGES_fedora}" ;;
 esac
 
 # Apache Settings
@@ -206,6 +209,7 @@ main() {
                 remove) remove_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        doc) rst-doc;;
 
         *) usage "unknown or missing command $1"; exit 42;;
     esac
@@ -443,7 +447,7 @@ test_local_searx() {
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
 cd ${SEARX_SRC}
-timeout 10 python3 searx/webapp.py &
+timeout 10 python searx/webapp.py &
 sleep 3
 curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
 EOF
@@ -631,6 +635,178 @@ This removes apache site ${APACHE_SEARX_SITE}."
     apache_remove_site "${APACHE_SEARX_SITE}"
 }
 
+rst-doc() {
+    local debian="${SEARX_PACKAGES_debian}"
+    local arch="${SEARX_PACKAGES_arch}"
+    local fedora="${SEARX_PACKAGES_fedora}"
+    debian="$(echo "${debian}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+    arch="$(echo "${arch}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+    fedora="$(echo "${fedora}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+
+    cat <<EOF
+
+.. START distro-packages
+
+.. tabs::
+
+  .. group-tab:: Ubuntu / debian
+
+    .. code-block:: sh
+
+      $ sudo -H apt-get install -y \\
+${debian}
+
+  .. group-tab:: Arch Linux
+
+    .. code-block:: sh
+
+      $ sudo -H pacman -S --noconfirm \\
+${arch}
+
+  .. group-tab::  Fedora / RHEL
+
+    .. code-block:: sh
+
+      $ sudo -H dnf install -y \\
+${fedora}
+
+.. END distro-packages
+
+
+.. START create user
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+      $ sudo -H useradd --shell /bin/bash --system \\
+          --home-dir "$SERVICE_HOME" \\
+          --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+      $ sudo -H mkdir "$SERVICE_HOME"
+      $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START clone searx
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H -u ${SERVICE_USER} -i
+       (${SERVICE_USER})$ git clone "https://github.com/asciimoo/searx.git" "$SEARX_SRC"
+
+.. END clone searx
+
+.. START create virtualenv
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       (${SERVICE_USER})$ python3 -m venv "${SEARX_PYENV}"
+       (${SERVICE_USER})$ echo ". ${SEARX_PYENV}/bin/activate" >>  "$SERVICE_HOME/.profile"
+
+.. END create virtualenv
+
+.. START manage.sh update_packages
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H -u ${SERVICE_USER} -i
+
+       (${SERVICE_USER})$ command -v python && python --version
+       $SEARX_PYENV/bin/python
+       Python 3.8.1
+
+       (${SERVICE_USER})$ cd "$SEARX_SRC"
+       (${SERVICE_USER})$ ./manage.sh update_packages
+
+.. END manage.sh update_packages
+
+.. START searx config
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H cp "$SEARX_SRC/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+       $ sudo -H sed -i -e "s/ultrasecretkey/\$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
+       $ sudo -H sed -i -e "s/{instance_name}/searx@\$(uname -n)/g" "$SEARX_SETTINGS_PATH"
+
+.. END searx config
+
+.. START check searx installation
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       # enable debug ..
+       $ sudo -H sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+
+       # start webapp
+       $ sudo -H -u ${SERVICE_USER} -i
+       (${SERVICE_USER})$ cd ${SEARX_SRC}
+       (${SERVICE_USER})$ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
+       (${SERVICE_USER})$ python searx/webapp.py
+
+       # disable debug
+       $ sudo -H sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+
+Open WEB browser and visit http://$SEARX_INTERNAL_URL .  If you are inside a
+container or in a script, test with curl:
+
+.. tabs::
+
+  .. group-tab:: WEB browser
+
+    .. code-block:: sh
+
+       $ xgd-open http://$SEARX_INTERNAL_URL
+
+  .. group-tab:: curl
+
+    .. code-block:: none
+
+       $ curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
+
+       *   Trying 127.0.0.1:8888...
+       * TCP_NODELAY set
+       * Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
+       > HEAD / HTTP/1.1
+       > Host: 127.0.0.1:8888
+       > User-Agent: curl/7.68.0
+       > Accept: */*
+       >
+       * Mark bundle as not supporting multiuse
+       * HTTP 1.0, assume close after body
+       < HTTP/1.0 200 OK
+       HTTP/1.0 200 OK
+       ...
+
+.. END check searx installation
+
+EOF
+
+}
+
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------

From 7af991acda955c5d4edbf9405178fb099725d254 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 2 Mar 2020 19:01:08 +0100
Subject: [PATCH 057/125] LXC: add ubuntu 19.10 and 20.04 to the list of test
 images

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 6 +++++-
 1 file changed, 5 insertions(+), 1 deletion(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index 674eab4a4..7c7edccb3 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -25,6 +25,8 @@ LXC_REPO_ROOT="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")"
 TEST_IMAGES=(
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
     "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
+    "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.10"  "ubu1910"
+    "$LINUXCONTAINERS_ORG_NAME:ubuntu/20.04"  "ubu2004"
     "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
     "$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
 )
@@ -35,8 +37,10 @@ apt-get update -y
 apt-get upgrade -y
 apt-get install -y git curl wget
 "
-# shellcheck disable=SC2034
 ubu1904_boilerplate="$ubu1804_boilerplate"
+ubu1910_boilerplate="$ubu1904_boilerplate"
+# shellcheck disable=SC2034
+ubu2004_boilerplate="$ubu1910_boilerplate"
 
 # shellcheck disable=SC2034
 archlinux_boilerplate="

From f25da6070e398f17c24b194c8688595050dc473c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 3 Mar 2020 16:26:02 +0100
Subject: [PATCH 058/125] docs: generic documentation get templating

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/build-templates/searx.rst | 159 ++++++++++++++++++++++++++++++++
 docs/conf.py                   |   2 +
 utils/searx.sh                 | 162 +--------------------------------
 3 files changed, 162 insertions(+), 161 deletions(-)
 create mode 100644 docs/build-templates/searx.rst

diff --git a/docs/build-templates/searx.rst b/docs/build-templates/searx.rst
new file mode 100644
index 000000000..9e33928ad
--- /dev/null
+++ b/docs/build-templates/searx.rst
@@ -0,0 +1,159 @@
+.. template evaluated by: ./utils/searx.sh docs
+.. hint: all dollar-names are variables, dollar sign itself is quoted by: \\$
+
+.. START distro-packages
+
+.. tabs::
+
+  .. group-tab:: Ubuntu / debian
+
+    .. code-block:: sh
+
+      $ sudo -H apt-get install -y \\
+${debian}
+
+  .. group-tab:: Arch Linux
+
+    .. code-block:: sh
+
+      $ sudo -H pacman -S --noconfirm \\
+${arch}
+
+  .. group-tab::  Fedora / RHEL
+
+    .. code-block:: sh
+
+      $ sudo -H dnf install -y \\
+${fedora}
+
+.. END distro-packages
+
+
+.. START create user
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+      $ sudo -H useradd --shell /bin/bash --system \\
+          --home-dir "$SERVICE_HOME" \\
+          --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+      $ sudo -H mkdir "$SERVICE_HOME"
+      $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START clone searx
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H -u ${SERVICE_USER} -i
+       (${SERVICE_USER})$ git clone "https://github.com/asciimoo/searx.git" "$SEARX_SRC"
+
+.. END clone searx
+
+.. START create virtualenv
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       (${SERVICE_USER})$ python3 -m venv "${SEARX_PYENV}"
+       (${SERVICE_USER})$ echo ". ${SEARX_PYENV}/bin/activate" >>  "$SERVICE_HOME/.profile"
+
+.. END create virtualenv
+
+.. START manage.sh update_packages
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H -u ${SERVICE_USER} -i
+
+       (${SERVICE_USER})$ command -v python && python --version
+       $SEARX_PYENV/bin/python
+       Python 3.8.1
+
+       (${SERVICE_USER})$ cd "$SEARX_SRC"
+       (${SERVICE_USER})$ ./manage.sh update_packages
+
+.. END manage.sh update_packages
+
+.. START searx config
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       $ sudo -H cp "$SEARX_SRC/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
+       $ sudo -H sed -i -e "s/ultrasecretkey/\\$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
+       $ sudo -H sed -i -e "s/{instance_name}/searx@\\$(uname -n)/g" "$SEARX_SETTINGS_PATH"
+
+.. END searx config
+
+.. START check searx installation
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+       # enable debug ..
+       $ sudo -H sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
+
+       # start webapp
+       $ sudo -H -u ${SERVICE_USER} -i
+       (${SERVICE_USER})$ cd ${SEARX_SRC}
+       (${SERVICE_USER})$ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
+       (${SERVICE_USER})$ python searx/webapp.py
+
+       # disable debug
+       $ sudo -H sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
+
+Open WEB browser and visit http://$SEARX_INTERNAL_URL .  If you are inside a
+container or in a script, test with curl:
+
+.. tabs::
+
+  .. group-tab:: WEB browser
+
+    .. code-block:: sh
+
+       $ xgd-open http://$SEARX_INTERNAL_URL
+
+  .. group-tab:: curl
+
+    .. code-block:: none
+
+       $ curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
+
+       *   Trying 127.0.0.1:8888...
+       * TCP_NODELAY set
+       * Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
+       > HEAD / HTTP/1.1
+       > Host: 127.0.0.1:8888
+       > User-Agent: curl/7.68.0
+       > Accept: */*
+       >
+       * Mark bundle as not supporting multiuse
+       * HTTP 1.0, assume close after body
+       < HTTP/1.0 200 OK
+       HTTP/1.0 200 OK
+       ...
+
+.. END check searx installation
diff --git a/docs/conf.py b/docs/conf.py
index 8f11b1c6c..6a6633341 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -23,6 +23,8 @@ master_doc = "index"
 source_suffix = '.rst'
 numfig = True
 
+exclude_patterns = ['build-templates/*.rst']
+
 from searx import webapp
 jinja_contexts = {
     'webapp': dict(**webapp.__dict__)
diff --git a/utils/searx.sh b/utils/searx.sh
index de903bc09..38ccfdcab 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -643,170 +643,10 @@ rst-doc() {
     arch="$(echo "${arch}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"
     fedora="$(echo "${fedora}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
 
-    cat <<EOF
-
-.. START distro-packages
-
-.. tabs::
-
-  .. group-tab:: Ubuntu / debian
-
-    .. code-block:: sh
-
-      $ sudo -H apt-get install -y \\
-${debian}
-
-  .. group-tab:: Arch Linux
-
-    .. code-block:: sh
-
-      $ sudo -H pacman -S --noconfirm \\
-${arch}
-
-  .. group-tab::  Fedora / RHEL
-
-    .. code-block:: sh
-
-      $ sudo -H dnf install -y \\
-${fedora}
-
-.. END distro-packages
-
-
-.. START create user
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-      $ sudo -H useradd --shell /bin/bash --system \\
-          --home-dir "$SERVICE_HOME" \\
-          --comment "Privacy-respecting metasearch engine" $SERVICE_USER
-
-      $ sudo -H mkdir "$SERVICE_HOME"
-      $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
-
-.. END create user
-
-.. START clone searx
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-       $ sudo -H -u ${SERVICE_USER} -i
-       (${SERVICE_USER})$ git clone "https://github.com/asciimoo/searx.git" "$SEARX_SRC"
-
-.. END clone searx
-
-.. START create virtualenv
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-       (${SERVICE_USER})$ python3 -m venv "${SEARX_PYENV}"
-       (${SERVICE_USER})$ echo ". ${SEARX_PYENV}/bin/activate" >>  "$SERVICE_HOME/.profile"
-
-.. END create virtualenv
-
-.. START manage.sh update_packages
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-       $ sudo -H -u ${SERVICE_USER} -i
-
-       (${SERVICE_USER})$ command -v python && python --version
-       $SEARX_PYENV/bin/python
-       Python 3.8.1
-
-       (${SERVICE_USER})$ cd "$SEARX_SRC"
-       (${SERVICE_USER})$ ./manage.sh update_packages
-
-.. END manage.sh update_packages
-
-.. START searx config
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-       $ sudo -H cp "$SEARX_SRC/searx/settings.yml" "${SEARX_SETTINGS_PATH}"
-       $ sudo -H sed -i -e "s/ultrasecretkey/\$(openssl rand -hex 16)/g" "$SEARX_SETTINGS_PATH"
-       $ sudo -H sed -i -e "s/{instance_name}/searx@\$(uname -n)/g" "$SEARX_SETTINGS_PATH"
-
-.. END searx config
-
-.. START check searx installation
-
-.. tabs::
-
-  .. group-tab:: bash
-
-    .. code-block:: sh
-
-       # enable debug ..
-       $ sudo -H sed -i -e "s/debug : False/debug : True/g" "$SEARX_SETTINGS_PATH"
-
-       # start webapp
-       $ sudo -H -u ${SERVICE_USER} -i
-       (${SERVICE_USER})$ cd ${SEARX_SRC}
-       (${SERVICE_USER})$ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
-       (${SERVICE_USER})$ python searx/webapp.py
-
-       # disable debug
-       $ sudo -H sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
-
-Open WEB browser and visit http://$SEARX_INTERNAL_URL .  If you are inside a
-container or in a script, test with curl:
-
-.. tabs::
-
-  .. group-tab:: WEB browser
-
-    .. code-block:: sh
-
-       $ xgd-open http://$SEARX_INTERNAL_URL
-
-  .. group-tab:: curl
-
-    .. code-block:: none
-
-       $ curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
-
-       *   Trying 127.0.0.1:8888...
-       * TCP_NODELAY set
-       * Connected to 127.0.0.1 (127.0.0.1) port 8888 (#0)
-       > HEAD / HTTP/1.1
-       > Host: 127.0.0.1:8888
-       > User-Agent: curl/7.68.0
-       > Accept: */*
-       >
-       * Mark bundle as not supporting multiuse
-       * HTTP 1.0, assume close after body
-       < HTTP/1.0 200 OK
-       HTTP/1.0 200 OK
-       ...
-
-.. END check searx installation
-
-EOF
+    eval "echo \"$(< ${REPO_ROOT}/docs/build-templates/searx.rst)\""
 
 }
 
-
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------

From 3cb7daedb7c5c93a98dfefa191c161f31a1347c6 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 3 Mar 2020 19:57:15 +0100
Subject: [PATCH 059/125] docs: generic doocumentation for searx-uwsgi

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-searx.rst |   5 +-
 docs/admin/installation-uwsgi.rst | 110 ++++++++++++------------------
 utils/lib.sh                      |  60 ++++++++--------
 utils/searx.sh                    |  30 ++++++++
 4 files changed, 107 insertions(+), 98 deletions(-)

diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index 11018bb3b..76449beb6 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -30,6 +30,9 @@ Create user
    :start-after: START create user
    :end-before: END create user
 
+install searx & dependencies
+============================
+
 Start a interactive shell from new created user and clone searx:
 
 .. include:: ../../build/docs/includes/searx.rst
@@ -55,7 +58,6 @@ from the login (*~/.profile*):
    Open a second terminal for the configuration tasks and left the ``(searx)$``
    terminal open for the tasks below.
 
-
 Configuration
 ==============
 
@@ -67,7 +69,6 @@ a name of your choice -- *and/or* edit ``/etc/searx/settings.yml`` if necessary.
    :start-after: START searx config
    :end-before: END searx config
 
-
 Check
 =====
 
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
index 0ee4800ad..f9161679f 100644
--- a/docs/admin/installation-uwsgi.rst
+++ b/docs/admin/installation-uwsgi.rst
@@ -4,79 +4,53 @@
 uwsgi
 =====
 
-Create the configuration file ``/etc/uwsgi/apps-available/searx.ini`` with this
-content:
+Create the configuration ini-file according to your distribution (see below) and
+restart the uwsgi application.
 
-.. code:: ini
+.. tabs::
 
-   [uwsgi]
+   .. group-tab:: Ubuntu / debian
 
-   # uWSGI core
-   # ----------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
-
-   # Who will run the code
-   uid = searx
-   gid = searx
-
-   # chdir to specified directory before apps loading
-   chdir = /usr/local/searx/searx-src/searx
-
-   # disable logging for privacy
-   disable-logging = true
-
-   # The right granted on the created socket
-   chmod-socket = 666
-
-   # Plugin to use and interpretor config
-   single-interpreter = true
-
-   # enable master process
-   master = true
-
-   # load apps in each worker instead of the master
-   lazy-apps = true
-
-   # load uWSGI plugins
-   plugin = python3,http
-
-   # By default the Python plugin does not initialize the GIL.  This means your
-   # app-generated threads will not run.  If you need threads, remember to enable
-   # them with enable-threads.  Running uWSGI in multithreading mode (with the
-   # threads options) will automatically enable threading support. This *strange*
-   # default behaviour is for performance reasons.
-   enable-threads = true
-
-   # plugin: python
-   # --------------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
-
-   # load a WSGI module
-   module = searx.webapp
-
-   # set PYTHONHOME/virtualenv
-   virtualenv = /usr/local/searx/searx-pyenv
-
-   # add directory (or glob) to pythonpath
-   pythonpath = /usr/local/searx/searx-src
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :start-after: START searx uwsgi-description ubuntu-20.04
+         :end-before: END searx uwsgi-description ubuntu-20.04
 
 
-   # plugin http
-   # -----------
-   #
-   # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+   .. group-tab:: Arch Linux
 
-   # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
-   http = 127.0.0.1:8888
-
-Activate the uwsgi application and restart:
-
-.. code:: sh
-
-    cd /etc/uwsgi/apps-enabled
-    ln -s ../apps-available/searx.ini
-    /etc/init.d/uwsgi restart
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :start-after: START searx uwsgi-description arch
+         :end-before: END searx uwsgi-description arch
+
+
+   .. group-tab::  Fedora / RHEL
+
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :start-after: START searx uwsgi-description fedora
+         :end-before: END searx uwsgi-description fedora
+
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :language: ini
+         :start-after: START searx uwsgi-appini ubuntu-20.04
+         :end-before: END searx uwsgi-appini ubuntu-20.04
+
+   .. group-tab:: Arch Linux
+
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :language: ini
+         :start-after: START searx uwsgi-appini arch
+         :end-before: END searx uwsgi-appini arch
+
+   .. group-tab::  Fedora / RHEL
+
+      .. literalinclude:: ../../build/docs/includes/searx.rst
+         :language: ini
+         :start-after: START searx uwsgi-appini fedora
+         :end-before: END searx uwsgi-appini fedora
 
 
diff --git a/utils/lib.sh b/utils/lib.sh
index 6de66bcaf..f0c710311 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -708,35 +708,39 @@ uWSGI_GROUP=
 # How distros manage uWSGI apps is very different.  From uWSGI POV read:
 # - https://uwsgi-docs.readthedocs.io/en/latest/Management.html
 
-case $DIST_ID-$DIST_VERS in
-    ubuntu-*|debian-*)
-        # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
-        # For uWSGI debian uses the LSB init process, this might be changed
-        # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
-        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
-        uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
-        ;;
-    arch-*)
-        # systemd --> /usr/lib/systemd/system/uwsgi@.service
-        # For uWSGI archlinux uses systemd template units, see
-        # - http://0pointer.de/blog/projects/instances.html
-        # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
-        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux"
-        uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
-        ;;
-    fedora-*)
-        # systemd --> /usr/lib/systemd/system/uwsgi.service
-        # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
-        # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
-        uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
-        uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d"
-        uWSGI_USER="uwsgi"
-        uWSGI_GROUP="uwsgi"
-        ;;
-    *)
-        info_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
-        ;;
+uWSGI_distro_setup() {
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            # init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+            # For uWSGI debian uses the LSB init process, this might be changed
+            # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
+            uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+            uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
+            ;;
+        arch-*)
+            # systemd --> /usr/lib/systemd/system/uwsgi@.service
+            # For uWSGI archlinux uses systemd template units, see
+            # - http://0pointer.de/blog/projects/instances.html
+            # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+            uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux"
+            uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
+            ;;
+        fedora-*)
+            # systemd --> /usr/lib/systemd/system/uwsgi.service
+            # The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+            # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+            uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
+            uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d"
+            uWSGI_USER="uwsgi"
+            uWSGI_GROUP="uwsgi"
+            ;;
+        *)
+            info_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            ;;
 esac
+}
+
+uWSGI_distro_setup
 
 uWSGI_restart() {
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 38ccfdcab..447e9911e 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -645,8 +645,38 @@ rst-doc() {
 
     eval "echo \"$(< ${REPO_ROOT}/docs/build-templates/searx.rst)\""
 
+    # I use ubuntu-20.04 here to demonstrate that versions are also suported,
+    # normaly debian-* and ubuntu-* are most the same.
+
+    for DIST_NAME in ubuntu-20.04 arch fedora; do
+        (
+            DIST_ID=${DIST_NAME%-*}
+            DIST_VERS=${DIST_NAME#*-}
+            [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+            uWSGI_distro_setup
+
+            echo -e "\n.. START searx uwsgi-description $DIST_NAME"
+            echo "location:  ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}"
+            case $DIST_ID-$DIST_VERS in
+                ubuntu-*|debian-*)
+                    echo "restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}" ;;
+                arch-*)
+                    echo "restart:   sudo -H systemctl restart uwsgi@${SEARX_UWSGI_APP%.*}" ;;
+                fedora-*)
+                    echo "restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}";;
+            esac
+            echo -e ".. END searx uwsgi-description $DIST_NAME"
+
+            echo -e "\n.. START searx uwsgi-appini $DIST_NAME"
+            eval "echo \"$(< ${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP})\""
+            echo -e "\n.. END searx uwsgi-appini $DIST_NAME"
+
+        )
+    done
+
 }
 
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
+

From 3aff2c19d16619d949dc52216b29ef637d1a44a1 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 4 Mar 2020 12:22:20 +0100
Subject: [PATCH 060/125] docs: complement uwsgi documentation for all distros

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-searx.rst |  2 +
 docs/admin/installation-uwsgi.rst | 91 +++++++++++++++++++++++++++++++
 utils/searx.sh                    | 49 ++++++++++++++---
 3 files changed, 133 insertions(+), 9 deletions(-)

diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index 76449beb6..88562f3e3 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -12,6 +12,8 @@ Step by step installation
 Step by step installation with virtualenv.  For Ubuntu, be sure to have enable
 universe repository.
 
+.. _install packages:
+
 Install packages
 ================
 
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
index f9161679f..7996bf937 100644
--- a/docs/admin/installation-uwsgi.rst
+++ b/docs/admin/installation-uwsgi.rst
@@ -4,6 +4,97 @@
 uwsgi
 =====
 
+.. sidebar:: further read
+
+   - `systemd.unit`_
+   - `uWSGI Emperor`_
+
+.. contents:: Contents
+   :depth: 2
+   :local:
+   :backlinks: entry
+
+
+.. _systemd.unit: https://www.freedesktop.org/software/systemd/man/systemd.unit.html
+.. _One service per app in systemd:
+    https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+.. _uWSGI Emperor:
+    https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+.. _uwsgi ini file:
+   https://uwsgi-docs.readthedocs.io/en/latest/Configuration.html#ini-files
+.. _systemd unit template:
+   http://0pointer.de/blog/projects/instances.html
+
+
+Origin uWSGI
+============
+
+How uWSGI is implemented by distributors is different.  uWSGI itself
+recommend two methods
+
+`systemd.unit`_ template files as described here `One service per app in systemd`_.
+
+  There is one `systemd unit template`_ and one `uwsgi ini file`_ per uWSGI-app
+  placed at dedicated locations.  Take archlinux and a searx.ini as example::
+
+    unit template    -->  /usr/lib/systemd/system/uwsgi@.service
+    uwsgi ini files  -->  /etc/uwsgi/searx.ini
+
+  The searx app can be maintained as know from common systemd units::
+
+    systemctl enable  uwsgi@searx
+    systemctl start   uwsgi@searx
+    systemctl restart uwsgi@searx
+    systemctl stop    uwsgi@searx
+
+The `uWSGI Emperor`_ mode which fits for maintaining a large range of uwsgi apps.
+
+  The Emperor mode is a special uWSGI instance that will monitor specific
+  events.  The Emperor mode (service) is started by a (common, not template)
+  systemd unit.  The Emperor service will scan specific directories for `uwsgi
+  ini file`_\s (also know as *vassals*).  If a *vassal* is added, removed or the
+  timestamp is modified, a corresponding action takes place: a new uWSGI
+  instance is started, reload or stopped.  Take Fedora and a searx.ini as
+  example::
+
+    to start a new searx instance create   --> /etc/uwsgi.d/searx.ini
+    to reload the instance edit timestamp  --> touch /etc/uwsgi.d/searx.ini
+    to stop instance remove ini            --> rm /etc/uwsgi.d/searx.ini
+
+Distributors
+============
+
+The `uWSGI Emperor`_ mode and `systemd unit template`_ is what the distributors
+mostly offer their users, even if they differ in the way they implement both
+modes and their defaults.  Another point they might differ is the packaging of
+plugins (if so, compare :ref:`install packages`) and what the default python
+interpreter is (python2 vs. python3).
+
+Fedora starts a Emperor by default, while archlinux does not start any uwsgi
+service by default.  Worth to know; debian (ubuntu) follow a complete different
+approach.  *debian*: your are familiar with the apache infrastructure? .. they
+do similar for the uWSGI infrastructure (with less comfort), the folders are::
+
+    /etc/uwsgi/apps-available/
+    /etc/uwsgi/apps-enabled/
+
+The `uwsgi ini file`_ is enabled by a symbolic link::
+
+  ln -s /etc/uwsgi/apps-available/searx.ini /etc/uwsgi/apps-enabled/
+
+From debian's documentation (``/usr/share/doc/uwsgi/README.Debian.gz``): You
+could control specific instance(s) by issuing::
+
+  service uwsgi <command> <confname> <confname> ...
+
+  sudo -H service uwsgi start searx
+  sudo -H service uwsgi stop  searx
+
+My experience is, that this command is a bit buggy.
+
+Alltogether
+===========
+
 Create the configuration ini-file according to your distribution (see below) and
 restart the uwsgi application.
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 447e9911e..03911dff6 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -643,7 +643,7 @@ rst-doc() {
     arch="$(echo "${arch}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"
     fedora="$(echo "${fedora}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
 
-    eval "echo \"$(< ${REPO_ROOT}/docs/build-templates/searx.rst)\""
+    eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searx.rst")\""
 
     # I use ubuntu-20.04 here to demonstrate that versions are also suported,
     # normaly debian-* and ubuntu-* are most the same.
@@ -656,19 +656,50 @@ rst-doc() {
             uWSGI_distro_setup
 
             echo -e "\n.. START searx uwsgi-description $DIST_NAME"
-            echo "location:  ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}"
+
             case $DIST_ID-$DIST_VERS in
-                ubuntu-*|debian-*)
-                    echo "restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}" ;;
-                arch-*)
-                    echo "restart:   sudo -H systemctl restart uwsgi@${SEARX_UWSGI_APP%.*}" ;;
-                fedora-*)
-                    echo "restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}";;
+                ubuntu-*|debian-*)  cat <<EOF
+# init.d --> /usr/share/doc/uwsgi/README.Debian.gz
+# For uWSGI debian uses the LSB init process, this might be changed
+# one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
+
+create     ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}
+enable:    sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP} ${uWSGI_APPS_ENABLED}/
+start:     sudo -H service uwsgi start   ${SEARX_UWSGI_APP%.*}
+restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}
+stop:      sudo -H service uwsgi stop    ${SEARX_UWSGI_APP%.*}
+disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${CONF}
+EOF
+                ;;
+                arch-*) cat <<EOF
+# systemd --> /usr/lib/systemd/system/uwsgi@.service
+# For uWSGI archlinux uses systemd template units, see
+# - http://0pointer.de/blog/projects/instances.html
+# - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
+
+create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+enable:    sudo -H systemctl enable   uwsgi@${SEARX_UWSGI_APP%.*}
+start:     sudo -H systemctl start    uwsgi@${SEARX_UWSGI_APP%.*}
+restart:   sudo -H systemctl restart  uwsgi@${SEARX_UWSGI_APP%.*}
+stop:      sudo -H systemctl stop     uwsgi@${SEARX_UWSGI_APP%.*}
+disable:   sudo -H systemctl disable  uwsgi@${SEARX_UWSGI_APP%.*}
+EOF
+                ;;
+                fedora-*) cat <<EOF
+# systemd --> /usr/lib/systemd/system/uwsgi.service
+# The unit file starts uWSGI in emperor mode (/etc/uwsgi.ini), see
+# - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
+
+create:    ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+restart:   sudo -H touch ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
+EOF
+                ;;
             esac
             echo -e ".. END searx uwsgi-description $DIST_NAME"
 
             echo -e "\n.. START searx uwsgi-appini $DIST_NAME"
-            eval "echo \"$(< ${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP})\""
+            eval "echo \"$(< "${TEMPLATES}/${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP}")\""
             echo -e "\n.. END searx uwsgi-appini $DIST_NAME"
 
         )

From cbc08fdc26e96bf2cb02b76a30be095f5f60df9f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 4 Mar 2020 19:56:40 +0100
Subject: [PATCH 061/125] docs: describe uwsgi setup of all suported
 distributions

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-apache.rst |   7 +-
 docs/admin/installation-nginx.rst  | 289 +++++++++++++++++++++--------
 docs/admin/installation.rst        |   3 +-
 searx/settings.yml                 |   2 +-
 utils/lxc.sh                       |   3 +-
 utils/searx.sh                     |   2 +-
 6 files changed, 223 insertions(+), 83 deletions(-)

diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
index 7f92e47d5..8c6228380 100644
--- a/docs/admin/installation-apache.rst
+++ b/docs/admin/installation-apache.rst
@@ -4,10 +4,11 @@
 Install with apache
 ===================
 
-.. sidebar:: public HTTP servers
+.. sidebar:: public to the internet?
 
-   On public searx instances use an application firewall (:ref:`filtron
-   <filtron.sh>`).
+   If your searx instance is public, stop here and first install :ref:`filtron
+   reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
+   :ref:`installation scripts`.
 
 .. contents:: Contents
    :depth: 2
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 37d3e7532..6cd815d1d 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -4,18 +4,27 @@
 Install with nginx
 ==================
 
-.. sidebar:: public HTTP servers
-
-   On public searx instances use an application firewall (:ref:`filtron
-   <filtron.sh>`).
+.. _nginx:
+   https://docs.nginx.com/nginx/admin-guide/
+.. _nginx server configuration:
+   https://docs.nginx.com/nginx/admin-guide/web-server/web-server/#setting-up-virtual-servers
+.. _nginx beginners guide:
+   http://nginx.org/en/docs/beginners_guide.html
+.. _Getting Started wiki:
+   https://www.nginx.com/resources/wiki/start/
 
 .. contents:: Contents
    :depth: 2
    :local:
    :backlinks: entry
 
-If nginx is not installed (uwsgi will not work with the package
-nginx-light):
+
+The nginx HTTP server
+=====================
+
+If nginx_ is not installed (uwsgi will not work with the package nginx-light)
+install it now.
+
 
 .. tabs::
 
@@ -25,35 +34,25 @@ nginx-light):
 
          sudo -H apt-get install nginx
 
-Hosted at ``/``
-===============
+   .. group-tab:: Arch Linux
 
-Create the configuration file ``/etc/nginx/sites-available/searx`` with this
-content:
+      .. code-block:: sh
 
-.. code:: nginx
+         sudo -H pacman -S nginx-mainline
+         sudo -H systemctl enable nginx
+         sudo -H systemctl start nginx
 
-    server {
-        listen 80;
-        server_name searx.example.com;
-        root /usr/local/searx/searx;
+   .. group-tab::  Fedora / RHEL
 
-        location /static {
-        }
+      .. code-block:: sh
 
-        location / {
-                include uwsgi_params;
-                uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-        }
-    }
+         sudo -H dnf install nginx
+         sudo -H systemctl enable nginx
+         sudo -H systemctl start nginx
 
-Create a symlink to sites-enabled:
-
-.. code:: sh
-
-   sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
-
-Restart service:
+Now at http://localhost you should see a *Welcome to nginx!* page, on Fedora you
+see a *Fedora Webserver - Test Page*.  The test page comes from the default
+`nginx server configuration`_:
 
 .. tabs::
 
@@ -61,74 +60,182 @@ Restart service:
 
       .. code:: sh
 
-         sudo -H systemctl restart nginx
-         sudo -H systemctl restart uwsgi
+         less /etc/nginx/nginx.conf
 
-from subdirectory URL (``/searx``)
-==================================
+      there is a line including site configurations from:
 
-Add this configuration in the server config file
-``/etc/nginx/sites-enabled/default``:
+      .. code:: nginx
 
-.. code:: nginx
+         include /etc/nginx/sites-enabled/*;
 
-    location /searx/static {
-            alias /usr/local/searx/searx/static;
-    }
+   .. group-tab:: Arch Linux
 
-    location /searx {
-            uwsgi_param SCRIPT_NAME /searx;
-            include uwsgi_params;
-            uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-    }
+      .. code-block:: sh
 
+         less /etc/nginx/nginx.conf
 
-**OR** using reverse proxy (Please, note that reverse proxy advised to be used
-in case of single-user or low-traffic instances.)
+      in there is a configuration section named ``server``:
 
-.. code:: nginx
+      .. code-block:: nginx
 
-    location /searx/static {
-            alias /usr/local/searx/searx/static;
-    }
+         server {
+             listen       80;
+             server_name  localhost;
+             # ...
+         }
 
-    location /searx {
-        proxy_pass http://127.0.0.1:8888;
-        proxy_set_header Host $host;
-        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header X-Scheme $scheme;
-        proxy_set_header X-Script-Name /searx;
-        proxy_buffering off;
-    }
+   .. group-tab::  Fedora / RHEL
 
-Enable ``base_url`` in ``searx/settings.yml``
+      .. code-block:: sh
 
-.. code:: yaml
+         less /etc/nginx/nginx.conf
 
-    base_url : http://your.domain.tld/searx/
+      there is a line including site configurations from:
 
-Restart service:
+      .. code:: nginx
 
-.. tabs::
+          include /etc/nginx/conf.d/*.conf;
 
-   .. group-tab:: Ubuntu / debian
+.. _nginx searx site:
 
-      .. code:: sh
-
-         sudo -H systemctl restart nginx
-         sudo -H systemctl restart uwsgi
-
-
-disable logs
+A searx site
 ============
 
-For better privacy you can disable nginx logs about searx.  How to proceed:
-below ``uwsgi_pass`` in ``/etc/nginx/sites-available/default`` add:
+.. sidebar:: public to the internet?
 
-.. code:: nginx
+   If your searx instance is public, stop here and first install :ref:`filtron
+   reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
+   :ref:`installation scripts`.
+
+Now you have to create a configuration for the searx site.  If nginx_ is new to
+you, the `nginx beginners guide`_ is a good starting point and the `Getting
+Started wiki`_ is always a good resource *to keep in the pocket*.
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      Create configuration at ``/etc/nginx/sites-available/searx`` and place a
+      symlink to sites-enabled:
+
+      .. code:: sh
+
+         sudo -H ln -s /etc/nginx/sites-available/searx /etc/nginx/sites-enabled/searx
+
+   .. group-tab:: Arch Linux
+
+      In the ``/etc/nginx/nginx.conf`` file, replace the configuration section
+      named ``server``.
+
+   .. group-tab::  Fedora / RHEL
+
+      Create configuration at ``/etc/nginx/conf.d/searx`` and place a
+      symlink to sites-enabled:
+
+.. tabs::
+
+
+   .. group-tab:: filtron at ``/`` & ``/morty``
+
+      Use this setup, if your instance is public to the internet:
+
+      .. code:: nginx
+
+         location / {
+             proxy_set_header   Host    $http_host;
+             proxy_set_header   X-Real-IP $remote_addr;
+             proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
+             proxy_set_header   X-Scheme $scheme;
+             proxy_pass         http://127.0.0.1:4004/;
+         }
+
+      .. code:: nginx
+
+         location /morty {
+             proxy_set_header   Host    $http_host;
+             proxy_set_header   X-Real-IP $remote_addr;
+             proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
+             proxy_set_header   X-Scheme $scheme;
+             proxy_pass         http://127.0.0.1:3000/;
+         }
+
+      For a fully result proxification add :ref:`morty's <searx_morty>` public
+      URL to your :origin:`searx/settings.yml`:
+
+      .. code:: yaml
+
+         result_proxy:
+             # replace searx.example.com with your server's public name
+             url : http://searx.example.com/
+
+
+   .. group-tab:: searx at ``/``
+
+      Use this setup only, if your instance is **NOT** public to the internet:
+
+      .. code:: nginx
+
+         server {
+             listen 80;
+             listen [::]:80;
+
+             # replace searx.example.com with your server's public name
+             server_name searx.example.com;
+
+             root /usr/local/searx/searx;
+
+             location /static {
+             }
+
+             location / {
+                 include uwsgi_params;
+                 uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+             }
+         }
+
+   .. group-tab:: searx at ``/searx``
+
+      Use this setup only, if your instance is **NOT** public to the internet:
+
+      .. code:: nginx
+
+          location /searx/static {
+                  alias /usr/local/searx/searx/static;
+          }
+
+          location /searx {
+                  uwsgi_param SCRIPT_NAME /searx;
+                  include uwsgi_params;
+                  uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+          }
+
+
+      **OR** using reverse proxy.  Please, note that reverse proxy advised to be
+      used in case of single-user or low-traffic instances.
+
+      .. code:: nginx
+
+          location /searx/static {
+                  alias /usr/local/searx/searx/static;
+          }
+
+          location /searx {
+              proxy_pass http://127.0.0.1:8888;
+              proxy_set_header Host $host;
+              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+              proxy_set_header X-Scheme $scheme;
+              proxy_set_header X-Script-Name /searx;
+              proxy_buffering off;
+          }
+
+      Enable ``base_url`` in :origin:`searx/settings.yml`
+
+      .. code:: yaml
+
+         server:
+             # replace searx.example.com with your server's public name
+             base_url : http://searx.example.com/searx/
 
-    access_log /dev/null;
-    error_log /dev/null;
 
 Restart service:
 
@@ -139,3 +246,33 @@ Restart service:
       .. code:: sh
 
          sudo -H systemctl restart nginx
+         sudo -H systemctl restart uwsgi
+
+   .. group-tab:: Arch Linux
+
+      .. code:: sh
+
+         sudo -H systemctl restart nginx
+         sudo -H systemctl restart uwsgi
+
+   .. group-tab:: Fedora
+
+      .. code:: sh
+
+         sudo -H systemctl restart nginx
+         sudo -H systemctl restart uwsgi
+
+
+Disable logs
+============
+
+For better privacy you can disable nginx logs in ``/etc/nginx/nginx.conf``.
+
+.. code:: nginx
+
+    http {
+        # ...
+        access_log /dev/null;
+        error_log  /dev/null;
+        # ...
+    }
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 330a51fd1..25d7f0ea9 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -7,7 +7,7 @@ Installation
 *You're spoilt for choice*, choose your preferred method of installation.
 
 - :ref:`installation docker`
-- `Installation scripts`_
+- :ref:`installation scripts`
 - :ref:`installation basic`
 
 The :ref:`installation basic` is good enough for intranet usage and it is a
@@ -20,6 +20,7 @@ Therefore, if you do not have any special preferences, its recommend to use the
 :ref:`installation docker` or the `Installation scripts`_ from our :ref:`tooling
 box <toolboxing>` as described below.
 
+.. _installation scripts:
 
 Installation scripts
 ====================
diff --git a/searx/settings.yml b/searx/settings.yml
index 078d95b72..667338e12 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -1,5 +1,5 @@
 general:
-    debug : False # Debug mode, only for development
+    debug : True # Debug mode, only for development
     instance_name : "searx" # displayed name
 
 search:
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 7c7edccb3..f9e6e6b7a 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -192,8 +192,9 @@ main() {
                 if [[ $exit_val -ne 0 ]]; then
                     warn_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
                 else
-                    info_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+                    info_msg "[${_BBlue}${i}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
                 fi
+                echo
             done
             ;;
         install)
diff --git a/utils/searx.sh b/utils/searx.sh
index 03911dff6..a93e36c95 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -668,7 +668,7 @@ enable:    sudo -H ln -s ${uWSGI_APPS_AVAILABLE}/${SEARX_UWSGI_APP} ${uWSGI_APPS
 start:     sudo -H service uwsgi start   ${SEARX_UWSGI_APP%.*}
 restart:   sudo -H service uwsgi restart ${SEARX_UWSGI_APP%.*}
 stop:      sudo -H service uwsgi stop    ${SEARX_UWSGI_APP%.*}
-disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${CONF}
+disable:   sudo -H rm ${uWSGI_APPS_ENABLED}/${SEARX_UWSGI_APP}
 EOF
                 ;;
                 arch-*) cat <<EOF

From 387c6a77691fec514704bdf178b9ab94ad4abb40 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 6 Mar 2020 14:47:00 +0100
Subject: [PATCH 062/125] docs: improve description of uwsgi & ngingx setup

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst                        |  20 +-
 docs/admin/installation-nginx.rst             | 189 ++++++++++++------
 docs/admin/installation-searx.rst             |   2 +
 docs/admin/installation-uwsgi.rst             |   2 +
 docs/admin/morty.rst                          |   2 +-
 docs/utils/filtron.sh.rst                     |   3 +-
 docs/utils/morty.sh.rst                       |   2 +-
 utils/lib.sh                                  |   1 +
 utils/searx.sh                                |   4 +-
 .../etc/uwsgi/apps-archlinux/searx.ini        |  20 +-
 .../etc/uwsgi/apps-available/searx.ini        |  20 +-
 11 files changed, 182 insertions(+), 83 deletions(-)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 2bc663411..785b02261 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -1,5 +1,5 @@
 
-.. _searx_filtron:
+.. _searx filtron:
 
 ==========================
 How to protect an instance
@@ -8,6 +8,8 @@ How to protect an instance
 .. sidebar:: further reading
 
    - :ref:`filtron.sh`
+   - :ref:`nginx searx site`
+
 
 .. contents:: Contents
    :depth: 2
@@ -150,6 +152,8 @@ of:
    ]
 
 
+.. _filtron route request:
+
 Route request through filtron
 =============================
 
@@ -167,12 +171,14 @@ Use it along with ``nginx`` with the following example configuration.
 .. code:: nginx
 
    location / {
-        proxy_set_header   Host    $http_host;
-        proxy_set_header   X-Real-IP $remote_addr;
-        proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
-        proxy_set_header   X-Scheme $scheme;
-        proxy_pass         http://127.0.0.1:4004/;
+       proxy_pass         http://127.0.0.1:4004/;
+
+       proxy_set_header   Host             $http_host;
+       proxy_set_header   X-Real-IP        $remote_addr;
+       proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
+       proxy_set_header   X-Scheme         $scheme;
    }
 
 Requests are coming from port 4004 going through filtron and then forwarded to
-port 8888 where a searx is being run.
+port 8888 where a searx is being run. For a complete setup see: :ref:`nginx
+searx site`.
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 6cd815d1d..0b3ea022f 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -12,6 +12,12 @@ Install with nginx
    http://nginx.org/en/docs/beginners_guide.html
 .. _Getting Started wiki:
    https://www.nginx.com/resources/wiki/start/
+.. _uWSGI support from nginx:
+   https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html
+.. _uwsgi_params:
+   https://uwsgi-docs.readthedocs.io/en/latest/Nginx.html#configuring-nginx
+.. _SCRIPT_NAME:
+   https://werkzeug.palletsprojects.com/en/1.0.x/wsgi/#werkzeug.wsgi.get_script_name
 
 .. contents:: Contents
    :depth: 2
@@ -98,8 +104,8 @@ see a *Fedora Webserver - Test Page*.  The test page comes from the default
 
 .. _nginx searx site:
 
-A searx site
-============
+A nginx searx site
+==================
 
 .. sidebar:: public to the internet?
 
@@ -134,33 +140,42 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
 .. tabs::
 
+   .. group-tab:: searx via filtron plus morty
 
-   .. group-tab:: filtron at ``/`` & ``/morty``
-
-      Use this setup, if your instance is public to the internet:
+      Use this setup, if your instance is public to the internet, compare
+      figure: :ref:`architecture <arch public>`.  Configure a reverse proxy for
+      :ref:`filtron <filtron.sh>`, listening on *localhost 4004* (:ref:`filtron
+      route request`):
 
       .. code:: nginx
 
          location / {
-             proxy_set_header   Host    $http_host;
-             proxy_set_header   X-Real-IP $remote_addr;
-             proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
-             proxy_set_header   X-Scheme $scheme;
              proxy_pass         http://127.0.0.1:4004/;
+
+             proxy_set_header   Host             $http_host;
+             proxy_set_header   X-Real-IP        $remote_addr;
+             proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
+             proxy_set_header   X-Scheme         $scheme;
          }
 
+
+      Configure reverse proxy for :ref:`morty <searx morty>`, listening on
+      *localhost 3000*:
+
       .. code:: nginx
 
          location /morty {
-             proxy_set_header   Host    $http_host;
-             proxy_set_header   X-Real-IP $remote_addr;
-             proxy_set_header   X-Forwarded-For $proxy_add_x_forwarded_for;
-             proxy_set_header   X-Scheme $scheme;
              proxy_pass         http://127.0.0.1:3000/;
+
+             proxy_set_header   Host             $http_host;
+             proxy_set_header   X-Real-IP        $remote_addr;
+             proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
+             proxy_set_header   X-Scheme         $scheme;
          }
 
-      For a fully result proxification add :ref:`morty's <searx_morty>` public
-      URL to your :origin:`searx/settings.yml`:
+      Note that reverse proxy advised to be used in case of single-user or
+      low-traffic instances.  For a fully result proxification add :ref:`morty's
+      <searx morty>` **public URL** to your :origin:`searx/settings.yml`:
 
       .. code:: yaml
 
@@ -169,58 +184,20 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
              url : http://searx.example.com/
 
 
-   .. group-tab:: searx at ``/``
+   .. group-tab:: proxy or uWSGI
 
-      Use this setup only, if your instance is **NOT** public to the internet:
+      Be warned, with this setup, your Instance isn't :ref:`protected <searx
+      filtron>`.  Nevertheless it is good enough for intranet usage and it is a
+      excellent example of; *how different services can be set up*.  The next
+      example shows a reverse proxy configuration wrapping the :ref:`searx-uWSGI
+      application <uwsgi configuration>`, listening on ``http =
+      127.0.0.1:8888``.
 
       .. code:: nginx
 
-         server {
-             listen 80;
-             listen [::]:80;
-
-             # replace searx.example.com with your server's public name
-             server_name searx.example.com;
-
-             root /usr/local/searx/searx;
-
-             location /static {
-             }
-
-             location / {
-                 include uwsgi_params;
-                 uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-             }
-         }
-
-   .. group-tab:: searx at ``/searx``
-
-      Use this setup only, if your instance is **NOT** public to the internet:
-
-      .. code:: nginx
-
-          location /searx/static {
-                  alias /usr/local/searx/searx/static;
-          }
-
-          location /searx {
-                  uwsgi_param SCRIPT_NAME /searx;
-                  include uwsgi_params;
-                  uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-          }
-
-
-      **OR** using reverse proxy.  Please, note that reverse proxy advised to be
-      used in case of single-user or low-traffic instances.
-
-      .. code:: nginx
-
-          location /searx/static {
-                  alias /usr/local/searx/searx/static;
-          }
-
-          location /searx {
+          location / {
               proxy_pass http://127.0.0.1:8888;
+
               proxy_set_header Host $host;
               proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
               proxy_set_header X-Scheme $scheme;
@@ -228,7 +205,87 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
               proxy_buffering off;
           }
 
-      Enable ``base_url`` in :origin:`searx/settings.yml`
+      Alternatively you can use the `uWSGI support from nginx`_ via unix
+      sockets.  For socket communication, you have to activate ``socket =
+      /run/uwsgi/app/searx/socket`` and comment out the ``http =
+      127.0.0.1:8888`` configuration in your :ref:`uwsgi ini file <uwsgi
+      configuration>`.
+
+      The example shows a nginx virtual ``server`` configuration, listening on
+      port 80 (IPv4 and IPv6 http://[::]:80).  The uWSGI app is configured at
+      location ``/`` by importing the `uwsgi_params`_ and passing requests to
+      the uWSGI socket (``uwsgi_pass``).  The ``server``\'s root points to the
+      :ref:`searx-src clone <searx-src>` and wraps directly the
+      :origin:`searx/static/` content at ``location /static``.
+
+      .. code:: nginx
+
+         server {
+             # replace searx.example.com with your server's public name
+             server_name searx.example.com;
+
+             listen 80;
+             listen [::]:80;
+
+             location / {
+                 include uwsgi_params;
+                 uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+             }
+
+	     root /usr/local/searx/searx-src/searx;
+             location /static { }
+         }
+
+      If not already exists, create a folder for the unix sockets, which can be
+      used by the searx account:
+
+      .. code:: bash
+
+	 mkdir -p /run/uwsgi/app/searx/
+	 sudo -H chown -R searx:searx /run/uwsgi/app/searx/
+
+   .. group-tab:: subdirectory URL
+
+      Be warned, with these setups, your Instance isn't :ref:`protected <searx
+      filtron>`.  The examples are just here to demonstrate how to export the
+      searx application from a subdirectory URL
+      http://searx.example.com/searx/\.
+
+      .. code:: nginx
+
+          location /searx {
+              proxy_pass http://127.0.0.1:8888;
+
+              proxy_set_header Host $host;
+              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+              proxy_set_header X-Scheme $scheme;
+              proxy_set_header X-Script-Name /searx;
+              proxy_buffering off;
+          }
+
+          location /searx/static {
+              alias /usr/local/searx/searx-src/searx/static;
+          }
+
+      The ``X-Script-Name /searx`` is needed by the searx implementation to
+      calculate relative URLs correct.  The next example shows a uWSGI
+      configuration.  Since there are no HTTP headers in a (u)WSGI protocol, the
+      value is shipped via the SCRIPT_NAME_ in the WSGI environment.
+
+      .. code:: nginx
+
+          location /searx/static {
+              alias /usr/local/searx/searx-src/searx;
+          }
+
+          location /searx {
+              uwsgi_param SCRIPT_NAME /searx;
+              include uwsgi_params;
+              uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+          }
+
+      For searx to work correctly the ``base_url`` must be set in the
+      :origin:`searx/settings.yml`.
 
       .. code:: yaml
 
@@ -246,21 +303,21 @@ Restart service:
       .. code:: sh
 
          sudo -H systemctl restart nginx
-         sudo -H systemctl restart uwsgi
+         sudo -H service uwsgi restart searx
 
    .. group-tab:: Arch Linux
 
       .. code:: sh
 
          sudo -H systemctl restart nginx
-         sudo -H systemctl restart uwsgi
+         sudo -H systemctl restart uwsgi@searx
 
    .. group-tab:: Fedora
 
       .. code:: sh
 
          sudo -H systemctl restart nginx
-         sudo -H systemctl restart uwsgi
+         sudo -H touch /etc/uwsgi.d/searx.ini
 
 
 Disable logs
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index 88562f3e3..91891d85c 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -32,6 +32,8 @@ Create user
    :start-after: START create user
    :end-before: END create user
 
+.. _searx-src:
+
 install searx & dependencies
 ============================
 
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
index 7996bf937..67a8e127a 100644
--- a/docs/admin/installation-uwsgi.rst
+++ b/docs/admin/installation-uwsgi.rst
@@ -92,6 +92,8 @@ could control specific instance(s) by issuing::
 
 My experience is, that this command is a bit buggy.
 
+.. _uwsgi configuration:
+
 Alltogether
 ===========
 
diff --git a/docs/admin/morty.rst b/docs/admin/morty.rst
index 9af9b6ae9..48ff5b9c4 100644
--- a/docs/admin/morty.rst
+++ b/docs/admin/morty.rst
@@ -1,5 +1,5 @@
 
-.. _searx_morty:
+.. _searx morty:
 
 =========================
 How to setup result proxy
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index 1bba1b3e4..e6d7d6251 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -8,7 +8,7 @@
 .. sidebar:: further reading
 
    - :ref:`installation`
-   - :ref:`searx_filtron`
+   - :ref:`searx filtron`
    - :ref:`architecture`
 
 .. _Go: https://golang.org/
@@ -64,6 +64,7 @@ To install searx in your public HTTP server use:
 		  $ sudo -H a2enmod proxy
 		  $ sudo -H a2enmod proxy_http
 
+.. _filtron.sh overview:
 
 Overview
 ========
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 9997ffec5..5ab6ee982 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -26,7 +26,7 @@ into this user account.
 
 .. hint::
 
-   To add morty to your searx instance read chapter :reF:`searx_morty`.
+   To add morty to your searx instance read chapter :ref:`searx morty`.
 
 
 Overview
diff --git a/utils/lib.sh b/utils/lib.sh
index f0c710311..09883df3b 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -881,6 +881,7 @@ uWSGI_enable_app() {
             mkdir -p "${uWSGI_APPS_ENABLED}"
             rm -f "${uWSGI_APPS_ENABLED}/${CONF}"
             ln -s "${uWSGI_APPS_AVAILABLE}/${CONF}" "${uWSGI_APPS_ENABLED}/${CONF}"
+            systemctl enable "uwsgi@${CONF%.*}"
             info_msg "enabled uWSGI app: ${CONF} (restart required)"
             ;;
         fedora-*)
diff --git a/utils/searx.sh b/utils/searx.sh
index a93e36c95..9d73f46e2 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -88,7 +88,7 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|searx-src|pyenv|apache]
+  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
@@ -104,6 +104,7 @@ install / remove
   :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
   :searx-src:  clone $SEARX_GIT_URL
   :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
+  :uwsgi:      install searx uWSGI application
   :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
 update searx
   Update searx installation ($SERVICE_HOME)
@@ -165,6 +166,7 @@ main() {
                 pyenv) create_pyenv ;;
                 searx-src) clone_searx ;;
                 settings) install_settings ;;
+                uwsgi) install_searx_uwsgi;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index 78ad50443..f96554060 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -57,10 +57,24 @@ virtualenv = ${SEARX_PYENV}
 pythonpath = ${SEARX_SRC}
 
 
-# plugin http
-# -----------
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
 #
 # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
-
 # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
 http = ${SEARX_INTERNAL_URL}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+#   mkdir -p /run/uwsgi/app/searx/socket
+#   chmod -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx/socket
+#
+# socket = /run/uwsgi/app/searx/socket
\ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index bc62e5864..4f8674012 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -56,10 +56,24 @@ virtualenv = ${SEARX_PYENV}
 pythonpath = ${SEARX_SRC}
 
 
-# plugin http
-# -----------
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
 #
 # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
-
 # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
 http = ${SEARX_INTERNAL_URL}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+#   mkdir -p /run/uwsgi/app/searx/socket
+#   chmod -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx/socket
+#
+# socket = /run/uwsgi/app/searx/socket
\ No newline at end of file

From 857a1458b18476e18f1df9e338ba270c4ff2e35d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 6 Mar 2020 15:04:12 +0100
Subject: [PATCH 063/125] docs: for example URLs use iana's example.org

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-nginx.rst | 15 +++++++--------
 1 file changed, 7 insertions(+), 8 deletions(-)

diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 0b3ea022f..264bdacda 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -180,8 +180,8 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
       .. code:: yaml
 
          result_proxy:
-             # replace searx.example.com with your server's public name
-             url : http://searx.example.com/
+             # replace example.org with your server's public name
+             url : https://example.org/
 
 
    .. group-tab:: proxy or uWSGI
@@ -221,8 +221,8 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
       .. code:: nginx
 
          server {
-             # replace searx.example.com with your server's public name
-             server_name searx.example.com;
+             # replace example.org with your server's public name
+             server_name example.org;
 
              listen 80;
              listen [::]:80;
@@ -248,8 +248,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       Be warned, with these setups, your Instance isn't :ref:`protected <searx
       filtron>`.  The examples are just here to demonstrate how to export the
-      searx application from a subdirectory URL
-      http://searx.example.com/searx/\.
+      searx application from a subdirectory URL ``https://example.org/searx/``.
 
       .. code:: nginx
 
@@ -290,8 +289,8 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
       .. code:: yaml
 
          server:
-             # replace searx.example.com with your server's public name
-             base_url : http://searx.example.com/searx/
+             # replace example.org with your server's public name
+             base_url : https://example.org/searx/
 
 
 Restart service:

From c15337850e64562c376e5de57d8809a3a05b8a5d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 6 Mar 2020 22:06:19 +0100
Subject: [PATCH 064/125] fix: minor typos

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst                             | 2 +-
 docs/admin/installation-nginx.rst                  | 4 ++--
 utils/templates/etc/uwsgi/apps-archlinux/searx.ini | 2 +-
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 785b02261..970ce25d2 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -18,7 +18,7 @@ How to protect an instance
 
 .. _filtron: https://github.com/asciimoo/filtron
 
-Searx depens on external search services.  To avoid the abuse of these services
+Searx depends on external search services.  To avoid the abuse of these services
 it is advised to limit the number of requests processed by searx.
 
 An application firewall, filtron_ solves exactly this problem.  Filtron is just
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 264bdacda..2097d7daf 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -186,7 +186,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
    .. group-tab:: proxy or uWSGI
 
-      Be warned, with this setup, your Instance isn't :ref:`protected <searx
+      Be warned, with this setup, your instance isn't :ref:`protected <searx
       filtron>`.  Nevertheless it is good enough for intranet usage and it is a
       excellent example of; *how different services can be set up*.  The next
       example shows a reverse proxy configuration wrapping the :ref:`searx-uWSGI
@@ -246,7 +246,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
    .. group-tab:: subdirectory URL
 
-      Be warned, with these setups, your Instance isn't :ref:`protected <searx
+      Be warned, with these setups, your instance isn't :ref:`protected <searx
       filtron>`.  The examples are just here to demonstrate how to export the
       searx application from a subdirectory URL ``https://example.org/searx/``.
 
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index f96554060..66653fc0e 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -17,7 +17,7 @@ env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
 
 # disable logging for privacy
 logger = systemd
-disable-logging = false
+disable-logging = true
 
 # The right granted on the created socket
 chmod-socket = 666

From b1e90cff23eae2181d2b430e77471f488947d1a9 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 7 Mar 2020 20:24:08 +0100
Subject: [PATCH 065/125] LXC: separate lxc-suite from lxc & improved command
 line.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile            |   2 +-
 utils/filtron.sh    |   2 +-
 utils/lib.sh        |  65 +++++++-
 utils/lxc-searx.env |  62 ++++++++
 utils/lxc.env       |  14 --
 utils/lxc.sh        | 367 ++++++++++++++++++++++++++------------------
 utils/morty.sh      |   2 +-
 utils/searx.sh      |  12 +-
 8 files changed, 345 insertions(+), 181 deletions(-)
 create mode 100644 utils/lxc-searx.env
 delete mode 100644 utils/lxc.env

diff --git a/Makefile b/Makefile
index 63e7e97f0..822adb1ae 100644
--- a/Makefile
+++ b/Makefile
@@ -110,7 +110,7 @@ test.sh:
 	shellcheck -x utils/searx.sh
 	shellcheck -x utils/morty.sh
 	shellcheck -x utils/lxc.sh
-	shellcheck -x utils/lxc.env
+	shellcheck -x utils/lxc-searx.env
 	shellcheck -x .config.sh
 
 test.pep8: pyenvinstall
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 89971d61d..537cea145 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -6,7 +6,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
-source "${REPO_ROOT}/utils/lxc.env"
+source "${REPO_ROOT}/utils/lxc-searx.env"
 
 # ----------------------------------------------------------------------------
 # config
diff --git a/utils/lib.sh b/utils/lib.sh
index 09883df3b..26a26d750 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1062,14 +1062,69 @@ EOF
 # containers
 # ----------
 
-is_container() {
-    sudo_or_exit
-
-    # usage:  is_container && echo "process running inside a LXC container"
-    #         is_container || echo "process is not running inside a LXC container"
+in_container() {
+    # Test if shell runs in a container.
     #
     # hint:   Reads init process environment, therefore root access is required!
+    #
+    # usage:  in_container && echo "process running inside a LXC container"
+    #         in_container || echo "process is not running inside a LXC container"
+    #
 
+    sudo_or_exit
     # to be safe, take a look at the environment of process 1 (/sbin/init)
     grep -qa 'container=lxc' /proc/1/environ
 }
+
+
+lxc_exists(){
+
+    # usage: lxc_exists <name> || echo "container <name> does not exists"
+
+    lxc info "$1" &>/dev/null
+}
+
+lxc_image_exists(){
+    # usage: lxc_image_exists <alias> || echo "image <alias> does locally not exists"
+
+    lxc image info "local:$1" &>/dev/null
+
+}
+
+lxc_delete_container() {
+
+    #  usage: lxc_delete_container <container-name>
+
+    if lxc info "$1" &>/dev/null; then
+        info_msg "stop & delete instance ${_BBlue}${1}${_creset}"
+        lxc stop "$1" &>/dev/null
+        lxc delete "$1" | prefix_stdout
+    else
+        warn_msg "instance '$1' does not exist / can't delete :o"
+    fi
+}
+
+lxc_delete_local_image() {
+
+    #  usage: lxc_delete_local_image <container-name>
+
+    info_msg "delete image 'local:$i'"
+    lxc image delete "local:$i"
+}
+
+
+# IP
+# --
+
+global_IPs(){
+    # usage: global_IPS
+    #
+    # print list of host's SCOPE global addresses and adapters e.g::
+    #
+    #   $ global_IPs
+    #   enp4s0|192.168.1.127
+    #   lxdbr0|10.246.86.1
+    #   lxdbr0|fd42:8c58:2cd:b73f::1
+
+    ip -o addr show | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\1|\2/p'
+}
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
new file mode 100644
index 000000000..b088ca792
--- /dev/null
+++ b/utils/lxc-searx.env
@@ -0,0 +1,62 @@
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+# shellcheck shell=bash
+
+# This file is a setup of a LXC suite.  It is sourced from different context, do
+# not manipulate the environment directly, implement functions and manipulate
+# environment only is subshells!
+
+# ----------------------------------------------------------------------------
+# config
+# ----------------------------------------------------------------------------
+
+lxc_set_suite_env() {
+    # name of https://images.linuxcontainers.org
+    export LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
+    export LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-searx}"
+    export LXC_SUITE=(
+        # end of standard support see https://wiki.ubuntu.com/Releases
+        "$LINUXCONTAINERS_ORG_NAME:ubuntu/16.04"  "ubu1604" # April 2021
+        "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804" # April 2023
+        "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.10"  "ubu1910" # July 2020
+        "$LINUXCONTAINERS_ORG_NAME:ubuntu/20.04"  "ubu2004" # future (EOL 2030)
+
+        # EOL see https://fedoraproject.org/wiki/Releases
+        "$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
+
+        # rolling releases see https://www.archlinux.org/releng/releases/
+        "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
+    )
+    export FILTRON_API="0.0.0.0:4005"
+    export FILTRON_LISTEN="0.0.0.0:4004"
+    export MORTY_LISTEN="0.0.0.0:3000"
+}
+
+lxc_suite_install() {
+    (
+        lxc_set_suite_env
+        export FORCE_TIMEOUT=0
+        "${LXC_REPO_ROOT}/utils/searx.sh"   install all
+        "${LXC_REPO_ROOT}/utils/morty.sh"   install all
+        "${LXC_REPO_ROOT}/utils/filtron.sh" install all
+
+        rst_title "suite installation finished ($(hostname))" part
+        lxc_suite_info
+        echo
+    )
+}
+
+lxc_suite_info() {
+    (
+        lxc_set_suite_env
+        for ip in $(global_IPs) ; do
+            if [[ $ip =~ .*:.* ]]; then
+                info_msg "(${ip%|*}) IPv6:     http://[${ip#*|}]"
+            else
+                # IPv4:
+                info_msg "(${ip%|*}) filtron:  http://${ip#*|}:4004/"
+                info_msg "(${ip%|*}) morty:    http://${ip#*|}:3000/"
+            fi
+        done
+    )
+}
diff --git a/utils/lxc.env b/utils/lxc.env
deleted file mode 100644
index d470f256c..000000000
--- a/utils/lxc.env
+++ /dev/null
@@ -1,14 +0,0 @@
-# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
-# SPDX-License-Identifier: AGPL-3.0-or-later
-
-# Herein we place all, what is needed to know when inside containers.  Its
-# sourced from different context --> do not manipulate the environment directly,
-# implement functions!
-
-# shellcheck shell=sh
-
-searx_suite_set_env() {
-    export FILTRON_API="0.0.0.0:4005"
-    export FILTRON_LISTEN="0.0.0.0:4004"
-    export MORTY_LISTEN="0.0.0.0:3000"
-}
diff --git a/utils/lxc.sh b/utils/lxc.sh
index f9e6e6b7a..b5ae59a7b 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -5,7 +5,11 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
-source "${REPO_ROOT}/utils/lxc.env"
+
+# load environment of the LXC suite
+LXC_ENV="${LXC_ENV:-${REPO_ROOT}/utils/lxc-searx.env}"
+source "$LXC_ENV"
+lxc_set_suite_env
 
 # ----------------------------------------------------------------------------
 # config
@@ -14,33 +18,27 @@ source "${REPO_ROOT}/utils/lxc.env"
 # read also:
 # - https://lxd.readthedocs.io/en/latest/
 
-# name of https://images.linuxcontainers.org
-LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
-HOST_PREFIX="${HOST_PREFIX:-searx}"
+LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-test}"
 
 # where all folders from HOST are mounted
 LXC_SHARE_FOLDER="/share"
 LXC_REPO_ROOT="${LXC_SHARE_FOLDER}/$(basename "${REPO_ROOT}")"
 
-TEST_IMAGES=(
-    "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804"
-    "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.04"  "ubu1904"
-    "$LINUXCONTAINERS_ORG_NAME:ubuntu/19.10"  "ubu1910"
-    "$LINUXCONTAINERS_ORG_NAME:ubuntu/20.04"  "ubu2004"
-    "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
-    "$LINUXCONTAINERS_ORG_NAME:fedora/31"     "fedora31"
-)
-
-ubu1804_boilerplate="
+ubu1604_boilerplate="
 export DEBIAN_FRONTEND=noninteractive
 apt-get update -y
 apt-get upgrade -y
 apt-get install -y git curl wget
 "
+ubu1804_boilerplate="$ubu1604_boilerplate"
 ubu1904_boilerplate="$ubu1804_boilerplate"
 ubu1910_boilerplate="$ubu1904_boilerplate"
+
 # shellcheck disable=SC2034
-ubu2004_boilerplate="$ubu1910_boilerplate"
+ubu2004_boilerplate="
+$ubu1910_boilerplate
+echo 'Set disable_coredump false' >> /etc/sudo.conf
+"
 
 # shellcheck disable=SC2034
 archlinux_boilerplate="
@@ -57,11 +55,13 @@ echo 'Set disable_coredump false' >> /etc/sudo.conf
 "
 
 REMOTE_IMAGES=()
+CONTAINERS=()
 LOCAL_IMAGES=()
 
-for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
-    REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${TEST_IMAGES[i]}")
-    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${HOST_PREFIX}-${TEST_IMAGES[i+1]}")
+for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+    REMOTE_IMAGES=("${REMOTE_IMAGES[@]}" "${LXC_SUITE[i]}")
+    CONTAINERS=("${CONTAINERS[@]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}")
+    LOCAL_IMAGES=("${LOCAL_IMAGES[@]}" "${LXC_SUITE[i+1]}")
 done
 
 HOST_USER="${SUDO_USER:-$USER}"
@@ -71,36 +71,48 @@ HOST_GROUP_ID=$(id -g "${HOST_USER}")
 # ----------------------------------------------------------------------------
 usage() {
 # ----------------------------------------------------------------------------
-
+    _cmd="$(basename "$0")"
     cat <<EOF
 
 usage::
 
-  $(basename "$0") build        [containers]
-  $(basename "$0") install      [searx-suite]
-  $(basename "$0") remove       [containers|subordinate]
-  $(basename "$0") [start|stop] [containers|<container-name>]
-  $(basename "$0") show         [info|config|searx-suite]
-  $(basename "$0") cmd          ...
+  $_cmd build        [containers]
+  $_cmd copy         [images]
+  $_cmd remove       [containers|<name>|images|subordinate]
+  $_cmd add          [subordinate]
+  $_cmd [start|stop] [containers|<name>]
+  $_cmd show         [info|config|suite|images]
+  $_cmd cmd          [--|<name>] ...
+  $_cmd install      [suite]
 
-build / remove
-  :containers:   build & launch (or remove) all LXC containers
+build
+  :containers:   build & launch all LXC containers of the suite
+copy:
+  :images:       copy remote images of the suite into local storage
+remove
+  :containers:   delete all 'containers' or only <container-name>
+  :images:       delete local images of the suite
 add / remove
-  :subordinate:  lxd permission to map ${HOST_USER}'s user/group id through
+  :subordinate:  LXD permission to map ${HOST_USER}'s user/group id through
 start/stop
-  :containers:   start/stop of all 'containers' or only <container-name>
+  :containers:   start/stop all 'containers' from the suite
+  :<name>:       start/stop conatiner <name> from suite
 show
-  :info:         show info of all containers
-  :config:       show config of all containers
-  :searx-suite:  show searx-suite services of all containers
-cmd ...
-  run commandline ... in all containers
+  :info:         show info of all the containers from LXC suite
+  :config:       show config of all the containers from the LXC suite
+  :suite:        show services of all the containers from the LXC suite
+  :images:       show information of local images
+cmd
+  --             run command ... in all containers of the LXC suite
+  :<name>:       run command ... in container <name>
 install
-  :searx-suite:  install searx suite, includes morty & filtron
+  :suite:        install LXC suite, includes morty & filtron
 
-all LXC containers:
-  ${LOCAL_IMAGES[@]}
+Images of the LXC suite:
+$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
 
+Containers of the LXC suite:
+$(echo "  ${CONTAINERS[*]}" | $FMT)
 EOF
     [ -n "${1+x}" ] &&  err_msg "$1"
 }
@@ -122,10 +134,12 @@ main() {
     local exit_val
     local _usage="unknown or missing $1 command $2"
 
-    if [[ ! $1 == __* ]] && ! required_commands lxc; then
-        lxd_info
-        exit 42
+    # don't check prerequisite when in recursion
+    if [[ ! $1 == __* ]]; then
+        ! required_commands lxc && lxd_info && exit 42
+        [[ -z $LXC_SUITE ]] && err_msg "missing LXC_SUITE" && exit 42 
     fi
+
     case $1 in
         --source-only)  ;;
         -h|--help) usage; exit 0;;
@@ -133,16 +147,28 @@ main() {
         build)
             sudo_or_exit
             case $2 in
-                containers) build_instances ;;
+                ''|containers) build_instances ;;
+                *) usage "$_usage"; exit 42;;
+            esac
+            ;;
+        copy)
+            case $2 in
+                ''|images) lxc_copy_images_localy;;
                 *) usage "$_usage"; exit 42;;
             esac
             ;;
         remove)
             sudo_or_exit
             case $2 in
-                containers) remove_instances ;;
+                ''|containers) remove_instances ;;
+                images) lxc_delete_images_localy ;;
                 subordinate) echo; del_subordinate_ids ;;
-                *) usage "$_usage"; exit 42;;
+                ${LXC_HOST_PREFIX}-*)
+                    if ask_yn "Do you really want to delete conatiner $2"; then
+                        lxc_delete_container "$2"
+                    fi
+                    ;;
+                *) usage "unknown (or mising) container <name> $2"; exit 42;;
             esac
             ;;
         add)
@@ -155,116 +181,86 @@ main() {
         start|stop)
             sudo_or_exit
             case $2 in
-                containers)  lxc_cmd "$1" ;;
-                *)
+                ''|containers)  lxc_cmd "$1" ;;
+                ${LXC_HOST_PREFIX}-*)
                     info_msg "lxc $1 $2"
                     lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "
                     ;;
+                *) usage "ukknown or missing container <name> $2"; exit 42;;
             esac
             ;;
         show)
             sudo_or_exit
             case $2 in
-                config) lxc_cmd config show;;
-                info) lxc_cmd info;;
-                searx-suite)
-                    for i in "${LOCAL_IMAGES[@]}"; do
-                        info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install $2${_creset}"
-                        lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show "$2"  | prefix_stdout "[${i}] "
-                    done
+                suite)  show_suite ;;
+                images) show_images ;;
+                config)
+                    rst_title "container configurations"
+                    echo
+                    lxc list "$LXC_HOST_PREFIX-"
+                    echo
+                    lxc_cmd config show
+                    ;;
+                info)
+                    rst_title "container info"
+                    echo
+                    lxc_cmd info
                     ;;
                 *) usage "$_usage"; exit 42;;
             esac
             ;;
         __show)
             case $2 in
-                searx-suite) searx_suite_info ;;
+                suite) lxc_suite_info ;;
             esac
             ;;
         cmd)
             sudo_or_exit
             shift
-            for i in "${LOCAL_IMAGES[@]}"; do
-                exit_val=
-                info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${*}${_creset}"
-                lxc exec "${i}" -- "$@"
-                exit_val=$?
-                if [[ $exit_val -ne 0 ]]; then
-                    warn_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
-                else
-                    info_msg "[${_BBlue}${i}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
-                fi
-                echo
-            done
+            case $1 in
+                --)
+                    shift
+                    for name in "${CONTAINERS[@]}"; do
+                        lxc_exec_cmd "${name}" "$@"
+                    done
+                    ;;
+                ${LXC_HOST_PREFIX}-*)
+                    local name=$1
+                    shift
+                    lxc_exec_cmd "${name}" "$@"
+                    ;;
+
+                *) usage "unknown <name>: $1"; exit 42
+                   ;;
+            esac
             ;;
         install)
             sudo_or_exit
             case $2 in
-                searx-suite)
-                    for i in "${LOCAL_IMAGES[@]}"; do
-                        info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install $2${_creset}"
-                        lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"  | prefix_stdout "[${i}] "
-                    done
-                    ;;
-                *) usage "$_usage"; exit 42;;
+                suite) install_suite ;;
+                *) usage "$_usage"; exit 42 ;;
             esac
             ;;
         __install)
             case $2 in
-                searx-suite) searx_suite_install ;;
+                suite) lxc_suite_install ;;
             esac
             ;;
         doc)
             echo
             echo ".. generic utils/lxc.sh documentation"
             ;;
-
-        *)
-            usage "unknown or missing command $1"; exit 42;;
+        -*) usage "unknown option $1"; exit 42;;
+        *)  usage "unknown or missing command $1"; exit 42;;
     esac
 }
 
-searx_suite_install() {
-    (
-        searx_suite_set_env
-        export FORCE_TIMEOUT=0
-        "${LXC_REPO_ROOT}/utils/searx.sh"   install all
-        "${LXC_REPO_ROOT}/utils/morty.sh"   install all
-        "${LXC_REPO_ROOT}/utils/filtron.sh" install all
-
-        rst_title "searx-suite installation finished ($(hostname))" part
-        searx_suite_info
-        echo
-    )
-}
-
-searx_suite_info() {
-    (
-        searx_suite_set_env
-        rst_para "Services of the container $(hostname)"
-        for ip in $(hostname -I); do
-            echo
-            if [[ $ip =~ .*:.* ]]; then
-                :
-                # IPv6: not yet implemented / tested
-                # echo " searx (filtron) --> http://[$ip]:4004/"
-                # echo " morty           --> http://[$ip]:3000/"
-            else
-                # IPv4:
-                echo " searx (filtron) --> http://$ip:4004/"
-                echo " morty           --> http://$ip:3000/"
-            fi
-        done
-    )
-}
 
 build_instances() {
     rst_title "Build LXC instances"
-
-    rst_title "copy images" section
     echo
+    add_subordinate_ids
     lxc_copy_images_localy
-    # lxc image list local: && wait_key
     echo
     rst_title "build containers" section
     echo
@@ -272,64 +268,141 @@ build_instances() {
     lxc_config_containers
     lxc_boilerplate_containers
     echo
-    lxc list "$HOST_PREFIX"
+    lxc list "$LXC_HOST_PREFIX"
 }
 
 remove_instances() {
     rst_title "Remove LXC instances"
-    lxc list "$HOST_PREFIX"
-    echo -en "\\nLXC containers(s)::\\n\\n  ${LOCAL_IMAGES[*]}\\n" | $FMT
-    if ask_yn "Do you really want to delete all images"; then
-        lxc_delete_containers
+    rst_para "existing containers matching ${_BGreen}$LXC_HOST_PREFIX-*${_creset}"
+    echo
+    lxc list "$LXC_HOST_PREFIX-"
+    echo -en "\\n${_BRed}LXC containers to delete::${_creset}\\n\\n  ${CONTAINERS[*]}\\n" | $FMT
+    if ask_yn "Do you really want to delete these conatiners"; then
+        for i in "${CONTAINERS[@]}"; do
+            lxc_delete_container "$i"
+        done
     fi
     echo
-    lxc list "$HOST_PREFIX"
-    # lxc image list local: && wait_key
+    lxc list "$LXC_HOST_PREFIX-"
 }
 
 # images
 # ------
 
 lxc_copy_images_localy() {
-    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
-        if lxc image info "local:${TEST_IMAGES[i+1]}" &>/dev/null; then
-            info_msg "image ${TEST_IMAGES[i]} already copied --> ${TEST_IMAGES[i+1]}"
+    rst_title "copy images" section
+    echo
+    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+        if lxc_image_exists "local:${LXC_SUITE[i+1]}"; then
+            info_msg "image ${LXC_SUITE[i]} already copied --> ${LXC_SUITE[i+1]}"
         else
-            info_msg "copy image locally ${TEST_IMAGES[i]} --> ${TEST_IMAGES[i+1]}"
-            lxc image copy "${TEST_IMAGES[i]}" local: \
-                --alias  "${TEST_IMAGES[i+1]}" | prefix_stdout
+            info_msg "copy image locally ${LXC_SUITE[i]} --> ${LXC_SUITE[i+1]}"
+            lxc image copy "${LXC_SUITE[i]}" local: \
+                --alias  "${LXC_SUITE[i+1]}" | prefix_stdout
         fi
     done
+    # lxc image list local: && wait_key
 }
 
 lxc_delete_images_localy() {
+    rst_title "Delete LXC images"
+    rst_para "local existing images"
     echo
-    for i in "${LOCAL_IMAGES[@]}"; do
-        info_msg "delete image 'local:$i'"
-        lxc image delete "local:$i"
-    done
-    #lxc image list local:
+    lxc image list local:
+    echo -en "\\n${_BRed}LXC images to delete::${_creset}\\n\\n  ${LOCAL_IMAGES[*]}\\n"
+    if ask_yn "Do you really want to delete these images"; then
+        for i in "${LOCAL_IMAGES[@]}"; do
+            lxc_delete_local_image "$i"
+        done
+    fi
+    echo
+    lxc image list local:
 }
 
+show_images(){
+    rst_title "local images"
+    echo
+    lxc image list local:
+    echo -en "\\n${_Green}LXC suite images::${_creset}\\n\\n  ${LOCAL_IMAGES[*]}\\n"
+    wait_key
+    for i in "${LOCAL_IMAGES[@]}"; do
+        if lxc_image_exists "$i"; then
+            info_msg "lxc image info ${_BBlue}${i}${_creset}"
+            lxc image info "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+        else
+            warn_msg "image ${_BBlue}$i${_creset} does not yet exists"
+        fi
+    done
+
+}
+
+
 # container
 # ---------
 
-lxc_cmd() {
-    for i in "${LOCAL_IMAGES[@]}"; do
-        info_msg "lxc $* $i"
-        lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+show_suite(){
+    rst_title "LXC suite ($LXC_HOST_PREFIX-*)"
+    echo
+    lxc list "$LXC_HOST_PREFIX-"
+    echo
+    for i in "${CONTAINERS[@]}"; do
+        if ! lxc_exists "$i"; then
+            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+        else
+            lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
+                | prefix_stdout "[${_BBlue}${i}${_creset}]  "
+        fi
     done
 }
 
+install_suite() {
+    for i in "${CONTAINERS[@]}"; do
+        if ! lxc_exists "$i"; then
+            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+        else
+            info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install suite${_creset}"
+            lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install suite \
+                | prefix_stdout "[${_BBlue}${i}${_creset}]  "
+        fi
+    done
+}
+
+lxc_cmd() {
+    for i in "${CONTAINERS[@]}"; do
+        if ! lxc_exists "$i"; then
+            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+        else
+            info_msg "lxc $* $i"
+            lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+            echo
+        fi
+    done
+}
+
+lxc_exec_cmd() {
+    local name="$1"
+    shift
+    exit_val=
+    info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"
+    lxc exec "${name}" -- "$@"
+    exit_val=$?
+    if [[ $exit_val -ne 0 ]]; then
+        warn_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+    else
+        info_msg "[${_BBlue}${i}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
+    fi
+    echo
+}
+
 lxc_init_containers() {
 
     local image_name
     local container_name
 
-    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
 
-        image_name="${TEST_IMAGES[i+1]}"
-        container_name="${HOST_PREFIX}-${image_name}"
+        image_name="${LXC_SUITE[i+1]}"
+        container_name="${LXC_HOST_PREFIX}-${image_name}"
 
         if lxc info "${container_name}" &>/dev/null; then
             info_msg "container '${container_name}' already exists"
@@ -341,7 +414,7 @@ lxc_init_containers() {
 }
 
 lxc_config_containers() {
-    for i in "${LOCAL_IMAGES[@]}"; do
+    for i in "${CONTAINERS[@]}"; do
         info_msg "[${_BBlue}${i}${_creset}] configure container ..."
 
         info_msg "[${_BBlue}${i}${_creset}] map uid/gid from host to container"
@@ -364,10 +437,10 @@ lxc_boilerplate_containers() {
     local container_name
     local boilerplate_script
 
-    for ((i=0; i<${#TEST_IMAGES[@]}; i+=2)); do
+    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
 
-        image_name="${TEST_IMAGES[i+1]}"
-        container_name="${HOST_PREFIX}-${image_name}"
+        image_name="${LXC_SUITE[i+1]}"
+        container_name="${LXC_HOST_PREFIX}-${image_name}"
         boilerplate_script="${image_name}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
 
@@ -386,18 +459,6 @@ lxc_boilerplate_containers() {
     done
 }
 
-lxc_delete_containers() {
-    for i in "${LOCAL_IMAGES[@]}"; do
-        if lxc info "$i" &>/dev/null; then
-            info_msg "stop & delete instance ${_BBlue}${i}${_creset}"
-            lxc stop "$i" &>/dev/null
-            lxc delete "$i" | prefix_stdout
-        else
-            warn_msg "instance '$i' does not exist / can't delete :o"
-        fi
-    done
-}
-
 # subordinates
 # ------------
 #
diff --git a/utils/morty.sh b/utils/morty.sh
index 143e7e0c8..175bdf019 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -5,7 +5,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
-source "${REPO_ROOT}/utils/lxc.env"
+source "${REPO_ROOT}/utils/lxc-searx.env"
 
 # ----------------------------------------------------------------------------
 # config
diff --git a/utils/searx.sh b/utils/searx.sh
index 9d73f46e2..205c7068d 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -6,7 +6,7 @@
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
-source "${REPO_ROOT}/utils/lxc.env"
+source "${REPO_ROOT}/utils/lxc-searx.env"
 
 # ----------------------------------------------------------------------------
 # config
@@ -544,13 +544,13 @@ EOF
     uWSGI_app_available "$SEARX_UWSGI_APP" \
         || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
 
-    if is_container; then
+    if in_container; then
         warn_msg "runnning inside container ..."
-        for ip in $(hostname -I); do
+        for ip in $(global_IPs); do
             if [[ $ip =~ .*:.* ]]; then
-                info_msg "  public HTTP service (IPv6) --> http://[$ip]"
+                info_msg "  public HTTP service (IPv6) --> http://${ip#*|}"
             else
-                info_msg "  public HTTP service (IPv4) --> http://$ip"
+                info_msg "  public HTTP service (IPv4) --> http://${ip#*|}"
             fi
         done
         warn_msg "SEARX_INTERNAL_URL not available from outside"
@@ -564,7 +564,7 @@ EOF
 
     if ! service_is_available "${PUBLIC_URL}"; then
         warn_msg "Public service at ${PUBLIC_URL} is not available!"
-        if is_container; then
+        if in_container; then
             warn_msg "Check if public name is correct and routed or use the public IP from above."
         fi
     fi

From e7f69b63f1169a60f6f15b827dfe4f5748a41d2c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 8 Mar 2020 02:41:45 +0100
Subject: [PATCH 066/125] LXC: from searx.sh, morty.sh and filtron.sh tests

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh | 18 +++++++++++++----
 utils/lxc.sh     | 38 +++++++++++++++++++++++++-----------
 utils/morty.sh   | 37 ++++++++++++++++++++++++++---------
 utils/searx.sh   | 51 ++++++++++++++++++++++++++++++------------------
 4 files changed, 101 insertions(+), 43 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index 537cea145..85000c592 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -7,6 +7,7 @@
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
 
 # ----------------------------------------------------------------------------
 # config
@@ -329,16 +330,25 @@ EOF
     fi
 
     if ! service_is_available "${PUBLIC_URL}"; then
-        err_msg "Public service at ${PUBLIC_URL} is not available!"
-        wait_key
+        warn_msg "Public service at ${PUBLIC_URL} is not available!"
+        if ! in_container; then
+            warn_msg "Check if public name is correct and routed or use the public IP from above."
+        fi
     fi
 
+    if in_container; then
+        lxc_suite_info
+    else
+        info_msg "public URL   --> ${PUBLIC_URL}"
+        info_msg "internal URL --> http://${FILTRON_LISTEN}"
+    fi
+
+
     local _debug_on
     if ask_yn "Enable filtron debug mode?"; then
         enable_debug
         _debug_on=1
     fi
-
     echo
     systemctl --no-pager -l status "${SERVICE_NAME}"
     echo
@@ -346,7 +356,7 @@ EOF
     info_msg "public URL --> ${PUBLIC_URL}"
     # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
-    read -r -s -n1 -t 2
+    read -r -s -n1 -t 5
     echo
     while true;  do
         trap break 2
diff --git a/utils/lxc.sh b/utils/lxc.sh
index b5ae59a7b..bcee16743 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -108,15 +108,29 @@ cmd
 install
   :suite:        install LXC suite, includes morty & filtron
 
-Images of the LXC suite:
-$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
+EOF
+    usage_images
+    echo
+    usage_containers
+    echo
+    [ -n "${1+x}" ] &&  err_msg "$1"
+}
 
-Containers of the LXC suite:
+usage_containers() {
+    cat <<EOF
+LXC suite containers:
 $(echo "  ${CONTAINERS[*]}" | $FMT)
 EOF
     [ -n "${1+x}" ] &&  err_msg "$1"
 }
 
+usage_images() {
+    cat <<EOF
+LXC suite images:
+$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
+EOF
+}
+
 lxd_info() {
 
     cat <<EOF
@@ -164,11 +178,12 @@ main() {
                 images) lxc_delete_images_localy ;;
                 subordinate) echo; del_subordinate_ids ;;
                 ${LXC_HOST_PREFIX}-*)
+                    ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
                     if ask_yn "Do you really want to delete conatiner $2"; then
                         lxc_delete_container "$2"
                     fi
                     ;;
-                *) usage "unknown (or mising) container <name> $2"; exit 42;;
+                *) usage "uknown or missing container <name> $2"; exit 42;;
             esac
             ;;
         add)
@@ -183,10 +198,11 @@ main() {
             case $2 in
                 ''|containers)  lxc_cmd "$1" ;;
                 ${LXC_HOST_PREFIX}-*)
+                    ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
                     info_msg "lxc $1 $2"
                     lxc "$1" "$2" | prefix_stdout "[${_BBlue}${i}${_creset}] "
                     ;;
-                *) usage "ukknown or missing container <name> $2"; exit 42;;
+                *) usage "uknown or missing container <name> $2"; exit 42;;
             esac
             ;;
         show)
@@ -225,13 +241,12 @@ main() {
                     done
                     ;;
                 ${LXC_HOST_PREFIX}-*)
+                    ! lxc_exists "$1" && usage_containers "unknown container: $1" && exit 42
                     local name=$1
                     shift
                     lxc_exec_cmd "${name}" "$@"
                     ;;
-
-                *) usage "unknown <name>: $1"; exit 42
-                   ;;
+                *) usage "uknown or missing container <name> $2"; exit 42;;
             esac
             ;;
         install)
@@ -351,6 +366,7 @@ show_suite(){
         else
             lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
                 | prefix_stdout "[${_BBlue}${i}${_creset}]  "
+            echo
         fi
     done
 }
@@ -384,12 +400,12 @@ lxc_exec_cmd() {
     shift
     exit_val=
     info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"
-    lxc exec "${name}" -- "$@"
+    lxc exec --cwd "${LXC_REPO_ROOT}" "${name}" -- "$@"
     exit_val=$?
     if [[ $exit_val -ne 0 ]]; then
-        warn_msg "[${_BBlue}${i}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
+        warn_msg "[${_BBlue}${name}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
     else
-        info_msg "[${_BBlue}${i}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
+        info_msg "[${_BBlue}${name}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
     fi
     echo
 }
diff --git a/utils/morty.sh b/utils/morty.sh
index 175bdf019..58de0cc99 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -6,6 +6,7 @@
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
 
 # ----------------------------------------------------------------------------
 # config
@@ -83,7 +84,14 @@ ${DOT_CONFIG#"$REPO_ROOT/"} file::
 
   MORTY_LISTEN :   ${MORTY_LISTEN}
   SERVICE_USER :   ${SERVICE_USER}
+
 EOF
+    if in_container; then
+        lxc_suite_info
+    else
+        info_msg "public URL   --> ${PUBLIC_URL}"
+        info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
+    fi
     info_searx
     [[ -n ${1} ]] &&  err_msg "$1"
 }
@@ -333,21 +341,32 @@ EOF
         wait_key
     fi
 
-    local _debug_on
-    if ask_yn "Enable filtron debug mode?"; then
-        enable_debug
-        _debug_on=1
+    if ! service_is_available "${PUBLIC_URL}"; then
+        warn_msg "Public service at ${PUBLIC_URL} is not available!"
+        if ! in_container; then
+            warn_msg "Check if public name is correct and routed or use the public IP from above."
+        fi
     fi
 
-    echo
-    systemctl --no-pager -l status "${SERVICE_NAME}"
+    if in_container; then
+        lxc_suite_info
+    else
+        info_msg "public URL --> ${PUBLIC_URL_MORTY}"
+        info_msg "morty URL --> http://${MORTY_LISTEN}"
+    fi
+
+    local _debug_on
+    if ask_yn "Enable filtron debug mode (needs reinstall of systemd service)?"; then
+        enable_debug
+        _debug_on=1
+    else
+        systemctl --no-pager -l status "${SERVICE_NAME}"
+    fi
     echo
 
-    info_msg "morty URL --> http://${MORTY_LISTEN}"
-    info_msg "public URL --> ${PUBLIC_URL_MORTY}"
     # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
-    read -r -s -n1 -t 2
+    read -r -s -n1 -t 5
     echo
     while true;  do
         trap break 2
diff --git a/utils/searx.sh b/utils/searx.sh
index 205c7068d..14af32d0f 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -7,6 +7,7 @@
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
+in_container && lxc_set_suite_env
 
 # ----------------------------------------------------------------------------
 # config
@@ -223,6 +224,11 @@ install_all() {
     rst_title "Install $SEARX_INSTANCE_NAME (service)"
     pkg_install "$SEARX_PACKAGES"
     wait_key
+    case $DIST_ID-$DIST_VERS in
+        fedora-*)
+            systemctl enable uwsgi
+            ;;
+    esac
     assert_user
     wait_key
     clone_searx
@@ -545,26 +551,21 @@ EOF
         || err_msg "uWSGI app $SEARX_UWSGI_APP not available!"
 
     if in_container; then
-        warn_msg "runnning inside container ..."
-        for ip in $(global_IPs); do
-            if [[ $ip =~ .*:.* ]]; then
-                info_msg "  public HTTP service (IPv6) --> http://${ip#*|}"
-            else
-                info_msg "  public HTTP service (IPv4) --> http://${ip#*|}"
-            fi
-        done
-        warn_msg "SEARX_INTERNAL_URL not available from outside"
+        lxc_suite_info
+    else
+        info_msg "public URL   --> ${PUBLIC_URL}"
+        info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
     fi
 
     if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
         err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
-        echo -e "${_Green}stop with [${_BCyan}CTRL-C${_Green}] or .."
-        wait_key
+        MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue"\
+           wait_key
     fi
 
     if ! service_is_available "${PUBLIC_URL}"; then
         warn_msg "Public service at ${PUBLIC_URL} is not available!"
-        if in_container; then
+        if ! in_container; then
             warn_msg "Check if public name is correct and routed or use the public IP from above."
         fi
     fi
@@ -575,19 +576,31 @@ EOF
         _debug_on=1
     fi
     echo
-    systemctl --no-pager -l status "${SERVICE_NAME}"
-    echo
 
-    info_msg "public URL   --> ${PUBLIC_URL}"
-    info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            systemctl --no-pager -l status "${SERVICE_NAME}"
+            ;;
+        arch-*)
+            systemctl --no-pager -l status "uwsgi@${SERVICE_NAME%.*}"
+            ;;
+        fedora-*)
+            systemctl --no-pager -l status uwsgi
+            ;;
+    esac
+
     # shellcheck disable=SC2059
     printf "// use ${_BCyan}CTRL-C${_creset} to stop monitoring the log"
-    read -r -s -n1 -t 2
+    read -r -s -n1 -t 5
     echo
+
     while true;  do
         trap break 2
-        #journalctl -f -u "${SERVICE_NAME}"
-        tail -f /var/log/uwsgi/app/searx.log
+        case $DIST_ID-$DIST_VERS in
+            ubuntu-*|debian-*) tail -f /var/log/uwsgi/app/searx.log ;;
+            arch-*)  journalctl -f -u "uwsgi@${SERVICE_NAME%.*}" ;;
+            fedora-*)  journalctl -f -u uwsgi ;;
+        esac
     done
 
     if [[ $_debug_on == 1 ]]; then

From d48c7bf6783ba9220c88b3c3ccb7b2ce3721e905 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 8 Mar 2020 18:30:25 +0100
Subject: [PATCH 067/125] LXC: fixed raw.idmap & removed obsolete uid/gid
 subordinate

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 67 ++++++----------------------------------------------
 1 file changed, 7 insertions(+), 60 deletions(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index bcee16743..f9dd453ee 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -78,11 +78,10 @@ usage::
 
   $_cmd build        [containers]
   $_cmd copy         [images]
-  $_cmd remove       [containers|<name>|images|subordinate]
-  $_cmd add          [subordinate]
+  $_cmd remove       [containers|<name>|images]
   $_cmd [start|stop] [containers|<name>]
   $_cmd show         [info|config|suite|images]
-  $_cmd cmd          [--|<name>] ...
+  $_cmd cmd          [--|<name>] '...'
   $_cmd install      [suite]
 
 build
@@ -92,8 +91,6 @@ copy:
 remove
   :containers:   delete all 'containers' or only <container-name>
   :images:       delete local images of the suite
-add / remove
-  :subordinate:  LXD permission to map ${HOST_USER}'s user/group id through
 start/stop
   :containers:   start/stop all 'containers' from the suite
   :<name>:       start/stop conatiner <name> from suite
@@ -103,8 +100,9 @@ show
   :suite:        show services of all the containers from the LXC suite
   :images:       show information of local images
 cmd
-  --             run command ... in all containers of the LXC suite
-  :<name>:       run command ... in container <name>
+  use single qoutes to evaluate in container's bash, e.g. 'echo $(hostname)'
+  --             run command '...' in all containers of the LXC suite
+  :<name>:       run command '...' in container <name>
 install
   :suite:        install LXC suite, includes morty & filtron
 
@@ -176,7 +174,6 @@ main() {
             case $2 in
                 ''|containers) remove_instances ;;
                 images) lxc_delete_images_localy ;;
-                subordinate) echo; del_subordinate_ids ;;
                 ${LXC_HOST_PREFIX}-*)
                     ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
                     if ask_yn "Do you really want to delete conatiner $2"; then
@@ -186,13 +183,6 @@ main() {
                 *) usage "uknown or missing container <name> $2"; exit 42;;
             esac
             ;;
-        add)
-            sudo_or_exit
-            case $2 in
-                subordinate) echo; add_subordinate_ids ;;
-                *) usage "$_usage"; exit 42;;
-            esac
-            ;;
         start|stop)
             sudo_or_exit
             case $2 in
@@ -274,7 +264,6 @@ main() {
 build_instances() {
     rst_title "Build LXC instances"
     echo
-    add_subordinate_ids
     lxc_copy_images_localy
     echo
     rst_title "build containers" section
@@ -400,7 +389,7 @@ lxc_exec_cmd() {
     shift
     exit_val=
     info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"
-    lxc exec --cwd "${LXC_REPO_ROOT}" "${name}" -- "$@"
+    lxc exec --cwd "${LXC_REPO_ROOT}" "${name}" -- bash -c "$*"
     exit_val=$?
     if [[ $exit_val -ne 0 ]]; then
         warn_msg "[${_BBlue}${name}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
@@ -435,7 +424,7 @@ lxc_config_containers() {
 
         info_msg "[${_BBlue}${i}${_creset}] map uid/gid from host to container"
         # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
-        echo -e -n "uid $HOST_USER_ID 1000\\ngid $HOST_GROUP_ID 1000"\
+        echo -e -n "uid $HOST_USER_ID 0\\ngid $HOST_GROUP_ID 0"\
             | lxc config set "$i" raw.idmap -
 
         info_msg "[${_BBlue}${i}${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"
@@ -475,48 +464,6 @@ lxc_boilerplate_containers() {
     done
 }
 
-# subordinates
-# ------------
-#
-# see man: subgid(5), subuid(5), https://lxd.readthedocs.io/en/latest/userns-idmap
-#
-# E.g. in the HOST you have uid=1001(user) and/or gid=1001(user) ::
-#
-#   root:1001:1
-#
-# in the CONTAINER::
-#
-#   config:
-#     raw.idmap: |
-#       uid 1001 1000
-#       gid 1001 1000
-
-add_subordinate_ids() {
-    if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
-        info_msg "lxd already has permission to map ${HOST_USER_ID}'s user/group id through"
-    else
-        info_msg "add lxd permission to map ${HOST_USER_ID}'s user/group id through"
-        usermod --add-subuids "${HOST_USER_ID}-${HOST_USER_ID}" \
-                --add-subgids "${HOST_GROUP_ID}-${HOST_GROUP_ID}" root
-    fi
-}
-
-del_subordinate_ids() {
-    local out
-    local exit_val
-    if  grep "root:${HOST_USER_ID}:1" /etc/subuid -qs; then
-        # TODO: root user is always in use by process 1, how can we remove subordinates?
-        info_msg "remove lxd permission to map ${HOST_USER_ID}'s user/group id through"
-        out=$(usermod --del-subuids "${HOST_USER_ID}-${HOST_USER_ID}" --del-subgids "${HOST_GROUP_ID}-${HOST_GROUP_ID}" root 2>&1)
-        exit_val=$?
-        if [ $exit_val -ne 0 ]; then
-            err_msg "$out"
-        fi
-    else
-        info_msg "lxd does not have permission to map ${HOST_USER_ID}'s user/group id through"
-    fi
-}
-
 
 # ----------------------------------------------------------------------------
 main "$@"

From 938bb0255045eb0f5e21027805fa018cd53d6cad Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 8 Mar 2020 18:32:30 +0100
Subject: [PATCH 068/125] searx.sh: add commandline 'install packages'

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/searx.sh | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/utils/searx.sh b/utils/searx.sh
index 14af32d0f..9d33dc31a 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -41,19 +41,22 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 SEARX_PACKAGES_debian="\
 python3-dev python3-babel python3-venv
 uwsgi uwsgi-plugin-python3
-git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
+git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
+shellcheck"
 
 # pacman packages
 SEARX_PACKAGES_arch="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python
-git base-devel libxml2"
+git base-devel libxml2
+shellcheck"
 
 # dnf packages
 SEARX_PACKAGES_fedora="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python3
-git @development-tools libxml2"
+git @development-tools libxml2
+ShellCheck"
 
 case $DIST_ID in
     ubuntu|debian) SEARX_PACKAGES="${SEARX_PACKAGES_debian}" ;;
@@ -89,7 +92,7 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache]
+  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache|packages]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
@@ -107,6 +110,7 @@ install / remove
   :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
   :uwsgi:      install searx uWSGI application
   :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
+  :packages:   install needed packages from OS package manager
 update searx
   Update searx installation ($SERVICE_HOME)
 activate service
@@ -168,6 +172,7 @@ main() {
                 searx-src) clone_searx ;;
                 settings) install_settings ;;
                 uwsgi) install_searx_uwsgi;;
+                packages) pkg_install "$SEARX_PACKAGES" ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)

From 04ad648105a65a27a0654204a461775d178b49ce Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 8 Mar 2020 18:35:38 +0100
Subject: [PATCH 069/125] makefile.python: remove python2 support

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/dev/quickstart.rst |  2 +-
 utils/makefile.python   | 36 +++---------------------------------
 2 files changed, 4 insertions(+), 34 deletions(-)

diff --git a/docs/dev/quickstart.rst b/docs/dev/quickstart.rst
index e40772b3b..0fb22d934 100644
--- a/docs/dev/quickstart.rst
+++ b/docs/dev/quickstart.rst
@@ -27,7 +27,7 @@ searx-ve virtualenv and install the required packages using ``manage.sh``.
     cd ~/myprojects
     git clone https://github.com/asciimoo/searx.git
     cd searx
-    virtualenv searx-ve
+    python3 -m venv searx-ve
     . ./searx-ve/bin/activate
     ./manage.sh update_dev_packages
 
diff --git a/utils/makefile.python b/utils/makefile.python
index 789cc4c90..f6aa4470a 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -26,7 +26,7 @@ PYLINT_RC ?= .pylintrc
 TEST_FOLDER  ?= ./tests
 TEST         ?= .
 
-VTENV_OPTS   = "--no-site-packages"
+VTENV_OPTS   ?=
 PY_ENV       = ./local/py$(PY)
 PY_ENV_BIN   = $(PY_ENV)/bin
 PY_ENV_ACT   = . $(PY_ENV_BIN)/activate
@@ -37,20 +37,6 @@ ifeq ($(OS),Windows_NT)
   PY_ENV_ACT = $(PY_ENV_BIN)/activate
 endif
 
-ifeq ($(PYTHON),python)
-  VIRTUALENV   = virtualenv
-else
-  VIRTUALENV   = virtualenv --python=$(PYTHON)
-endif
-
-ifeq ($(KBUILD_VERBOSE),1)
-  PIP_VERBOSE =
-  VIRTUALENV_VERBOSE =
-else
-  PIP_VERBOSE = "-q"
-  VIRTUALENV_VERBOSE = "-q"
-endif
-
 python-help::
 	@echo  'makefile.python:'
 	@echo  '  pyenv | pyenv[un]install'
@@ -110,22 +96,6 @@ pip-exe:
 	@:
 endif
 
-PHONY += msg-virtualenv-exe virtualenv-exe
-msg-virtualenv-exe:
-	@echo "\n  virtualenv is required\n\n\
-  Make sure you have an updated virtualenv installed, grab it from\n\
-  https://virtualenv.pypa.io/en/stable/installation/ or install it\n\
-  via pip by::\n\n\
-    pip install --user https://github.com/pypa/virtualenv/tarball/master\n" | $(FMT)
-
-ifeq ($(shell which virtualenv >/dev/null 2>&1; echo $$?), 1)
-virtualenv-exe: msg-virtualenv-exe
-	$(error The 'virtualenv' command was not found)
-else
-virtualenv-exe:
-	@:
-endif
-
 # ------------------------------------------------------------------------------
 # commands
 # ------------------------------------------------------------------------------
@@ -153,7 +123,7 @@ quiet_cmd_pyenvuninstall = PYENV     uninstall   $2
 quiet_cmd_virtualenv  = PYENV     usage: $ source ./$@/bin/activate
       cmd_virtualenv  = \
 	if [ ! -d "./$(PY_ENV)" ];then                                  \
-		$(VIRTUALENV) $(VIRTUALENV_VERBOSE) $(VTENV_OPTS) $2;   \
+		$(PYTHON) -m venv $(VTENV_OPTS) $2;                     \
 	else                                                            \
 		echo "PYENV     using virtualenv from $2";              \
         fi
@@ -232,7 +202,7 @@ pyclean:
 
 # to build *local* environment, python and virtualenv from the OS is needed!
 pyenv: $(PY_ENV)
-$(PY_ENV): virtualenv-exe python-exe
+$(PY_ENV): python-exe
 	$(call cmd,virtualenv,$(PY_ENV))
 	@$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) -r requirements.txt
 

From f5f83b8a99a45198822f4e382575d3147e652e56 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 8 Mar 2020 18:37:16 +0100
Subject: [PATCH 070/125] shellcheck: minor fixes to support all dsitros

checked with::

  sudo ./utils/lxc.sh cmd -- make test.sh

on ubu1804, ubu1910, fedora31 and archlinux.  All checks OK except:

ubu1604: uses shellcheck v0.3.7 (from 04/2015) which is no longer supported!

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile     | 3 ++-
 utils/lib.sh | 4 ++--
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 822adb1ae..ac63842e1 100644
--- a/Makefile
+++ b/Makefile
@@ -1,5 +1,5 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
-
+.DEFAULT_GOAL=help
 include ./.config.mk
 
 PYOBJECTS = searx
@@ -104,6 +104,7 @@ test.pylint: pyenvinstall
 	$(call cmd,pylint,searx/preferences.py)
 	$(call cmd,pylint,searx/testing.py)
 
+# ubu1604: uses shellcheck v0.3.7 (from 04/2015), no longer supported!
 test.sh:
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
diff --git a/utils/lib.sh b/utils/lib.sh
index 26a26d750..e13acca3e 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -4,9 +4,9 @@
 # shellcheck disable=SC2059,SC1117
 
 # ubuntu, debian, arch, fedora ...
-DIST_ID=$(source /etc/os-release; echo $ID);
+DIST_ID=$(source /etc/os-release; echo "$ID");
 # shellcheck disable=SC2034
-DIST_VERS=$(source /etc/os-release; echo $VERSION_ID);
+DIST_VERS=$(source /etc/os-release; echo "$VERSION_ID");
 
 ADMIN_NAME="${ADMIN_NAME:-$(git config user.name)}"
 ADMIN_NAME="${ADMIN_NAME:-$USER}"

From 80f7b658047a3541697ef5ae1aae897817b3f43c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 9 Mar 2020 01:37:26 +0100
Subject: [PATCH 071/125] searx.sh: add buildhost cmd, installs OS packages for
 builds

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/buildhosts.rst |  3 ++-
 utils/lxc.sh              |  2 +-
 utils/makefile.python     |  1 +
 utils/searx.sh            | 48 ++++++++++++++++++++++++++++++---------
 4 files changed, 41 insertions(+), 13 deletions(-)

diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index 27784764f..a6820aa61 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -104,7 +104,8 @@ For PDF output you also need:
 
       	 $ sudo dnf install \
 	        texlive-collection-fontsrecommended texlive-collection-latex \
-		dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts
+		dejavu-sans-fonts dejavu-serif-fonts dejavu-sans-mono-fonts \
+		ImageMagick
 
 .. _sh lint:
 
diff --git a/utils/lxc.sh b/utils/lxc.sh
index f9dd453ee..3c4d2016e 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -236,7 +236,7 @@ main() {
                     shift
                     lxc_exec_cmd "${name}" "$@"
                     ;;
-                *) usage "uknown or missing container <name> $2"; exit 42;;
+                *) usage "uknown or missing container <name> $1"; exit 42;;
             esac
             ;;
         install)
diff --git a/utils/makefile.python b/utils/makefile.python
index f6aa4470a..f7e4315c1 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -204,6 +204,7 @@ pyclean:
 pyenv: $(PY_ENV)
 $(PY_ENV): python-exe
 	$(call cmd,virtualenv,$(PY_ENV))
+	@$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) -U wheel pip setuptools
 	@$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) -r requirements.txt
 
 PHONY += pylint-exe
diff --git a/utils/searx.sh b/utils/searx.sh
index 9d33dc31a..19cb41986 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -41,27 +41,47 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 SEARX_PACKAGES_debian="\
 python3-dev python3-babel python3-venv
 uwsgi uwsgi-plugin-python3
-git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
-shellcheck"
+git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
+
+BUILD_PACKAGES_debian="\
+shellcheck graphviz imagemagick texlive-xetex librsvg2-bin
+texlive-latex-recommended texlive-extra-utils ttf-dejavu"
 
 # pacman packages
 SEARX_PACKAGES_arch="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python
-git base-devel libxml2
-shellcheck"
+git base-devel libxml2"
+
+BUILD_PACKAGES_arch="\
+shellcheck graphviz imagemagick texlive-bin extra/librsvg
+texlive-core texlive-latexextra ttf-dejavu"
 
 # dnf packages
 SEARX_PACKAGES_fedora="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python3
-git @development-tools libxml2
-ShellCheck"
+git @development-tools libxml2"
+
+BUILD_PACKAGES_fedora="\
+ShellCheck graphviz graphviz-gd ImageMagick librsvg2-tools
+texlive-xetex-bin texlive-collection-fontsrecommended
+texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
+dejavu-sans-mono-fonts"
 
 case $DIST_ID in
-    ubuntu|debian) SEARX_PACKAGES="${SEARX_PACKAGES_debian}" ;;
-    arch) SEARX_PACKAGES="${SEARX_PACKAGES_arch}" ;;
-    fedora) SEARX_PACKAGES="${SEARX_PACKAGES_fedora}" ;;
+    ubuntu|debian)
+        SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
+        BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+        ;;
+    arch)
+        SEARX_PACKAGES="${SEARX_PACKAGES_arch}"
+        BUILD_PACKAGES="${BUILD_PACKAGES_arch}"
+        ;;
+    fedora)
+        SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
+        BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
+        ;;
 esac
 
 # Apache Settings
@@ -111,6 +131,7 @@ install / remove
   :uwsgi:      install searx uWSGI application
   :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
   :packages:   install needed packages from OS package manager
+  :buildhost:  install packages from OS package manager needed by buildhost
 update searx
   Update searx installation ($SERVICE_HOME)
 activate service
@@ -172,7 +193,13 @@ main() {
                 searx-src) clone_searx ;;
                 settings) install_settings ;;
                 uwsgi) install_searx_uwsgi;;
-                packages) pkg_install "$SEARX_PACKAGES" ;;
+                packages)
+                    pkg_install "$SEARX_PACKAGES"
+                    ;;
+                buildhost)
+                    pkg_install "$SEARX_PACKAGES"
+                    pkg_install "$BUILD_PACKAGES"
+                    ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         update)
@@ -730,4 +757,3 @@ EOF
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
-

From 86e79488aab3ff434c6682a9464ba2eee49158e9 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 15 Mar 2020 17:01:36 +0100
Subject: [PATCH 072/125] LXC: utils/makefile.lxc (inital) add /.lxcenv.mk to
 contaiiners

Get LXC environment when building make targets.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile               | 11 ++++-----
 utils/lxc-searx.env    | 11 +++++++++
 utils/lxc.sh           | 52 +++++++++++++++++++++++-------------------
 utils/makefile.include | 14 +++++++++++-
 utils/makefile.lxc     | 29 +++++++++++++++++++++++
 utils/makefile.python  | 13 ++++++-----
 utils/makefile.sphinx  | 12 ++++++----
 7 files changed, 99 insertions(+), 43 deletions(-)
 create mode 100644 utils/makefile.lxc

diff --git a/Makefile b/Makefile
index ac63842e1..d46c96a49 100644
--- a/Makefile
+++ b/Makefile
@@ -1,15 +1,12 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 .DEFAULT_GOAL=help
 include ./.config.mk
+include utils/makefile.include
 
 PYOBJECTS = searx
 DOC       = docs
 PY_SETUP_EXTRAS ?= \[test\]
 
-PYDIST=./dist/py
-PYBUILD=./build/py
-
-include utils/makefile.include
 include utils/makefile.python
 include utils/makefile.sphinx
 
@@ -32,9 +29,9 @@ help:
 	@echo  '  GIT_URL   = $(GIT_URL)'
 	@echo  '  DOCS_URL  = $(DOCS_URL)'
 	@echo  ''
-	@$(MAKE) -s -f utils/makefile.include make-help
+	@$(MAKE) -e -s -f utils/makefile.include make-help
 	@echo  ''
-	@$(MAKE) -s -f utils/makefile.python python-help
+	@$(MAKE) -e -s -f utils/makefile.python python-help
 
 PHONY += install
 install: pyenvinstall
@@ -43,7 +40,7 @@ PHONY += uninstall
 uninstall: pyenvuninstall
 
 PHONY += clean
-clean: pyclean
+clean: pyclean docs-clean
 	$(call cmd,common_clean)
 
 PHONY += run
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index b088ca792..a5ef5712c 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -32,6 +32,7 @@ lxc_set_suite_env() {
     export MORTY_LISTEN="0.0.0.0:3000"
 }
 
+lxc_suite_install_info="suite includes searx, morty & filtron"
 lxc_suite_install() {
     (
         lxc_set_suite_env
@@ -46,6 +47,16 @@ lxc_suite_install() {
     )
 }
 
+lxc_suite_prepare_buildhost() {
+    (
+        lxc_set_suite_env
+        export FORCE_TIMEOUT=0
+        "${LXC_REPO_ROOT}/utils/searx.sh"   install buildhost
+        rst_title "buildhost installation finished ($(hostname))" part
+        echo
+    )
+}
+
 lxc_suite_info() {
     (
         lxc_set_suite_env
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 3c4d2016e..64805272e 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -104,7 +104,8 @@ cmd
   --             run command '...' in all containers of the LXC suite
   :<name>:       run command '...' in container <name>
 install
-  :suite:        install LXC suite, includes morty & filtron
+  :suite:        install LXC suite; ${lxc_suite_install_info}
+  :buildhost:    prepare LXC; buildhost
 
 EOF
     usage_images
@@ -224,12 +225,7 @@ main() {
             sudo_or_exit
             shift
             case $1 in
-                --)
-                    shift
-                    for name in "${CONTAINERS[@]}"; do
-                        lxc_exec_cmd "${name}" "$@"
-                    done
-                    ;;
+                --) shift; lxc_exec "$@" ;;
                 ${LXC_HOST_PREFIX}-*)
                     ! lxc_exists "$1" && usage_containers "unknown container: $1" && exit 42
                     local name=$1
@@ -242,13 +238,15 @@ main() {
         install)
             sudo_or_exit
             case $2 in
-                suite) install_suite ;;
+                suite) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install suite;;
+                buildhost) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install buildhost;;
                 *) usage "$_usage"; exit 42 ;;
             esac
             ;;
         __install)
             case $2 in
                 suite) lxc_suite_install ;;
+                buildhost) lxc_suite_prepare_buildhost ;;
             esac
             ;;
         doc)
@@ -263,7 +261,6 @@ main() {
 
 build_instances() {
     rst_title "Build LXC instances"
-    echo
     lxc_copy_images_localy
     echo
     rst_title "build containers" section
@@ -360,18 +357,6 @@ show_suite(){
     done
 }
 
-install_suite() {
-    for i in "${CONTAINERS[@]}"; do
-        if ! lxc_exists "$i"; then
-            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
-        else
-            info_msg "[${_BBlue}${i}${_creset}] ${_BGreen}${LXC_REPO_ROOT}/utils/lxc.sh install suite${_creset}"
-            lxc exec -t "${i}" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __install suite \
-                | prefix_stdout "[${_BBlue}${i}${_creset}]  "
-        fi
-    done
-}
-
 lxc_cmd() {
     for i in "${CONTAINERS[@]}"; do
         if ! lxc_exists "$i"; then
@@ -389,14 +374,23 @@ lxc_exec_cmd() {
     shift
     exit_val=
     info_msg "[${_BBlue}${name}${_creset}] ${_BGreen}${*}${_creset}"
-    lxc exec --cwd "${LXC_REPO_ROOT}" "${name}" -- bash -c "$*"
+    lxc exec -t --cwd "${LXC_REPO_ROOT}" "${name}" -- bash -c "$*"
     exit_val=$?
     if [[ $exit_val -ne 0 ]]; then
         warn_msg "[${_BBlue}${name}${_creset}] exit code (${_BRed}${exit_val}${_creset}) from ${_BGreen}${*}${_creset}"
     else
         info_msg "[${_BBlue}${name}${_creset}] exit code (${exit_val}) from ${_BGreen}${*}${_creset}"
     fi
-    echo
+}
+
+lxc_exec() {
+    for i in "${CONTAINERS[@]}"; do
+        if ! lxc_exists "$i"; then
+            warn_msg "container ${_BBlue}$i${_creset} does not yet exists"
+        else
+            lxc_exec_cmd "${i}" "$@" | prefix_stdout "[${_BBlue}${i}${_creset}] "
+        fi
+    done
 }
 
 lxc_init_containers() {
@@ -449,7 +443,17 @@ lxc_boilerplate_containers() {
         boilerplate_script="${image_name}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
 
-        info_msg "[${_BBlue}${container_name}${_creset}] install boilerplate"
+        info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
+        if lxc start -q "${container_name}" &>/dev/null; then
+            sleep 5 # guest needs some time to come up and get an IP
+        fi
+        cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+rm -f "/.lxcenv.mk"
+ln -s "${LXC_REPO_ROOT}/utils/makefile.lxc" "/.lxcenv.mk"
+ls -l "/.lxcenv.mk"
+EOF
+
+        info_msg "[${_BBlue}${container_name}${_creset}] install boilerplate .."
         if lxc start -q "${container_name}" &>/dev/null; then
             sleep 5 # guest needs some time to come up and get an IP
         fi
diff --git a/utils/makefile.include b/utils/makefile.include
index 716889c02..933d2b57a 100644
--- a/utils/makefile.include
+++ b/utils/makefile.include
@@ -1,12 +1,23 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 
+ifeq (,$(wildcard /.lxcenv.mk))
+PHONY += lxc-activate
+lxc-activate:
+	@$(MAKE) -s -f  /share/searx/utils/makefile.lxc lxc-activate
+else
+	include /.lxcenv.mk
+endif
+
+ifeq (,$(wildcard /.lxcenv.mk))
 make-help:
+else
+make-help: lxc-help
+endif
 	@echo  '  make V=0|1 [targets] 0 => quiet build (default), 1 => verbose build'
 	@echo  '  make V=2   [targets] 2 => give reason for rebuild of target'
 
 quiet_cmd_common_clean = CLEAN     $@
       cmd_common_clean = \
-	rm -rf tests/build ;\
 	find . -name '*.orig' -exec rm -f {} +     ;\
 	find . -name '*.rej' -exec rm -f {} +      ;\
 	find . -name '*~' -exec rm -f {} +         ;\
@@ -126,3 +137,4 @@ echo-cmd = $(if $($(quiet)cmd_$(1)),echo '$(call escsq,$($(quiet)cmd_$(1)))$(ech
 # printing commands
 cmd = @$(echo-cmd) $(cmd_$(1))
 
+.PHONY: $(PHONY)
diff --git a/utils/makefile.lxc b/utils/makefile.lxc
new file mode 100644
index 000000000..8b79994ee
--- /dev/null
+++ b/utils/makefile.lxc
@@ -0,0 +1,29 @@
+# -*- coding: utf-8; mode: makefile-gmake -*-
+#
+# LXC environment
+# ===============
+#
+# To activate/deactivate LXC makefile environment in a container, set/unset link
+# from root '/.lxcenv.mk' to *this* file::
+#
+#   sudo make ./utils/makefile.lxc lxc-activate
+#   sudo make ./utils/makefile.lxc lxc-deactivate
+
+LXC_ENV_FOLDER=/lxc-$(shell hostname)
+
+lxc-help::
+	@echo  'LXC: running in container LXC_ENV_FOLDER=$(LXC_ENV_FOLDER)'
+
+# If not activated, serve target 'lxc-activate' ..
+ifeq (,$(wildcard /.lxcenv.mk))
+PHONY += lxc-activate
+lxc-activate:
+	ln -s "$(abspath $(lastword $(MAKEFILE_LIST)))" "/.lxcenv.mk"
+else
+# .. and if activated, serve target 'lxc-deactivate'.
+PHONY += lxc-deactivate
+lxc-deactivate:
+	rm /.lxcenv.mk
+endif
+
+.PHONY: $(PHONY)
diff --git a/utils/makefile.python b/utils/makefile.python
index f7e4315c1..bd7d0d14e 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -5,11 +5,12 @@ PYOBJECTS ?=
 
 SITE_PYTHON ?=$(dir $(abspath $(lastword $(MAKEFILE_LIST))))site-python
 export PYTHONPATH := $(SITE_PYTHON):$$PYTHONPATH
+export PY_ENV PYDIST PYBUILD
 
 # folder where the python distribution takes place
-PYDIST   ?= ./py_dist
+PYDIST   = ./dist$(LXC_ENV_FOLDER)
 # folder where the python intermediate build files take place
-PYBUILD  ?= ./py_build
+PYBUILD  = ./build$(LXC_ENV_FOLDER)
 # python version to use
 PY       ?=3
 PYTHON   ?= python$(PY)
@@ -27,7 +28,7 @@ TEST_FOLDER  ?= ./tests
 TEST         ?= .
 
 VTENV_OPTS   ?=
-PY_ENV       = ./local/py$(PY)
+PY_ENV       = ./local$(LXC_ENV_FOLDER)/py$(PY)
 PY_ENV_BIN   = $(PY_ENV)/bin
 PY_ENV_ACT   = . $(PY_ENV_BIN)/activate
 
@@ -45,7 +46,7 @@ python-help::
 	@echo  '    pylint    - run pylint *linting*'
 	@echo  '    pytest    - run *tox* test on python objects'
 	@echo  '    pydebug   - run tests within a PDB debug session'
-	@echo  '    pybuild   - build python packages'
+	@echo  '    pybuild   - build python packages ($(PYDIST) $(PYBUILD))'
 	@echo  '    pyclean   - clean intermediate python objects'
 	@echo  '  targets using system users environment:'
 	@echo  '    py[un]install - [un]install python objects in editable mode'
@@ -169,7 +170,7 @@ quiet_cmd_pybuild     = BUILD     $@
 quiet_cmd_pyclean     = CLEAN     $@
 # remove 'build' folder since bdist_wheel does not care the --bdist-dir
       cmd_pyclean     = \
-	rm -rf $(PYDIST) $(PYBUILD) ./local ./.tox *.egg-info     ;\
+	rm -rf $(PYDIST) $(PYBUILD) $(PY_ENV) ./.tox *.egg-info     ;\
 	find . -name '*.pyc' -exec rm -f {} +      ;\
 	find . -name '*.pyo' -exec rm -f {} +      ;\
 	find . -name __pycache__ -exec rm -rf {} +
@@ -241,7 +242,7 @@ pyenv-uninstall: $(PY_ENV)
 
 # runs python interpreter from ./local/py<N>/bin/python
 pyenv-python: pyenv-install
-	cd ./local; ../$(PY_ENV_BIN)/python -i
+	$(PY_ENV_BIN)/python -i
 
 # With 'dependency_links=' setuptools supports dependencies on packages hosted
 # on other reposetories then PyPi, see "Packages Not On PyPI" [1].  The big
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index 2c1922fc9..4926bb53a 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -1,17 +1,19 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 
+export DOCS_FOLDER DOCS_BUILD DOCS_DIST BOOKS_FOLDER BOOKS_DIST
+
 # You can set these variables from the command line.
 SPHINXOPTS  ?=
 SPHINXBUILD ?= $(PY_ENV_BIN)/sphinx-build
 SPHINX_CONF ?= conf.py
 
-DOCS_FOLDER ?= docs
-DOCS_BUILD  ?= build/docs
-DOCS_DIST   ?= dist/docs
+DOCS_FOLDER = docs$(LXC_ENV_FOLDER)
+DOCS_BUILD  = build/docs$(LXC_ENV_FOLDER)
+DOCS_DIST   = dist/docs$(LXC_ENV_FOLDER)
 GH_PAGES    ?= gh-pages
 
-BOOKS_FOLDER ?= docs
-BOOKS_DIST   ?= dist/books
+BOOKS_FOLDER = docs$(LXC_ENV_FOLDER)
+BOOKS_DIST   = dist/books$(LXC_ENV_FOLDER)
 
 ifeq ($(KBUILD_VERBOSE),1)
   SPHINX_VERBOSE = "-v"

From d2cfe9ce5bb409472f10590aa1ae069b2a971a49 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 18 Mar 2020 15:34:46 +0100
Subject: [PATCH 073/125] LXC: add /.lxcenv

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh        | 23 ++++++++++++++++++-----
 utils/lxc-searx.env |  1 +
 utils/lxc.sh        |  6 ++++--
 3 files changed, 23 insertions(+), 7 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index e13acca3e..e9bfc929f 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1065,18 +1065,31 @@ EOF
 in_container() {
     # Test if shell runs in a container.
     #
-    # hint:   Reads init process environment, therefore root access is required!
-    #
     # usage:  in_container && echo "process running inside a LXC container"
     #         in_container || echo "process is not running inside a LXC container"
     #
-
-    sudo_or_exit
+    # sudo_or_exit
+    # hint:   Reads init process environment, therefore root access is required!
     # to be safe, take a look at the environment of process 1 (/sbin/init)
-    grep -qa 'container=lxc' /proc/1/environ
+    # grep -qa 'container=lxc' /proc/1/environ
+
+    # see lxc_init_container
+    [[ -f /.lxcenv ]]
 }
 
+lxc_init_container() {
 
+    # Create a /.lxcenv file in the root folder.  Call this once after container
+    # is inital started.
+
+    # usage: lxc_create_root_dot_lxcenv <name>
+
+    info_msg "create /.lxcenv in container $1"
+    cat <<EOF | lxc exec "${1}" -- bash | prefix_stdout "[${_BBlue}${1}${_creset}] "
+touch "/.lxcenv"
+ls -l "/.lxcenv"
+EOF
+}
 lxc_exists(){
 
     # usage: lxc_exists <name> || echo "container <name> does not exists"
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index a5ef5712c..f81ee1c08 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -51,6 +51,7 @@ lxc_suite_prepare_buildhost() {
     (
         lxc_set_suite_env
         export FORCE_TIMEOUT=0
+        # "${LXC_REPO_ROOT}/utils/searx.sh* install packages
         "${LXC_REPO_ROOT}/utils/searx.sh"   install buildhost
         rst_title "buildhost installation finished ($(hostname))" part
         echo
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 64805272e..56450c4db 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -82,7 +82,7 @@ usage::
   $_cmd [start|stop] [containers|<name>]
   $_cmd show         [info|config|suite|images]
   $_cmd cmd          [--|<name>] '...'
-  $_cmd install      [suite]
+  $_cmd install      [suite|buildhost]
 
 build
   :containers:   build & launch all LXC containers of the suite
@@ -443,10 +443,12 @@ lxc_boilerplate_containers() {
         boilerplate_script="${image_name}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
 
-        info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
+        info_msg "[${_BBlue}${container_name}${_creset}] init .."
         if lxc start -q "${container_name}" &>/dev/null; then
             sleep 5 # guest needs some time to come up and get an IP
         fi
+        lxc_init_container "${container_name}"
+        info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
         cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
 rm -f "/.lxcenv.mk"
 ln -s "${LXC_REPO_ROOT}/utils/makefile.lxc" "/.lxcenv.mk"

From 62da7699529b57f3bf851ec9258da89be42094dc Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 18 Mar 2020 17:47:48 +0100
Subject: [PATCH 074/125] ./utils/lxc.sh: add command 'install base'

to install basic packages like git and *build essentials*::

  $ ./utils/lxc.sh install base

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh | 22 ++++++++++++++++++++++
 utils/lxc.sh | 17 ++++++++++++-----
 2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index e9bfc929f..50f15a690 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1090,6 +1090,28 @@ touch "/.lxcenv"
 ls -l "/.lxcenv"
 EOF
 }
+
+# apt packages
+LXC_BASE_PACKAGES_debian="git build-essential"
+
+# pacman packages
+LXC_BASE_PACKAGES_arch="git base-devel"
+
+# dnf packages
+LXC_BASE_PACKAGES_fedora="git @development-tools"
+
+case $DIST_ID in
+    ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;;
+    arch)          LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_arch}" ;;
+    fedora)        LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_fedora}" ;;
+    *) err_msg "$DIST_ID-$DIST_VERS: pkg_install LXC_BASE_PACKAGES not yet implemented" ;;
+esac
+
+lxc_install_base_packages() {
+    info_msg "install LXC_BASE_PACKAGES in container $1"
+    pkg_install "${LXC_BASE_PACKAGES}"
+}
+
 lxc_exists(){
 
     # usage: lxc_exists <name> || echo "container <name> does not exists"
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 56450c4db..6070735ea 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -82,10 +82,10 @@ usage::
   $_cmd [start|stop] [containers|<name>]
   $_cmd show         [info|config|suite|images]
   $_cmd cmd          [--|<name>] '...'
-  $_cmd install      [suite|buildhost]
+  $_cmd install      [suite|base|buildhost]
 
 build
-  :containers:   build & launch all LXC containers of the suite
+  :containers:   build, launch and 'install basic' packages on 'containers'
 copy:
   :images:       copy remote images of the suite into local storage
 remove
@@ -105,7 +105,8 @@ cmd
   :<name>:       run command '...' in container <name>
 install
   :suite:        install LXC suite; ${lxc_suite_install_info}
-  :buildhost:    prepare LXC; buildhost
+  :base:         prepare LXC; install basic packages
+  :buildhost:    prepare LXC; install buildhost packages
 
 EOF
     usage_images
@@ -217,6 +218,7 @@ main() {
             esac
             ;;
         __show)
+            # wrapped show commands, called once in each container
             case $2 in
                 suite) lxc_suite_info ;;
             esac
@@ -238,14 +240,17 @@ main() {
         install)
             sudo_or_exit
             case $2 in
-                suite) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install suite;;
-                buildhost) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install buildhost;;
+                suite|base|buildhost)
+                    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install $2
+                    ;;
                 *) usage "$_usage"; exit 42 ;;
             esac
             ;;
         __install)
+            # wrapped install commands, called once in each container
             case $2 in
                 suite) lxc_suite_install ;;
+                base) FORCE_TIMEOUT=0 lxc_install_base_packages ;;
                 buildhost) lxc_suite_prepare_buildhost ;;
             esac
             ;;
@@ -269,6 +274,8 @@ build_instances() {
     lxc_config_containers
     lxc_boilerplate_containers
     echo
+    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install base
+    echo
     lxc list "$LXC_HOST_PREFIX"
 }
 

From 2d956696e94e5639db6e12648442989919c48c22 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 21 Mar 2020 18:45:38 +0100
Subject: [PATCH 075/125] docs: building (PDF) books / build user book

BTW: cleaned up Makefile target help

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile                                | 17 ++++++---
 docs/conf.py                            |  7 ++++
 docs/user/conf.py                       | 19 ++++++++++
 utils/makefile.sphinx                   |  6 ++--
 utils/site-python/sphinx_build_tools.py | 48 +++++++++++++++++++++++++
 5 files changed, 90 insertions(+), 7 deletions(-)
 create mode 100644 docs/user/conf.py
 create mode 100644 utils/site-python/sphinx_build_tools.py

diff --git a/Makefile b/Makefile
index d46c96a49..84f0ac24b 100644
--- a/Makefile
+++ b/Makefile
@@ -12,8 +12,13 @@ include utils/makefile.sphinx
 
 all: clean install
 
-PHONY += help
-help:
+PHONY += help-min help-all help
+
+help: help-min
+	@echo  ''
+	@echo  'to get more help:  make help-all'
+
+help-min:
 	@echo  '  test      - run developer tests'
 	@echo  '  docs      - build documentation'
 	@echo  '  docs-live - autobuild HTML documentation while editing'
@@ -29,9 +34,13 @@ help:
 	@echo  '  GIT_URL   = $(GIT_URL)'
 	@echo  '  DOCS_URL  = $(DOCS_URL)'
 	@echo  ''
-	@$(MAKE) -e -s -f utils/makefile.include make-help
+	@$(MAKE) -e -s make-help
+
+help-all: help-min
 	@echo  ''
-	@$(MAKE) -e -s -f utils/makefile.python python-help
+	@$(MAKE) -e -s python-help
+	@echo  ''
+	@$(MAKE) -e -s docs-help
 
 PHONY += install
 install: pyenvinstall
diff --git a/docs/conf.py b/docs/conf.py
index 6a6633341..c2b2bbd84 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -1,6 +1,7 @@
 # -*- coding: utf-8 -*-
 
 import  sys, os
+from sphinx_build_tools import load_sphinx_config
 from searx.version import VERSION_STRING
 from pallets_sphinx_themes import ProjectLink
 
@@ -116,3 +117,9 @@ html_show_sourcelink = False
 latex_documents = [
     (master_doc, "searx-{}.tex".format(VERSION_STRING), html_title, author, "manual")
 ]
+
+# ------------------------------------------------------------------------------
+# Since loadConfig overwrites settings from the global namespace, it has to be
+# the last statement in the conf.py file
+# ------------------------------------------------------------------------------
+load_sphinx_config(globals())
diff --git a/docs/user/conf.py b/docs/user/conf.py
new file mode 100644
index 000000000..d4bdb576b
--- /dev/null
+++ b/docs/user/conf.py
@@ -0,0 +1,19 @@
+# -*- coding: utf-8; mode: python -*-
+"""Configuration for the CDB 15 Infrastruktur book
+"""
+project   = 'Searx User-HB'
+version   = release = VERSION_STRING
+
+# Grouping the document tree into LaTeX files. List of tuples
+# (source start file, target name, title,
+#  author, documentclass [howto, manual, or own class]).
+latex_documents = [
+    ('index'                       # startdocname
+     , 'searx-user-hb.tex'         # targetname
+     , ''                          # take title from .rst
+     , author                      # author
+     , 'howto'                     # documentclass
+     , False                       # toctree_only
+    ),
+]
+
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index 4926bb53a..8a1f6b76b 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -156,7 +156,7 @@ $(BOOKS_HTML): sphinx-doc | $(BOOKS_DIST)
 	  -b html \
 	  -c $(DOCS_FOLDER) \
 	  -d $(DOCS_BUILD)/books/$(patsubst books/%.html,%,$@)/.doctrees \
-	  $(patsubst books/%.html,%,$@) \
+	  $(BOOKS_FOLDER)/$(patsubst books/%.html,%,$@) \
 	  $(BOOKS_DIST)/$(patsubst books/%.html,%,$@)
 	@echo "SPHINX    $@ --> file://$(abspath $(BOOKS_DIST)/$(patsubst books/%.html,%,$@))"
 
@@ -168,7 +168,7 @@ $(BOOKS_LIVE): sphinx-live | $(BOOKS_DIST)
 	  -b html \
 	  -c $(DOCS_FOLDER) \
 	  -d $(DOCS_BUILD)/books/$(patsubst books/%.live,%,$@)/.doctrees \
-	  $(patsubst books/%.live,%,$@) \
+	  $(BOOKS_FOLDER)/$(patsubst books/%.live,%,$@) \
 	  $(BOOKS_DIST)/$(patsubst books/%.live,%,$@)
 
 $(BOOKS_PDF): %.pdf : %.latex
@@ -184,7 +184,7 @@ $(BOOKS_LATEX): sphinx-doc | $(BOOKS_DIST)
 	  -b latex \
 	  -c $(DOCS_FOLDER) \
 	  -d $(DOCS_BUILD)/books/$(patsubst books/%.latex,%,$@)/.doctrees \
-	  $(patsubst books/%.latex,%,$@) \
+	  $(BOOKS_FOLDER)/$(patsubst books/%.latex,%,$@) \
 	  $(DOCS_BUILD)/latex/$(patsubst books/%.latex,%,$@)
 	@echo "SPHINX    $@ --> file://$(abspath $(DOCS_BUILD)/latex/$(patsubst books/%.latex,%,$@))"
 
diff --git a/utils/site-python/sphinx_build_tools.py b/utils/site-python/sphinx_build_tools.py
new file mode 100644
index 000000000..b9ebdeacc
--- /dev/null
+++ b/utils/site-python/sphinx_build_tools.py
@@ -0,0 +1,48 @@
+# -*- coding: utf-8; mode: python -*-
+"""Implement some sphinx-build tools.
+
+"""
+
+import os
+import sys
+from sphinx.util.pycompat import execfile_
+
+# ------------------------------------------------------------------------------
+def load_sphinx_config(namespace):
+# ------------------------------------------------------------------------------
+
+    u"""Load an additional configuration file into *namespace*.
+
+    The name of the configuration file is taken from the environment
+    ``SPHINX_CONF``. The external configuration file extends (or overwrites) the
+    configuration values from the origin ``conf.py``.  With this you are able to
+    maintain *build themes*.  To your docs/conf.py add::
+
+        from sphinx_build_tools import load_sphinx_config
+        ...
+
+        # Since loadConfig overwrites settings from the global namespace, it has to be
+        # the last statement in the conf.py file
+
+        load_sphinx_config(globals())
+
+    """
+
+    config_file = os.environ.get("SPHINX_CONF", None)
+    if (config_file is not None
+        and os.path.normpath(namespace["__file__"]) != os.path.normpath(config_file) ):
+        config_file = os.path.abspath(config_file)
+
+        if os.path.isfile(config_file):
+            sys.stdout.write(
+                "load additional sphinx-config: %s\n"
+                % config_file)
+            config = namespace.copy()
+            config['__file__'] = config_file
+            execfile_(config_file, config)
+            del config['__file__']
+            namespace.update(config)
+        else:
+            sys.stderr.write(
+                "WARNING: additional sphinx-config not found: %s\n"
+                % config_file)

From baf1ffd1fa4923f3bd105f0d8daac4a9527bdc50 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 23 Mar 2020 18:05:46 +0100
Subject: [PATCH 076/125] LXC: set LXC_ENV_FOLDER=lxc/$(shell hostname)/

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh          |  5 +++++
 utils/makefile.lxc    |  2 +-
 utils/makefile.python |  6 +++---
 utils/makefile.sphinx | 10 +++++-----
 4 files changed, 14 insertions(+), 9 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index 50f15a690..dfa1e9cda 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1077,6 +1077,11 @@ in_container() {
     [[ -f /.lxcenv ]]
 }
 
+LXC_ENV_FOLDER=
+if in_container; then
+    LXC_ENV_FOLDER="lxc/$(hostname)/"
+fi
+
 lxc_init_container() {
 
     # Create a /.lxcenv file in the root folder.  Call this once after container
diff --git a/utils/makefile.lxc b/utils/makefile.lxc
index 8b79994ee..fd999e40d 100644
--- a/utils/makefile.lxc
+++ b/utils/makefile.lxc
@@ -9,7 +9,7 @@
 #   sudo make ./utils/makefile.lxc lxc-activate
 #   sudo make ./utils/makefile.lxc lxc-deactivate
 
-LXC_ENV_FOLDER=/lxc-$(shell hostname)
+LXC_ENV_FOLDER=lxc/$(shell hostname)/
 
 lxc-help::
 	@echo  'LXC: running in container LXC_ENV_FOLDER=$(LXC_ENV_FOLDER)'
diff --git a/utils/makefile.python b/utils/makefile.python
index bd7d0d14e..1660468f5 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -8,9 +8,9 @@ export PYTHONPATH := $(SITE_PYTHON):$$PYTHONPATH
 export PY_ENV PYDIST PYBUILD
 
 # folder where the python distribution takes place
-PYDIST   = ./dist$(LXC_ENV_FOLDER)
+PYDIST   = ./$(LXC_ENV_FOLDER)dist
 # folder where the python intermediate build files take place
-PYBUILD  = ./build$(LXC_ENV_FOLDER)
+PYBUILD  = ./$(LXC_ENV_FOLDER)build
 # python version to use
 PY       ?=3
 PYTHON   ?= python$(PY)
@@ -28,7 +28,7 @@ TEST_FOLDER  ?= ./tests
 TEST         ?= .
 
 VTENV_OPTS   ?=
-PY_ENV       = ./local$(LXC_ENV_FOLDER)/py$(PY)
+PY_ENV       = ./$(LXC_ENV_FOLDER)local/py$(PY)
 PY_ENV_BIN   = $(PY_ENV)/bin
 PY_ENV_ACT   = . $(PY_ENV_BIN)/activate
 
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index 8a1f6b76b..fd4c3a6f1 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -7,13 +7,13 @@ SPHINXOPTS  ?=
 SPHINXBUILD ?= $(PY_ENV_BIN)/sphinx-build
 SPHINX_CONF ?= conf.py
 
-DOCS_FOLDER = docs$(LXC_ENV_FOLDER)
-DOCS_BUILD  = build/docs$(LXC_ENV_FOLDER)
-DOCS_DIST   = dist/docs$(LXC_ENV_FOLDER)
+DOCS_FOLDER = ./$(LXC_ENV_FOLDER)docs
+DOCS_BUILD  = ./$(LXC_ENV_FOLDER)build/docs
+DOCS_DIST   = ./$(LXC_ENV_FOLDER)dist/docs
 GH_PAGES    ?= gh-pages
 
-BOOKS_FOLDER = docs$(LXC_ENV_FOLDER)
-BOOKS_DIST   = dist/books$(LXC_ENV_FOLDER)
+BOOKS_FOLDER = ./$(LXC_ENV_FOLDER)docs
+BOOKS_DIST   = ./$(LXC_ENV_FOLDER)dist/books
 
 ifeq ($(KBUILD_VERBOSE),1)
   SPHINX_VERBOSE = "-v"

From 1fcec0bbdad54958abf83715297f77a1f9d81afc Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 23 Mar 2020 18:06:35 +0100
Subject: [PATCH 077/125] lxc.sh install base: add bash and python3 to
 LXC_BASE_PACKAGES

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index dfa1e9cda..632d47b5b 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1097,13 +1097,13 @@ EOF
 }
 
 # apt packages
-LXC_BASE_PACKAGES_debian="git build-essential"
+LXC_BASE_PACKAGES_debian="bash git build-essential python3"
 
 # pacman packages
-LXC_BASE_PACKAGES_arch="git base-devel"
+LXC_BASE_PACKAGES_arch="bash git base-devel python"
 
 # dnf packages
-LXC_BASE_PACKAGES_fedora="git @development-tools"
+LXC_BASE_PACKAGES_fedora="bash git @development-tools python"
 
 case $DIST_ID in
     ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;;

From 7b4cf2eb489d8f6c95ccac5af180254b78e7c460 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 29 Mar 2020 15:09:34 +0200
Subject: [PATCH 078/125] tooling box: simplify build enviroments

- no more need for a .config.mk
- docs: use searx.brands environment
- searx.sh, filtron.sh & morty.sh are sourcing utils/brand.env

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.mk                                    | 13 -------
 .config.sh                                    | 35 ++++++++-----------
 Makefile                                      |  6 +++-
 docs/conf.py                                  |  6 ++--
 docs/dev/makefile.rst                         |  3 +-
 utils/filtron.sh                              |  5 +++
 utils/morty.sh                                |  7 ++--
 utils/searx.sh                                | 17 ++++-----
 .../etc/apache2/sites-available/morty.conf    |  2 +-
 9 files changed, 44 insertions(+), 50 deletions(-)
 delete mode 100644 .config.mk

diff --git a/.config.mk b/.config.mk
deleted file mode 100644
index e03d29056..000000000
--- a/.config.mk
+++ /dev/null
@@ -1,13 +0,0 @@
-# -*- coding: utf-8; mode: makefile-gmake -*-
-# SPDX-License-Identifier: AGPL-3.0-or-later
-#
-# This environment is used by Makefile targets.  If you not maintain your own
-# searx brand, you normally not need to change the defaults (except SEARX_URL).
-# Compare your settings here with file .config.sh used by the toolboxing in
-# utils.
-
-export SEARX_URL:=$(or ${SEARX_URL},https://searx.me)
-
-export DOCS_URL:=$(or ${DOCS_URL},https://asciimoo.github.io/searx)
-export GIT_URL:=$(or ${GIT_URL},https://github.com/asciimoo/searx)
-
diff --git a/.config.sh b/.config.sh
index 0fdc0ca12..57411ce6f 100644
--- a/.config.sh
+++ b/.config.sh
@@ -11,46 +11,41 @@
 # adding them to you local brand (git branch)::
 #
 #     git add .config
-#
-# Compare your settings here with file .config.mk used by the Makefile targets.
 
 # The public URL of the searx instance: PUBLIC_URL="https://mydomain.xy/searx"
-# The default is taken from the Makefile environment (SEARX_URL).
-PUBLIC_URL="${SEARX_URL:-http://$(uname -n)/searx}"
-PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
+# The default is taken from ./utils/brand.env.
+
+PUBLIC_URL="${SEARX_URL}"
+#PUBLIC_URL="http://$(uname -n)/searx"
 
 # searx.sh
 # ---------
 
-SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
+# SEARX_INTERNAL_URL="127.0.0.1:8888"
 
-# Only change, if you maintain a searx brand in your searx fork.  The default is
-# taken from the Makefile environment (DOCS_URL, GIT_URL).
-SEARX_DOCS_URL="${DOCS_URL:-https://asciimoo.github.io/searx}"
-SEARX_GIT_URL="${GIT_URL:-https://github.com/asciimoo/searx.git}"
-SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
+# Only change, if you maintain a searx brand in your searx fork.
+# GIT_BRANCH="${GIT_BRANCH:-master}"
 
 # filtron.sh
 # ----------
 
-FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
-FILTRON_LISTEN="${FILTRON_LISTEN:-127.0.0.1:4004}"
-FILTRON_TARGET="${FILTRON_TARGET:-127.0.0.1:8888}"
+# FILTRON_API="127.0.0.1:4005"
+# FILTRON_LISTEN="127.0.0.1:4004"
+# FILTRON_TARGET="127.0.0.1:8888"
 
 # morty.sh
 # --------
 
 # morty listen address
-MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+# MORTY_LISTEN="127.0.0.1:3000"
+# PUBLIC_URL_PATH_MORTY="/morty"
 
 # system services
 # ---------------
 
+# Common $HOME folder of the service accounts
+# SERVICE_HOME_BASE="/usr/local"
+
 # **experimental**: Set SERVICE_USER to run all services by one account, but be
 # aware that removing discrete components might conflict!
-#
 # SERVICE_USER=searx
-
-# Common $HOME folder of the service accounts
-SERVICE_HOME_BASE="${SERVICE_HOME_BASE:-/usr/local}"
-
diff --git a/Makefile b/Makefile
index acff120f3..335fa8efd 100644
--- a/Makefile
+++ b/Makefile
@@ -1,6 +1,10 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 .DEFAULT_GOAL=help
-include ./.config.mk
+
+export GIT_URL=https://github.com/asciimoo/searx
+export SEARX_URL=https://searx.me
+export DOCS_URL=https://asciimoo.github.io/searx
+
 include utils/makefile.include
 
 PYOBJECTS = searx
diff --git a/docs/conf.py b/docs/conf.py
index c2b2bbd84..761465c47 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -5,10 +5,10 @@ from sphinx_build_tools import load_sphinx_config
 from searx.version import VERSION_STRING
 from pallets_sphinx_themes import ProjectLink
 
-GIT_URL = os.environ.get("GIT_URL", "https://github.com/asciimoo/searx")
+from searx.brand import GIT_URL
 GIT_BRANCH =os.environ.get("GIT_BRANCH", "master")
-SEARX_URL = os.environ.get("SEARX_URL", "https://searx.me")
-DOCS_URL = os.environ.get("DOCS_URL", "https://asciimoo.github.io/searx/")
+from searx.brand import SEARX_URL
+from searx.brand import DOCS_URL
 
 # Project --------------------------------------------------------------
 
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index ed9942f4c..85aea8ae9 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -44,8 +44,7 @@ Makefile setup
 
 .. _git stash: https://git-scm.com/docs/git-stash
 
-The main setup is done in the :origin:`.config.sh` (read :ref:`toolboxing
-setup`)::
+The main setup is done in the :origin:`Makefile`::
 
   export SEARX_URL=https://searx.me
 
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 85000c592..c81a252a5 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -5,6 +5,8 @@
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
 in_container && lxc_set_suite_env
@@ -13,6 +15,9 @@ in_container && lxc_set_suite_env
 # config
 # ----------------------------------------------------------------------------
 
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
+
 FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
 | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
 [[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
diff --git a/utils/morty.sh b/utils/morty.sh
index 58de0cc99..a20cde36c 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -4,6 +4,8 @@
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
 in_container && lxc_set_suite_env
@@ -12,8 +14,9 @@ in_container && lxc_set_suite_env
 # config
 # ----------------------------------------------------------------------------
 
-PUBLIC_URL_PATH_MORTY="/morty"
-PUBLIC_URL_MORTY="$(dirname "${PUBLIC_URL}")${PUBLIC_URL_PATH_MORTY}"
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
+PUBLIC_URL_MORTY="$(echo $PUBLIC_URL |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
 
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
 # shellcheck disable=SC2034
diff --git a/utils/searx.sh b/utils/searx.sh
index 19cb41986..a6f2d5ee6 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -5,6 +5,8 @@
 
 # shellcheck source=utils/lib.sh
 source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
+# shellcheck source=utils/brand.env
+source "${REPO_ROOT}/utils/brand.env"
 source_dot_config
 source "${REPO_ROOT}/utils/lxc-searx.env"
 in_container && lxc_set_suite_env
@@ -13,6 +15,8 @@ in_container && lxc_set_suite_env
 # config
 # ----------------------------------------------------------------------------
 
+PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+
 SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
 
 SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
@@ -28,8 +32,7 @@ SERVICE_HOME="${SERVICE_HOME_BASE}/${SERVICE_USER}"
 # shellcheck disable=SC2034
 SERVICE_GROUP="${SERVICE_USER}"
 
-SEARX_GIT_URL="${SEARX_GIT_URL:-https://github.com/asciimoo/searx.git}"
-SEARX_GIT_BRANCH="${SEARX_GIT_BRANCH:-master}"
+GIT_BRANCH="${GIT_BRANCH:-master}"
 SEARX_PYENV="${SERVICE_HOME}/searx-pyenv"
 SEARX_SRC="${SERVICE_HOME}/searx-src"
 SEARX_SETTINGS_PATH="/etc/searx/settings.yml"
@@ -126,7 +129,7 @@ shell
 install / remove
   :all:        complete (de-) installation of searx service
   :user:       add/remove service user '$SERVICE_USER' ($SERVICE_HOME)
-  :searx-src:  clone $SEARX_GIT_URL
+  :searx-src:  clone $GIT_URL
   :pyenv:      create/remove virtualenv (python) in $SEARX_PYENV
   :uwsgi:      install searx uWSGI application
   :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
@@ -151,7 +154,6 @@ searx settings: ${SEARX_SETTINGS_PATH}
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
 
   PUBLIC_URL          : ${PUBLIC_URL}
-  PUBLIC_HOST         : ${PUBLIC_HOST}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
   SERVICE_USER        : ${SERVICE_USER}
 
@@ -286,7 +288,7 @@ update_searx() {
     echo
     tee_stderr 0.3 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
 cd ${SEARX_SRC}
-git checkout -B "$SEARX_GIT_BRANCH"
+git checkout -B "$GIT_BRANCH"
 git pull
 ${SEARX_SRC}/manage.sh update_packages
 EOF
@@ -344,12 +346,12 @@ clone_searx() {
     fi
     export SERVICE_HOME
     git_clone "$REPO_ROOT" "$SEARX_SRC" \
-              "$SEARX_GIT_BRANCH" "$SERVICE_USER"
+              "$GIT_BRANCH" "$SERVICE_USER"
 
     pushd "${SEARX_SRC}" > /dev/null
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 | prefix_stdout "$_service_prefix"
 cd "${SEARX_SRC}"
-git remote set-url origin ${SEARX_GIT_URL}
+git remote set-url origin ${GIT_URL}
 git config user.email "$ADMIN_EMAIL"
 git config user.name "$ADMIN_NAME"
 git config --list
@@ -546,7 +548,6 @@ inspect_service() {
 sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
 
   PUBLIC_URL          : ${PUBLIC_URL}
-  PUBLIC_HOST         : ${PUBLIC_HOST}
   SEARX_URL_PATH      : ${SEARX_URL_PATH}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
   SEARX_INTERNAL_URL  : ${SEARX_INTERNAL_URL}
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/apache2/sites-available/morty.conf
index 6bcc77b78..2c5727b19 100644
--- a/utils/templates/etc/apache2/sites-available/morty.conf
+++ b/utils/templates/etc/apache2/sites-available/morty.conf
@@ -23,6 +23,6 @@ ProxyPreserveHost On
     # needed by searx to render correct *Search URL* in the *Link* box and
     # *saved preference*.
 
-    # RequestHeader set Host ${PUBLIC_URL_PATH_MORTY}
+    # RequestHeader set Host ${PUBLIC_HOST}
 
 </Location>

From 0742c2504dae04ac054d263de8a2b8ab6161312c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 29 Mar 2020 16:28:17 +0200
Subject: [PATCH 079/125] build environment: add GIT_BRANCH variable

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile              | 3 +++
 docs/conf.py          | 2 +-
 docs/dev/makefile.rst | 6 ++++--
 searx/brand.py        | 1 +
 utils/brand.env       | 1 +
 5 files changed, 10 insertions(+), 3 deletions(-)

diff --git a/Makefile b/Makefile
index 335fa8efd..3c1bbdc66 100644
--- a/Makefile
+++ b/Makefile
@@ -2,6 +2,7 @@
 .DEFAULT_GOAL=help
 
 export GIT_URL=https://github.com/asciimoo/searx
+export GIT_BRANCH=master
 export SEARX_URL=https://searx.me
 export DOCS_URL=https://asciimoo.github.io/searx
 
@@ -114,12 +115,14 @@ useragents.update:  pyenvinstall
 searx.brand:
 	$(Q)echo "build searx/brand.py"
 	$(Q)echo "GIT_URL = '$(GIT_URL)'"  > searx/brand.py
+	$(Q)echo "GIT_BRANCH = '$(GIT_BRANCH)'"  >> searx/brand.py
 	$(Q)echo "ISSUE_URL = 'https://github.com/asciimoo/searx/issues'" >> searx/brand.py
 	$(Q)echo "SEARX_URL = '$(SEARX_URL)'" >> searx/brand.py
 	$(Q)echo "DOCS_URL = '$(DOCS_URL)'" >> searx/brand.py
 	$(Q)echo "PUBLIC_INSTANCES = 'https://searx.space'" >> searx/brand.py
 	$(Q)echo "build utils/brand.env"
 	$(Q)echo "export GIT_URL='$(GIT_URL)'"  > utils/brand.env
+	$(Q)echo "export GIT_BRANCH='$(GIT_BRANCH)'"  >> utils/brand.env
 	$(Q)echo "export ISSUE_URL='https://github.com/asciimoo/searx/issues'" >> utils/brand.env
 	$(Q)echo "export SEARX_URL='$(SEARX_URL)'" >> utils/brand.env
 	$(Q)echo "export DOCS_URL='$(DOCS_URL)'" >> utils/brand.env
diff --git a/docs/conf.py b/docs/conf.py
index 761465c47..9e6d32931 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -6,7 +6,7 @@ from searx.version import VERSION_STRING
 from pallets_sphinx_themes import ProjectLink
 
 from searx.brand import GIT_URL
-GIT_BRANCH =os.environ.get("GIT_BRANCH", "master")
+GIT_BRANCH = os.environ.get("GIT_BRANCH", "master")
 from searx.brand import SEARX_URL
 from searx.brand import DOCS_URL
 
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index 85aea8ae9..fc0b4639c 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -46,9 +46,9 @@ Makefile setup
 
 The main setup is done in the :origin:`Makefile`::
 
-  export SEARX_URL=https://searx.me
-
   export GIT_URL=https://github.com/asciimoo/searx
+  export GIT_BRANCH=master
+  export SEARX_URL=https://searx.me
   export DOCS_URL=https://asciimoo.github.io/searx
 
 .. sidebar:: fork & upstream
@@ -58,6 +58,8 @@ The main setup is done in the :origin:`Makefile`::
 
 :GIT_URL: Changes this, to point to your searx fork.
 
+:GIT_BRANCH: Changes this, to point to your searx branch.
+
 :SEARX_URL: Changes this, to point to your searx instance.
 
 :DOCS_URL: If you host your own (branded) documentation, change this URL.
diff --git a/searx/brand.py b/searx/brand.py
index d563447f6..91d2ab321 100644
--- a/searx/brand.py
+++ b/searx/brand.py
@@ -1,4 +1,5 @@
 GIT_URL = 'https://github.com/asciimoo/searx'
+GIT_BRANCH = 'master'
 ISSUE_URL = 'https://github.com/asciimoo/searx/issues'
 SEARX_URL = 'https://searx.me'
 DOCS_URL = 'https://asciimoo.github.io/searx'
diff --git a/utils/brand.env b/utils/brand.env
index 7fe1a3911..5d5b39513 100644
--- a/utils/brand.env
+++ b/utils/brand.env
@@ -1,4 +1,5 @@
 export GIT_URL='https://github.com/asciimoo/searx'
+export GIT_BRANCH='master'
 export ISSUE_URL='https://github.com/asciimoo/searx/issues'
 export SEARX_URL='https://searx.me'
 export DOCS_URL='https://asciimoo.github.io/searx'

From 5e2b161379420acd39e0474bb7f59545df7e1f3a Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 29 Mar 2020 16:46:07 +0200
Subject: [PATCH 080/125] Makefile: marginal fix

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile              | 2 +-
 utils/lxc-searx.env   | 7 ++++---
 utils/makefile.python | 6 +++---
 3 files changed, 8 insertions(+), 7 deletions(-)

diff --git a/Makefile b/Makefile
index 3c1bbdc66..60d31690c 100644
--- a/Makefile
+++ b/Makefile
@@ -79,7 +79,7 @@ PHONY += docs
 docs:  pyenvinstall sphinx-doc prebuild-includes
 	$(call cmd,sphinx,html,docs,docs)
 
-PHONY += docs-live
+PHONY += docs-live prebuild-includes
 docs-live:  pyenvinstall sphinx-live prebuild-includes
 	$(call cmd,sphinx_autobuild,html,docs,docs)
 
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index f81ee1c08..123108bf7 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -63,11 +63,12 @@ lxc_suite_info() {
         lxc_set_suite_env
         for ip in $(global_IPs) ; do
             if [[ $ip =~ .*:.* ]]; then
-                info_msg "(${ip%|*}) IPv6:     http://[${ip#*|}]"
+                info_msg "(${ip%|*}) IPv6:       http://[${ip#*|}]"
             else
                 # IPv4:
-                info_msg "(${ip%|*}) filtron:  http://${ip#*|}:4004/"
-                info_msg "(${ip%|*}) morty:    http://${ip#*|}:3000/"
+                info_msg "(${ip%|*}) filtron:    http://${ip#*|}:4004/"
+                info_msg "(${ip%|*}) morty:      http://${ip#*|}:3000/"
+                info_msg "(${ip%|*}) docs-live:  http://${ip#*|}:8080/"
             fi
         done
     )
diff --git a/utils/makefile.python b/utils/makefile.python
index 1660468f5..fddcb223f 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -12,9 +12,9 @@ PYDIST   = ./$(LXC_ENV_FOLDER)dist
 # folder where the python intermediate build files take place
 PYBUILD  = ./$(LXC_ENV_FOLDER)build
 # python version to use
-PY       ?=3
-PYTHON   ?= python$(PY)
-PIP      ?= pip$(PY)
+PY       =3
+PYTHON   = python$(PY)
+PIP      = pip$(PY)
 PIP_INST ?= --user
 
 # https://www.python.org/dev/peps/pep-0508/#extras

From 3e4d022d0425e4f7647573c97f105aa5208e5537 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 30 Mar 2020 11:03:46 +0200
Subject: [PATCH 081/125] [fix] make test.sh: fix various shellcheck error
 messages

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile            | 1 +
 utils/lib.sh        | 1 +
 utils/lxc-searx.env | 9 ++++++---
 utils/lxc.sh        | 5 +++--
 utils/morty.sh      | 2 +-
 5 files changed, 12 insertions(+), 6 deletions(-)

diff --git a/Makefile b/Makefile
index 60d31690c..8fa8e4f85 100644
--- a/Makefile
+++ b/Makefile
@@ -219,6 +219,7 @@ test.pylint: pyenvinstall
 
 # ubu1604: uses shellcheck v0.3.7 (from 04/2015), no longer supported!
 test.sh:
+	shellcheck -x -s bash utils/brand.env
 	shellcheck -x utils/lib.sh
 	shellcheck -x utils/filtron.sh
 	shellcheck -x utils/searx.sh
diff --git a/utils/lib.sh b/utils/lib.sh
index 632d47b5b..76173d8a9 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1079,6 +1079,7 @@ in_container() {
 
 LXC_ENV_FOLDER=
 if in_container; then
+    # shellcheck disable=SC2034
     LXC_ENV_FOLDER="lxc/$(hostname)/"
 fi
 
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index 123108bf7..a5de6c3de 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -32,11 +32,13 @@ lxc_set_suite_env() {
     export MORTY_LISTEN="0.0.0.0:3000"
 }
 
-lxc_suite_install_info="suite includes searx, morty & filtron"
+# shellcheck disable=SC2034
+LXC_SUITE_INSTALL_INFO="suite includes searx, morty & filtron"
 lxc_suite_install() {
     (
         lxc_set_suite_env
-        export FORCE_TIMEOUT=0
+        FORCE_TIMEOUT=0
+        export FORCE_TIMEOUT
         "${LXC_REPO_ROOT}/utils/searx.sh"   install all
         "${LXC_REPO_ROOT}/utils/morty.sh"   install all
         "${LXC_REPO_ROOT}/utils/filtron.sh" install all
@@ -50,7 +52,8 @@ lxc_suite_install() {
 lxc_suite_prepare_buildhost() {
     (
         lxc_set_suite_env
-        export FORCE_TIMEOUT=0
+        FORCE_TIMEOUT=0
+        export FORCE_TIMEOUT
         # "${LXC_REPO_ROOT}/utils/searx.sh* install packages
         "${LXC_REPO_ROOT}/utils/searx.sh"   install buildhost
         rst_title "buildhost installation finished ($(hostname))" part
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 6070735ea..b03ce2af8 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -104,7 +104,7 @@ cmd
   --             run command '...' in all containers of the LXC suite
   :<name>:       run command '...' in container <name>
 install
-  :suite:        install LXC suite; ${lxc_suite_install_info}
+  :suite:        install LXC suite; ${LXC_SUITE_INSTALL_INFO}
   :base:         prepare LXC; install basic packages
   :buildhost:    prepare LXC; install buildhost packages
 
@@ -241,13 +241,14 @@ main() {
             sudo_or_exit
             case $2 in
                 suite|base|buildhost)
-                    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install $2
+                    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
                     ;;
                 *) usage "$_usage"; exit 42 ;;
             esac
             ;;
         __install)
             # wrapped install commands, called once in each container
+            # shellcheck disable=SC2119
             case $2 in
                 suite) lxc_suite_install ;;
                 base) FORCE_TIMEOUT=0 lxc_install_base_packages ;;
diff --git a/utils/morty.sh b/utils/morty.sh
index a20cde36c..e6e14be8a 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -16,7 +16,7 @@ in_container && lxc_set_suite_env
 
 PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
 PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
-PUBLIC_URL_MORTY="$(echo $PUBLIC_URL |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
+PUBLIC_URL_MORTY="$(echo "$PUBLIC_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
 
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
 # shellcheck disable=SC2034

From 4e307edb2d3e5a726d78d73dea1ebe4b9d831615 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 30 Mar 2020 13:31:13 +0200
Subject: [PATCH 082/125] docs: document Makefile setup and 'buildenv' (aka
 brand)

Add documentation of the Makefile environment (aka brand, buildenv), introduced
in PR #1900.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile              |  2 ++
 docs/dev/makefile.rst | 37 ++++++++++++++++---------------------
 2 files changed, 18 insertions(+), 21 deletions(-)

diff --git a/Makefile b/Makefile
index d2c7438b7..73279ad25 100644
--- a/Makefile
+++ b/Makefile
@@ -1,10 +1,12 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 .DEFAULT_GOAL=help
 
+# START Makefile setup
 export GIT_URL=https://github.com/asciimoo/searx
 export GIT_BRANCH=master
 export SEARX_URL=https://searx.me
 export DOCS_URL=https://asciimoo.github.io/searx
+# END Makefile setup
 
 include utils/makefile.include
 
diff --git a/docs/dev/makefile.rst b/docs/dev/makefile.rst
index fc0b4639c..62cd0a984 100644
--- a/docs/dev/makefile.rst
+++ b/docs/dev/makefile.rst
@@ -18,18 +18,10 @@ With the aim to simplify development cycles, started with :pull:`1756` a
 Makefiles, we recommend to read gnu-make_ introduction.
 
 The usage is simple, just type ``make {target-name}`` to *build* a target.
-Calling the ``help`` target gives a first overview::
+Calling the ``help`` target gives a first overview (``make help``):
+
+.. program-output:: bash -c "cd ..; make --no-print-directory help"
 
-  $ make help
-    test      - run developer tests
-    docs      - build documentation
-    docs-live - autobuild HTML documentation while editing
-    run       - run developer instance
-    install   - developer install (./local)
-    uninstall - uninstall (./local)
-    gh-pages  - build docs & deploy on gh-pages branch
-    clean     - drop builds and environments
-    ...
 
 .. contents:: Contents
    :depth: 2
@@ -44,25 +36,28 @@ Makefile setup
 
 .. _git stash: https://git-scm.com/docs/git-stash
 
-The main setup is done in the :origin:`Makefile`::
-
-  export GIT_URL=https://github.com/asciimoo/searx
-  export GIT_BRANCH=master
-  export SEARX_URL=https://searx.me
-  export DOCS_URL=https://asciimoo.github.io/searx
-
 .. sidebar:: fork & upstream
 
    Commit changes in your (local) branch, fork or whatever, but do not push them
    upstream / `git stash`_ is your friend.
 
-:GIT_URL: Changes this, to point to your searx fork.
+The main setup is done in the :origin:`Makefile`.
 
+.. literalinclude:: ../../Makefile
+   :start-after: START Makefile setup
+   :end-before: END Makefile setup
+
+:GIT_URL:    Changes this, to point to your searx fork.
 :GIT_BRANCH: Changes this, to point to your searx branch.
+:SEARX_URL:  Changes this, to point to your searx instance.
+:DOCS_URL:   If you host your own (*brand*) documentation, change this URL.
 
-:SEARX_URL: Changes this, to point to your searx instance.
+If you change any of this build environment variables, you have to run ``make
+buildenv``::
 
-:DOCS_URL: If you host your own (branded) documentation, change this URL.
+  $ make buildenv
+  build searx/brand.py
+  build utils/brand.env
 
 .. _make pyenv:
 

From dd53c45a2cb46f882a856869de26215942749cba Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 30 Mar 2020 18:47:01 +0200
Subject: [PATCH 083/125] docs: add utils/lxc.sh docs, normalize filtron, morty
 & searx docs

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/utils/filtron.sh.rst | 25 ++++++-------
 docs/utils/index.rst      |  1 +
 docs/utils/lxc.sh.rst     | 79 +++++++++++++++++++++++++++++++++++++++
 docs/utils/morty.sh.rst   | 11 +++++-
 docs/utils/searx.sh.rst   | 77 +++++++++++++++++++++-----------------
 utils/filtron.sh          |  2 +-
 utils/lxc.sh              |  7 ++++
 utils/searx.sh            | 23 ++++++------
 8 files changed, 164 insertions(+), 61 deletions(-)
 create mode 100644 docs/utils/lxc.sh.rst

diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index e6d7d6251..40e57aa3c 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -27,8 +27,8 @@ The script adds a ``${SERVICE_USER}`` (default:``filtron``) and installs filtron
 into this user account:
 
 #. Create a separated user account (``filtron``).
-#. Download and install Go_ binary in users $HOME (``~filtron``).
-#. Install filtron with the package management of Go_ (``go get -v -u
+#. Download and install Go_ binary in user's $HOME (``~filtron``).
+#. Install filtron with the package management from Go_ (``go get -v -u
    github.com/asciimoo/filtron``)
 #. Setup a proper rule configuration :origin:`[ref]
    <utils/templates/etc/filtron/rules.json>` (``/etc/filtron/rules.json``).
@@ -36,6 +36,16 @@ into this user account:
    <utils/templates/lib/systemd/system/filtron.service>`
    (``/lib/systemd/system/filtron.service``).
 
+.. _filtron.sh overview:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory
+(:ref:`toolboxing common`):
+
+.. program-output:: ../utils/filtron.sh --help
+
 .. _reverse proxy:
 
 Public Reverse Proxy
@@ -63,14 +73,3 @@ To install searx in your public HTTP server use:
 		  $ sudo -H a2enmod headers
 		  $ sudo -H a2enmod proxy
 		  $ sudo -H a2enmod proxy_http
-
-.. _filtron.sh overview:
-
-Overview
-========
-
-The ``--help`` output of the script is largely self-explanatory
-(:ref:`toolboxing common`):
-
-.. program-output:: ../utils/filtron.sh --help
-
diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 1c77cbfcd..088586e43 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -15,6 +15,7 @@ developers.
    searx.sh
    filtron.sh
    morty.sh
+   lxc.sh
 
 .. _toolboxing common:
 
diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
new file mode 100644
index 000000000..68577ea13
--- /dev/null
+++ b/docs/utils/lxc.sh.rst
@@ -0,0 +1,79 @@
+
+
+.. _snap: https://snapcraft.io
+.. _snapcraft LXD: https://snapcraft.io/lxd
+.. _LXC/LXD Image Server: https://uk.images.linuxcontainers.org/
+.. _LXC: https://linuxcontainers.org/lxc/introduction/
+.. _LXD: https://linuxcontainers.org/lxd/introduction/
+.. _`LXD@github`: https://github.com/lxc/lxd
+
+.. _lxc.sh:
+
+================
+``utils/lxc.sh``
+================
+
+.. sidebar:: further reading
+
+   - snap_, `snapcraft LXD`_
+   - LXC_,  LXD_
+   - `LXC/LXD Image Server`_
+   - `LXD@github`_
+
+With the use of *Linux Containers* (LXC_) we can scale our tasks over a stack of
+containers, what we call the: *lxc suite*.  Before you can start with
+containers, you need to install and initiate LXD_ once::
+
+  $ snap install lxd
+  $ lxd init --auto
+
+The *searx suite* (:origin:`lxc-searx.env <utils/lxc-searx.env>`) is loaded by
+default, every time you start the ``lxc.sh`` script (you do not need to care
+about).  To make use of the containers from the *searx suite*, you have to build
+the :ref:`LXC suite containers <lxc.sh --help>` first.  But be warned, this
+might take some time::
+
+  $ sudo -H ./utils/lxc.sh build
+
+A cup of coffee later, your LXC suite is build up and you can run whatever task
+you want / in a selected or even in all :ref:`LXC suite containers <lxc.sh
+--help>`.  Each container shares the root folder of the repository and the
+command ``utils/lxc.sh cmd`` handles relative path names *transparent*::
+
+  $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
+  ...
+  [searx-ubu2004]   -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
+  [searx-fedora31]  -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
+  [searx-archlinux] -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
+
+With this in mind, you can run :ref:`searx.sh` and install packages, needed by
+searx::
+
+  $ sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh install packages
+
+And run one of the :origin:`Makefile` targets::
+
+  $ sudo -H ./utils/lxc.sh cmd -- make test.sh
+
+You can install a *buildhost environment* into the containers (time for another
+cup of coffee)::
+
+  $ sudo -H ./utils/lxc.sh install buildhost
+
+If you want to get rid off all the containers, just type::
+
+  $ sudo -H ./utils/lxc.sh remove
+
+To clean up your local images use::
+
+  $ sudo -H ./utils/lxc.sh remove images
+
+.. _lxc.sh --help:
+
+Overview
+========
+
+The ``--help`` output of the script is largely self-explanatory:
+
+.. program-output:: ../utils/lxc.sh --help
+
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 5ab6ee982..2bdc4d9c8 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -1,6 +1,7 @@
 
 .. _morty: https://github.com/asciimoo/morty
 .. _morty's README: https://github.com/asciimoo/morty
+.. _Go: https://golang.org/
 
 .. _morty.sh:
 
@@ -22,7 +23,15 @@ running the command:
    sudo -H ./utils/morty.sh install all
 
 The script adds a ``${SERVICE_USER}`` (default:``morty``) and installs morty_
-into this user account.
+into this user account:
+
+#. Create a separated user account (``morty``).
+#. Download and install Go_ binary in user's $HOME (``~morty``).
+#. Install morty_ with the package management from Go_ (``go get -v -u
+   github.com/asciimoo/morty``)
+#. Setup a systemd service unit :origin:`[ref]
+   <utils/templates/lib/systemd/system/morty.service>`
+   (``/lib/systemd/system/morty.service``).
 
 .. hint::
 
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
index 988c08438..905f765dd 100644
--- a/docs/utils/searx.sh.rst
+++ b/docs/utils/searx.sh.rst
@@ -29,41 +29,6 @@ into this user account.  The installation is described in chapter
 
 .. _intranet reverse proxy:
 
-Intranet Reverse Proxy
-======================
-
-To install searx in your intranet HTTP server use:
-
-.. code::  bash
-
-   sudo -H ./utils/searx.sh apache install
-
-.. warning::
-
-   This setup is **not** suitable **for public instances**, go on with
-   :ref:`reverse proxy`!
-
-.. tabs::
-
-   .. group-tab:: apache
-
-      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
-	 :language: apache
-
-      .. tabs::
-
-	 .. group-tab:: Ubuntu / debian
-
-	       .. code-block:: sh
-
-		  $ sudo -H apt install libapache2-mod-uwsgi
-
-	 .. group-tab:: Arch Linux
-
-	    .. code-block:: sh
-
-	       $ sudo pacman -S uwsgi
-
 Overview
 ========
 
@@ -71,3 +36,45 @@ The ``--help`` output of the script is largely self-explanatory
 (:ref:`toolboxing common`):
 
 .. program-output:: ../utils/searx.sh --help
+
+
+Intranet Reverse Proxy
+======================
+
+.. warning::
+
+   This setup is **not** suitable **for public instances**, go on with
+   :ref:`reverse proxy`!
+
+To install searx in your intranet HTTP server use:
+
+.. code::  bash
+
+   sudo -H ./utils/searx.sh apache install
+
+.. tabs::
+
+   .. group-tab:: apache
+
+      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
+         :language: apache
+
+      .. tabs::
+
+         .. group-tab:: Ubuntu / debian
+
+            .. code-block:: sh
+
+               $ sudo -H apt install libapache2-mod-uwsgi
+
+         .. group-tab:: Arch Linux
+
+            .. code-block:: sh
+
+               $ sudo -H pacman -S uwsgi
+
+         .. group-tab:: Fedora / RHEL
+
+            .. code-block:: sh
+
+               $ sudo -H dnf install uwsgi
diff --git a/utils/filtron.sh b/utils/filtron.sh
index c81a252a5..208166fda 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -90,7 +90,7 @@ inspect service
   show service status and log
 option
   set one of the available options
-apache : ${PUBLIC_URL}
+apache (${PUBLIC_URL})
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_FILTRON_SITE}
 
diff --git a/utils/lxc.sh b/utils/lxc.sh
index b03ce2af8..2526a257d 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -324,6 +324,13 @@ lxc_delete_images_localy() {
             lxc_delete_local_image "$i"
         done
     fi
+
+    for i in $(lxc image list --format csv | grep '^,' | sed 's/,\([^,]*\).*$/\1/'); do
+        if ask_yn "Image $i has no alias, do you want to delete the image?" Yn; then
+            lxc_delete_local_image "$i"
+        fi
+    done
+
     echo
     lxc image list local:
 }
diff --git a/utils/searx.sh b/utils/searx.sh
index a6f2d5ee6..71ab978a1 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -44,30 +44,33 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 SEARX_PACKAGES_debian="\
 python3-dev python3-babel python3-venv
 uwsgi uwsgi-plugin-python3
-git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev"
+git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
+shellcheck"
 
 BUILD_PACKAGES_debian="\
-shellcheck graphviz imagemagick texlive-xetex librsvg2-bin
+graphviz imagemagick texlive-xetex librsvg2-bin
 texlive-latex-recommended texlive-extra-utils ttf-dejavu"
 
 # pacman packages
 SEARX_PACKAGES_arch="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python
-git base-devel libxml2"
+git base-devel libxml2
+shellcheck"
 
 BUILD_PACKAGES_arch="\
-shellcheck graphviz imagemagick texlive-bin extra/librsvg
+graphviz imagemagick texlive-bin extra/librsvg
 texlive-core texlive-latexextra ttf-dejavu"
 
 # dnf packages
 SEARX_PACKAGES_fedora="\
 python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python3
-git @development-tools libxml2"
+git @development-tools libxml2
+ShellCheck"
 
 BUILD_PACKAGES_fedora="\
-ShellCheck graphviz graphviz-gd ImageMagick librsvg2-tools
+graphviz graphviz-gd ImageMagick librsvg2-tools
 texlive-xetex-bin texlive-collection-fontsrecommended
 texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
 dejavu-sans-mono-fonts"
@@ -76,23 +79,21 @@ case $DIST_ID in
     ubuntu|debian)
         SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
         BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+        APACHE_APT_PACKAGES="libapache2-mod-uwsgi"
         ;;
     arch)
         SEARX_PACKAGES="${SEARX_PACKAGES_arch}"
         BUILD_PACKAGES="${BUILD_PACKAGES_arch}"
+        APACHE_APT_PACKAGES="uwsgi"
         ;;
     fedora)
         SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
         BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
+        APACHE_APT_PACKAGES="uwsgi"
         ;;
 esac
 
 # Apache Settings
-
-APACHE_APT_PACKAGES="\
-  libapache2-mod-uwsgi \
-"
-
 APACHE_SEARX_SITE="searx.conf"
 
 # shellcheck disable=SC2034

From f27f6c6c5d372b257dfe671ec26eb73f6e12ed28 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 31 Mar 2020 18:25:40 +0200
Subject: [PATCH 084/125] docs: use make and uitls/searx.sh in containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/buildhosts.rst      |  20 +++++-
 docs/build-templates/searx.rst |  26 ++++++++
 docs/utils/lxc.sh.rst          | 115 ++++++++++++++++++++++++---------
 utils/lxc-searx.env            |  12 ----
 utils/lxc.sh                   |  10 +--
 utils/searx.sh                 |  16 +++--
 6 files changed, 145 insertions(+), 54 deletions(-)

diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index a6820aa61..04698d8d2 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -4,9 +4,6 @@
 Buildhosts
 ==========
 
-To get best results from build, its recommend to install additional packages
-on build hosts.
-
 .. sidebar:: This article needs some work
 
    If you have any contribution send us your :pull:`PR <../pulls>`, see
@@ -17,6 +14,23 @@ on build hosts.
    :local:
    :backlinks: entry
 
+To get best results from build, its recommend to install additional packages
+on build hosts (see :ref:`searx.sh`).::
+
+  sudo -H ./utils/searx.sh buildhost
+
+This will install packages needed by searx:
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START distro-packages
+   :end-before: END distro-packages
+
+and packages needed to build docuemtation and run tests:
+
+.. include:: ../../build/docs/includes/searx.rst
+   :start-after: START build-packages
+   :end-before: END build-packages
+
 .. _docs build:
 
 Build docs
diff --git a/docs/build-templates/searx.rst b/docs/build-templates/searx.rst
index 9e33928ad..964deb23d 100644
--- a/docs/build-templates/searx.rst
+++ b/docs/build-templates/searx.rst
@@ -28,6 +28,32 @@ ${fedora}
 
 .. END distro-packages
 
+.. START build-packages
+
+.. tabs::
+
+  .. group-tab:: Ubuntu / debian
+
+    .. code-block:: sh
+
+      $ sudo -H apt-get install -y \\
+${debian_build}
+
+  .. group-tab:: Arch Linux
+
+    .. code-block:: sh
+
+      $ sudo -H pacman -S --noconfirm \\
+${arch_build}
+
+  .. group-tab::  Fedora / RHEL
+
+    .. code-block:: sh
+
+      $ sudo -H dnf install -y \\
+${fedora_build}
+
+.. END build-packages
 
 .. START create user
 
diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
index 68577ea13..20f387158 100644
--- a/docs/utils/lxc.sh.rst
+++ b/docs/utils/lxc.sh.rst
@@ -1,5 +1,4 @@
 
-
 .. _snap: https://snapcraft.io
 .. _snapcraft LXD: https://snapcraft.io/lxd
 .. _LXC/LXD Image Server: https://uk.images.linuxcontainers.org/
@@ -7,6 +6,8 @@
 .. _LXD: https://linuxcontainers.org/lxd/introduction/
 .. _`LXD@github`: https://github.com/lxc/lxd
 
+.. _archlinux: https://www.archlinux.org/
+
 .. _lxc.sh:
 
 ================
@@ -21,24 +22,31 @@
    - `LXD@github`_
 
 With the use of *Linux Containers* (LXC_) we can scale our tasks over a stack of
-containers, what we call the: *lxc suite*.  Before you can start with
-containers, you need to install and initiate LXD_ once::
+containers, what we call the: *lxc suite*.  The *searx suite*
+(:origin:`lxc-searx.env <utils/lxc-searx.env>`) is loaded by default, every time
+you start the ``lxc.sh`` script (*you do not need to care about*).
+
+Before you can start with containers, you need to install and initiate LXD_
+once::
 
   $ snap install lxd
   $ lxd init --auto
 
-The *searx suite* (:origin:`lxc-searx.env <utils/lxc-searx.env>`) is loaded by
-default, every time you start the ``lxc.sh`` script (you do not need to care
-about).  To make use of the containers from the *searx suite*, you have to build
-the :ref:`LXC suite containers <lxc.sh --help>` first.  But be warned, this
-might take some time::
+To make use of the containers from the *searx suite*, you have to build the
+:ref:`LXC suite containers <lxc.sh help>` initial.  But be warned, this might
+take some time::
 
   $ sudo -H ./utils/lxc.sh build
 
 A cup of coffee later, your LXC suite is build up and you can run whatever task
 you want / in a selected or even in all :ref:`LXC suite containers <lxc.sh
---help>`.  Each container shares the root folder of the repository and the
-command ``utils/lxc.sh cmd`` handles relative path names *transparent*::
+help>`.
+
+*Good to know ...*
+
+Eeach container shares the root folder of the repository and the
+command ``utils/lxc.sh cmd`` **handles relative path names transparent**,
+compare output of::
 
   $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
   ...
@@ -46,29 +54,70 @@ command ``utils/lxc.sh cmd`` handles relative path names *transparent*::
   [searx-fedora31]  -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
   [searx-archlinux] -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
 
-With this in mind, you can run :ref:`searx.sh` and install packages, needed by
-searx::
-
-  $ sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh install packages
-
-And run one of the :origin:`Makefile` targets::
-
-  $ sudo -H ./utils/lxc.sh cmd -- make test.sh
-
-You can install a *buildhost environment* into the containers (time for another
-cup of coffee)::
-
-  $ sudo -H ./utils/lxc.sh install buildhost
-
-If you want to get rid off all the containers, just type::
+If there comes the time you want to **get rid off all** the containers and
+**clean up local images** just type::
 
   $ sudo -H ./utils/lxc.sh remove
-
-To clean up your local images use::
-
   $ sudo -H ./utils/lxc.sh remove images
 
-.. _lxc.sh --help:
+
+Install suite
+=============
+
+To install the complete :ref:`searx suite (includes searx, morty & filtron)
+<lxc-searx.env>` into all LXC_ use::
+
+  $ sudo -H ./utils/lxc.sh install suite
+
+The command above installs a searx suite (see :ref:`installation scripts`).  To
+get the IP (URL) of the filtron service in the containers use ``show suite``
+command.  To test instances from containers just open the URLs in your
+WEB-Browser::
+
+  $ sudo ./utils/lxc.sh show suite | grep filtron
+  [searx-ubu1604]  INFO:  (eth0) filtron:    http://n.n.n.135:4004/
+  [searx-ubu1804]  INFO:  (eth0) filtron:    http://n.n.n.141:4004/
+  [searx-ubu1910]  INFO:  (eth0) filtron:    http://n.n.n.137:4004/
+  [searx-ubu2004]  INFO:  (eth0) filtron:    http://n.n.n.127:4004/
+  [searx-fedora31]  INFO:  (eth0) filtron:    http://n.n.n.18:4004/
+  [searx-archlinux]  INFO:  (eth0) filtron:    http://n.n.n.12:4004/
+
+  
+Running commands
+================
+
+**Inside containers, you can use make or run scripts** from the
+:ref:`toolboxing`.  By example: to setup a :ref:`buildhosts` and run the
+Makefile target ``test`` in the archlinux_ container::
+
+  sudo -H ./utils/lxc.sh cmd searx-archlinux ./utils/searx.sh buildhost
+  sudo -H ./utils/lxc.sh cmd searx-archlinux make test
+
+
+Setup searx buildhost
+=====================
+
+You can **install the searx buildhost environment** into one or all containers.
+The installation procedure to set up a :ref:`build host<buildhosts>` takes its
+time.  Installation in all containers will take more time (time for another cup
+of coffee).::
+
+  sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh buildhost
+
+To build (live) documentation inside a archlinux_ container::
+
+  sudo -H ./utils/lxc.sh cmd searx-archlinux make docs-clean docs-live
+  ...
+  [I 200331 15:00:42 server:296] Serving on http://0.0.0.0:8080
+
+To get IP of the container and the port number *live docs* is listening::
+
+  $ sudo ./utils/lxc.sh show suite | grep docs-live
+  ...
+  [searx-archlinux]  INFO:  (eth0) docs-live:  http://n.n.n.12:8080/
+
+
+.. _lxc.sh help:
 
 Overview
 ========
@@ -77,3 +126,11 @@ The ``--help`` output of the script is largely self-explanatory:
 
 .. program-output:: ../utils/lxc.sh --help
 
+
+.. _lxc-searx.env:
+
+searx suite
+===========
+
+.. literalinclude:: ../../utils/lxc-searx.env
+   :language: bash
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index a5de6c3de..41bbae468 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -49,18 +49,6 @@ lxc_suite_install() {
     )
 }
 
-lxc_suite_prepare_buildhost() {
-    (
-        lxc_set_suite_env
-        FORCE_TIMEOUT=0
-        export FORCE_TIMEOUT
-        # "${LXC_REPO_ROOT}/utils/searx.sh* install packages
-        "${LXC_REPO_ROOT}/utils/searx.sh"   install buildhost
-        rst_title "buildhost installation finished ($(hostname))" part
-        echo
-    )
-}
-
 lxc_suite_info() {
     (
         lxc_set_suite_env
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 2526a257d..612ca566d 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -82,7 +82,7 @@ usage::
   $_cmd [start|stop] [containers|<name>]
   $_cmd show         [info|config|suite|images]
   $_cmd cmd          [--|<name>] '...'
-  $_cmd install      [suite|base|buildhost]
+  $_cmd install      [suite|base]
 
 build
   :containers:   build, launch and 'install basic' packages on 'containers'
@@ -106,7 +106,6 @@ cmd
 install
   :suite:        install LXC suite; ${LXC_SUITE_INSTALL_INFO}
   :base:         prepare LXC; install basic packages
-  :buildhost:    prepare LXC; install buildhost packages
 
 EOF
     usage_images
@@ -150,7 +149,9 @@ main() {
 
     # don't check prerequisite when in recursion
     if [[ ! $1 == __* ]]; then
-        ! required_commands lxc && lxd_info && exit 42
+        if ! in_container; then
+            ! required_commands lxc && lxd_info && exit 42
+        fi
         [[ -z $LXC_SUITE ]] && err_msg "missing LXC_SUITE" && exit 42 
     fi
 
@@ -240,7 +241,7 @@ main() {
         install)
             sudo_or_exit
             case $2 in
-                suite|base|buildhost)
+                suite|base)
                     lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
                     ;;
                 *) usage "$_usage"; exit 42 ;;
@@ -252,7 +253,6 @@ main() {
             case $2 in
                 suite) lxc_suite_install ;;
                 base) FORCE_TIMEOUT=0 lxc_install_base_packages ;;
-                buildhost) lxc_suite_prepare_buildhost ;;
             esac
             ;;
         doc)
diff --git a/utils/searx.sh b/utils/searx.sh
index 71ab978a1..17e8db84e 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -48,7 +48,7 @@ git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
 shellcheck"
 
 BUILD_PACKAGES_debian="\
-graphviz imagemagick texlive-xetex librsvg2-bin
+firefox graphviz imagemagick texlive-xetex librsvg2-bin
 texlive-latex-recommended texlive-extra-utils ttf-dejavu"
 
 # pacman packages
@@ -59,7 +59,7 @@ git base-devel libxml2
 shellcheck"
 
 BUILD_PACKAGES_arch="\
-graphviz imagemagick texlive-bin extra/librsvg
+firefox graphviz imagemagick texlive-bin extra/librsvg
 texlive-core texlive-latexextra ttf-dejavu"
 
 # dnf packages
@@ -70,7 +70,7 @@ git @development-tools libxml2
 ShellCheck"
 
 BUILD_PACKAGES_fedora="\
-graphviz graphviz-gd ImageMagick librsvg2-tools
+firefox graphviz graphviz-gd ImageMagick librsvg2-tools
 texlive-xetex-bin texlive-collection-fontsrecommended
 texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
 dejavu-sans-mono-fonts"
@@ -116,7 +116,7 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache|packages]
+  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache|packages|buildhost]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
@@ -135,7 +135,7 @@ install / remove
   :uwsgi:      install searx uWSGI application
   :settings:   reinstall settings from ${REPO_ROOT}/searx/settings.yml
   :packages:   install needed packages from OS package manager
-  :buildhost:  install packages from OS package manager needed by buildhost
+  :buildhost:  install packages from OS package manager needed by buildhosts
 update searx
   Update searx installation ($SERVICE_HOME)
 activate service
@@ -688,9 +688,15 @@ rst-doc() {
     local debian="${SEARX_PACKAGES_debian}"
     local arch="${SEARX_PACKAGES_arch}"
     local fedora="${SEARX_PACKAGES_fedora}"
+    local debian_build="${BUILD_PACKAGES_debian}"
+    local arch_build="${BUILD_PACKAGES_arch}"
+    local fedora_build="${BUILD_PACKAGES_fedora}"
     debian="$(echo "${debian}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
     arch="$(echo "${arch}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"
     fedora="$(echo "${fedora}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+    debian_build="$(echo "${debian_build}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+    arch_build="$(echo "${arch_build}"     | sed 's/.*/          & \\/' | sed '$ s/.$//')"
+    fedora_build="$(echo "${fedora_build}" | sed 's/.*/          & \\/' | sed '$ s/.$//')"
 
     eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/searx.rst")\""
 

From 6007bd7fc898bed7bdcd565f3f21aecdd86b6b83 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 1 Apr 2020 10:59:51 +0200
Subject: [PATCH 085/125] makefile.python: fix (old) typo

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/makefile.python | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/makefile.python b/utils/makefile.python
index fddcb223f..b0f1c247a 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -123,7 +123,7 @@ quiet_cmd_pyenvuninstall = PYENV     uninstall   $2
 # $2 path to folder where virtualenv take place
 quiet_cmd_virtualenv  = PYENV     usage: $ source ./$@/bin/activate
       cmd_virtualenv  = \
-	if [ ! -d "./$(PY_ENV)" ];then                                  \
+	if [ ! -d "$(PY_ENV)" ];then                                    \
 		$(PYTHON) -m venv $(VTENV_OPTS) $2;                     \
 	else                                                            \
 		echo "PYENV     using virtualenv from $2";              \

From d599bdff0d63e1b21946389c690287ee896c267f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 1 Apr 2020 11:15:34 +0200
Subject: [PATCH 086/125] travis: make travis build more verbose

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .travis.yml | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/.travis.yml b/.travis.yml
index 8c3f7285c..59913af90 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -8,15 +8,15 @@ addons:
   firefox: "latest"
 
 install:
-  - make install
-  - make gecko.driver
-  - make node.env
+  - make V=1 install
+  - make V=1 gecko.driver
+  - make V=1 node.env
   - local/py3/bin/pip install codecov
 script:
-  - make themes
-  - make test
+  - make V=1 themes
+  - make V=1 test
 after_success:
-  - make test.coverage
+  - make V=1 test.coverage
   - codecov
 
 stages:

From 3bbcfe6ab3513315f2491160355ea671726d5b69 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 1 Apr 2020 11:57:48 +0200
Subject: [PATCH 087/125] travis: make travis build more verbose

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/makefile.python | 8 +++++---
 1 file changed, 5 insertions(+), 3 deletions(-)

diff --git a/utils/makefile.python b/utils/makefile.python
index b0f1c247a..2e488bf2b 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -124,10 +124,12 @@ quiet_cmd_pyenvuninstall = PYENV     uninstall   $2
 quiet_cmd_virtualenv  = PYENV     usage: $ source ./$@/bin/activate
       cmd_virtualenv  = \
 	if [ ! -d "$(PY_ENV)" ];then                                    \
+		echo "PYENV     create virtualenv $2";                  \
 		$(PYTHON) -m venv $(VTENV_OPTS) $2;                     \
 	else                                                            \
 		echo "PYENV     using virtualenv from $2";              \
-        fi
+        fi; \
+	echo "commands available at $(PY_ENV_BIN):"; ls $(PY_ENV_BIN) | $(FMT) ;
 
 # $2 path to lint
 quiet_cmd_pylint      = LINT      $@
@@ -205,8 +207,8 @@ pyclean:
 pyenv: $(PY_ENV)
 $(PY_ENV): python-exe
 	$(call cmd,virtualenv,$(PY_ENV))
-	@$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) -U wheel pip setuptools
-	@$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) -r requirements.txt
+	$(Q)$(PY_ENV_BIN)/python -m pip install $(PIP_VERBOSE) -U pip wheel pip setuptools
+	$(Q)$(PY_ENV_BIN)/python -m pip install $(PIP_VERBOSE) -r requirements.txt
 
 PHONY += pylint-exe
 pylint-exe: $(PY_ENV)

From a62e3c9358d4cb290d0c821fc8e79fb3a4f0830a Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 1 Apr 2020 12:22:08 +0200
Subject: [PATCH 088/125] travis: build on os: linux, dist: bionic, use
 platform arch: arm64

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .travis.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index 59913af90..c79c5c4c5 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,5 +1,7 @@
+os: linux
+dist: bionic
+arch: arm64
 language: python
-sudo: false
 cache:
   - directories:
     - $HOME/.cache/pip

From 0a951d0e550089ec8499b87ca7077420b23ff368 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 1 Apr 2020 12:44:32 +0200
Subject: [PATCH 089/125] [fix] travis:  Permission denied:
 '/home/travis/.cache/pip/...'

Change owner https://docs.travis-ci.com/user/caching/#caches-and-read-permissions
and remove old log in the *before_caching* phase:

  https://docs.travis-ci.com/user/caching/#before_cache-phase

About pip cache see: https://docs.travis-ci.com/user/caching/#pip-cache

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .travis.yml | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/.travis.yml b/.travis.yml
index c79c5c4c5..c1a9c2b21 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,7 +5,9 @@ language: python
 cache:
   - directories:
     - $HOME/.cache/pip
-
+before_cache:
+    - sudo chown -R travis:travis $HOME/.cache/pip
+    - rm -f $HOME/.cache/pip/log/debug.log
 addons:
   firefox: "latest"
 

From f32b4fceddf698f6a33c42a866ca21c6cc202841 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 2 Apr 2020 20:27:44 +0200
Subject: [PATCH 090/125] LXC: add virtualenv to LXC_BASE_PACKAGES

Commit 09a40625 adds virtualenv dependency.  BTW remove deprecated
--no-site-packages.  Not having access to global site-packages is now the
default behavior.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh          | 8 ++++----
 utils/makefile.python | 2 +-
 utils/searx.sh        | 6 +++---
 3 files changed, 8 insertions(+), 8 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index 76173d8a9..60476610a 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -952,7 +952,7 @@ pkg_install() {
             ;;
         arch)
             # shellcheck disable=SC2068
-            pacman -S --noconfirm $@
+            pacman -Sy --noconfirm $@
             ;;
         fedora)
             # shellcheck disable=SC2068
@@ -1098,13 +1098,13 @@ EOF
 }
 
 # apt packages
-LXC_BASE_PACKAGES_debian="bash git build-essential python3"
+LXC_BASE_PACKAGES_debian="bash git build-essential python3 virtualenv"
 
 # pacman packages
-LXC_BASE_PACKAGES_arch="bash git base-devel python"
+LXC_BASE_PACKAGES_arch="bash git base-devel python python-virtualenv"
 
 # dnf packages
-LXC_BASE_PACKAGES_fedora="bash git @development-tools python"
+LXC_BASE_PACKAGES_fedora="bash git @development-tools python virtualenv"
 
 case $DIST_ID in
     ubuntu|debian) LXC_BASE_PACKAGES="${LXC_BASE_PACKAGES_debian}" ;;
diff --git a/utils/makefile.python b/utils/makefile.python
index 49944442e..df16acbbf 100644
--- a/utils/makefile.python
+++ b/utils/makefile.python
@@ -30,7 +30,6 @@ PYLINT_RC ?= .pylintrc
 TEST_FOLDER  ?= ./tests
 TEST         ?= .
 
-VTENV_OPTS   = "--no-site-packages"
 PY_ENV       = ./$(LXC_ENV_FOLDER)local/py$(PY)
 PY_ENV_BIN   = $(PY_ENV)/bin
 PY_ENV_ACT   = . $(PY_ENV_BIN)/activate
@@ -41,6 +40,7 @@ ifeq ($(OS),Windows_NT)
   PY_ENV_ACT = $(PY_ENV_BIN)/activate
 endif
 
+VTENV_OPTS ?=
 ifeq ($(PYTHON),python)
   VIRTUALENV   = virtualenv
 else
diff --git a/utils/searx.sh b/utils/searx.sh
index 17e8db84e..981c498fc 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -42,7 +42,7 @@ SEARX_UWSGI_SOCKET="/run/uwsgi/app/searx/socket"
 
 # apt packages
 SEARX_PACKAGES_debian="\
-python3-dev python3-babel python3-venv
+virtualenv python3-dev python3-babel python3-venv
 uwsgi uwsgi-plugin-python3
 git build-essential libxslt-dev zlib1g-dev libffi-dev libssl-dev
 shellcheck"
@@ -53,7 +53,7 @@ texlive-latex-recommended texlive-extra-utils ttf-dejavu"
 
 # pacman packages
 SEARX_PACKAGES_arch="\
-python python-pip python-lxml python-babel
+python-virtualenv python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python
 git base-devel libxml2
 shellcheck"
@@ -64,7 +64,7 @@ texlive-core texlive-latexextra ttf-dejavu"
 
 # dnf packages
 SEARX_PACKAGES_fedora="\
-python python-pip python-lxml python-babel
+virtualenv python python-pip python-lxml python-babel
 uwsgi uwsgi-plugin-python3
 git @development-tools libxml2
 ShellCheck"

From af988dbf71ce4b14d2056676d7d9691335266d83 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 3 Apr 2020 17:08:42 +0200
Subject: [PATCH 091/125] utils/lxc.sh: support build of selected containers

  $ sudo -H ./utils/lxc.sh build  <name>
  $ sudo -H ./utils/lxc.sh show   [images|suite|info|config [<name>]]

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/utils/lxc.sh.rst |  18 ++--
 utils/lib.sh          |  31 ++++++
 utils/lxc-searx.env   |   3 +
 utils/lxc.sh          | 230 +++++++++++++++++++++++++++---------------
 4 files changed, 195 insertions(+), 87 deletions(-)

diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
index 20f387158..fcddfb340 100644
--- a/docs/utils/lxc.sh.rst
+++ b/docs/utils/lxc.sh.rst
@@ -33,14 +33,17 @@ once::
   $ lxd init --auto
 
 To make use of the containers from the *searx suite*, you have to build the
-:ref:`LXC suite containers <lxc.sh help>` initial.  But be warned, this might
-take some time::
+:ref:`LXC suite containers <lxc.sh help>` initial.  But be warned, **this might
+take some time**::
 
   $ sudo -H ./utils/lxc.sh build
 
 A cup of coffee later, your LXC suite is build up and you can run whatever task
 you want / in a selected or even in all :ref:`LXC suite containers <lxc.sh
-help>`.
+help>`.  If you do not want to build all containers, **you can build just
+one**::
+
+  $ sudo -H ./utils/lxc.sh build searx-ubu1804
 
 *Good to know ...*
 
@@ -50,9 +53,12 @@ compare output of::
 
   $ sudo -H ./utils/lxc.sh cmd -- ls -la Makefile
   ...
-  [searx-ubu2004]   -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
-  [searx-fedora31]  -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
-  [searx-archlinux] -rw-r--r-- 1 root root 7603 Mar 30 11:54 Makefile
+
+In the containers, you can run what ever you want, e.g. to start a bash use::
+
+  $ sudo -H ./utils/lxc.sh cmd searx-ubu1804 bash
+  INFO:  [searx-ubu1804] bash
+  root@searx-ubu1804:/share/searx#
 
 If there comes the time you want to **get rid off all** the containers and
 **clean up local images** just type::
diff --git a/utils/lib.sh b/utils/lib.sh
index 60476610a..e466100c1 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1118,6 +1118,37 @@ lxc_install_base_packages() {
     pkg_install "${LXC_BASE_PACKAGES}"
 }
 
+
+lxc_image_copy() {
+
+    # usage: lxc_copy_image <remote image> <local image>
+    #
+    #        lxc_copy_image "images:ubuntu/19.10"  "ubu1910"
+
+    if lxc_image_exists "local:${LXC_SUITE[i+1]}"; then
+        info_msg "image ${LXC_SUITE[i]} already copied --> ${LXC_SUITE[i+1]}"
+    else
+        info_msg "copy image locally ${LXC_SUITE[i]} --> ${LXC_SUITE[i+1]}"
+        lxc image copy "${LXC_SUITE[i]}" local: \
+            --alias  "${LXC_SUITE[i+1]}" | prefix_stdout
+    fi
+}
+
+lxc_init_container() {
+
+    # usage: lxc_init_container <image name> <container name>
+
+    local image_name="$1"
+    local container_name="$2"
+
+    if lxc info "${container_name}" &>/dev/null; then
+        info_msg "container '${container_name}' already exists"
+    else
+        info_msg "create container instance: ${container_name}"
+        lxc init "local:${image_name}" "${container_name}"
+    fi
+}
+
 lxc_exists(){
 
     # usage: lxc_exists <name> || echo "container <name> does not exists"
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index 41bbae468..2a3fdf37b 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -15,6 +15,9 @@ lxc_set_suite_env() {
     export LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
     export LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-searx}"
     export LXC_SUITE=(
+
+        # to disable containers, comment out lines ..
+
         # end of standard support see https://wiki.ubuntu.com/Releases
         "$LINUXCONTAINERS_ORG_NAME:ubuntu/16.04"  "ubu1604" # April 2021
         "$LINUXCONTAINERS_ORG_NAME:ubuntu/18.04"  "ubu1804" # April 2023
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 612ca566d..813b50843 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -76,16 +76,17 @@ usage() {
 
 usage::
 
-  $_cmd build        [containers]
+  $_cmd build        [containers|<name>]
   $_cmd copy         [images]
   $_cmd remove       [containers|<name>|images]
   $_cmd [start|stop] [containers|<name>]
-  $_cmd show         [info|config|suite|images]
+  $_cmd show         [images|suite|info|config [<name>]]
   $_cmd cmd          [--|<name>] '...'
   $_cmd install      [suite|base]
 
 build
-  :containers:   build, launch and 'install basic' packages on 'containers'
+  :containers:   build, launch all containers and 'install base' packages
+  :<name>:       build, launch container <name>  and 'install base' packages
 copy:
   :images:       copy remote images of the suite into local storage
 remove
@@ -93,10 +94,10 @@ remove
   :images:       delete local images of the suite
 start/stop
   :containers:   start/stop all 'containers' from the suite
-  :<name>:       start/stop conatiner <name> from suite
+  :<name>:       start/stop container <name> from suite
 show
-  :info:         show info of all the containers from LXC suite
-  :config:       show config of all the containers from the LXC suite
+  :info:         show info of all (or <name>) containers from LXC suite
+  :config:       show config of all (or <name>) containers from the LXC suite
   :suite:        show services of all the containers from the LXC suite
   :images:       show information of local images
 cmd
@@ -162,7 +163,8 @@ main() {
         build)
             sudo_or_exit
             case $2 in
-                ''|containers) build_instances ;;
+                ${LXC_HOST_PREFIX}-*) build_container "$2" ;;
+                ''|containers) build_all_containers ;;
                 *) usage "$_usage"; exit 42;;
             esac
             ;;
@@ -175,11 +177,11 @@ main() {
         remove)
             sudo_or_exit
             case $2 in
-                ''|containers) remove_instances ;;
+                ''|containers) remove_containers ;;
                 images) lxc_delete_images_localy ;;
                 ${LXC_HOST_PREFIX}-*)
                     ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
-                    if ask_yn "Do you really want to delete conatiner $2"; then
+                    if ask_yn "Do you really want to delete container $2"; then
                         lxc_delete_container "$2"
                     fi
                     ;;
@@ -201,19 +203,41 @@ main() {
         show)
             sudo_or_exit
             case $2 in
-                suite)  show_suite ;;
+                suite)
+                    case $3 in
+                        ${LXC_HOST_PREFIX}-*)
+                            lxc exec -t "$3" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
+                                | prefix_stdout "[${_BBlue}$3${_creset}]  "
+                        ;;
+                        *) show_suite;;
+                    esac
+                    ;;
                 images) show_images ;;
                 config)
-                    rst_title "container configurations"
-                    echo
-                    lxc list "$LXC_HOST_PREFIX-"
-                    echo
-                    lxc_cmd config show
+                    case $3 in
+                        ${LXC_HOST_PREFIX}-*)
+                            lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
+                        ;;
+                        *)
+                            rst_title "container configurations"
+                            echo
+                            lxc list "$LXC_HOST_PREFIX-"
+                            echo
+                            lxc_cmd config show
+                            ;;
+                    esac
                     ;;
                 info)
-                    rst_title "container info"
-                    echo
-                    lxc_cmd info
+                    case $3 in
+                        ${LXC_HOST_PREFIX}-*)
+                            lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
+                            ;;
+                        *)
+                            rst_title "container info"
+                            echo
+                            lxc_cmd info
+                            ;;
+                    esac
                     ;;
                 *) usage "$_usage"; exit 42;;
             esac
@@ -265,28 +289,69 @@ main() {
 }
 
 
-build_instances() {
-    rst_title "Build LXC instances"
+build_all_containers() {
+    rst_title "Build all LXC containers of suite"
+    usage_containers
     lxc_copy_images_localy
     echo
     rst_title "build containers" section
     echo
-    lxc_init_containers
-    lxc_config_containers
-    lxc_boilerplate_containers
+    lxc_init_all_containers
+    lxc_config_all_containers
+    lxc_boilerplate_all_containers
+    rst_title "install LXC base packages" section
     echo
     lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install base
     echo
     lxc list "$LXC_HOST_PREFIX"
 }
 
-remove_instances() {
-    rst_title "Remove LXC instances"
+build_container() {
+    rst_title "Build container $1"
+
+    local remote_image
+    local container
+    local image
+    local boilerplate_script
+
+    for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
+        if [ "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}" = "$1" ]; then
+            remote_image="${LXC_SUITE[i]}"
+            container="${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"
+            image="${LXC_SUITE[i+1]}"
+            boilerplate_script="${image}_boilerplate"
+            boilerplate_script="${!boilerplate_script}"
+            break
+        fi
+    done
+    echo
+    if [ -z "$container" ]; then
+        err_msg "container $1 unknown"
+        usage_containers
+        return 42
+    fi
+    lxc_image_copy "${remote_image}" "${image}"
+    rst_title "init container" section
+    lxc_init_container "${image}" "${container}"
+    rst_title "configure container" section
+    lxc_config_container "${container}"
+    rst_title "run LXC boilerplate scripts" section
+    lxc_install_boilerplate "${container}" "$boilerplate_script"
+    echo
+    rst_title "install LXC base packages" section
+    lxc_exec_cmd "${container}" "${LXC_REPO_ROOT}/utils/lxc.sh" __install base \
+        | prefix_stdout "[${_BBlue}${container}${_creset}] "
+    echo
+    lxc list "$container"
+}
+
+remove_containers() {
+    rst_title "Remove all LXC containers of suite"
     rst_para "existing containers matching ${_BGreen}$LXC_HOST_PREFIX-*${_creset}"
     echo
     lxc list "$LXC_HOST_PREFIX-"
     echo -en "\\n${_BRed}LXC containers to delete::${_creset}\\n\\n  ${CONTAINERS[*]}\\n" | $FMT
-    if ask_yn "Do you really want to delete these conatiners"; then
+    if ask_yn "Do you really want to delete these containers"; then
         for i in "${CONTAINERS[@]}"; do
             lxc_delete_container "$i"
         done
@@ -302,13 +367,7 @@ lxc_copy_images_localy() {
     rst_title "copy images" section
     echo
     for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
-        if lxc_image_exists "local:${LXC_SUITE[i+1]}"; then
-            info_msg "image ${LXC_SUITE[i]} already copied --> ${LXC_SUITE[i+1]}"
-        else
-            info_msg "copy image locally ${LXC_SUITE[i]} --> ${LXC_SUITE[i+1]}"
-            lxc image copy "${LXC_SUITE[i]}" local: \
-                --alias  "${LXC_SUITE[i+1]}" | prefix_stdout
-        fi
+        lxc_image_copy "${LXC_SUITE[i]}" "${LXC_SUITE[i+1]}"
     done
     # lxc image list local: && wait_key
 }
@@ -408,81 +467,90 @@ lxc_exec() {
     done
 }
 
-lxc_init_containers() {
+lxc_init_all_containers() {
+    rst_title "init all containers" section
 
     local image_name
     local container_name
 
     for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
-
-        image_name="${LXC_SUITE[i+1]}"
-        container_name="${LXC_HOST_PREFIX}-${image_name}"
-
-        if lxc info "${container_name}" &>/dev/null; then
-            info_msg "container '${container_name}' already exists"
-        else
-            info_msg "create conatiner instance: ${container_name}"
-            lxc init "local:${image_name}" "${container_name}"
-        fi
+        lxc_init_container "${LXC_SUITE[i+1]}" "${LXC_HOST_PREFIX}-${image_name}"
     done
 }
 
-lxc_config_containers() {
+lxc_config_all_containers() {
+    rst_title "configure all containers" section
+
     for i in "${CONTAINERS[@]}"; do
-        info_msg "[${_BBlue}${i}${_creset}] configure container ..."
-
-        info_msg "[${_BBlue}${i}${_creset}] map uid/gid from host to container"
-        # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
-        echo -e -n "uid $HOST_USER_ID 0\\ngid $HOST_GROUP_ID 0"\
-            | lxc config set "$i" raw.idmap -
-
-        info_msg "[${_BBlue}${i}${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"
-        # https://lxd.readthedocs.io/en/latest/instances/#type-disk
-        lxc config device add "$i" repo_share disk \
-            source="${REPO_ROOT}" \
-            path="${LXC_REPO_ROOT}" &>/dev/null
-        # lxc config show "$i" && wait_key
+        lxc_config_container "${i}"
     done
 }
 
-lxc_boilerplate_containers() {
+lxc_config_container() {
+    info_msg "[${_BBlue}$1${_creset}] configure container ..."
+
+    info_msg "[${_BBlue}$1${_creset}] map uid/gid from host to container"
+    # https://lxd.readthedocs.io/en/latest/userns-idmap/#custom-idmaps
+    echo -e -n "uid $HOST_USER_ID 0\\ngid $HOST_GROUP_ID 0"\
+        | lxc config set "$1" raw.idmap -
+
+    info_msg "[${_BBlue}$1${_creset}] share ${REPO_ROOT} (repo_share) from HOST into container"
+    # https://lxd.readthedocs.io/en/latest/instances/#type-disk
+    lxc config device add "$1" repo_share disk \
+        source="${REPO_ROOT}" \
+        path="${LXC_REPO_ROOT}" &>/dev/null
+    # lxc config show "$1" && wait_key
+}
+
+lxc_boilerplate_all_containers() {
+    rst_title "run LXC boilerplate scripts" section
 
-    local image_name
-    local container_name
     local boilerplate_script
+    local image_name
 
     for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
 
         image_name="${LXC_SUITE[i+1]}"
-        container_name="${LXC_HOST_PREFIX}-${image_name}"
         boilerplate_script="${image_name}_boilerplate"
         boilerplate_script="${!boilerplate_script}"
 
-        info_msg "[${_BBlue}${container_name}${_creset}] init .."
-        if lxc start -q "${container_name}" &>/dev/null; then
-            sleep 5 # guest needs some time to come up and get an IP
+        lxc_install_boilerplate "${LXC_HOST_PREFIX}-${image_name}" "$boilerplate_script"
+
+        if [[ -z "${boilerplate_script}" ]]; then
+            err_msg "[${_BBlue}${container_name}${_creset}] no boilerplate for image '${image_name}'"
         fi
-        lxc_init_container "${container_name}"
-        info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
-        cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+    done
+}
+
+lxc_install_boilerplate() {
+
+    # usage:  lxc_install_boilerplate <container-name> <string: shell commands ..>
+    #
+    # usage:  lxc_install_boilerplate searx-archlinux "${archlinux_boilerplate}"
+
+    local container_name="$1"
+    local boilerplate_script="$2"
+
+    info_msg "[${_BBlue}${container_name}${_creset}] init .."
+    if lxc start -q "${container_name}" &>/dev/null; then
+        sleep 5 # guest needs some time to come up and get an IP
+    fi
+    info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
+    cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
 rm -f "/.lxcenv.mk"
 ln -s "${LXC_REPO_ROOT}/utils/makefile.lxc" "/.lxcenv.mk"
 ls -l "/.lxcenv.mk"
 EOF
 
-        info_msg "[${_BBlue}${container_name}${_creset}] install boilerplate .."
-        if lxc start -q "${container_name}" &>/dev/null; then
-            sleep 5 # guest needs some time to come up and get an IP
-        fi
-        if [[ -n "${boilerplate_script}" ]]; then
-            echo "${boilerplate_script}" \
-                | lxc exec "${container_name}" -- bash \
-                | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
-        else
-            err_msg "[${_BBlue}${container_name}${_creset}] no boilerplate for image '${image_name}'"
-        fi
-
-    done
+    info_msg "[${_BBlue}${container_name}${_creset}] run LXC boilerplate scripts .."
+    if lxc start -q "${container_name}" &>/dev/null; then
+        sleep 5 # guest needs some time to come up and get an IP
+    fi
+    if [[ -n "${boilerplate_script}" ]]; then
+        echo "${boilerplate_script}" \
+            | lxc exec "${container_name}" -- bash \
+            | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
+    fi
 }
 
 

From 853be4e11cd8a82b6b08f04db1717329907a5aaf Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 3 Apr 2020 20:24:40 +0200
Subject: [PATCH 092/125] utils/filtron.sh: add generic documentation

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst           |  1 +
 docs/build-templates/filtron.rst | 52 ++++++++++++++++++++++++++++++++
 docs/utils/filtron.sh.rst        | 32 ++++++++++++++++++++
 utils/filtron.sh                 | 32 +++++++++++++++++---
 utils/searx.sh                   |  1 -
 5 files changed, 112 insertions(+), 6 deletions(-)
 create mode 100644 docs/build-templates/filtron.rst

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 970ce25d2..9615cd935 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -37,6 +37,7 @@ installed by ``go get`` package management (see `filtron README`_).  If you use
 filtron as middleware, a more isolated setup is recommended.  To simplify such
 an installation and the maintenance of, use our script :ref:`filtron.sh`.
 
+.. _Sample configuration of filtron:
 
 Sample configuration of filtron
 ===============================
diff --git a/docs/build-templates/filtron.rst b/docs/build-templates/filtron.rst
new file mode 100644
index 000000000..83a451820
--- /dev/null
+++ b/docs/build-templates/filtron.rst
@@ -0,0 +1,52 @@
+.. START create user
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+      $ sudo -H useradd --shell /bin/bash --system \\
+          --home-dir "$SERVICE_HOME" \\
+          --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+      $ sudo -H mkdir "$SERVICE_HOME"
+      $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START install go
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: bash
+
+       $ cat > "$GO_ENV" <<EOF
+       export GOPATH=${SERVICE_HOME}/go-apps
+       export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
+       EOF
+       $ sudo -i -u "${SERVICE_USER}"
+       (${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
+       (${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
+       (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
+                   "${GO_PKG_URL}"
+       (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+       (${SERVICE_USER}) $ which go
+       ${SERVICE_HOME}/local/go/bin/go
+
+.. END install go
+
+.. START install filtron
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: bash
+
+       $ sudo -i -u "${SERVICE_USER}"
+       (${SERVICE_USER}) $ go get -v -u github.com/asciimoo/filtron
+
+.. END install filtron
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index 40e57aa3c..a3c1aed41 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -36,6 +36,38 @@ into this user account:
    <utils/templates/lib/systemd/system/filtron.service>`
    (``/lib/systemd/system/filtron.service``).
 
+
+Create user
+===========
+
+.. include:: ../../build/docs/includes/filtron.rst
+   :start-after: START create user
+   :end-before: END create user
+
+
+Install go
+==========
+
+.. include:: ../../build/docs/includes/filtron.rst
+   :start-after: START install go
+   :end-before: END install go
+
+
+Install filtron
+===============
+
+Install :origin:`rules.json <utils/templates/etc/filtron/rules.json>` at
+``/etc/filtron/rules.json`` (see :ref:`Sample configuration of filtron`) and
+install filtron software and systemd unit:
+
+.. include:: ../../build/docs/includes/filtron.rst
+   :start-after: START install filtron
+   :end-before: END install filtron
+
+.. include:: ../../build/docs/includes/filtron.rst
+   :start-after: START install systemd unit
+   :end-before: END install systemd unit
+
 .. _filtron.sh overview:
 
 Overview
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 208166fda..d2be60737 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -186,11 +186,7 @@ main() {
                 debug-off)  echo; disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
-        doc)
-            echo
-            echo ".. generic utils/filtron.sh documentation"
-            ;;
-
+        doc) rst-doc ;;
         *) usage "unknown or missing command $1"; exit 42;;
     esac
 }
@@ -469,6 +465,32 @@ This removes apache site ${APACHE_FILTRON_SITE}."
     apache_remove_site "$APACHE_FILTRON_SITE"
 }
 
+rst-doc() {
+
+    eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/filtron.rst")\""
+
+    echo -e "\n.. START install systemd unit"
+    cat <<EOF
+.. tabs::
+
+   .. group-tab:: systemd
+
+      .. code:: bash
+
+EOF
+    eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout "         "
+    echo -e "\n.. END install systemd unit"
+
+    # for DIST_NAME in ubuntu-20.04 arch fedora; do
+    #     (
+    #         DIST_ID=${DIST_NAME%-*}
+    #         DIST_VERS=${DIST_NAME#*-}
+    #         [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+    #         # ...
+    #     )
+    # done
+}
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------
diff --git a/utils/searx.sh b/utils/searx.sh
index 981c498fc..5b118478e 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -248,7 +248,6 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
         doc) rst-doc;;
-
         *) usage "unknown or missing command $1"; exit 42;;
     esac
 }

From e32b08f61681da42104bc94b7b3266e41a79c58b Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 3 Apr 2020 20:26:20 +0200
Subject: [PATCH 093/125] utils/morty.sh: add generic documentation

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/build-templates/morty.rst | 52 ++++++++++++++++++++++++++++++++++
 docs/utils/morty.sh.rst        | 29 +++++++++++++++++++
 utils/morty.sh                 | 33 +++++++++++++++++----
 3 files changed, 109 insertions(+), 5 deletions(-)
 create mode 100644 docs/build-templates/morty.rst

diff --git a/docs/build-templates/morty.rst b/docs/build-templates/morty.rst
new file mode 100644
index 000000000..4a5d1f27f
--- /dev/null
+++ b/docs/build-templates/morty.rst
@@ -0,0 +1,52 @@
+.. START create user
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: sh
+
+      $ sudo -H useradd --shell /bin/bash --system \\
+          --home-dir "$SERVICE_HOME" \\
+          --comment "Privacy-respecting metasearch engine" $SERVICE_USER
+
+      $ sudo -H mkdir "$SERVICE_HOME"
+      $ sudo -H chown -R "$SERVICE_GROUP:$SERVICE_GROUP" "$SERVICE_HOME"
+
+.. END create user
+
+.. START install go
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: bash
+
+       $ cat > "$GO_ENV" <<EOF
+       export GOPATH=${SERVICE_HOME}/go-apps
+       export PATH=\$PATH:${SERVICE_HOME}/local/go/bin:\$GOPATH/bin
+       EOF
+       $ sudo -i -u "${SERVICE_USER}"
+       (${SERVICE_USER}) $ echo 'source $GO_ENV' >> ~/.profile
+       (${SERVICE_USER}) $ mkdir ${SERVICE_HOME}/local
+       (${SERVICE_USER}) $ wget --progress=bar -O "${GO_TAR}" \\
+                   "${GO_PKG_URL}"
+       (${SERVICE_USER}) $ tar -C ${SERVICE_HOME}/local/go -xzf "${GO_TAR}"
+       (${SERVICE_USER}) $ which go
+       ${SERVICE_HOME}/local/go/bin/go
+
+.. END install go
+
+.. START install morty
+
+.. tabs::
+
+  .. group-tab:: bash
+
+    .. code-block:: bash
+
+       $ sudo -i -u "${SERVICE_USER}"
+       (${SERVICE_USER}) $ go get -v -u github.com/asciimoo/morty
+
+.. END install morty
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 2bdc4d9c8..34f0c9b5e 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -13,6 +13,7 @@
 
    - :ref:`installation`
    - :ref:`architecture`
+   - :ref:`searx morty`
 
 To simplify installation and maintenance of a morty_ instance you can use the
 script :origin:`utils/morty.sh`.  In most cases you will install morty_ simply by
@@ -37,6 +38,34 @@ into this user account:
 
    To add morty to your searx instance read chapter :ref:`searx morty`.
 
+Create user
+===========
+
+.. include:: ../../build/docs/includes/morty.rst
+   :start-after: START create user
+   :end-before: END create user
+
+
+Install go
+==========
+
+.. include:: ../../build/docs/includes/morty.rst
+   :start-after: START install go
+   :end-before: END install go
+
+
+Install morty
+=============
+
+Install morty software and systemd unit:
+
+.. include:: ../../build/docs/includes/morty.rst
+   :start-after: START install morty
+   :end-before: END install morty
+
+.. include:: ../../build/docs/includes/morty.rst
+   :start-after: START install systemd unit
+   :end-before: END install systemd unit
 
 Overview
 ========
diff --git a/utils/morty.sh b/utils/morty.sh
index e6e14be8a..ec1d47cb6 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -194,11 +194,7 @@ main() {
                 debug-off)  disable_debug ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
-        doc)
-            echo
-            echo ".. generic utils/morty.sh documentation"
-            ;;
-
+        doc) rst-doc ;;
         *) usage "ERROR: unknown or missing command $1"; exit 42;;
     esac
 }
@@ -436,6 +432,33 @@ This removes apache site ${APACHE_MORTY_SITE}."
     apache_remove_site "$APACHE_MORTY_SITE"
 }
 
+rst-doc() {
+
+    eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
+
+    echo -e "\n.. START install systemd unit"
+    cat <<EOF
+.. tabs::
+
+   .. group-tab:: systemd
+
+      .. code:: bash
+
+EOF
+    eval "echo \"$(< "${TEMPLATES}/${SERVICE_SYSTEMD_UNIT}")\"" | prefix_stdout "         "
+    echo -e "\n.. END install systemd unit"
+
+    # for DIST_NAME in ubuntu-20.04 arch fedora; do
+    #     (
+    #         DIST_ID=${DIST_NAME%-*}
+    #         DIST_VERS=${DIST_NAME#*-}
+    #         [[ $DIST_VERS =~ $DIST_ID ]] && DIST_VERS=
+    #         # ...
+    #     )
+    # done
+}
+
+
 # ----------------------------------------------------------------------------
 main "$@"
 # ----------------------------------------------------------------------------

From 84daf9999c9b52bf0b938725755a871aa359c62c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 4 Apr 2020 08:36:48 +0200
Subject: [PATCH 094/125] docs: remove PR #1803 hints

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation.rst | 11 -----------
 1 file changed, 11 deletions(-)

diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 25d7f0ea9..ac17c326a 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -35,17 +35,6 @@ and some maintenance tasks (alternatively you can create your own fork).
    $ git clone https://github.com/asciimoo/searx searx
    $ cd searx
 
-.. hint::
-
-   The *tooling box* is not yet merged into `asciimoo/searx master
-   <https://github.com/asciimoo/searx>`_.  As long as PR is not merged, you need
-   to merge the PR into your local clone (see below).  The discussion takes
-   place in :pull:`1803`.  To merge the :pull:`1803` in your local branch use:
-
-   .. code:: bash
-
-      $ git pull origin refs/pull/1803/head
-
 **Install** :ref:`searx service <searx.sh>`
 
 This installs searx as described in :ref:`installation basic`.

From 3c19f2f1cef31fd512975c70a25442d078992a42 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 4 Apr 2020 12:40:14 +0200
Subject: [PATCH 095/125] utils/lxc.sh install: support installation on
 selected containers

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc-searx.env |  4 +++-
 utils/lxc.sh        | 35 +++++++++++++++++++----------------
 2 files changed, 22 insertions(+), 17 deletions(-)

diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index 2a3fdf37b..fc5b33f06 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -10,10 +10,12 @@
 # config
 # ----------------------------------------------------------------------------
 
+# shellcheck disable=SC2034
+LXC_SUITE_NAME="searx"
 lxc_set_suite_env() {
     # name of https://images.linuxcontainers.org
     export LINUXCONTAINERS_ORG_NAME="${LINUXCONTAINERS_ORG_NAME:-images}"
-    export LXC_HOST_PREFIX="${LXC_HOST_PREFIX:-searx}"
+    export LXC_HOST_PREFIX="${LXC_SUITE_NAME:-searx}"
     export LXC_SUITE=(
 
         # to disable containers, comment out lines ..
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 813b50843..92dfff2d1 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -82,7 +82,7 @@ usage::
   $_cmd [start|stop] [containers|<name>]
   $_cmd show         [images|suite|info|config [<name>]]
   $_cmd cmd          [--|<name>] '...'
-  $_cmd install      [suite|base]
+  $_cmd install      [suite|base [<name>]]
 
 build
   :containers:   build, launch all containers and 'install base' packages
@@ -105,32 +105,26 @@ cmd
   --             run command '...' in all containers of the LXC suite
   :<name>:       run command '...' in container <name>
 install
-  :suite:        install LXC suite; ${LXC_SUITE_INSTALL_INFO}
   :base:         prepare LXC; install basic packages
+  :suite:        install LXC ${LXC_SUITE_NAME} suite into all (or <name>) containers
 
 EOF
-    usage_images
-    echo
     usage_containers
-    echo
     [ -n "${1+x}" ] &&  err_msg "$1"
 }
 
 usage_containers() {
     cat <<EOF
-LXC suite containers:
+LXC suite: ${LXC_SUITE_NAME}
+$(echo "  ${LXC_SUITE_INSTALL_INFO}" | $FMT)
+suite images:
+$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
+suite containers:
 $(echo "  ${CONTAINERS[*]}" | $FMT)
 EOF
     [ -n "${1+x}" ] &&  err_msg "$1"
 }
 
-usage_images() {
-    cat <<EOF
-LXC suite images:
-$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
-EOF
-}
-
 lxd_info() {
 
     cat <<EOF
@@ -216,6 +210,7 @@ main() {
                 config)
                     case $3 in
                         ${LXC_HOST_PREFIX}-*)
+                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                         ;;
                         *)
@@ -230,6 +225,7 @@ main() {
                 info)
                     case $3 in
                         ${LXC_HOST_PREFIX}-*)
+                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                             ;;
                         *)
@@ -259,14 +255,21 @@ main() {
                     shift
                     lxc_exec_cmd "${name}" "$@"
                     ;;
-                *) usage "uknown or missing container <name> $1"; exit 42;;
-            esac
+                *) usage_containers "unknown container: $1" && exit 42
+           esac
             ;;
         install)
             sudo_or_exit
             case $2 in
                 suite|base)
-                    lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
+                    case $3 in
+                        ${LXC_HOST_PREFIX}-*)
+                            ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
+                            lxc_exec_cmd "$3" "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
+                            ;;
+                        '') lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2" ;;
+                        *) usage_containers "unknown container: $3" && exit 42
+                    esac
                     ;;
                 *) usage "$_usage"; exit 42 ;;
             esac

From e530e20ae6a4d1f8621f535523cd2af76f715b0b Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 4 Apr 2020 17:53:16 +0200
Subject: [PATCH 096/125] misc: fix variuous marginals

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.sh                                                | 5 ++++-
 utils/lib.sh                                              | 8 ++++----
 utils/morty.sh                                            | 2 +-
 utils/templates/etc/apache2/sites-available/morty.conf    | 4 +---
 .../etc/apache2/sites-available/searx.conf:filtron        | 6 +++---
 5 files changed, 13 insertions(+), 12 deletions(-)

diff --git a/.config.sh b/.config.sh
index 57411ce6f..f4e6273c4 100644
--- a/.config.sh
+++ b/.config.sh
@@ -16,7 +16,10 @@
 # The default is taken from ./utils/brand.env.
 
 PUBLIC_URL="${SEARX_URL}"
-#PUBLIC_URL="http://$(uname -n)/searx"
+
+if [[ ${PUBLIC_URL} == "https://searx.me" ]]; then
+    PUBLIC_URL=
+fi
 
 # searx.sh
 # ---------
diff --git a/utils/lib.sh b/utils/lib.sh
index e466100c1..85ac77456 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -449,14 +449,14 @@ install_template() {
                 break
                 ;;
             "interactiv shell")
-                echo "// edit ${dst} to your needs"
+                echo -e "// edit ${_Red}${dst}${_creset} to your needs"
                 echo -e "// exit with [${_BCyan}CTRL-D${_creset}]"
                 sudo -H -u "${owner}" -i
                 $DIFF_CMD "${dst}" "${template_file}"
                 echo
-                echo "${_BBlack}did you edit file ...${_creset}"
-                printf "  ${template_file}"
-                if ask_yn "... to your needs?"; then
+                echo -e "// ${_BBlack}did you edit file ...${_creset}"
+                echo -en "//  ${_Red}${dst}${_creset}"
+                if ask_yn "//${_BBlack}... to your needs?${_creset}"; then
                     break
                 fi
                 ;;
diff --git a/utils/morty.sh b/utils/morty.sh
index ec1d47cb6..d79faa037 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -15,6 +15,7 @@ in_container && lxc_set_suite_env
 # ----------------------------------------------------------------------------
 
 PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
 PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
 PUBLIC_URL_MORTY="$(echo "$PUBLIC_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
 
@@ -378,7 +379,6 @@ EOF
     return 0
 }
 
-
 enable_debug() {
     warn_msg "Do not enable debug in production enviroments!!"
     info_msg "Enabling debug option needs to reinstall systemd service!"
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/apache2/sites-available/morty.conf
index 2c5727b19..48b4dd766 100644
--- a/utils/templates/etc/apache2/sites-available/morty.conf
+++ b/utils/templates/etc/apache2/sites-available/morty.conf
@@ -19,9 +19,7 @@ ProxyPreserveHost On
     RequestHeader set X-Script-Name ${PUBLIC_URL_PATH_MORTY}
 
     # In Apache it seems, that setting HTTP_HOST header direct here does have no
-    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST is
-    # needed by searx to render correct *Search URL* in the *Link* box and
-    # *saved preference*.
+    # effect.  I needed to set 'ProxyPreserveHost On' (see above).
 
     # RequestHeader set Host ${PUBLIC_HOST}
 
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index 5ede66301..e34049063 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -24,9 +24,9 @@ ProxyPreserveHost On
     RequestHeader set X-Script-Name ${FILTRON_URL_PATH}
 
     # In Apache it seems, that setting HTTP_HOST header direct here does have no
-    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST is
-    # needed by searx to render correct *Search URL* in the *Link* box and
-    # *saved preference*.
+    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST
+    # (ProxyPreserveHost On) is needed by searx to render correct *Search URL*
+    # in the *Link* box and *saved preference*.
 
     # RequestHeader set Host ${PUBLIC_HOST}
 

From c748047e926ce6979ab07a8a3ed3d0cc96f534ba Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 4 Apr 2020 17:54:20 +0200
Subject: [PATCH 097/125] utils/searx.sh: add cmd to (un)set option image_proxy
 and result_proxy

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/searx.sh | 74 ++++++++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 74 insertions(+)

diff --git a/utils/searx.sh b/utils/searx.sh
index 5b118478e..b607d86ca 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -238,6 +238,9 @@ main() {
             case $2 in
                 debug-on)  echo; enable_debug ;;
                 debug-off)  echo; disable_debug ;;
+                result-proxy) set_result_proxy "$3" "$4" ;;
+                image-proxy-on) enable_image_proxy ;;
+                image-proxy-off) disable_image_proxy ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
         apache)
@@ -522,6 +525,24 @@ deactivate_service() {
     uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
+enable_image_proxy() {
+    info_msg "try to enable image_proxy ..."
+    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/image_proxy : False/image_proxy : True/g" "$SEARX_SETTINGS_PATH"
+EOF
+    uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
+disable_image_proxy() {
+    info_msg "try to enable image_proxy ..."
+    tee_stderr 0.1 <<EOF | sudo -H -i 2>&1 |  prefix_stdout "$_service_prefix"
+cd ${SEARX_SRC}
+sed -i -e "s/image_proxy : True/image_proxy : False/g" "$SEARX_SETTINGS_PATH"
+EOF
+    uWSGI_restart "$SEARX_UWSGI_APP"
+}
+
 enable_debug() {
     warn_msg "Do not enable debug in production enviroments!!"
     info_msg "try to enable debug mode ..."
@@ -541,6 +562,59 @@ EOF
     uWSGI_restart "$SEARX_UWSGI_APP"
 }
 
+set_result_proxy() {
+    info_msg "try to set result proxy ..."
+    local line
+    local stage=0
+    local url="    url: $1"
+    local key="    key: $2"
+    if [[ -z $2 ]]; then
+       key=
+    fi
+    cp "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_PATH}.bak"
+    _set_result_proxy "$1" "$2" > "${SEARX_SETTINGS_PATH}"
+}
+
+_set_result_proxy() {
+    local line
+    local stage=0
+    local url="    url: $1"
+    local key="    key: $2"
+    if [[ -z $2 ]]; then
+       key=
+    fi
+
+    while IFS=  read -r line
+    do
+        if [[ $stage = 0 ]] || [[ $stage = 2 ]] ; then
+            if [[ $line =~ ^[[:space:]]*#*[[:space:]]*result_proxy[[:space:]]*:[[:space:]]*$ ]]; then
+                if [[ $stage = 0 ]]; then
+                    stage=1
+                    echo "result_proxy:"
+                    continue
+                elif [[ $stage = 2 ]]; then
+                    continue
+                fi
+            fi
+        fi
+        if [[ $stage = 1 ]] || [[ $stage = 2 ]] ; then
+            if [[ $line =~ ^[[:space:]]*#*[[:space:]]*url[[:space:]]*:[[:space:]] ]]; then
+                [[ $stage = 1 ]]  && echo "$url"
+                continue
+            elif [[ $line =~ ^[[:space:]]*#*[[:space:]]*key[[:space:]]*:[[:space:]] ]]; then
+                [[ $stage = 1 ]] && [[ -n $key ]] && echo "$key"
+                continue
+            elif [[ $line =~ ^[[:space:]]*$ ]]; then
+                stage=2
+            fi
+        fi
+        echo "$line"
+    done < "${SEARX_SETTINGS_PATH}.bak"
+}
+
+function has_substring() {
+   [[ "$1" != "${2/$1/}" ]]
+}
 inspect_service() {
     rst_title "service status & log"
     cat <<EOF

From c2caf9569c5b6e1a9d143382c2045983f7c6dd61 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 5 Apr 2020 15:15:28 +0200
Subject: [PATCH 098/125] [fix] creation of /.lxcenv in containers

In utils/lib.sh there are two functions with the same name, but different tasks.
Rename one of them from lxc_init_container() into lxc_init_container_env().

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh | 10 +++++-----
 utils/lxc.sh |  1 +
 2 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index 85ac77456..9d5068646 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1073,7 +1073,7 @@ in_container() {
     # to be safe, take a look at the environment of process 1 (/sbin/init)
     # grep -qa 'container=lxc' /proc/1/environ
 
-    # see lxc_init_container
+    # see lxc_init_container_env
     [[ -f /.lxcenv ]]
 }
 
@@ -1083,12 +1083,12 @@ if in_container; then
     LXC_ENV_FOLDER="lxc/$(hostname)/"
 fi
 
-lxc_init_container() {
+lxc_init_container_env() {
 
-    # Create a /.lxcenv file in the root folder.  Call this once after container
-    # is inital started.
+    # usage: lxc_init_container_env <name>
 
-    # usage: lxc_create_root_dot_lxcenv <name>
+    # Create a /.lxcenv file in the root folder.  Call this once after the
+    # container is inital started and before installing any boilerplate stuff.
 
     info_msg "create /.lxcenv in container $1"
     cat <<EOF | lxc exec "${1}" -- bash | prefix_stdout "[${_BBlue}${1}${_creset}] "
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 92dfff2d1..9eb28f498 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -538,6 +538,7 @@ lxc_install_boilerplate() {
     if lxc start -q "${container_name}" &>/dev/null; then
         sleep 5 # guest needs some time to come up and get an IP
     fi
+    lxc_init_container_env "${container_name}"
     info_msg "[${_BBlue}${container_name}${_creset}] install /.lxcenv.mk .."
     cat <<EOF | lxc exec "${container_name}" -- bash | prefix_stdout "[${_BBlue}${container_name}${_creset}] "
 rm -f "/.lxcenv.mk"

From c81849cb5a22d937c0f1de1d02d1fb8e3a7849cd Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 5 Apr 2020 17:40:37 +0200
Subject: [PATCH 099/125] filtron.sh & morty.sh: improve usage message (if used
 in containers)

BTW: normalize soma variable names

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/filtron.sh                              | 15 ++++-
 utils/morty.sh                                | 47 ++++++++--------
 utils/searx.sh                                | 55 +++++++++++--------
 .../etc/uwsgi/apps-archlinux/searx.ini        |  2 +-
 .../etc/uwsgi/apps-available/searx.ini        |  2 +-
 5 files changed, 72 insertions(+), 49 deletions(-)

diff --git a/utils/filtron.sh b/utils/filtron.sh
index d2be60737..6c58b07a2 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -97,15 +97,24 @@ apache (${PUBLIC_URL})
 filtron rules: ${FILTRON_RULES}
 
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
-
   PUBLIC_URL     : ${PUBLIC_URL}
   PUBLIC_HOST    : ${PUBLIC_HOST}
   SERVICE_USER   : ${SERVICE_USER}
+  FILTRON_TARGET : ${FILTRON_TARGET}
   FILTRON_API    : ${FILTRON_API}
   FILTRON_LISTEN : ${FILTRON_LISTEN}
-  FILTRON_TARGET : ${FILTRON_TARGET}
-
 EOF
+    if in_container; then
+        # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+        for ip in $(global_IPs) ; do
+            if [[ $ip =~ .*:.* ]]; then
+                echo "  container URL (IPv6): http://[${ip#*|}]:4005/"
+            else
+                # IPv4:
+                echo "  container URL (IPv4): http://${ip#*|}:4005/"
+            fi
+        done
+    fi
     [[ -n ${1} ]] &&  err_msg "$1"
 }
 
diff --git a/utils/morty.sh b/utils/morty.sh
index d79faa037..17039a05e 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -83,39 +83,37 @@ apache : ${PUBLIC_URL_MORTY}
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_MORTY_SITE}
 
-If needed, set the environment variable MORTY_LISTEN in the
-${DOT_CONFIG#"$REPO_ROOT/"} file::
-
-  MORTY_LISTEN :   ${MORTY_LISTEN}
-  SERVICE_USER :   ${SERVICE_USER}
-
+If needed, set the environment variables in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
+  PUBLIC_URL_MORTY:     ${PUBLIC_URL_MORTY}
+  MORTY_LISTEN:         ${MORTY_LISTEN}
+  SERVICE_USER:         ${SERVICE_USER}
 EOF
     if in_container; then
-        lxc_suite_info
-    else
-        info_msg "public URL   --> ${PUBLIC_URL}"
-        info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
+        # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+        for ip in $(global_IPs) ; do
+            if [[ $ip =~ .*:.* ]]; then
+                echo "  container URL (IPv6): http://[${ip#*|}]:3000/"
+            else
+                # IPv4:
+                echo "  container URL (IPv4): http://${ip#*|}:3000/"
+            fi
+        done
     fi
+    echo
     info_searx
+
     [[ -n ${1} ]] &&  err_msg "$1"
 }
 
 info_searx() {
     # shellcheck disable=SC1117
     cat <<EOF
-
-To activate morty in searx, add result_proxy to your settings.yml::
-
+To activate result and image proxy in searx, edit settings.yml (read:
+${DOCS_URL}/admin/morty.html)::
   result_proxy:
-      url : ${PUBLIC_URL_MORTY}/
-
+      url : ${PUBLIC_URL_MORTY}
   server:
-      ...
-      image_proxy : True # Proxying image results through searx
-      ...
-
-further read: ${DOCS_URL}/admin/morty.html
-
+      image_proxy : True
 EOF
 }
 
@@ -210,7 +208,6 @@ install_all() {
     wait_key
     systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
     wait_key
-    info_searx
     if ! service_is_available "http://${MORTY_LISTEN}" ; then
         err_msg "Morty does not listening on: http://${MORTY_LISTEN}"
     fi
@@ -220,6 +217,12 @@ install_all() {
             install_apache_site
         fi
     fi
+    info_searx
+    if ask_yn "Add image and result proxy to searx settings.yml?" Yn; then
+        "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}"
+        "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
+    fi
+
     if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
     fi
diff --git a/utils/searx.sh b/utils/searx.sh
index b607d86ca..a74aec393 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -17,7 +17,7 @@ in_container && lxc_set_suite_env
 
 PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
 
-SEARX_INTERNAL_URL="${SEARX_INTERNAL_URL:-127.0.0.1:8888}"
+SEARX_INTERNAL_HTTP="${SEARX_INTERNAL_HTTP:-127.0.0.1:8888}"
 
 SEARX_URL_PATH="${SEARX_URL_PATH:-$(echo "${PUBLIC_URL}" \
 | sed -e 's,^.*://[^/]*\(/.*\),\1,g')}"
@@ -79,17 +79,17 @@ case $DIST_ID in
     ubuntu|debian)
         SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
         BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
-        APACHE_APT_PACKAGES="libapache2-mod-uwsgi"
+        APACHE_PACKAGES="libapache2-mod-uwsgi"
         ;;
     arch)
         SEARX_PACKAGES="${SEARX_PACKAGES_arch}"
         BUILD_PACKAGES="${BUILD_PACKAGES_arch}"
-        APACHE_APT_PACKAGES="uwsgi"
+        APACHE_PACKAGES="uwsgi"
         ;;
     fedora)
         SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
         BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
-        APACHE_APT_PACKAGES="uwsgi"
+        APACHE_PACKAGES="uwsgi"
         ;;
 esac
 
@@ -116,13 +116,13 @@ usage() {
 usage::
 
   $(basename "$0") shell
-  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|apache|packages|buildhost]
+  $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|packages|buildhost]
   $(basename "$0") update     [searx]
   $(basename "$0") remove     [all|user|pyenv|searx-src]
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
   $(basename "$0") inspect    [service]
-  $(basename "$0") option     [debug-on|debug-off]
+  $(basename "$0") option     [debug-[on|off]|image-proxy-[on|off]|result-proxy <url> <key>]
   $(basename "$0") apache     [install|remove]
 
 shell
@@ -153,12 +153,25 @@ apache
 searx settings: ${SEARX_SETTINGS_PATH}
 
 If needed, set PUBLIC_URL of your WEB service in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
-
   PUBLIC_URL          : ${PUBLIC_URL}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
   SERVICE_USER        : ${SERVICE_USER}
-
+  SEARX_INTERNAL_HTTP : http://${SEARX_INTERNAL_HTTP}
 EOF
+    if in_container; then
+        # searx is listening on 127.0.0.1 and not available from outside container
+        # in containers the service is listening on 0.0.0.0 (see lxc-searx.env)
+        echo -e "${_BBlack}HINT:${_creset} searx only listen on loopback device" \
+             "${_BBlack}inside${_creset} the container."
+        for ip in $(global_IPs) ; do
+            if [[ $ip =~ .*:.* ]]; then
+                echo "  container (IPv6): [${ip#*|}]"
+            else
+                # IPv4:
+                echo "  container (IPv4): ${ip#*|}"
+            fi
+        done
+    fi
     [[ -n ${1} ]] &&  err_msg "$1"
 }
 
@@ -277,8 +290,8 @@ install_all() {
     test_local_searx
     wait_key
     install_searx_uwsgi
-    if ! service_is_available "http://$SEARX_INTERNAL_URL"; then
-        err_msg "URL http://$SEARX_INTERNAL_URL not available, check searx & uwsgi setup!"
+    if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+        err_msg "URL http://${SEARX_INTERNAL_HTTP} not available, check searx & uwsgi setup!"
     fi
     if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
@@ -481,8 +494,8 @@ test_local_searx() {
     rst_title "Testing searx instance localy" section
     echo
 
-    if service_is_available "http://$SEARX_INTERNAL_URL" &>/dev/null; then
-        err_msg "URL/port http://$SEARX_INTERNAL_URL is already in use, you"
+    if service_is_available "http://${SEARX_INTERNAL_HTTP}" &>/dev/null; then
+        err_msg "URL/port http://${SEARX_INTERNAL_HTTP} is already in use, you"
         err_msg "should stop that service before starting local tests!"
         if ! ask_yn "Continue with local tests?"; then
             return
@@ -494,7 +507,7 @@ export SEARX_SETTINGS_PATH="${SEARX_SETTINGS_PATH}"
 cd ${SEARX_SRC}
 timeout 10 python searx/webapp.py &
 sleep 3
-curl --location --verbose --head --insecure $SEARX_INTERNAL_URL
+curl --location --verbose --head --insecure $SEARX_INTERNAL_HTTP
 EOF
     sed -i -e "s/debug : True/debug : False/g" "$SEARX_SETTINGS_PATH"
 }
@@ -624,12 +637,10 @@ sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
   PUBLIC_URL          : ${PUBLIC_URL}
   SEARX_URL_PATH      : ${SEARX_URL_PATH}
   SEARX_INSTANCE_NAME : ${SEARX_INSTANCE_NAME}
-  SEARX_INTERNAL_URL  : ${SEARX_INTERNAL_URL}
+  SEARX_INTERNAL_HTTP  : ${SEARX_INTERNAL_HTTP}
 
 EOF
 
-    apache_is_installed && info_msg "Apache is installed."
-
     if service_account_is_available "$SERVICE_USER"; then
         info_msg "Service account $SERVICE_USER exists."
     else
@@ -661,11 +672,11 @@ EOF
         lxc_suite_info
     else
         info_msg "public URL   --> ${PUBLIC_URL}"
-        info_msg "internal URL --> http://${SEARX_INTERNAL_URL}"
+        info_msg "internal URL --> http://${SEARX_INTERNAL_HTTP}"
     fi
 
-    if ! service_is_available "http://${SEARX_INTERNAL_URL}"; then
-        err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_URL} is not available!"
+    if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+        err_msg "uWSGI app (service) at http://${SEARX_INTERNAL_HTTP} is not available!"
         MSG="${_Green}[${_BCyan}CTRL-C${_Green}] to stop or [${_BCyan}KEY${_Green}] to continue"\
            wait_key
     fi
@@ -720,8 +731,8 @@ install_apache_site() {
     rst_title "Install Apache site $APACHE_SEARX_SITE"
 
     rst_para "\
-This installs the searx uwsgi app as apache site.  If your server ist public to
-the internet you should instead use a reverse proxy (filtron) to block
+This installs the searx uwsgi app as apache site.  If your server is public to
+the internet, you should instead use a reverse proxy (filtron) to block
 excessively bot queries."
 
     ! apache_is_installed && err_msg "Apache is not installed."
@@ -730,7 +741,7 @@ excessively bot queries."
         return
     fi
 
-    pkg_install "$APACHE_APT_PACKAGES"
+    pkg_install "$APACHE_PACKAGES"
     a2enmod uwsgi
 
     echo
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index 66653fc0e..8d3349c58 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -68,7 +68,7 @@ pythonpath = ${SEARX_SRC}
 # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
 # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
 
-http = ${SEARX_INTERNAL_URL}
+http = ${SEARX_INTERNAL_HTTP}
 
 # using unix-sockets:
 #
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index 4f8674012..806f74439 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -67,7 +67,7 @@ pythonpath = ${SEARX_SRC}
 # https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
 # Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
 
-http = ${SEARX_INTERNAL_URL}
+http = ${SEARX_INTERNAL_HTTP}
 
 # using unix-sockets:
 #

From eb0d4646d818fe12032379aae2fcd8b5bdb6467e Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 6 Apr 2020 17:59:06 +0200
Subject: [PATCH 100/125] docs: rework of chapter "Install with apache"

BTW: normalize installation-nginx.rst
---
 docs/admin/installation-apache.rst            | 470 ++++++++++++++++--
 docs/admin/installation-nginx.rst             |  27 +-
 docs/admin/installation.rst                   |   6 +
 utils/lib.sh                                  |  12 +-
 utils/searx.sh                                |   5 +
 .../etc/apache2/sites-available/morty.conf    |   4 +-
 .../sites-available/searx.conf:filtron        |   4 +-
 7 files changed, 465 insertions(+), 63 deletions(-)

diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
index 8c6228380..da551c3a9 100644
--- a/docs/admin/installation-apache.rst
+++ b/docs/admin/installation-apache.rst
@@ -4,19 +4,303 @@
 Install with apache
 ===================
 
-.. sidebar:: public to the internet?
+.. _Apache: https://httpd.apache.org/
+.. _Apache Debian:
+    https://cwiki.apache.org/confluence/display/HTTPD/DistrosDefaultLayout#DistrosDefaultLayout-Debian,Ubuntu(Apachehttpd2.x):
+.. _README.Debian:
+    https://salsa.debian.org/apache-team/apache2/raw/master/debian/apache2.README.Debian
+.. _Apache Arch Linux:
+    https://wiki.archlinux.org/index.php/Apache_HTTP_Server
+.. _Apache Fedora:
+    https://docs.fedoraproject.org/en-US/quick-docs/getting-started-with-apache-http-server/index.html
+.. _Apache directives:
+    https://httpd.apache.org/docs/trunk/mod/directives.html
+.. _Getting Started:
+    https://httpd.apache.org/docs/current/en/getting-started.html
+.. _Terms Used to Describe Directives:
+    https://httpd.apache.org/docs/current/en/mod/directive-dict.html
+.. _Configuration Files:
+    https://httpd.apache.org/docs/current/en/configuring.html
+.. _ProxyPreserveHost: https://httpd.apache.org/docs/trunk/mod/mod_proxy.html#proxypreservehost
+.. _LoadModule:
+    https://httpd.apache.org/docs/2.4/mod/mod_so.html#loadmodule
+.. _DocumentRoot:
+    https://httpd.apache.org/docs/trunk/mod/core.html#documentroot
+.. _Location:
+    https://httpd.apache.org/docs/trunk/mod/core.html#location
+.. _uWSGI Apache support:
+    https://uwsgi-docs.readthedocs.io/en/latest/Apache.html
+.. _apache uwsgi:
+    https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
+.. _mod_proxy_uwsgi:
+    https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
 
-   If your searx instance is public, stop here and first install :ref:`filtron
-   reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
-   :ref:`installation scripts`.
+.. sidebar:: further read
+
+   - `Apache Arch Linux`_
+   - `Apache Debian`_ and `README.Debian`_
+   - `Apache Fedora`_
+   - `Apache directives`_
 
 .. contents:: Contents
    :depth: 2
    :local:
    :backlinks: entry
 
-Add wsgi mod
-============
+The apache HTTP server
+======================
+
+If Apache_ is not installed, install it now. If apache_ is new to you, the
+`Getting Started`_, `Configuration Files`_ and `Terms Used to Describe
+Directives`_ documentation gives first orientation.  There is also a list of
+`Apache directives`_ *to keep in the pocket*.
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         sudo -H apt-get install apache2
+
+   .. group-tab:: Arch Linux
+
+      .. code:: sh
+
+         sudo -H pacman -S apache
+         sudo -H systemctl enable httpd
+         sudo -H systemctl start http
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code:: sh
+
+         sudo -H dnf install httpd
+         sudo -H systemctl enable httpd
+         sudo -H systemctl start httpd
+
+Now at http://localhost you should see any kind of *Welcome* or *Test* page.
+How this default intro site is configured, depends on the linux distribution
+(compare `Apache directives`_).
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      .. code:: sh
+
+         less /etc/apache2/sites-enabled/000-default.conf
+
+      In this file, there is a line setting the `DocumentRoot`_ directive:
+
+      .. code:: apache
+
+         DocumentRoot /var/www/html
+
+      And the *welcome* page is the HTML file at ``/var/www/html/index.html``.
+
+   .. group-tab:: Arch Linux
+
+      .. code:: sh
+
+         less /etc/httpd/conf/httpd.conf
+
+      In this file, there is a line setting the `DocumentRoot`_ directive:
+
+      .. code:: apache
+
+         DocumentRoot "/srv/http"
+         <Directory "/srv/http">
+             Options Indexes FollowSymLinks
+             AllowOverride None
+             Require all granted
+         </Directory>
+
+      The *welcome* page of Arch Linux is a page showing directory located at
+      ``DocumentRoot``.  This is *directory* page is generated by the Module
+      `mod_autoindex <https://httpd.apache.org/docs/2.4/mod/mod_autoindex.html>`_:
+
+      .. code:: apache
+
+         LoadModule autoindex_module modules/mod_autoindex.so
+         ...
+         Include conf/extra/httpd-autoindex.conf
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code:: sh
+
+         less /etc/httpd/conf/httpd.conf
+
+      In this file, there is a line setting the ``DocumentRoot`` directive:
+
+      .. code:: apache
+
+          DocumentRoot "/var/www/html"
+          ...
+          <Directory "/var/www">
+              AllowOverride None
+              # Allow open access:
+              Require all granted
+          </Directory>
+
+      On fresh installations, the ``/var/www`` is empty and the *default
+      welcome page* is shown, the configuration is located at::
+
+        less /etc/httpd/conf.d/welcome.conf
+
+.. _The Debian Layout:
+
+The Debian Layout
+=================
+
+Be aware that the Debian layout is quite different from the standard Apache
+configuration.  For details look at the README.Debian_
+(``/usr/share/doc/apache2/README.Debian.gz``).  Some commands you should know on
+Debian:
+
+* :man:`apache2ctl`:  Apache HTTP server control interface
+* :man:`a2enmod`, :man:`a2dismod`: switch on/off modules
+* :man:`a2enconf`, :man:`a2disconf`: switch on/off configurations
+* :man:`a2ensite`, :man:`a2dissite`: switch on/off sites
+
+
+.. _apache searx site:
+
+Apache Reverse Proxy
+====================
+
+.. sidebar:: public to the internet?
+
+   If your searx instance is public, stop here and first install :ref:`filtron
+   reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
+   :ref:`installation scripts`.  If already done, follow setup: *searx via
+   filtron plus morty*.
+
+To setup a Apache revers proxy you have to enable the *headers* and *proxy*
+modules and create a `Location`_ configuration for the searx site.  In most
+distributions you have to uncomment the lines in the main configuration file,
+except in the :ref:`The Debian Layout`.
+
+.. tabs::
+
+   .. group-tab:: Ubuntu / debian
+
+      In the Apache setup, enable headers and proxy modules:
+
+      .. code:: sh
+
+         sudo -H a2enmod headers
+         sudo -H a2enmod proxy
+         sudo -H a2enmod proxy_http
+
+      In :ref:`The Debian Layout` you create a ``searx.conf`` with the
+      ``<Location /searx >`` directive and save this file in the *sites
+      available* folder at ``/etc/apache2/sites-available``.  To enable the
+      ``searx.conf`` use :man:`a2ensite`:
+
+      .. code:: sh
+
+         sudo -H a2ensite searx.conf
+
+   .. group-tab:: Arch Linux
+
+      In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
+      modules (LoadModule_):
+
+      .. code:: apache
+
+         LoadModule headers_module modules/mod_headers.so
+         LoadModule proxy_module modules/mod_proxy.so
+         LoadModule proxy_http_module modules/mod_proxy_http.so
+
+   .. group-tab::  Fedora / RHEL
+
+      In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
+      modules (LoadModule_):
+
+      .. code:: apache
+
+         LoadModule headers_module modules/mod_headers.so
+         LoadModule proxy_module modules/mod_proxy.so
+         LoadModule proxy_http_module modules/mod_proxy_http.so
+
+.. tabs::
+
+   .. group-tab:: searx via filtron plus morty
+
+      Use this setup, if your instance is public to the internet, compare
+      figure: :ref:`architecture <arch public>` and :ref:`installation scripts`.
+
+      1. Configure a reverse proxy for :ref:`filtron <filtron.sh>`, listening on
+         *localhost 4004* (:ref:`filtron route request`):
+
+      .. code:: apache
+
+         <Location /searx >
+
+             # SetEnvIf Request_URI "/searx" dontlog
+             # CustomLog /dev/null combined env=dontlog
+
+             Require all granted
+
+             Order deny,allow
+             Deny from all
+             #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+             Allow from all
+
+             ProxyPreserveHost On
+             ProxyPass http://127.0.0.1:4004
+             RequestHeader set X-Script-Name /searx
+
+         </Location>
+
+      2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
+      *localhost 3000* (FYI: ``ProxyPreserveHost On`` is already set, see
+      above):
+
+      .. code:: apache
+
+         ProxyPreserveHost On
+
+         <Location /morty >
+
+             # SetEnvIf Request_URI "/morty" dontlog
+             # CustomLog /dev/null combined env=dontlog
+
+             Require all granted
+
+             Order deny,allow
+             Deny from all
+             #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+             Allow from all
+
+             ProxyPass http://127.0.0.1:3000
+             RequestHeader set X-Script-Name /morty
+
+         </Location>
+
+      Note that reverse proxy advised to be used in case of single-user or
+      low-traffic instances.  For a fully result proxification add :ref:`morty's
+      <searx morty>` **public URL** to your :origin:`searx/settings.yml`:
+
+      .. code:: yaml
+
+         result_proxy:
+             # replace example.org with your server's public name
+             url : https://example.org/morty
+
+         server:
+             image_proxy : True
+
+uWSGI support
+=============
+
+Be warned, with this setup, your instance isn't :ref:`protected <searx
+filtron>`.  Nevertheless it is good enough for intranet usage and it
+demonstrates: *how different the uwsgi support is, depending on the
+distribution*.  To enable :ref:`uWSGI <searx uwsgi>` support you need to install
+the apache `apache uwsgi`_ support:
 
 .. tabs::
 
@@ -27,37 +311,130 @@ Add wsgi mod
          sudo -H apt-get install libapache2-mod-uwsgi
          sudo -H a2enmod uwsgi
 
-Add this configuration in the file ``/etc/apache2/apache2.conf``.  To limit
-acces to your intranet replace ``Allow from all`` directive and replace
+   .. group-tab:: Arch Linux
+
+      .. code:: sh
+
+         sudo -H pacman -S uwsgi
+
+      In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
+      modules (LoadModule_):
+
+      .. code:: apache
+
+         LoadModule proxy_module modules/mod_proxy.so
+         LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code:: sh
+
+         sudo -H dnf install uwsgi
+         FIXME: enable uwsgi in apache
+
+The next example shows a configuration using the `uWSGI Apache support`_ via
+unix sockets.  For socket communication, you have to activate ``socket =
+/run/uwsgi/app/searx/socket`` and comment out the ``http = 127.0.0.1:8888``
+configuration in your :ref:`uwsgi ini file <uwsgi configuration>`.
+
+If not already exists, create a folder for the unix sockets, which can be
+used by the searx account:
+
+.. code:: bash
+
+   sudo -H mkdir -p /run/uwsgi/app/searx/
+   sudo -H chown -R searx:searx /run/uwsgi/app/searx/
+
+To limit acces to your intranet replace ``Allow from all`` directive and replace
 ``192.168.0.0/16`` with your subnet IP/class.
 
-.. _inranet apache site:
+.. tabs::
 
-Note that if your instance of searx is not at the root, you should change
-``<Location />`` by the location of your instance, like ``<Location /searx>``:
+   .. group-tab:: Ubuntu / debian
 
-.. code:: apache
+      Debian uses the (old) `mod_uwsgi
+      <https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-uwsgi>`_.
 
-   # CustomLog /dev/null combined
+      .. code:: apache
 
-   <IfModule mod_uwsgi.c>
+         <IfModule mod_uwsgi.c>
 
-     <Location />
+             # SetEnvIf Request_URI "/searx" dontlog
+             # CustomLog /dev/null combined env=dontlog
 
-          Options FollowSymLinks Indexes
-          SetHandler uwsgi-handler
-          uWSGISocket /run/uwsgi/app/searx/socket
+             <Location /searx >
 
-          Order deny,allow
-          Deny from all
-          # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
-          Allow from all
+                 Require all granted
 
-     </Location>
+                 Options FollowSymLinks Indexes
+                 SetHandler uwsgi-handler
+                 uWSGISocket /run/uwsgi/app/searx/socket
 
-   </IfModule>
+                 Order deny,allow
+                 Deny from all
+                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+                 Allow from all
 
-Enable apache mod_uwsgi and restart apache:
+             </Location>
+
+         </IfModule>
+
+   .. group-tab:: Arch Linux
+
+      Arch Linux uses the (recommend) `mod_proxy_uwsgi`_.
+
+      .. code:: apache
+
+         <IfModule proxy_uwsgi_module>
+
+             # SetEnvIf Request_URI /searx dontlog
+             # CustomLog /dev/null combined env=dontlog
+
+             <Location /searx>
+
+                 Require all granted
+                 Order deny,allow
+                 Deny from all
+                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+                 Allow from all
+
+                 ProxyPreserveHost On
+                 ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+	     </Location>
+
+         </IfModule>
+
+   .. group-tab::  Fedora / RHEL
+
+      RHEL uses the (recommend) `mod_proxy_uwsgi`_.
+
+      .. code:: apache
+
+         <IfModule proxy_uwsgi_module>
+
+             # SetEnvIf Request_URI /searx dontlog
+             # CustomLog /dev/null combined env=dontlog
+
+             <Location /searx>
+
+                 Require all granted
+                 Order deny,allow
+                 Deny from all
+                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+                 Allow from all
+
+                 ProxyPreserveHost On
+                 ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+	     </Location>
+
+         </IfModule>
+
+.. _restart apache:
+
+Restart service
+===============
 
 .. tabs::
 
@@ -65,31 +442,34 @@ Enable apache mod_uwsgi and restart apache:
 
       .. code:: sh
 
-         a2enmod uwsgi
          sudo -H systemctl restart apache2
+         sudo -H service uwsgi restart searx
+
+   .. group-tab:: Arch Linux
+
+      .. code:: sh
+
+         sudo -H systemctl restart httpd
+         sudo -H systemctl restart uwsgi@searx
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code:: sh
+
+         sudo -H systemctl restart httpd
+         sudo -H touch /etc/uwsgi.d/searx.ini
+
 
 disable logs
 ============
 
-For better privacy you can disable Apache logs.  Go back to
-``/etc/apache2/apache2.conf`` :ref:`[example] <inranet apache site>` and above
-``<Location />`` activate directive:
+For better privacy you can disable Apache logs.  In the examples above activate
+one of the lines and `restart apache`_::
 
-.. code:: apache
 
-    CustomLog /dev/null combined
+  # SetEnvIf Request_URI "/searx" dontlog
+  # CustomLog /dev/null combined env=dontlog
 
-Restart apache:
-
-.. tabs::
-
-   .. group-tab:: Ubuntu / debian
-
-      .. code:: sh
-
-         sudo -H systemctl restart apache2
-
-.. warning::
-
-   You can only disable logs for the whole (virtual) server not for a specific
-   path.
+The ``CustomLog`` directive disable logs for the whole (virtual) server, use it
+when the URL of the service does not have a path component (``/searx``) / is
+located at root (``/``).
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 2097d7daf..b02c555f6 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -28,10 +28,9 @@ Install with nginx
 The nginx HTTP server
 =====================
 
-If nginx_ is not installed (uwsgi will not work with the package nginx-light)
+If nginx_ is not installed (uwsgi will not work with the package nginx-light),
 install it now.
 
-
 .. tabs::
 
    .. group-tab:: Ubuntu / debian
@@ -58,7 +57,8 @@ install it now.
 
 Now at http://localhost you should see a *Welcome to nginx!* page, on Fedora you
 see a *Fedora Webserver - Test Page*.  The test page comes from the default
-`nginx server configuration`_:
+`nginx server configuration`_.  How this default intro site is configured,
+depends on the linux distribution:
 
 .. tabs::
 
@@ -111,7 +111,8 @@ A nginx searx site
 
    If your searx instance is public, stop here and first install :ref:`filtron
    reverse proxy <filtron.sh>` and :ref:`result proxy morty <morty.sh>`, see
-   :ref:`installation scripts`.
+   :ref:`installation scripts`.  If already done, follow setup: *searx via
+   filtron plus morty*.
 
 Now you have to create a configuration for the searx site.  If nginx_ is new to
 you, the `nginx beginners guide`_ is a good starting point and the `Getting
@@ -143,9 +144,10 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
    .. group-tab:: searx via filtron plus morty
 
       Use this setup, if your instance is public to the internet, compare
-      figure: :ref:`architecture <arch public>`.  Configure a reverse proxy for
-      :ref:`filtron <filtron.sh>`, listening on *localhost 4004* (:ref:`filtron
-      route request`):
+      figure: :ref:`architecture <arch public>` and :ref:`installation scripts`.
+
+      1. Configure a reverse proxy for :ref:`filtron <filtron.sh>`, listening on
+         *localhost 4004* (:ref:`filtron route request`):
 
       .. code:: nginx
 
@@ -159,8 +161,8 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
          }
 
 
-      Configure reverse proxy for :ref:`morty <searx morty>`, listening on
-      *localhost 3000*:
+      2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
+         *localhost 3000*:
 
       .. code:: nginx
 
@@ -181,7 +183,10 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
          result_proxy:
              # replace example.org with your server's public name
-             url : https://example.org/
+             url : https://example.org/morty
+
+         server:
+             image_proxy : True
 
 
    .. group-tab:: proxy or uWSGI
@@ -244,7 +249,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 	 mkdir -p /run/uwsgi/app/searx/
 	 sudo -H chown -R searx:searx /run/uwsgi/app/searx/
 
-   .. group-tab:: subdirectory URL
+   .. group-tab:: proxy at subdir URL
 
       Be warned, with these setups, your instance isn't :ref:`protected <searx
       filtron>`.  The examples are just here to demonstrate how to export the
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index ac17c326a..81fc4d3e2 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -54,3 +54,9 @@ This installs searx as described in :ref:`installation basic`.
 .. code:: bash
 
    $ sudo -H ./utils/morty.sh install all
+
+If all services are running fine, you can add it to your HTTP server:
+
+- :ref:`installation apache`
+- :ref:`installation nginx`
+
diff --git a/utils/lib.sh b/utils/lib.sh
index 9d5068646..aaeb5093b 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -634,9 +634,15 @@ if [[ -z "${APACHE_SITES_AVAILABE}" ]]; then
 fi
 
 apache_is_installed() {
-    (command -v apachectl \
-        && command -v a2ensite \
-        && command -v a2dissite ) &>/dev/null
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            (command -v apachectl \
+                 && command -v a2ensite \
+                 && command -v a2dissite ) &>/dev/null
+            ;;
+        arch) (command -v httpd) ;;
+        fedora) (command -v httpd) ;;
+    esac
 }
 
 apache_reload() {
diff --git a/utils/searx.sh b/utils/searx.sh
index a74aec393..86b651696 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -735,6 +735,11 @@ This installs the searx uwsgi app as apache site.  If your server is public to
 the internet, you should instead use a reverse proxy (filtron) to block
 excessively bot queries."
 
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*) : ;;
+        *) err_msg "sorry distro $DIST_ID $DIST_VERS not yet supported"; exit 42 ;;
+    esac
+
     ! apache_is_installed && err_msg "Apache is not installed."
 
     if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/apache2/sites-available/morty.conf
index 48b4dd766..4421cdd51 100644
--- a/utils/templates/etc/apache2/sites-available/morty.conf
+++ b/utils/templates/etc/apache2/sites-available/morty.conf
@@ -18,8 +18,8 @@ ProxyPreserveHost On
     ProxyPass http://${MORTY_LISTEN}
     RequestHeader set X-Script-Name ${PUBLIC_URL_PATH_MORTY}
 
-    # In Apache it seems, that setting HTTP_HOST header direct here does have no
-    # effect.  I needed to set 'ProxyPreserveHost On' (see above).
+    # In Apache it seems, that setting HTTP_HOST header directive here does have
+    # no effect.  I needed to set 'ProxyPreserveHost On' (see above).
 
     # RequestHeader set Host ${PUBLIC_HOST}
 
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
index e34049063..2d6af7889 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/apache2/sites-available/searx.conf:filtron
@@ -23,8 +23,8 @@ ProxyPreserveHost On
     ProxyPass http://${FILTRON_LISTEN}
     RequestHeader set X-Script-Name ${FILTRON_URL_PATH}
 
-    # In Apache it seems, that setting HTTP_HOST header direct here does have no
-    # effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST
+    # In Apache it seems, that setting HTTP_HOST header directive here does have
+    # no effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST
     # (ProxyPreserveHost On) is needed by searx to render correct *Search URL*
     # in the *Link* box and *saved preference*.
 

From ee39a098acb2386abd5382de5c9476cc4ffe2e03 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 7 Apr 2020 18:31:51 +0200
Subject: [PATCH 101/125] apache: normalize installation (docs and script)s
 over all distros

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-apache.rst            | 219 ++++++++++--------
 docs/admin/installation-searx.rst             |   2 +
 utils/filtron.sh                              |   8 +-
 utils/lib.sh                                  | 119 ++++++++--
 utils/lxc.sh                                  |   9 +-
 utils/makefile.include                        |   4 +-
 utils/morty.sh                                |   9 +-
 utils/searx.sh                                |  30 ++-
 utils/templates/etc/apache2                   |   1 +
 .../apache2/sites-available/searx.conf:uwsgi  |  27 ---
 .../sites-available/morty.conf                |  14 +-
 .../sites-available/searx.conf:filtron        |  16 +-
 .../httpd/sites-available/searx.conf:uwsgi    |  27 +++
 13 files changed, 289 insertions(+), 196 deletions(-)
 create mode 120000 utils/templates/etc/apache2
 delete mode 100644 utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
 rename utils/templates/etc/{apache2 => httpd}/sites-available/morty.conf (51%)
 rename utils/templates/etc/{apache2 => httpd}/sites-available/searx.conf:filtron (57%)
 create mode 100644 utils/templates/etc/httpd/sites-available/searx.conf:uwsgi

diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
index da551c3a9..0b6cc38df 100644
--- a/docs/admin/installation-apache.rst
+++ b/docs/admin/installation-apache.rst
@@ -30,8 +30,6 @@ Install with apache
     https://httpd.apache.org/docs/trunk/mod/core.html#location
 .. _uWSGI Apache support:
     https://uwsgi-docs.readthedocs.io/en/latest/Apache.html
-.. _apache uwsgi:
-    https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
 .. _mod_proxy_uwsgi:
     https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-proxy-uwsgi
 
@@ -149,22 +147,6 @@ How this default intro site is configured, depends on the linux distribution
 
         less /etc/httpd/conf.d/welcome.conf
 
-.. _The Debian Layout:
-
-The Debian Layout
-=================
-
-Be aware that the Debian layout is quite different from the standard Apache
-configuration.  For details look at the README.Debian_
-(``/usr/share/doc/apache2/README.Debian.gz``).  Some commands you should know on
-Debian:
-
-* :man:`apache2ctl`:  Apache HTTP server control interface
-* :man:`a2enmod`, :man:`a2dismod`: switch on/off modules
-* :man:`a2enconf`, :man:`a2disconf`: switch on/off configurations
-* :man:`a2ensite`, :man:`a2dissite`: switch on/off sites
-
-
 .. _apache searx site:
 
 Apache Reverse Proxy
@@ -179,8 +161,12 @@ Apache Reverse Proxy
 
 To setup a Apache revers proxy you have to enable the *headers* and *proxy*
 modules and create a `Location`_ configuration for the searx site.  In most
-distributions you have to uncomment the lines in the main configuration file,
-except in the :ref:`The Debian Layout`.
+distributions you have to un-comment the lines in the main configuration file,
+except in :ref:`The Debian Layout`.
+
+To pass the HTTP HOST header 
+With ProxyPreserveHost_ the incoming Host HTTP request header is passed to the
+proxied host.
 
 .. tabs::
 
@@ -210,6 +196,8 @@ except in the :ref:`The Debian Layout`.
 
       .. code:: apache
 
+	 FIXME needs test
+
          LoadModule headers_module modules/mod_headers.so
          LoadModule proxy_module modules/mod_proxy.so
          LoadModule proxy_http_module modules/mod_proxy_http.so
@@ -221,7 +209,9 @@ except in the :ref:`The Debian Layout`.
 
       .. code:: apache
 
-         LoadModule headers_module modules/mod_headers.so
+	 FIXME needs test
+
+	 LoadModule headers_module modules/mod_headers.so
          LoadModule proxy_module modules/mod_proxy.so
          LoadModule proxy_http_module modules/mod_proxy_http.so
 
@@ -256,8 +246,7 @@ except in the :ref:`The Debian Layout`.
          </Location>
 
       2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
-      *localhost 3000* (FYI: ``ProxyPreserveHost On`` is already set, see
-      above):
+      *localhost 3000*
 
       .. code:: apache
 
@@ -297,10 +286,9 @@ uWSGI support
 =============
 
 Be warned, with this setup, your instance isn't :ref:`protected <searx
-filtron>`.  Nevertheless it is good enough for intranet usage and it
-demonstrates: *how different the uwsgi support is, depending on the
-distribution*.  To enable :ref:`uWSGI <searx uwsgi>` support you need to install
-the apache `apache uwsgi`_ support:
+filtron>`, nevertheless it is good enough for intranet usage.  In modern Linux
+distributions, the `mod_proxy_uwsgi`_ is compiled into the *normal* apache
+package and you need to install only the :ref:`uWSGI <searx uwsgi>` package:
 
 .. tabs::
 
@@ -308,8 +296,10 @@ the apache `apache uwsgi`_ support:
 
       .. code:: sh
 
-         sudo -H apt-get install libapache2-mod-uwsgi
-         sudo -H a2enmod uwsgi
+         sudo -H apt-get install uwsgi
+
+         # Ubuntu =< 18.04
+         sudo -H apt-get install libapache2-mod-proxy-uwsgi
 
    .. group-tab:: Arch Linux
 
@@ -317,43 +307,113 @@ the apache `apache uwsgi`_ support:
 
          sudo -H pacman -S uwsgi
 
-      In the ``/etc/httpd/conf/httpd.conf`` file, activate headers and proxy
-      modules (LoadModule_):
-
-      .. code:: apache
-
-         LoadModule proxy_module modules/mod_proxy.so
-         LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
-
    .. group-tab::  Fedora / RHEL
 
       .. code:: sh
 
          sudo -H dnf install uwsgi
-         FIXME: enable uwsgi in apache
 
 The next example shows a configuration using the `uWSGI Apache support`_ via
-unix sockets.  For socket communication, you have to activate ``socket =
-/run/uwsgi/app/searx/socket`` and comment out the ``http = 127.0.0.1:8888``
-configuration in your :ref:`uwsgi ini file <uwsgi configuration>`.
+unix sockets and `mod_proxy_uwsgi`_.
 
-If not already exists, create a folder for the unix sockets, which can be
-used by the searx account:
+For socket communication, you have to activate ``socket =
+/run/uwsgi/app/searx/socket`` and comment out the ``http = 127.0.0.1:8888``
+configuration in your :ref:`uwsgi ini file <uwsgi configuration>`.  If not
+already exists, create a folder for the unix sockets, which can be used by the
+searx account (see :ref:`create searx user`):
 
 .. code:: bash
 
    sudo -H mkdir -p /run/uwsgi/app/searx/
    sudo -H chown -R searx:searx /run/uwsgi/app/searx/
 
-To limit acces to your intranet replace ``Allow from all`` directive and replace
-``192.168.0.0/16`` with your subnet IP/class.
+If the server is public; to limit access to your intranet replace ``Allow from
+all`` directive and replace ``192.168.0.0/16`` with your subnet IP/class.
 
 .. tabs::
 
    .. group-tab:: Ubuntu / debian
 
-      Debian uses the (old) `mod_uwsgi
+      .. code:: apache
+
+	 LoadModule headers_module /usr/lib/apache2/mod_headers.so
+	 LoadModule proxy_module /usr/lib/apache2/modules/mod_proxy.so
+	 LoadModule proxy_uwsgi_module /usr/lib/apache2/modules/mod_proxy_uwsgi.so
+
+	 # SetEnvIf Request_URI /searx dontlog
+	 # CustomLog /dev/null combined env=dontlog
+
+	 <Location /searx>
+
+	     Require all granted
+	     Order deny,allow
+	     Deny from all
+	     # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+	     Allow from all
+
+	     ProxyPreserveHost On
+	     ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+	 </Location>
+
+   .. group-tab:: Arch Linux
+
+      .. code:: apache
+
+	 FIXME needs test
+
+         LoadModule proxy_module modules/mod_proxy.so
+         LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
+
+         # SetEnvIf Request_URI /searx dontlog
+         # CustomLog /dev/null combined env=dontlog
+
+         <Location /searx>
+
+             Require all granted
+             Order deny,allow
+             Deny from all
+             # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+             Allow from all
+
+             ProxyPreserveHost On
+             ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+	 </Location>
+
+   .. group-tab::  Fedora / RHEL
+
+      .. code:: apache
+
+	 FIXME needs test
+
+	 LoadModule proxy_module modules/mod_proxy.so
+         LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
+         <IfModule proxy_uwsgi_module>
+
+             # SetEnvIf Request_URI /searx dontlog
+             # CustomLog /dev/null combined env=dontlog
+
+             <Location /searx>
+
+                 Require all granted
+                 Order deny,allow
+                 Deny from all
+                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+                 Allow from all
+
+                 ProxyPreserveHost On
+                 ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
+
+	     </Location>
+
+         </IfModule>
+
+   .. group-tab:: old mod_wsgi
+
+      We show this only for historical reasons, DON'T USE `mod_uwsgi
       <https://uwsgi-docs.readthedocs.io/en/latest/Apache.html#mod-uwsgi>`_.
+      ANYMORE!
 
       .. code:: apache
 
@@ -379,58 +439,6 @@ To limit acces to your intranet replace ``Allow from all`` directive and replace
 
          </IfModule>
 
-   .. group-tab:: Arch Linux
-
-      Arch Linux uses the (recommend) `mod_proxy_uwsgi`_.
-
-      .. code:: apache
-
-         <IfModule proxy_uwsgi_module>
-
-             # SetEnvIf Request_URI /searx dontlog
-             # CustomLog /dev/null combined env=dontlog
-
-             <Location /searx>
-
-                 Require all granted
-                 Order deny,allow
-                 Deny from all
-                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
-                 Allow from all
-
-                 ProxyPreserveHost On
-                 ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
-
-	     </Location>
-
-         </IfModule>
-
-   .. group-tab::  Fedora / RHEL
-
-      RHEL uses the (recommend) `mod_proxy_uwsgi`_.
-
-      .. code:: apache
-
-         <IfModule proxy_uwsgi_module>
-
-             # SetEnvIf Request_URI /searx dontlog
-             # CustomLog /dev/null combined env=dontlog
-
-             <Location /searx>
-
-                 Require all granted
-                 Order deny,allow
-                 Deny from all
-                 # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
-                 Allow from all
-
-                 ProxyPreserveHost On
-                 ProxyPass unix:/run/uwsgi/app/searx/socket|uwsgi://uwsgi-uds-searx/
-
-	     </Location>
-
-         </IfModule>
-
 .. _restart apache:
 
 Restart service
@@ -473,3 +481,18 @@ one of the lines and `restart apache`_::
 The ``CustomLog`` directive disable logs for the whole (virtual) server, use it
 when the URL of the service does not have a path component (``/searx``) / is
 located at root (``/``).
+
+.. _The Debian Layout:
+
+The Debian Layout
+=================
+
+Be aware that the Debian layout is quite different from the standard Apache
+configuration.  For details look at the README.Debian_
+(``/usr/share/doc/apache2/README.Debian.gz``).  Some commands you should know on
+Debian:
+
+* :man:`apache2ctl`:  Apache HTTP server control interface
+* :man:`a2enmod`, :man:`a2dismod`: switch on/off modules
+* :man:`a2enconf`, :man:`a2disconf`: switch on/off configurations
+* :man:`a2ensite`, :man:`a2dissite`: switch on/off sites
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index 91891d85c..d5c0063ee 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -25,6 +25,8 @@ Install packages
 
    This installs also the packages needed by :ref:`searx uwsgi`
 
+.. _create searx user:
+
 Create user
 ===========
 
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 6c58b07a2..0fbf74787 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -441,14 +441,12 @@ This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SIT
 
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to continue?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
+    else
+        install_apache
     fi
 
-    a2enmod headers
-    a2enmod proxy
-    a2enmod proxy_http
-
     echo
     apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
 
diff --git a/utils/lib.sh b/utils/lib.sh
index aaeb5093b..2c0c179bb 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -627,21 +627,56 @@ EOF
 # Apache
 # ------
 
-# FIXME: Arch Linux & RHEL should be added
+apache_distro_setup() {
+    # shellcheck disable=SC2034
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            # debian uses the /etc/apache2 path, while other distros use
+            # the apache default at /etc/httpd
+            APACHE_SITES_AVAILABLE="/etc/apache2/sites-available"
+            APACHE_SITES_ENABLED="/etc/apache2/sites-enabled"
+            APACHE_MODULES="/usr/lib/apache2/modules"
+            APACHE_PACKAGES="apache2"
+            ;;
+        arch-*)
+            APACHE_SITES_AVAILABLE="/etc/httpd/sites-available"
+            APACHE_SITES_ENABLED="/etc/httpd/sites-enabled"
+            APACHE_MODULES="modules"
+            APACHE_PACKAGES="apache"
+            ;;
+        fedora-*)
+            APACHE_SITES_AVAILABLE="/etc/httpd/sites-available"
+            APACHE_SITES_ENABLED="/etc/httpd/sites-enabled"
+            APACHE_MODULES="modules"
+            APACHE_PACKAGES="httpd"
+            ;;
+        *)
+            err_msg "$DIST_ID-$DIST_VERS: apache not yet implemented"
+            ;;
+    esac
+}
 
-if [[ -z "${APACHE_SITES_AVAILABE}" ]]; then
-    APACHE_SITES_AVAILABE="/etc/apache2/sites-available"
-fi
+apache_distro_setup
+
+install_apache(){
+    info_msg "installing apache ..."
+    pkg_install "$APACHE_PACKAGES"
+    case $DIST_ID-$DIST_VERS in
+        arch-*|fedora-*)
+            if ! grep "IncludeOptional sites-enabled" "/etc/httpd/conf/httpd.conf"; then
+                echo "IncludeOptional sites-enabled/*.conf" >> "/etc/httpd/conf/httpd.conf"
+            fi
+            systemctl enable httpd
+            systemctl start httpd
+            ;;
+    esac
+}
 
 apache_is_installed() {
     case $DIST_ID-$DIST_VERS in
-        ubuntu-*|debian-*)
-            (command -v apachectl \
-                 && command -v a2ensite \
-                 && command -v a2dissite ) &>/dev/null
-            ;;
-        arch) (command -v httpd) ;;
-        fedora) (command -v httpd) ;;
+        ubuntu-*|debian-*) (command -v apachectl) &>/dev/null;;
+        arch-*) (command -v httpd) &>/dev/null;;
+        fedora-*) (command -v httpd) &>/dev/null;;
     esac
 }
 
@@ -649,8 +684,16 @@ apache_reload() {
 
     info_msg "reload apache .."
     echo
-    sudo -H apachectl configtest
-    sudo -H service apache2 force-reload
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            sudo -H apachectl configtest
+            sudo -H systemctl force-reload apache2
+            ;;
+        arch-*| fedora-*)
+            sudo -H httpd -t
+            sudo -H systemctl force-reload httpd
+            ;;
+    esac
 }
 
 apache_install_site() {
@@ -670,9 +713,8 @@ apache_install_site() {
     done
 
     install_template "${template_opts[@]}" \
-                     "${APACHE_SITES_AVAILABE}/${pos_args[1]}" \
+                     "${APACHE_SITES_AVAILABLE}/${pos_args[1]}" \
                      root root 644
-
     apache_enable_site "${pos_args[1]}"
     info_msg "installed apache site: ${pos_args[1]}"
 }
@@ -683,15 +725,32 @@ apache_remove_site() {
 
     info_msg "remove apache site: $1"
     apache_dissable_site "$1"
-    rm -f "${APACHE_SITES_AVAILABE}/$1"
+    rm -f "${APACHE_SITES_AVAILABLE}/$1"
 }
 
 apache_enable_site() {
 
     # usage:  apache_enable_site <mysite.conf>
 
-    info_msg "enable apache site: $1"
-    sudo -H a2ensite -q "$1"
+    local CONF="$1"
+
+    info_msg "enable apache site: ${CONF}"
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            sudo -H a2ensite -q "${CONF}"
+            ;;
+        arch-*)
+            mkdir -p "${APACHE_SITES_ENABLED}"
+            rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+            ;;
+        fedora-*)
+            mkdir -p "${APACHE_SITES_ENABLED}"
+            rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+            ;;
+    esac
     apache_reload
 }
 
@@ -699,9 +758,25 @@ apache_dissable_site() {
 
     # usage:  apache_disable_site <mysite.conf>
 
-    info_msg "disable apache site: $1"
-    sudo -H a2dissite -q "$1"
-    apache_reload
+    local CONF="$1"
+
+    info_msg "disable apache site: ${CONF}"
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            sudo -H a2dissite -q "${CONF}"
+            ;;
+        arch-*)
+            mkdir -p "${APACHE_SITES_ENABLED}"
+            rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+            ;;
+        fedora-*)
+            mkdir -p "${APACHE_SITES_ENABLED}"
+            rm -f "${APACHE_SITES_ENABLED}/${CONF}"
+            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
+            ;;
+    esac
 }
 
 # uWSGI
@@ -741,7 +816,7 @@ uWSGI_distro_setup() {
             uWSGI_GROUP="uwsgi"
             ;;
         *)
-            info_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
+            err_msg "$DIST_ID-$DIST_VERS: uWSGI not yet implemented"
             ;;
 esac
 }
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 9eb28f498..6a26f80eb 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -98,7 +98,7 @@ start/stop
 show
   :info:         show info of all (or <name>) containers from LXC suite
   :config:       show config of all (or <name>) containers from the LXC suite
-  :suite:        show services of all the containers from the LXC suite
+  :suite:        show services of all (or <name>) containers from the LXC suite
   :images:       show information of local images
 cmd
   use single qoutes to evaluate in container's bash, e.g. 'echo $(hostname)'
@@ -294,11 +294,9 @@ main() {
 
 build_all_containers() {
     rst_title "Build all LXC containers of suite"
+    echo
     usage_containers
     lxc_copy_images_localy
-    echo
-    rst_title "build containers" section
-    echo
     lxc_init_all_containers
     lxc_config_all_containers
     lxc_boilerplate_all_containers
@@ -368,7 +366,6 @@ remove_containers() {
 
 lxc_copy_images_localy() {
     rst_title "copy images" section
-    echo
     for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
         lxc_image_copy "${LXC_SUITE[i]}" "${LXC_SUITE[i+1]}"
     done
@@ -477,7 +474,7 @@ lxc_init_all_containers() {
     local container_name
 
     for ((i=0; i<${#LXC_SUITE[@]}; i+=2)); do
-        lxc_init_container "${LXC_SUITE[i+1]}" "${LXC_HOST_PREFIX}-${image_name}"
+        lxc_init_container "${LXC_SUITE[i+1]}" "${LXC_HOST_PREFIX}-${LXC_SUITE[i+1]}"
     done
 }
 
diff --git a/utils/makefile.include b/utils/makefile.include
index 933d2b57a..65aca70f0 100644
--- a/utils/makefile.include
+++ b/utils/makefile.include
@@ -1,9 +1,11 @@
 # -*- coding: utf-8; mode: makefile-gmake -*-
 
 ifeq (,$(wildcard /.lxcenv.mk))
-PHONY += lxc-activate
+PHONY += lxc-activate lxc-purge
 lxc-activate:
 	@$(MAKE) -s -f  /share/searx/utils/makefile.lxc lxc-activate
+lxc-purge:
+	$(Q)rm -rf ./lxc
 else
 	include /.lxcenv.mk
 endif
diff --git a/utils/morty.sh b/utils/morty.sh
index 17039a05e..4de411999 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -402,15 +402,12 @@ This installs a reverse proxy (ProxyPass) into apache site (${APACHE_MORTY_SITE}
 
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to continue?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
+    else
+        install_apache
     fi
 
-    a2enmod headers
-    a2enmod proxy
-    a2enmod proxy_http
-
-    echo
     apache_install_site "${APACHE_MORTY_SITE}"
 
     info_msg "testing public url .."
diff --git a/utils/searx.sh b/utils/searx.sh
index 86b651696..6a8588c23 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -75,21 +75,23 @@ texlive-xetex-bin texlive-collection-fontsrecommended
 texlive-collection-latex dejavu-sans-fonts dejavu-serif-fonts
 dejavu-sans-mono-fonts"
 
-case $DIST_ID in
-    ubuntu|debian)
+case $DIST_ID-$DIST_VERS in
+    ubuntu-16.04|ubuntu-18.04)
         SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
         BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
-        APACHE_PACKAGES="libapache2-mod-uwsgi"
+        APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"
         ;;
-    arch)
+    ubuntu-*|debian-*)
+        SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
+        BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+        ;;
+    arch-*)
         SEARX_PACKAGES="${SEARX_PACKAGES_arch}"
         BUILD_PACKAGES="${BUILD_PACKAGES_arch}"
-        APACHE_PACKAGES="uwsgi"
         ;;
-    fedora)
+    fedora-*)
         SEARX_PACKAGES="${SEARX_PACKAGES_fedora}"
         BUILD_PACKAGES="${BUILD_PACKAGES_fedora}"
-        APACHE_PACKAGES="uwsgi"
         ;;
 esac
 
@@ -462,6 +464,7 @@ EOF
     wait_key
     info_msg "install needed python packages"
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
+pip install wheel
 ${SEARX_SRC}/manage.sh update_packages
 EOF
 }
@@ -735,21 +738,14 @@ This installs the searx uwsgi app as apache site.  If your server is public to
 the internet, you should instead use a reverse proxy (filtron) to block
 excessively bot queries."
 
-    case $DIST_ID-$DIST_VERS in
-        ubuntu-*|debian-*) : ;;
-        *) err_msg "sorry distro $DIST_ID $DIST_VERS not yet supported"; exit 42 ;;
-    esac
-
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to install apache site for searx-uwsgi?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
+    else
+        install_apache
     fi
 
-    pkg_install "$APACHE_PACKAGES"
-    a2enmod uwsgi
-
-    echo
     apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
 
     if ! service_is_available "${PUBLIC_URL}"; then
diff --git a/utils/templates/etc/apache2 b/utils/templates/etc/apache2
new file mode 120000
index 000000000..558a90717
--- /dev/null
+++ b/utils/templates/etc/apache2
@@ -0,0 +1 @@
+httpd
\ No newline at end of file
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi b/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
deleted file mode 100644
index 21e01ac4e..000000000
--- a/utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
+++ /dev/null
@@ -1,27 +0,0 @@
-# -*- coding: utf-8; mode: apache -*-
-
-<IfModule mod_uwsgi.c>
-
-    # SetEnvIf Request_URI "${SEARX_URL_PATH}" dontlog
-    # CustomLog /dev/null combined env=dontlog
-
-    <Location ${SEARX_URL_PATH}>
-
-        <IfModule mod_security2.c>
-            SecRuleEngine Off
-        </IfModule>
-
-	Require all granted
-
-        Options FollowSymLinks Indexes
-        SetHandler uwsgi-handler
-        uWSGISocket ${SEARX_UWSGI_SOCKET}
-
-        Order deny,allow
-        Deny from all
-        # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
-        Allow from all
-
-    </Location>
-
-</IfModule>
diff --git a/utils/templates/etc/apache2/sites-available/morty.conf b/utils/templates/etc/httpd/sites-available/morty.conf
similarity index 51%
rename from utils/templates/etc/apache2/sites-available/morty.conf
rename to utils/templates/etc/httpd/sites-available/morty.conf
index 4421cdd51..326fcc755 100644
--- a/utils/templates/etc/apache2/sites-available/morty.conf
+++ b/utils/templates/etc/httpd/sites-available/morty.conf
@@ -1,6 +1,12 @@
 # -*- coding: utf-8; mode: apache -*-
 
-ProxyPreserveHost On
+LoadModule headers_module       ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy_http.so
+#LoadModule setenvif_module      ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${PUBLIC_URL_PATH_MORTY}" dontlog
+# CustomLog /dev/null combined env=dontlog
 
 <Location ${PUBLIC_URL_PATH_MORTY} >
 
@@ -15,12 +21,8 @@ ProxyPreserveHost On
     #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
     Allow from all
 
+    ProxyPreserveHost On
     ProxyPass http://${MORTY_LISTEN}
     RequestHeader set X-Script-Name ${PUBLIC_URL_PATH_MORTY}
 
-    # In Apache it seems, that setting HTTP_HOST header directive here does have
-    # no effect.  I needed to set 'ProxyPreserveHost On' (see above).
-
-    # RequestHeader set Host ${PUBLIC_HOST}
-
 </Location>
diff --git a/utils/templates/etc/apache2/sites-available/searx.conf:filtron b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
similarity index 57%
rename from utils/templates/etc/apache2/sites-available/searx.conf:filtron
rename to utils/templates/etc/httpd/sites-available/searx.conf:filtron
index 2d6af7889..11dd360bc 100644
--- a/utils/templates/etc/apache2/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
@@ -1,6 +1,12 @@
 # -*- coding: utf-8; mode: apache -*-
 
-ProxyPreserveHost On
+LoadModule headers_module       ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy_http.so
+#LoadModule setenvif_module      ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${FILTRON_URL_PATH}" dontlog
+# CustomLog /dev/null combined env=dontlog
 
 # SecRuleRemoveById 981054
 # SecRuleRemoveById 981059
@@ -20,14 +26,8 @@ ProxyPreserveHost On
     #Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
     Allow from all
 
+    ProxyPreserveHost On
     ProxyPass http://${FILTRON_LISTEN}
     RequestHeader set X-Script-Name ${FILTRON_URL_PATH}
 
-    # In Apache it seems, that setting HTTP_HOST header directive here does have
-    # no effect.  I needed to set 'ProxyPreserveHost On' (see above).  HTTP_HOST
-    # (ProxyPreserveHost On) is needed by searx to render correct *Search URL*
-    # in the *Link* box and *saved preference*.
-
-    # RequestHeader set Host ${PUBLIC_HOST}
-
 </Location>
diff --git a/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi b/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi
new file mode 100644
index 000000000..ef702de3a
--- /dev/null
+++ b/utils/templates/etc/httpd/sites-available/searx.conf:uwsgi
@@ -0,0 +1,27 @@
+# -*- coding: utf-8; mode: apache -*-
+
+LoadModule headers_module       ${APACHE_MODULES}/mod_headers.so
+LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy.so
+LoadModule proxy_uwsgi_module   ${APACHE_MODULES}/mod_proxy_uwsgi.so
+# LoadModule setenvif_module ${APACHE_MODULES}/mod_setenvif.so
+
+# SetEnvIf Request_URI "${SEARX_URL_PATH}" dontlog
+# CustomLog /dev/null combined env=dontlog
+
+<Location ${SEARX_URL_PATH}>
+
+    <IfModule mod_security2.c>
+        SecRuleEngine Off
+    </IfModule>
+
+    Require all granted
+
+    Order deny,allow
+    Deny from all
+    # Allow from fd00::/8 192.168.0.0/16 fe80::/10 127.0.0.0/8 ::1
+    Allow from all
+
+    ProxyPreserveHost On
+    ProxyPass unix:${SEARX_UWSGI_SOCKET}|uwsgi://uwsgi-uds-searx/
+
+</Location>

From f693149cded4f783380f8f02154bd9288b72cdd5 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 8 Apr 2020 18:38:36 +0200
Subject: [PATCH 102/125] Changes from the installation tests on (all) LXC
 containers.

Tested and fixed HTTP & uWSGI installation on:

  ubu1604 ubu1804 ubu1910 ubu2004 fedora31 archlinux

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/utils/index.rst                          | 16 +++-
 docs/utils/lxc.sh.rst                         |  6 +-
 utils/filtron.sh                              |  7 +-
 utils/lib.sh                                  | 61 +++++++++++++-
 utils/lxc.sh                                  |  1 -
 utils/morty.sh                                | 16 ++--
 utils/searx.sh                                | 10 ++-
 .../etc/httpd/sites-available/morty.conf      |  2 +-
 .../httpd/sites-available/searx.conf:filtron  |  2 +-
 .../etc/uwsgi/apps-archlinux/searx.ini        |  4 +-
 .../etc/uwsgi/apps-archlinux/searx.ini:socket | 80 +++++++++++++++++++
 .../etc/uwsgi/apps-available/searx.ini        |  4 +-
 .../etc/uwsgi/apps-available/searx.ini:socket | 79 ++++++++++++++++++
 13 files changed, 263 insertions(+), 25 deletions(-)
 create mode 100644 utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
 create mode 100644 utils/templates/etc/uwsgi/apps-available/searx.ini:socket

diff --git a/docs/utils/index.rst b/docs/utils/index.rst
index 088586e43..13914af28 100644
--- a/docs/utils/index.rst
+++ b/docs/utils/index.rst
@@ -19,20 +19,28 @@ developers.
 
 .. _toolboxing common:
 
-Common commands
-===============
+Common commands & environment
+=============================
 
 Scripts to maintain services often dispose of common commands and environments.
 
-``shell``:
+``shell`` : command
   Opens a shell from the service user ``${SERVICE_USSR}``, very helpful for
   troubleshooting.
 
-``inspect service``:
+``inspect service`` : command
   Shows status and log of the service, most often you have a option to enable
   more verbose debug logs.  Very helpful for debugging, but be careful not to
   enable debugging in a production environment!
 
+``FORCE_TIMEOUT`` : environment
+  Sets timeout for interactive prompts. If you want to run a script in batch
+  job, with defaults choices, set ``FORCE_TIMEOUT=0``.  By example; to install a
+  reverse proxy for filtron on all containers of the :ref:`searx suite
+  <lxc-searx.env>` use ::
+
+    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh apache install
+ 
 .. _toolboxing setup:
 
 Tooling box setup
diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
index fcddfb340..44ab79030 100644
--- a/docs/utils/lxc.sh.rst
+++ b/docs/utils/lxc.sh.rst
@@ -88,7 +88,11 @@ WEB-Browser::
   [searx-fedora31]  INFO:  (eth0) filtron:    http://n.n.n.18:4004/
   [searx-archlinux]  INFO:  (eth0) filtron:    http://n.n.n.12:4004/
 
-  
+To install a reverse proxy for filtron and morty use::
+
+    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh apache install
+    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/morty.sh apache install
+
 Running commands
 ================
 
diff --git a/utils/filtron.sh b/utils/filtron.sh
index 0fbf74787..d24b6c39e 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -23,7 +23,6 @@ FILTRON_URL_PATH="${FILTRON_URL_PATH:-$(echo "${PUBLIC_URL}" \
 [[ "${FILTRON_URL_PATH}" == "${PUBLIC_URL}" ]] && FILTRON_URL_PATH=/
 
 FILTRON_ETC="/etc/filtron"
-
 FILTRON_RULES="$FILTRON_ETC/rules.json"
 
 FILTRON_API="${FILTRON_API:-127.0.0.1:4005}"
@@ -447,7 +446,8 @@ This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SIT
         install_apache
     fi
 
-    echo
+    "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
     apache_install_site --variant=filtron "${APACHE_FILTRON_SITE}"
 
     info_msg "testing public url .."
@@ -465,11 +465,12 @@ This removes apache site ${APACHE_FILTRON_SITE}."
 
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to continue?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
     fi
 
     apache_remove_site "$APACHE_FILTRON_SITE"
+
 }
 
 rst-doc() {
diff --git a/utils/lib.sh b/utils/lib.sh
index 2c0c179bb..a032d0465 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -389,7 +389,7 @@ install_template() {
     local chmod="${pos_args[4]-644}"
 
     info_msg "install (eval=$do_eval): ${dst}"
-    [[ -n $variant ]] && info_msg "variant: ${variant}"
+    [[ -n $variant ]] && info_msg "variant --> ${variant}"
 
     if [[ ! -f "${template_origin}" ]] ; then
         err_msg "${template_origin} does not exists"
@@ -777,6 +777,7 @@ apache_dissable_site() {
             ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
             ;;
     esac
+    apache_reload
 }
 
 # uWSGI
@@ -846,7 +847,7 @@ uWSGI_restart() {
             if uWSGI_app_available "${CONF}"; then
                 systemctl restart "uwsgi@${CONF%.*}"
             else
-                info_msg "in systemd template mode: ${CONF} not installed (nothing to restart)"
+                info_msg "[uWSGI:systemd-template] ${CONF} not installed (no need to restart)"
             fi
             ;;
         fedora-*)
@@ -854,7 +855,7 @@ uWSGI_restart() {
             if uWSGI_app_enabled "${CONF}"; then
                 touch "${uWSGI_APPS_ENABLED}/${CONF}"
             else
-                info_msg "in uWSGI emperor mode: ${CONF} not installed (nothing to restart)"
+                info_msg "[uWSGI:emperor] ${CONF} not installed (no need to restart)"
             fi
             ;;
         *)
@@ -864,6 +865,32 @@ uWSGI_restart() {
     esac
 }
 
+uWSGI_prepare_app() {
+
+    # usage:  uWSGI_prepare_app <myapp.ini>
+
+    local APP="${1%.*}"
+    if [[ -z $APP ]]; then
+        err_msg "uWSGI_prepare_app: missing arguments"
+        return 42
+    fi
+
+    case $DIST_ID-$DIST_VERS in
+        fedora-*)
+            # in emperor mode, the uwsgi user is the owner of the sockets
+            info_msg "prepare (uwsgi:uwsgi)  /run/uwsgi/app/${APP}"
+            mkdir -p "/run/uwsgi/app/${APP}"
+            chown -R "uwsgi:uwsgi"  "/run/uwsgi/app/${APP}"
+            ;;
+        *)
+            info_msg "prepare (${SERVICE_USER}:${SERVICE_GROUP})  /run/uwsgi/app/${APP}"
+            mkdir -p "/run/uwsgi/app/${APP}"
+            chown -R "${SERVICE_USER}:${SERVICE_GROUP}"  "/run/uwsgi/app/${APP}"
+            ;;
+    esac
+}
+
+
 uWSGI_app_available() {
     # usage:  uWSGI_app_available <myapp.ini>
     local CONF="$1"
@@ -888,6 +915,7 @@ uWSGI_install_app() {
             *)  pos_args+=("$i");;
         esac
     done
+    uWSGI_prepare_app "${pos_args[1]}"
     mkdir -p "${uWSGI_APPS_AVAILABLE}"
     install_template "${template_opts[@]}" \
                      "${uWSGI_APPS_AVAILABLE}/${pos_args[1]}" \
@@ -1281,3 +1309,30 @@ global_IPs(){
 
     ip -o addr show | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\1|\2/p'
 }
+
+primary_ip() {
+
+    case $DIST_ID in
+        arch)
+            echo "$(ip -o addr show \
+            | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\2/p' \
+            | head -n 1)"
+            ;;
+        *)  echo "$(hostname -I | cut -d' ' -f1)" ;;
+    esac
+}
+
+# URL
+# ---
+
+url_replace_hostname(){
+
+    # usage:  url_replace_hostname <url> <new hostname>
+
+    # to replace hostname by primary IP::
+    #
+    #   url_replace_hostname http://searx-ubu1604/morty $(primary_ip)
+    #   http://10.246.86.250/morty
+
+    echo "$1" | sed "s|\(http[s]*://\)[^/]*\(.*\)|\1$2\2|"
+}
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 6a26f80eb..6a8dce94f 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -438,7 +438,6 @@ lxc_cmd() {
         else
             info_msg "lxc $* $i"
             lxc "$@" "$i" | prefix_stdout "[${_BBlue}${i}${_creset}] "
-            echo
         fi
     done
 }
diff --git a/utils/morty.sh b/utils/morty.sh
index 4de411999..34d792dbe 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -14,12 +14,16 @@ in_container && lxc_set_suite_env
 # config
 # ----------------------------------------------------------------------------
 
-PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
-PUBLIC_HOST="${PUBLIC_HOST:-$(echo "$PUBLIC_URL" | sed -e 's/[^/]*\/\/\([^@]*@\)\?\([^:/]*\).*/\2/')}"
-PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
-PUBLIC_URL_MORTY="$(echo "$PUBLIC_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
-
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
+PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
+
+SEARX_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+PUBLIC_URL_MORTY="$(echo "$SEARX_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
+if in_container; then
+    # container hostnames do not have a DNS entry, use primary IP
+    PUBLIC_URL_MORTY="$(url_replace_hostname "$PUBLIC_URL_MORTY" "$(primary_ip)")"
+fi
+
 # shellcheck disable=SC2034
 MORTY_TIMEOUT=5
 
@@ -425,7 +429,7 @@ This removes apache site ${APACHE_MORTY_SITE}."
 
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to continue?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
     fi
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 6a8588c23..681b9a0e3 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -748,6 +748,10 @@ excessively bot queries."
 
     apache_install_site --variant=uwsgi "${APACHE_SEARX_SITE}"
 
+    rst_title "Install searx's uWSGI app (searx.ini)" section
+    echo
+    uWSGI_install_app --variant=socket "$SEARX_UWSGI_APP"
+
     if ! service_is_available "${PUBLIC_URL}"; then
         err_msg "Public service at ${PUBLIC_URL} is not available!"
     fi
@@ -762,11 +766,15 @@ This removes apache site ${APACHE_SEARX_SITE}."
 
     ! apache_is_installed && err_msg "Apache is not installed."
 
-    if ! ask_yn "Do you really want to continue?"; then
+    if ! ask_yn "Do you really want to continue?" Yn; then
         return
     fi
 
     apache_remove_site "${APACHE_SEARX_SITE}"
+
+    rst_title "Remove searx's uWSGI app (searx.ini)" section
+    echo
+    uWSGI_remove_app "$SEARX_UWSGI_APP"
 }
 
 rst-doc() {
diff --git a/utils/templates/etc/httpd/sites-available/morty.conf b/utils/templates/etc/httpd/sites-available/morty.conf
index 326fcc755..daeb3635a 100644
--- a/utils/templates/etc/httpd/sites-available/morty.conf
+++ b/utils/templates/etc/httpd/sites-available/morty.conf
@@ -2,7 +2,7 @@
 
 LoadModule headers_module       ${APACHE_MODULES}/mod_headers.so
 LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy.so
-LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy_http.so
+LoadModule proxy_http_module    ${APACHE_MODULES}/mod_proxy_http.so
 #LoadModule setenvif_module      ${APACHE_MODULES}/mod_setenvif.so
 
 # SetEnvIf Request_URI "${PUBLIC_URL_PATH_MORTY}" dontlog
diff --git a/utils/templates/etc/httpd/sites-available/searx.conf:filtron b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
index 11dd360bc..379d47e24 100644
--- a/utils/templates/etc/httpd/sites-available/searx.conf:filtron
+++ b/utils/templates/etc/httpd/sites-available/searx.conf:filtron
@@ -2,7 +2,7 @@
 
 LoadModule headers_module       ${APACHE_MODULES}/mod_headers.so
 LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy.so
-LoadModule proxy_module         ${APACHE_MODULES}/mod_proxy_http.so
+LoadModule proxy_http_module    ${APACHE_MODULES}/mod_proxy_http.so
 #LoadModule setenvif_module      ${APACHE_MODULES}/mod_setenvif.so
 
 # SetEnvIf Request_URI "${FILTRON_URL_PATH}" dontlog
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
index 8d3349c58..51f659d0f 100644
--- a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini
@@ -74,7 +74,7 @@ http = ${SEARX_INTERNAL_HTTP}
 #
 # On some distributions you need to create the app folder for the sockets::
 #
-#   mkdir -p /run/uwsgi/app/searx/socket
-#   chmod -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx/socket
+#   mkdir -p /run/uwsgi/app/searx
+#   chown -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx
 #
 # socket = /run/uwsgi/app/searx/socket
\ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
new file mode 100644
index 000000000..eeabb3715
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-archlinux/searx.ini:socket
@@ -0,0 +1,80 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+logger = systemd
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python
+
+# By default the Python plugin does not initialize the GIL.  This means your
+# app-generated threads will not run.  If you need threads, remember to enable
+# them with enable-threads.  Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+# http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+#   mkdir -p /run/uwsgi/app/searx
+#   chown -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx
+#
+socket = /run/uwsgi/app/searx/socket
\ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini b/utils/templates/etc/uwsgi/apps-available/searx.ini
index 806f74439..9785d7cd1 100644
--- a/utils/templates/etc/uwsgi/apps-available/searx.ini
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini
@@ -73,7 +73,7 @@ http = ${SEARX_INTERNAL_HTTP}
 #
 # On some distributions you need to create the app folder for the sockets::
 #
-#   mkdir -p /run/uwsgi/app/searx/socket
-#   chmod -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx/socket
+#   mkdir -p /run/uwsgi/app/searx
+#   chmod -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx
 #
 # socket = /run/uwsgi/app/searx/socket
\ No newline at end of file
diff --git a/utils/templates/etc/uwsgi/apps-available/searx.ini:socket b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
new file mode 100644
index 000000000..88436e5eb
--- /dev/null
+++ b/utils/templates/etc/uwsgi/apps-available/searx.ini:socket
@@ -0,0 +1,79 @@
+[uwsgi]
+
+# uWSGI core
+# ----------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#uwsgi-core
+
+# Who will run the code
+uid = ${SERVICE_USER}
+gid = ${SERVICE_GROUP}
+
+# chdir to specified directory before apps loading
+chdir = ${SEARX_SRC}/searx
+
+# searx configuration (settings.yml)
+env = SEARX_SETTINGS_PATH=${SEARX_SETTINGS_PATH}
+
+# disable logging for privacy
+disable-logging = true
+
+# The right granted on the created socket
+chmod-socket = 666
+
+# Plugin to use and interpretor config
+single-interpreter = true
+
+# enable master process
+master = true
+
+# load apps in each worker instead of the master
+lazy-apps = true
+
+# load uWSGI plugins
+plugin = python3,http
+
+# By default the Python plugin does not initialize the GIL.  This means your
+# app-generated threads will not run.  If you need threads, remember to enable
+# them with enable-threads.  Running uWSGI in multithreading mode (with the
+# threads options) will automatically enable threading support. This *strange*
+# default behaviour is for performance reasons.
+enable-threads = true
+
+
+# plugin: python
+# --------------
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-python
+
+# load a WSGI module
+module = searx.webapp
+
+# set PYTHONHOME/virtualenv
+virtualenv = ${SEARX_PYENV}
+
+# add directory (or glob) to pythonpath
+pythonpath = ${SEARX_SRC}
+
+
+# speak to upstream
+# -----------------
+#
+# Activate the 'http' configuration for filtron or activate the 'socket'
+# configuration if you setup your HTTP server to use uWSGI protocol via sockets.
+
+# using IP:
+#
+# https://uwsgi-docs.readthedocs.io/en/latest/Options.html#plugin-http
+# Native HTTP support: https://uwsgi-docs.readthedocs.io/en/latest/HTTP.html
+
+# http = ${SEARX_INTERNAL_HTTP}
+
+# using unix-sockets:
+#
+# On some distributions you need to create the app folder for the sockets::
+#
+#   mkdir -p /run/uwsgi/app/searx
+#   chown -R ${SERVICE_USER}:${SERVICE_GROUP}  /run/uwsgi/app/searx
+#
+socket = /run/uwsgi/app/searx/socket
\ No newline at end of file

From 44edc9256932961f5fd3c0778da009c493d9ce35 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 9 Apr 2020 14:28:34 +0200
Subject: [PATCH 103/125] [fix] morty: PUBLIC_URL_PATH_MORTY needs trailing
 slash

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.sh     | 2 +-
 utils/morty.sh | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/.config.sh b/.config.sh
index f4e6273c4..835bfecf4 100644
--- a/.config.sh
+++ b/.config.sh
@@ -41,7 +41,7 @@ fi
 
 # morty listen address
 # MORTY_LISTEN="127.0.0.1:3000"
-# PUBLIC_URL_PATH_MORTY="/morty"
+# PUBLIC_URL_PATH_MORTY="/morty/"
 
 # system services
 # ---------------
diff --git a/utils/morty.sh b/utils/morty.sh
index 34d792dbe..2aee0ac97 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -15,7 +15,7 @@ in_container && lxc_set_suite_env
 # ----------------------------------------------------------------------------
 
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
-PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty}"
+PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty/}"
 
 SEARX_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
 PUBLIC_URL_MORTY="$(echo "$SEARX_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"

From ebde9d10fd84de74ea99385dae4d95bcf932bcf3 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 9 Apr 2020 15:04:23 +0200
Subject: [PATCH 104/125] docs: proofreading of all the installation topics

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/architecture.rst        | 12 ++++----
 docs/admin/installation-apache.rst | 16 ++++++++++
 docs/admin/installation-nginx.rst  |  8 +++++
 docs/admin/installation-uwsgi.rst  |  2 +-
 docs/admin/installation.rst        |  2 +-
 docs/admin/morty.rst               |  4 +++
 docs/utils/filtron.sh.rst          | 31 ++------------------
 docs/utils/morty.sh.rst            |  5 +++-
 docs/utils/searx.sh.rst            | 47 ++----------------------------
 9 files changed, 46 insertions(+), 81 deletions(-)

diff --git a/docs/admin/architecture.rst b/docs/admin/architecture.rst
index fbc3bf483..464e765eb 100644
--- a/docs/admin/architecture.rst
+++ b/docs/admin/architecture.rst
@@ -4,12 +4,14 @@
 Architecture
 ============
 
-.. sidebar:: Tooling box
+.. sidebar:: Further reading
 
-   - :ref:`searx & uwsgi <searx.sh>`
-   - :ref:`filtron <filtron.sh>`
-   - :ref:`reverse proxy`
-   - :ref:`morty <morty.sh>`
+   - Reverse Proxy: :ref:`Apache <apache searx site>` & :ref:`nginx <nginx searx
+     site>`
+   - Filtron: :ref:`searx filtron`
+   - Morty: :ref:`searx morty`
+   - uWSGI: :ref:`searx uwsgi`
+   - Searx: :ref:`installation basic`
 
 Herein you will find some hints and suggestions about typical architectures of
 searx infrastructures.
diff --git a/docs/admin/installation-apache.rst b/docs/admin/installation-apache.rst
index 0b6cc38df..217e57718 100644
--- a/docs/admin/installation-apache.rst
+++ b/docs/admin/installation-apache.rst
@@ -45,6 +45,22 @@ Install with apache
    :local:
    :backlinks: entry
 
+----
+
+**Install** :ref:`apache searx site` using :ref:`filtron.sh <filtron.sh overview>`
+
+.. code:: bash
+
+   $ sudo -H ./utils/filtron.sh apache install
+
+**Install** :ref:`apache searx site` using :ref:`morty.sh <morty.sh overview>`
+
+.. code:: bash
+
+   $ sudo -H ./utils/morty.sh apache install
+
+----
+
 The apache HTTP server
 ======================
 
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index b02c555f6..e62c60df6 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -19,6 +19,14 @@ Install with nginx
 .. _SCRIPT_NAME:
    https://werkzeug.palletsprojects.com/en/1.0.x/wsgi/#werkzeug.wsgi.get_script_name
 
+.. sidebar:: further reading
+
+   - nginx_
+   - `nginx beginners guide`_
+   - `nginx server configuration`_
+   - `Getting Started wiki`_
+   - `uWSGI support from nginx`_
+
 .. contents:: Contents
    :depth: 2
    :local:
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
index 67a8e127a..72498c0bf 100644
--- a/docs/admin/installation-uwsgi.rst
+++ b/docs/admin/installation-uwsgi.rst
@@ -4,7 +4,7 @@
 uwsgi
 =====
 
-.. sidebar:: further read
+.. sidebar:: further reading
 
    - `systemd.unit`_
    - `uWSGI Emperor`_
diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index 81fc4d3e2..bfc0742de 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -31,7 +31,7 @@ and some maintenance tasks (alternatively you can create your own fork).
 
 .. code:: bash
 
-   $ cd ~/Download
+   $ cd ~/Downloads
    $ git clone https://github.com/asciimoo/searx searx
    $ cd searx
 
diff --git a/docs/admin/morty.rst b/docs/admin/morty.rst
index 48ff5b9c4..2858fde3c 100644
--- a/docs/admin/morty.rst
+++ b/docs/admin/morty.rst
@@ -5,6 +5,10 @@
 How to setup result proxy
 =========================
 
+.. sidebar:: further reading
+
+   - :ref:`morty.sh`
+
 .. _morty: https://github.com/asciimoo/morty
 .. _morty's README: https://github.com/asciimoo/morty
 
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index a3c1aed41..6c83bdf78 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -7,9 +7,10 @@
 
 .. sidebar:: further reading
 
-   - :ref:`installation`
    - :ref:`searx filtron`
    - :ref:`architecture`
+   - :ref:`installation` (:ref:`nginx <installation nginx>` & :ref:`apache
+     <installation apache>`)
 
 .. _Go: https://golang.org/
 .. _filtron: https://github.com/asciimoo/filtron
@@ -77,31 +78,3 @@ The ``--help`` output of the script is largely self-explanatory
 (:ref:`toolboxing common`):
 
 .. program-output:: ../utils/filtron.sh --help
-
-.. _reverse proxy:
-
-Public Reverse Proxy
-====================
-
-To install searx in your public HTTP server use:
-
-.. code::  bash
-
-   sudo -H ./utils/filtron.sh apache install
-
-.. tabs::
-
-   .. group-tab:: apache
-
-      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:filtron
-	 :language: apache
-
-      .. tabs::
-
-	 .. group-tab:: Ubuntu / debian
-
-	       .. code-block:: sh
-
-		  $ sudo -H a2enmod headers
-		  $ sudo -H a2enmod proxy
-		  $ sudo -H a2enmod proxy_http
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 34f0c9b5e..89629bea0 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -11,8 +11,9 @@
 
 .. sidebar:: further reading
 
-   - :ref:`installation`
    - :ref:`architecture`
+   - :ref:`installation` (:ref:`nginx <installation nginx>` & :ref:`apache
+     <installation apache>`)
    - :ref:`searx morty`
 
 To simplify installation and maintenance of a morty_ instance you can use the
@@ -67,6 +68,8 @@ Install morty software and systemd unit:
    :start-after: START install systemd unit
    :end-before: END install systemd unit
 
+.. _morty.sh overview:
+
 Overview
 ========
 
diff --git a/docs/utils/searx.sh.rst b/docs/utils/searx.sh.rst
index 905f765dd..dd4442f94 100644
--- a/docs/utils/searx.sh.rst
+++ b/docs/utils/searx.sh.rst
@@ -7,9 +7,10 @@
 
 .. sidebar:: further reading
 
-   - :ref:`installation`
    - :ref:`architecture`
-   - :ref:`filtron.sh`
+   - :ref:`installation`
+   - :ref:`installation nginx`
+   - :ref:`installation apache`
 
 To simplify installation and maintenance of a searx instance you can use the
 script :origin:`utils/searx.sh`.
@@ -36,45 +37,3 @@ The ``--help`` output of the script is largely self-explanatory
 (:ref:`toolboxing common`):
 
 .. program-output:: ../utils/searx.sh --help
-
-
-Intranet Reverse Proxy
-======================
-
-.. warning::
-
-   This setup is **not** suitable **for public instances**, go on with
-   :ref:`reverse proxy`!
-
-To install searx in your intranet HTTP server use:
-
-.. code::  bash
-
-   sudo -H ./utils/searx.sh apache install
-
-.. tabs::
-
-   .. group-tab:: apache
-
-      .. literalinclude:: ../../utils/templates/etc/apache2/sites-available/searx.conf:uwsgi
-         :language: apache
-
-      .. tabs::
-
-         .. group-tab:: Ubuntu / debian
-
-            .. code-block:: sh
-
-               $ sudo -H apt install libapache2-mod-uwsgi
-
-         .. group-tab:: Arch Linux
-
-            .. code-block:: sh
-
-               $ sudo -H pacman -S uwsgi
-
-         .. group-tab:: Fedora / RHEL
-
-            .. code-block:: sh
-
-               $ sudo -H dnf install uwsgi

From 67afa6b1fbb635626f278958980b87d3e8fed12c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Fri, 10 Apr 2020 14:00:52 +0200
Subject: [PATCH 105/125] bash scripts: source code cleaned up (shellcheck)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh   | 13 +++++--------
 utils/searx.sh |  7 -------
 2 files changed, 5 insertions(+), 15 deletions(-)

diff --git a/utils/lib.sh b/utils/lib.sh
index a032d0465..3a742f9da 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -767,14 +767,10 @@ apache_dissable_site() {
             sudo -H a2dissite -q "${CONF}"
             ;;
         arch-*)
-            mkdir -p "${APACHE_SITES_ENABLED}"
             rm -f "${APACHE_SITES_ENABLED}/${CONF}"
-            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
             ;;
         fedora-*)
-            mkdir -p "${APACHE_SITES_ENABLED}"
             rm -f "${APACHE_SITES_ENABLED}/${CONF}"
-            ln -s "${APACHE_SITES_AVAILABLE}/${CONF}" "${APACHE_SITES_ENABLED}/${CONF}"
             ;;
     esac
     apache_reload
@@ -1314,11 +1310,11 @@ primary_ip() {
 
     case $DIST_ID in
         arch)
-            echo "$(ip -o addr show \
-            | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\2/p' \
-            | head -n 1)"
+            ip -o addr show \
+                | sed -nr 's/[0-9]*:\s*([a-z0-9]*).*inet[6]?\s*([a-z0-9.:]*).*scope global.*/\2/p' \
+                | head -n 1
             ;;
-        *)  echo "$(hostname -I | cut -d' ' -f1)" ;;
+        *)  hostname -I | cut -d' ' -f1 ;;
     esac
 }
 
@@ -1334,5 +1330,6 @@ url_replace_hostname(){
     #   url_replace_hostname http://searx-ubu1604/morty $(primary_ip)
     #   http://10.246.86.250/morty
 
+    # shellcheck disable=SC2001
     echo "$1" | sed "s|\(http[s]*://\)[^/]*\(.*\)|\1$2\2|"
 }
diff --git a/utils/searx.sh b/utils/searx.sh
index 681b9a0e3..dde2b93ab 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -580,13 +580,6 @@ EOF
 
 set_result_proxy() {
     info_msg "try to set result proxy ..."
-    local line
-    local stage=0
-    local url="    url: $1"
-    local key="    key: $2"
-    if [[ -z $2 ]]; then
-       key=
-    fi
     cp "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_PATH}.bak"
     _set_result_proxy "$1" "$2" > "${SEARX_SETTINGS_PATH}"
 }

From 58d5da8b57c5aeab92f551e8d175be67537c351c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sat, 11 Apr 2020 13:19:11 +0200
Subject: [PATCH 106/125] nginx: normalize installation (docs and script)s over
 all distros

This is the revision of the documentation about the varous nginx installation
variants.  It also implements the nginx installation scripts for morty and
filtron.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst                        |  17 +-
 docs/admin/installation-nginx.rst             | 102 ++++++----
 utils/filtron.sh                              |  66 ++++++-
 utils/lib.sh                                  | 185 +++++++++++++++++-
 utils/lxc.sh                                  |  20 +-
 utils/morty.sh                                |  69 ++++++-
 utils/searx.sh                                |   8 +-
 .../nginx/default.apps-available/morty.conf   |  11 ++
 .../default.apps-available/searx.conf:filtron |  16 ++
 9 files changed, 414 insertions(+), 80 deletions(-)
 create mode 100644 utils/templates/etc/nginx/default.apps-available/morty.conf
 create mode 100644 utils/templates/etc/nginx/default.apps-available/searx.conf:filtron

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 9615cd935..8bf641649 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -158,6 +158,12 @@ of:
 Route request through filtron
 =============================
 
+.. sidebar:: further reading
+
+   - :ref:`filtron.sh overview`
+   - :ref:`installation nginx`
+   - :ref:`installation apache`
+
 Filtron can be started using the following command:
 
 .. code:: sh
@@ -171,15 +177,24 @@ Use it along with ``nginx`` with the following example configuration.
 
 .. code:: nginx
 
-   location / {
+   # https://example.org/searx
+
+   location /searx {
        proxy_pass         http://127.0.0.1:4004/;
 
        proxy_set_header   Host             $http_host;
+       proxy_set_header   Connection       $http_connection;
        proxy_set_header   X-Real-IP        $remote_addr;
        proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        proxy_set_header   X-Scheme         $scheme;
+       proxy_set_header   X-Script-Name    /searx;
    }
 
+   location /searx/static {
+       /usr/local/searx/searx-src/searx/static;
+   }
+
+
 Requests are coming from port 4004 going through filtron and then forwarded to
 port 8888 where a searx is being run. For a complete setup see: :ref:`nginx
 searx site`.
diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index e62c60df6..4ecc54066 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -159,14 +159,22 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: nginx
 
-         location / {
-             proxy_pass         http://127.0.0.1:4004/;
+	 # https://example.org/searx
 
-             proxy_set_header   Host             $http_host;
-             proxy_set_header   X-Real-IP        $remote_addr;
-             proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
-             proxy_set_header   X-Scheme         $scheme;
-         }
+	 location /searx {
+	     proxy_pass         http://127.0.0.1:4004/;
+
+	     proxy_set_header   Host             $http_host;
+	     proxy_set_header   Connection       $http_connection;
+	     proxy_set_header   X-Real-IP        $remote_addr;
+	     proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
+	     proxy_set_header   X-Scheme         $scheme;
+	     proxy_set_header   X-Script-Name    /searx;
+	 }
+
+	 location /searx/static {
+	     /usr/local/searx/searx-src/searx/static;
+	 }
 
 
       2. Configure reverse proxy for :ref:`morty <searx morty>`, listening on
@@ -174,10 +182,13 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: nginx
 
-         location /morty {
+	 # https://example.org/morty
+
+	 location /morty {
              proxy_pass         http://127.0.0.1:3000/;
 
              proxy_set_header   Host             $http_host;
+             proxy_set_header   Connection       $http_connection;
              proxy_set_header   X-Real-IP        $remote_addr;
              proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
              proxy_set_header   X-Scheme         $scheme;
@@ -197,7 +208,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
              image_proxy : True
 
 
-   .. group-tab:: proxy or uWSGI
+   .. group-tab:: proxy or uWSGI 
 
       Be warned, with this setup, your instance isn't :ref:`protected <searx
       filtron>`.  Nevertheless it is good enough for intranet usage and it is a
@@ -208,15 +219,17 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: nginx
 
-          location / {
-              proxy_pass http://127.0.0.1:8888;
+	 # https://hostname.local/
 
-              proxy_set_header Host $host;
-              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-              proxy_set_header X-Scheme $scheme;
-              proxy_set_header X-Script-Name /searx;
-              proxy_buffering off;
-          }
+	 location / {
+	     proxy_pass http://127.0.0.1:8888;
+
+             proxy_set_header Host $host;
+             proxy_set_header Connection       $http_connection;
+             proxy_set_header X-Forwarded-For  $proxy_add_x_forwarded_for;
+             proxy_set_header X-Scheme         $scheme;
+             proxy_buffering                   off;
+         }
 
       Alternatively you can use the `uWSGI support from nginx`_ via unix
       sockets.  For socket communication, you have to activate ``socket =
@@ -234,8 +247,8 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
       .. code:: nginx
 
          server {
-             # replace example.org with your server's public name
-             server_name example.org;
+             # replace hostname.local with your server's name
+             server_name hostname.local;
 
              listen 80;
              listen [::]:80;
@@ -245,7 +258,7 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
                  uwsgi_pass unix:/run/uwsgi/app/searx/socket;
              }
 
-	     root /usr/local/searx/searx-src/searx;
+             root /usr/local/searx/searx-src/searx;
              location /static { }
          }
 
@@ -254,10 +267,10 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: bash
 
-	 mkdir -p /run/uwsgi/app/searx/
-	 sudo -H chown -R searx:searx /run/uwsgi/app/searx/
+         mkdir -p /run/uwsgi/app/searx/
+         sudo -H chown -R searx:searx /run/uwsgi/app/searx/
 
-   .. group-tab:: proxy at subdir URL
+   .. group-tab:: \.\. at subdir URL
 
       Be warned, with these setups, your instance isn't :ref:`protected <searx
       filtron>`.  The examples are just here to demonstrate how to export the
@@ -265,19 +278,22 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: nginx
 
-          location /searx {
-              proxy_pass http://127.0.0.1:8888;
+	 # https://hostname.local/searx
 
-              proxy_set_header Host $host;
-              proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
-              proxy_set_header X-Scheme $scheme;
-              proxy_set_header X-Script-Name /searx;
-              proxy_buffering off;
-          }
+         location /searx {
+             proxy_pass http://127.0.0.1:8888;
 
-          location /searx/static {
-              alias /usr/local/searx/searx-src/searx/static;
-          }
+             proxy_set_header Host $host;
+             proxy_set_header Connection       $http_connection;
+             proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
+             proxy_set_header X-Scheme $scheme;
+             proxy_set_header X-Script-Name /searx;
+             proxy_buffering off;
+         }
+
+         location /searx/static {
+             alias /usr/local/searx/searx-src/searx/static;
+         }
 
       The ``X-Script-Name /searx`` is needed by the searx implementation to
       calculate relative URLs correct.  The next example shows a uWSGI
@@ -286,15 +302,17 @@ Started wiki`_ is always a good resource *to keep in the pocket*.
 
       .. code:: nginx
 
-          location /searx/static {
-              alias /usr/local/searx/searx-src/searx;
-          }
+	 # https://hostname.local/searx
 
-          location /searx {
-              uwsgi_param SCRIPT_NAME /searx;
-              include uwsgi_params;
-              uwsgi_pass unix:/run/uwsgi/app/searx/socket;
-          }
+         location /searx {
+             uwsgi_param SCRIPT_NAME /searx;
+             include uwsgi_params;
+             uwsgi_pass unix:/run/uwsgi/app/searx/socket;
+         }
+
+         location /searx/static {
+             alias /usr/local/searx/searx-src/searx;
+         }
 
       For searx to work correctly the ``base_url`` must be set in the
       :origin:`searx/settings.yml`.
diff --git a/utils/filtron.sh b/utils/filtron.sh
index d24b6c39e..c82c2d0a3 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -44,9 +44,8 @@ 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")
 
-# Apache Settings
-
 APACHE_FILTRON_SITE="searx.conf"
+NGINX_FILTRON_SITE="searx.conf"
 
 # shellcheck disable=SC2034
 CONFIG_FILES=(
@@ -60,9 +59,7 @@ usage() {
 
     # shellcheck disable=SC1117
     cat <<EOF
-
 usage::
-
   $(basename "$0") shell
   $(basename "$0") install    [all|user|rules]
   $(basename "$0") update     [filtron]
@@ -72,6 +69,7 @@ usage::
   $(basename "$0") inspect    [service]
   $(basename "$0") option     [debug-on|debug-off]
   $(basename "$0") apache     [install|remove]
+  $(basename "$0") nginx      [install|remove]
 
 shell
   start interactive shell from user ${SERVICE_USER}
@@ -92,6 +90,9 @@ option
 apache (${PUBLIC_URL})
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_FILTRON_SITE}
+nginx (${PUBLIC_URL})
+  :install: nginx site with a reverse proxy (ProxyPass)
+  :remove:  nginx site ${NGINX_FILTRON_SITE}
 
 filtron rules: ${FILTRON_RULES}
 
@@ -118,8 +119,6 @@ EOF
 }
 
 main() {
-    rst_title "$SERVICE_NAME" part
-
     required_commands \
         sudo install git wget curl \
         || exit
@@ -127,7 +126,7 @@ main() {
     local _usage="unknown or missing $1 command $2"
 
     case $1 in
-        --source-only)  ;;
+        --getenv)  var="$2"; echo "${!var}"; exit 0;;
         -h|--help) usage; exit 0;;
 
         shell)
@@ -143,6 +142,7 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
         install)
+            rst_title "$SERVICE_NAME" part
             sudo_or_exit
             case $2 in
                 all) install_all ;;
@@ -187,6 +187,13 @@ main() {
                 remove) remove_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        nginx)
+            sudo_or_exit
+            case $2 in
+                install) install_nginx_site ;;
+                remove) remove_nginx_site ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
         option)
             sudo_or_exit
             case $2 in
@@ -473,6 +480,51 @@ This removes apache site ${APACHE_FILTRON_SITE}."
 
 }
 
+install_nginx_site() {
+
+    rst_title "Install nginx site $NGINX_FILTRON_SITE"
+
+    rst_para "\
+This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE})"
+
+    ! nginx_is_installed && err_msg "nginx is not installed."
+
+    if ! ask_yn "Do you really want to continue?" Yn; then
+        return
+    else
+        install_nginx
+    fi
+
+    "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
+    SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+    SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
+    nginx_install_app --variant=filtron "${NGINX_FILTRON_SITE}"
+
+    info_msg "testing public url .."
+    if ! service_is_available "${PUBLIC_URL}"; then
+        err_msg "Public service at ${PUBLIC_URL} is not available!"
+    fi
+}
+
+remove_nginx_site() {
+
+    rst_title "Remove nginx site $NGINX_FILTRON_SITE"
+
+    rst_para "\
+This removes nginx site ${NGINX_FILTRON_SITE}."
+
+    ! nginx_is_installed && err_msg "nginx is not installed."
+
+    if ! ask_yn "Do you really want to continue?" Yn; then
+        return
+    fi
+
+    nginx_remove_site "$FILTRON_FILTRON_SITE"
+
+}
+
+
 rst-doc() {
 
     eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/filtron.rst")\""
diff --git a/utils/lib.sh b/utils/lib.sh
index 3a742f9da..4fc50ff39 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -138,6 +138,16 @@ rst_para() {
     fi
 }
 
+die() {
+    echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[1]}: line ${BASH_LINENO[0]}: ${2-died ${1-1}}" >&2;
+    exit "${1-1}"
+}
+
+die_caller() {
+    echo -e "${_BRed}ERROR:${_creset} ${BASH_SOURCE[2]}: line ${BASH_LINENO[1]}: ${FUNCNAME[1]}(): ${2-died ${1-1}}" >&2;
+    exit "${1-1}"
+}
+
 err_msg()  { echo -e "${_BRed}ERROR:${_creset} $*" >&2; }
 warn_msg() { echo -e "${_BBlue}WARN:${_creset}  $*" >&2; }
 info_msg() { echo -e "${_BYellow}INFO:${_creset}  $*" >&2; }
@@ -471,11 +481,7 @@ service_is_available() {
 
     # usage:  service_is_available <URL>
 
-    local URL="$1"
-    if [[ -z $URL ]]; then
-        err_msg "service_is_available: missing arguments"
-        return 42
-    fi
+    [[ -z $1 ]] && die_caller 42 "missing argument <URL>"
 
     http_code=$(curl -H 'Cache-Control: no-cache' \
          --silent -o /dev/null --head --write-out '%{http_code}' --insecure \
@@ -624,6 +630,175 @@ EOF
 }
 
 
+# nginx
+# -----
+
+nginx_distro_setup() {
+    # shellcheck disable=SC2034
+
+    NGINX_DEFAULT_SERVER=/etc/nginx/nginx.conf
+
+    # Including *location* directives from a dedicated config-folder into the
+    # server directive is, what what fedora (already) does.
+    NGINX_APPS_ENABLED="/etc/nginx/default.d"
+
+    # We add a apps-available folder and linking configurations into the
+    # NGINX_APPS_ENABLED folder.  See also nginx_include_apps_enabled().
+    NGINX_APPS_AVAILABLE="/etc/nginx/default.apps-available"
+
+    case $DIST_ID-$DIST_VERS in
+        ubuntu-*|debian-*)
+            NGINX_PACKAGES="nginx"
+            NGINX_DEFAULT_SERVER=/etc/nginx/sites-available/default
+            ;;
+        arch-*)
+            NGINX_PACKAGES="nginx-mainline"
+            ;;
+        fedora-*)
+            NGINX_PACKAGES="nginx"
+            ;;
+        *)
+            err_msg "$DIST_ID-$DIST_VERS: nginx not yet implemented"
+            ;;
+    esac
+}
+nginx_distro_setup
+
+install_nginx(){
+    info_msg "installing nginx ..."
+    pkg_install "${NGINX_PACKAGES}"
+    case $DIST_ID-$DIST_VERS in
+        arch-*|fedora-*)
+            systemctl enable nginx
+            systemctl start nginx
+            ;;
+    esac
+}
+
+nginx_is_installed() {
+    command -v nginx &>/dev/null
+}
+
+nginx_reload() {
+
+    info_msg "reload nginx .."
+    echo
+    if ! nginx -t; then
+       err_msg "testing nginx configuration failed"
+       return 42
+    fi
+    systemctl restart nginx
+}
+
+nginx_install_app() {
+
+    # usage:  nginx_install_app [<template option> ...] <myapp>
+    #
+    # <template option>:   see install_template
+
+    local template_opts=()
+    local pos_args=("$0")
+
+    for i in "$@"; do
+        case $i in
+            -*) template_opts+=("$i");;
+            *)  pos_args+=("$i");;
+        esac
+    done
+
+    nginx_include_apps_enabled "${NGINX_DEFAULT_SERVER}"
+
+    install_template "${template_opts[@]}" \
+                     "${NGINX_APPS_AVAILABLE}/${pos_args[1]}" \
+                     root root 644
+    nginx_enable_app "${pos_args[1]}"
+    info_msg "installed nginx app: ${pos_args[1]}"
+}
+
+nginx_include_apps_enabled() {
+
+    # Add the *NGINX_APPS_ENABLED* infrastruture to a nginx server block.  Such
+    # infrastruture is already known from fedora, including apps (location
+    # directives) from the /etc/nginx/default.d folder into the *default* nginx
+    # server.
+
+    # usage: nginx_include_apps_enabled <config-file>
+    #
+    #   config-file: Config file with server directive in.
+
+    [[ -z $1 ]] && die_caller 42 "missing argument <config-file>"
+    local server_conf="$1"
+
+    # include /etc/nginx/default.d/*.conf;
+    local include_directive="include ${NGINX_APPS_ENABLED}/*.conf;"
+    local include_directive_re="^\s*include ${NGINX_APPS_ENABLED}/\*\.conf;"
+
+    info_msg "checking existence: '${include_directive}' in file  ${server_conf}"
+    if grep "${include_directive_re}" "${server_conf}"; then
+        info_msg "OK, already exists."
+        return
+    fi
+
+    info_msg "add missing directive: '${include_directive}'"
+    cp "${server_conf}" "${server_conf}.bak"
+
+    (
+        local line
+        local stage=0
+        while IFS=  read -r line
+        do
+            echo "$line"
+            if [[ $stage = 0 ]]; then
+                if [[ $line =~ ^[[:space:]]*server*[[:space:]]*\{ ]]; then
+                    stage=1
+                fi
+            fi
+
+            if [[ $stage = 1 ]]; then
+                echo "        # Load configuration files for the default server block."
+                echo "        $include_directive"
+                echo ""
+                stage=2
+            fi
+        done < "${server_conf}.bak"
+    ) > "${server_conf}"
+
+}
+
+nginx_remove_app() {
+
+    # usage:  nginx_remove_app <myapp.conf>
+
+    info_msg "remove nginx app: $1"
+    nginx_dissable_app "$1"
+    rm -f "${NGINX_APPS_AVAILABLE}/$1"
+}
+
+nginx_enable_app() {
+
+    # usage:  nginx_enable_app <myapp.conf>
+
+    local CONF="$1"
+
+    info_msg "enable nginx app: ${CONF}"
+    mkdir -p "${NGINX_APPS_ENABLED}"
+    rm -f "${NGINX_APPS_ENABLED}/${CONF}"
+    ln -s "${NGINX_APPS_AVAILABLE}/${CONF}" "${NGINX_APPS_ENABLED}/${CONF}"
+    nginx_reload
+}
+
+nginx_dissable_app() {
+
+    # usage:  nginx_disable_app <myapp.conf>
+
+    local CONF="$1"
+
+    info_msg "disable nginx app: ${CONF}"
+    rm -f "${NGINX_APPS_ENABLED}/${CONF}"
+    nginx_reload
+}
+
+
 # Apache
 # ------
 
diff --git a/utils/lxc.sh b/utils/lxc.sh
index 6a8dce94f..ce306fe85 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -73,9 +73,7 @@ usage() {
 # ----------------------------------------------------------------------------
     _cmd="$(basename "$0")"
     cat <<EOF
-
 usage::
-
   $_cmd build        [containers|<name>]
   $_cmd copy         [images]
   $_cmd remove       [containers|<name>|images]
@@ -101,7 +99,7 @@ show
   :suite:        show services of all (or <name>) containers from the LXC suite
   :images:       show information of local images
 cmd
-  use single qoutes to evaluate in container's bash, e.g. 'echo $(hostname)'
+  use single qoutes to evaluate in container's bash, e.g.: 'echo \$(hostname)'
   --             run command '...' in all containers of the LXC suite
   :<name>:       run command '...' in container <name>
 install
@@ -151,14 +149,14 @@ main() {
     fi
 
     case $1 in
-        --source-only)  ;;
+        --getenv)  var="$2"; echo "${!var}"; exit 0;;
         -h|--help) usage; exit 0;;
 
         build)
             sudo_or_exit
             case $2 in
                 ${LXC_HOST_PREFIX}-*) build_container "$2" ;;
-                ''|containers) build_all_containers ;;
+                ''|--|containers) build_all_containers ;;
                 *) usage "$_usage"; exit 42;;
             esac
             ;;
@@ -171,7 +169,7 @@ main() {
         remove)
             sudo_or_exit
             case $2 in
-                ''|containers) remove_containers ;;
+                ''|--|containers) remove_containers ;;
                 images) lxc_delete_images_localy ;;
                 ${LXC_HOST_PREFIX}-*)
                     ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
@@ -185,7 +183,7 @@ main() {
         start|stop)
             sudo_or_exit
             case $2 in
-                ''|containers)  lxc_cmd "$1" ;;
+                ''|--|containers)  lxc_cmd "$1" ;;
                 ${LXC_HOST_PREFIX}-*)
                     ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
                     info_msg "lxc $1 $2"
@@ -203,7 +201,7 @@ main() {
                             lxc exec -t "$3" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
                                 | prefix_stdout "[${_BBlue}$3${_creset}]  "
                         ;;
-                        *) show_suite;;
+                        *|--) show_suite;;
                     esac
                     ;;
                 images) show_images ;;
@@ -213,7 +211,7 @@ main() {
                             ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                         ;;
-                        *)
+                        *|--)
                             rst_title "container configurations"
                             echo
                             lxc list "$LXC_HOST_PREFIX-"
@@ -228,7 +226,7 @@ main() {
                             ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                             ;;
-                        *)
+                        *|--)
                             rst_title "container info"
                             echo
                             lxc_cmd info
@@ -267,7 +265,7 @@ main() {
                             ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc_exec_cmd "$3" "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2"
                             ;;
-                        '') lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2" ;;
+                        ''|--) lxc_exec "${LXC_REPO_ROOT}/utils/lxc.sh" __install "$2" ;;
                         *) usage_containers "unknown container: $3" && exit 42
                     esac
                     ;;
diff --git a/utils/morty.sh b/utils/morty.sh
index 2aee0ac97..eae1b5bbc 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -47,6 +47,7 @@ CONFIG_FILES=()
 # Apache Settings
 
 APACHE_MORTY_SITE="morty.conf"
+NGINX_MORTY_SITE="morty.conf"
 
 # ----------------------------------------------------------------------------
 usage() {
@@ -54,9 +55,7 @@ usage() {
 
     # shellcheck disable=SC1117
     cat <<EOF
-
 usage::
-
   $(basename "$0") shell
   $(basename "$0") install    [all|user]
   $(basename "$0") update     [morty]
@@ -66,6 +65,7 @@ usage::
   $(basename "$0") inspect    [service]
   $(basename "$0") option     [debug-on|debug-off]
   $(basename "$0") apache     [install|remove]
+  $(basename "$0") nginx      [install|remove]
   $(basename "$0") info       [searx]
 
 shell
@@ -86,6 +86,9 @@ option
 apache : ${PUBLIC_URL_MORTY}
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_MORTY_SITE}
+nginx (${PUBLIC_URL_MORTY})
+  :install: nginx site with a reverse proxy (ProxyPass)
+  :remove:  nginx site ${NGINX_MORTY_SITE}
 
 If needed, set the environment variables in the '${DOT_CONFIG#"$REPO_ROOT/"}' file::
   PUBLIC_URL_MORTY:     ${PUBLIC_URL_MORTY}
@@ -122,8 +125,6 @@ EOF
 }
 
 main() {
-    rst_title "$SERVICE_NAME" part
-
     required_commands \
         sudo install git wget curl \
         || exit
@@ -131,7 +132,7 @@ main() {
     local _usage="ERROR: unknown or missing $1 command $2"
 
     case $1 in
-        --source-only)  ;;
+        --getenv)  var="$2"; echo "${!var}"; exit 0;;
         -h|--help) usage; exit 0;;
 
         shell)
@@ -147,6 +148,7 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
         install)
+            rst_title "$SERVICE_NAME" part
             sudo_or_exit
             case $2 in
                 all) install_all ;;
@@ -185,6 +187,13 @@ main() {
                 remove) remove_apache_site ;;
                 *) usage "$_usage"; exit 42;;
             esac ;;
+        nginx)
+            sudo_or_exit
+            case $2 in
+                install) install_nginx_site ;;
+                remove) remove_nginx_site ;;
+                *) usage "$_usage"; exit 42;;
+            esac ;;
         info)
             case $2 in
                 searx) info_searx ;;
@@ -348,8 +357,8 @@ EOF
         wait_key
     fi
 
-    if ! service_is_available "${PUBLIC_URL}"; then
-        warn_msg "Public service at ${PUBLIC_URL} is not available!"
+    if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+        warn_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
         if ! in_container; then
             warn_msg "Check if public name is correct and routed or use the public IP from above."
         fi
@@ -363,7 +372,7 @@ EOF
     fi
 
     local _debug_on
-    if ask_yn "Enable filtron debug mode (needs reinstall of systemd service)?"; then
+    if ask_yn "Enable morty debug mode (needs reinstall of systemd service)?"; then
         enable_debug
         _debug_on=1
     else
@@ -436,6 +445,50 @@ This removes apache site ${APACHE_MORTY_SITE}."
     apache_remove_site "$APACHE_MORTY_SITE"
 }
 
+install_nginx_site() {
+
+    rst_title "Install nginx site $NGINX_MORTY_SITE"
+
+    rst_para "\
+This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
+
+    ! nginx_is_installed && err_msg "nginx is not installed."
+
+    if ! ask_yn "Do you really want to continue?" Yn; then
+        return
+    else
+        install_nginx
+    fi
+
+    "${REPO_ROOT}/utils/searx.sh" install uwsgi
+
+    SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+    SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
+    nginx_install_app "${NGINX_MORTY_SITE}"
+
+    info_msg "testing public url .."
+    if ! service_is_available "${PUBLIC_URL_MORTY}"; then
+        err_msg "Public service at ${PUBLIC_URL_MORTY} is not available!"
+    fi
+}
+
+remove_nginx_site() {
+
+    rst_title "Remove nginx site $NGINX_MORTY_SITE"
+
+    rst_para "\
+This removes nginx site ${NGINX_MORTY_SITE}."
+
+    ! nginx_is_installed && err_msg "nginx is not installed."
+
+    if ! ask_yn "Do you really want to continue?" Yn; then
+        return
+    fi
+
+    nginx_remove_site "$NGINX_MORTY_SITE"
+
+}
+
 rst-doc() {
 
     eval "echo \"$(< "${REPO_ROOT}/docs/build-templates/morty.rst")\""
diff --git a/utils/searx.sh b/utils/searx.sh
index dde2b93ab..9a73c58cc 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -114,9 +114,7 @@ usage() {
 
     # shellcheck disable=SC1117
     cat <<EOF
-
 usage::
-
   $(basename "$0") shell
   $(basename "$0") install    [all|user|searx-src|pyenv|uwsgi|packages|buildhost]
   $(basename "$0") update     [searx]
@@ -178,8 +176,6 @@ EOF
 }
 
 main() {
-    rst_title "$SEARX_INSTANCE_NAME" part
-
     required_commands \
         sudo systemctl install git wget curl \
         || exit
@@ -187,9 +183,8 @@ main() {
     local _usage="unknown or missing $1 command $2"
 
     case $1 in
-        --source-only)  ;;
+        --getenv)  var="$2"; echo "${!var}"; exit 0;;
         -h|--help) usage; exit 0;;
-
         shell)
             sudo_or_exit
             interactive_shell "${SERVICE_USER}"
@@ -203,6 +198,7 @@ main() {
                 *) usage "$_usage"; exit 42;;
             esac ;;
         install)
+            rst_title "$SEARX_INSTANCE_NAME" part
             sudo_or_exit
             case $2 in
                 all) install_all ;;
diff --git a/utils/templates/etc/nginx/default.apps-available/morty.conf b/utils/templates/etc/nginx/default.apps-available/morty.conf
new file mode 100644
index 000000000..e7ffa27e2
--- /dev/null
+++ b/utils/templates/etc/nginx/default.apps-available/morty.conf
@@ -0,0 +1,11 @@
+# https://example.org/morty
+
+location /morty {
+    proxy_pass         http://127.0.0.1:3000/;
+
+    proxy_set_header   Host             \$http_host;
+    proxy_set_header   Connection       \$http_connection;
+    proxy_set_header   X-Real-IP        \$remote_addr;
+    proxy_set_header   X-Forwarded-For  \$proxy_add_x_forwarded_for;
+    proxy_set_header   X-Scheme         \$scheme;
+}
diff --git a/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron b/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron
new file mode 100644
index 000000000..d3137e42d
--- /dev/null
+++ b/utils/templates/etc/nginx/default.apps-available/searx.conf:filtron
@@ -0,0 +1,16 @@
+# https://example.org/searx
+
+location ${SEARX_URL_PATH} {
+    proxy_pass         http://127.0.0.1:4004/;
+
+    proxy_set_header   Host             \$http_host;
+    proxy_set_header   Connection       \$http_connection;
+    proxy_set_header   X-Real-IP        \$remote_addr;
+    proxy_set_header   X-Forwarded-For  \$proxy_add_x_forwarded_for;
+    proxy_set_header   X-Scheme         \$scheme;
+    proxy_set_header   X-Script-Name    ${SEARX_URL_PATH};
+}
+
+location ${SEARX_URL_PATH}/static {
+    alias ${SEARX_SRC}/searx/static;
+}

From 99ff16c465ed4d3b98041bf308dfeb0918b535ab Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 13 Apr 2020 11:34:28 +0200
Subject: [PATCH 107/125] tooling box: added nginx + polished bash scripts and
 environment

- add installation method for nginx sites, morty and filtron
- clean up PUBLIC_URL environment in and outside of containers
- clean up comand lines
- handle uWSGI quirks on fedora (emperor mode)
- handle Python quirks on debian (there is no 'python' command anymore)
- lib.sh: add die and die_caller functions
- lxc_suite_install_info is now a function
- lint: shellcheck

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 .config.sh          |  3 ++-
 utils/filtron.sh    | 13 +++++++---
 utils/lib.sh        | 59 ++++++++++++++++++++++++---------------------
 utils/lxc-searx.env | 38 +++++++++++++++++++++++------
 utils/lxc.sh        | 21 ++++++----------
 utils/morty.sh      | 15 +++++++-----
 utils/searx.sh      | 23 ++++++++++++------
 7 files changed, 106 insertions(+), 66 deletions(-)

diff --git a/.config.sh b/.config.sh
index 835bfecf4..4eff5f4c6 100644
--- a/.config.sh
+++ b/.config.sh
@@ -18,7 +18,8 @@
 PUBLIC_URL="${SEARX_URL}"
 
 if [[ ${PUBLIC_URL} == "https://searx.me" ]]; then
-    PUBLIC_URL=
+    # hint: Linux containers do not have DNS entries, lets use IPs
+    PUBLIC_URL="http://$(primary_ip)/searx"
 fi
 
 # searx.sh
diff --git a/utils/filtron.sh b/utils/filtron.sh
index c82c2d0a3..8986fb0ef 100755
--- a/utils/filtron.sh
+++ b/utils/filtron.sh
@@ -225,6 +225,11 @@ install_all() {
         if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
             install_apache_site
         fi
+    elif nginx_is_installed; then
+        info_msg "nginx is installed on this host."
+        if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+            install_nginx_site
+        fi
     fi
     if ask_yn "Do you want to inspect the installation?" Ny; then
         inspect_service
@@ -315,8 +320,6 @@ sourced ${DOT_CONFIG#"$REPO_ROOT/"} :
 
 EOF
 
-    apache_is_installed && info_msg "Apache is installed."
-
     if service_account_is_available "$SERVICE_USER"; then
         info_msg "service account $SERVICE_USER available."
     else
@@ -445,7 +448,7 @@ install_apache_site() {
     rst_para "\
 This installs a reverse proxy (ProxyPass) into apache site (${APACHE_FILTRON_SITE})"
 
-    ! apache_is_installed && err_msg "Apache is not installed."
+    ! apache_is_installed && info_msg "Apache is not installed."
 
     if ! ask_yn "Do you really want to continue?" Yn; then
         return
@@ -487,7 +490,7 @@ install_nginx_site() {
     rst_para "\
 This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE})"
 
-    ! nginx_is_installed && err_msg "nginx is not installed."
+    ! nginx_is_installed && info_msg "nginx is not installed."
 
     if ! ask_yn "Do you really want to continue?" Yn; then
         return
@@ -497,7 +500,9 @@ This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_FILTRON_SITE}
 
     "${REPO_ROOT}/utils/searx.sh" install uwsgi
 
+    # shellcheck disable=SC2034
     SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+    # shellcheck disable=SC2034
     SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
     nginx_install_app --variant=filtron "${NGINX_FILTRON_SITE}"
 
diff --git a/utils/lib.sh b/utils/lib.sh
index 4fc50ff39..eb55f356b 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -482,7 +482,7 @@ service_is_available() {
     # usage:  service_is_available <URL>
 
     [[ -z $1 ]] && die_caller 42 "missing argument <URL>"
-
+    local URL="$1"
     http_code=$(curl -H 'Cache-Control: no-cache' \
          --silent -o /dev/null --head --write-out '%{http_code}' --insecure \
          "${URL}")
@@ -969,6 +969,7 @@ uWSGI_distro_setup() {
             # one day, see https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=833067
             uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
             uWSGI_APPS_ENABLED="${uWSGI_SETUP}/apps-enabled"
+            uWSGI_PACKAGES="uwsgi"
             ;;
         arch-*)
             # systemd --> /usr/lib/systemd/system/uwsgi@.service
@@ -977,6 +978,7 @@ uWSGI_distro_setup() {
             # - https://uwsgi-docs.readthedocs.io/en/latest/Systemd.html#one-service-per-app-in-systemd
             uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-archlinux"
             uWSGI_APPS_ENABLED="${uWSGI_SETUP}"
+            uWSGI_PACKAGES="uwsgi"
             ;;
         fedora-*)
             # systemd --> /usr/lib/systemd/system/uwsgi.service
@@ -984,6 +986,7 @@ uWSGI_distro_setup() {
             # - https://uwsgi-docs.readthedocs.io/en/latest/Emperor.html
             uWSGI_APPS_AVAILABLE="${uWSGI_SETUP}/apps-available"
             uWSGI_APPS_ENABLED="${uWSGI_SETUP}.d"
+            uWSGI_PACKAGES="uwsgi"
             uWSGI_USER="uwsgi"
             uWSGI_GROUP="uwsgi"
             ;;
@@ -995,18 +998,26 @@ esac
 
 uWSGI_distro_setup
 
+install_uwsgi(){
+    info_msg "installing uwsgi ..."
+    pkg_install "$uWSGI_PACKAGES"
+    case $DIST_ID-$DIST_VERS in
+        fedora-*)
+            # enable & start should be called once at uWSGI installation time
+            systemctl enable uwsgi
+            systemctl restart uwsgi
+            ;;
+    esac
+}
+
 uWSGI_restart() {
 
     # usage:  uWSGI_restart() <myapp.ini>
 
     local CONF="$1"
-    if [[ -z $CONF ]]; then
-        err_msg "uWSGI_restart: missing arguments"
-        return 42
-    fi
 
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     info_msg "restart uWSGI service"
-
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
             # the 'service' method seems broken in that way, that it (re-)starts
@@ -1025,6 +1036,9 @@ uWSGI_restart() {
             # in emperor mode, just touch the file to restart
             if uWSGI_app_enabled "${CONF}"; then
                 touch "${uWSGI_APPS_ENABLED}/${CONF}"
+                # it seems, there is a polling time in between touch and restart
+                # of the service.
+                sleep 3
             else
                 info_msg "[uWSGI:emperor] ${CONF} not installed (no need to restart)"
             fi
@@ -1040,11 +1054,9 @@ uWSGI_prepare_app() {
 
     # usage:  uWSGI_prepare_app <myapp.ini>
 
+    [[ -z $1 ]] && die_caller 42 "missing argument <myapp.ini>"
+
     local APP="${1%.*}"
-    if [[ -z $APP ]]; then
-        err_msg "uWSGI_prepare_app: missing arguments"
-        return 42
-    fi
 
     case $DIST_ID-$DIST_VERS in
         fedora-*)
@@ -1065,10 +1077,8 @@ uWSGI_prepare_app() {
 uWSGI_app_available() {
     # usage:  uWSGI_app_available <myapp.ini>
     local CONF="$1"
-    if [[ -z $CONF ]]; then
-        err_msg "uWSGI_app_available: missing arguments"
-        return 42
-    fi
+
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     [[ -f "${uWSGI_APPS_AVAILABLE}/${CONF}" ]]
 }
 
@@ -1101,6 +1111,8 @@ uWSGI_remove_app() {
     # usage:  uWSGI_remove_app <myapp.ini>
 
     local CONF="$1"
+
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     info_msg "remove uWSGI app: ${CONF}"
     uWSGI_disable_app "${CONF}"
     uWSGI_restart "${CONF}"
@@ -1110,12 +1122,10 @@ uWSGI_remove_app() {
 uWSGI_app_enabled() {
     # usage:  uWSGI_app_enabled <myapp.ini>
 
-    local CONF="$1"
     local exit_val=0
-    if [[ -z $CONF ]]; then
-        err_msg "uWSGI_app_enabled: missing arguments"
-        return 42
-    fi
+    local CONF="$1"
+
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
             [[ -f "${uWSGI_APPS_ENABLED}/${CONF}" ]]
@@ -1145,11 +1155,7 @@ uWSGI_enable_app() {
 
     local CONF="$1"
 
-    if [[ -z $CONF ]]; then
-        err_msg "uWSGI_enable_app: missing arguments"
-        return 42
-    fi
-
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
             mkdir -p "${uWSGI_APPS_ENABLED}"
@@ -1183,11 +1189,8 @@ uWSGI_disable_app() {
     # usage:   uWSGI_disable_app <myapp.ini>
 
     local CONF="$1"
-    if [[ -z $CONF ]]; then
-        err_msg "uWSGI_disable_app: missing arguments"
-        return 42
-    fi
 
+    [[ -z $CONF ]] && die_caller 42 "missing argument <myapp.ini>"
     case $DIST_ID-$DIST_VERS in
         ubuntu-*|debian-*)
             service uwsgi stop "${CONF%.*}"
diff --git a/utils/lxc-searx.env b/utils/lxc-searx.env
index fc5b33f06..a51312fb8 100644
--- a/utils/lxc-searx.env
+++ b/utils/lxc-searx.env
@@ -32,13 +32,36 @@ lxc_set_suite_env() {
         # rolling releases see https://www.archlinux.org/releng/releases/
         "$LINUXCONTAINERS_ORG_NAME:archlinux"     "archlinux"
     )
-    export FILTRON_API="0.0.0.0:4005"
-    export FILTRON_LISTEN="0.0.0.0:4004"
-    export MORTY_LISTEN="0.0.0.0:3000"
+
+    PUBLIC_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
+    if in_container; then
+        # container hostnames do not have a DNS entry: use primary IP!
+        PUBLIC_URL="http://$(primary_ip)/searx"
+
+        # make GUEST's services public to the HOST
+        FILTRON_API="0.0.0.0:4005"
+        FILTRON_LISTEN="0.0.0.0:4004"
+        MORTY_LISTEN="0.0.0.0:3000"
+
+        # export LXC specific environment
+        export PUBLIC_URL FILTRON_API FILTRON_LISTEN MORTY_LISTEN
+    fi
 }
 
-# shellcheck disable=SC2034
-LXC_SUITE_INSTALL_INFO="suite includes searx, morty & filtron"
+lxc_suite_install_info() {
+    (
+        lxc_set_suite_env
+        cat <<EOF
+LXC suite: ${LXC_SUITE_NAME} --> ${PUBLIC_URL}
+  suite includes searx, morty & filtron
+suite images:
+$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
+suite containers:
+$(echo "  ${CONTAINERS[*]}" | $FMT)
+EOF
+    )
+    }
+
 lxc_suite_install() {
     (
         lxc_set_suite_env
@@ -62,8 +85,9 @@ lxc_suite_info() {
                 info_msg "(${ip%|*}) IPv6:       http://[${ip#*|}]"
             else
                 # IPv4:
-                info_msg "(${ip%|*}) filtron:    http://${ip#*|}:4004/"
-                info_msg "(${ip%|*}) morty:      http://${ip#*|}:3000/"
+                # shellcheck disable=SC2034,SC2031
+                info_msg "(${ip%|*}) filtron:    http://${ip#*|}:4004/ $PUBLIC_URL"
+                info_msg "(${ip%|*}) morty:      http://${ip#*|}:3000/ $PUBLIC_URL_MORTY"
                 info_msg "(${ip%|*}) docs-live:  http://${ip#*|}:8080/"
             fi
         done
diff --git a/utils/lxc.sh b/utils/lxc.sh
index ce306fe85..a324bdaf2 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -112,14 +112,7 @@ EOF
 }
 
 usage_containers() {
-    cat <<EOF
-LXC suite: ${LXC_SUITE_NAME}
-$(echo "  ${LXC_SUITE_INSTALL_INFO}" | $FMT)
-suite images:
-$(echo "  ${LOCAL_IMAGES[*]}" | $FMT)
-suite containers:
-$(echo "  ${CONTAINERS[*]}" | $FMT)
-EOF
+    lxc_suite_install_info
     [ -n "${1+x}" ] &&  err_msg "$1"
 }
 
@@ -172,7 +165,7 @@ main() {
                 ''|--|containers) remove_containers ;;
                 images) lxc_delete_images_localy ;;
                 ${LXC_HOST_PREFIX}-*)
-                    ! lxc_exists "$2" && usage_containers "unknown container: $2" && exit 42
+                    ! lxc_exists "$2" && warn_msg "container not yet exists: $2" && exit 0
                     if ask_yn "Do you really want to delete container $2"; then
                         lxc_delete_container "$2"
                     fi
@@ -201,7 +194,7 @@ main() {
                             lxc exec -t "$3" -- "${LXC_REPO_ROOT}/utils/lxc.sh" __show suite \
                                 | prefix_stdout "[${_BBlue}$3${_creset}]  "
                         ;;
-                        *|--) show_suite;;
+                        *) show_suite;;
                     esac
                     ;;
                 images) show_images ;;
@@ -211,7 +204,7 @@ main() {
                             ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc config show "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                         ;;
-                        *|--)
+                        *)
                             rst_title "container configurations"
                             echo
                             lxc list "$LXC_HOST_PREFIX-"
@@ -226,7 +219,7 @@ main() {
                             ! lxc_exists "$3" && usage_containers "unknown container: $3" && exit 42
                             lxc info "$3" | prefix_stdout "[${_BBlue}${3}${_creset}] "
                             ;;
-                        *|--)
+                        *)
                             rst_title "container info"
                             echo
                             lxc_cmd info
@@ -350,7 +343,9 @@ remove_containers() {
     echo
     lxc list "$LXC_HOST_PREFIX-"
     echo -en "\\n${_BRed}LXC containers to delete::${_creset}\\n\\n  ${CONTAINERS[*]}\\n" | $FMT
-    if ask_yn "Do you really want to delete these containers"; then
+    local default=Ny
+    [[ $FORCE_TIMEOUT = 0 ]] && default=Yn
+    if ask_yn "Do you really want to delete these containers" $default; then
         for i in "${CONTAINERS[@]}"; do
             lxc_delete_container "$i"
         done
diff --git a/utils/morty.sh b/utils/morty.sh
index eae1b5bbc..851b8864d 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -7,6 +7,7 @@ source "$(dirname "${BASH_SOURCE[0]}")/lib.sh"
 # shellcheck source=utils/brand.env
 source "${REPO_ROOT}/utils/brand.env"
 source_dot_config
+SEARX_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
 source "${REPO_ROOT}/utils/lxc-searx.env"
 in_container && lxc_set_suite_env
 
@@ -17,12 +18,7 @@ in_container && lxc_set_suite_env
 MORTY_LISTEN="${MORTY_LISTEN:-127.0.0.1:3000}"
 PUBLIC_URL_PATH_MORTY="${PUBLIC_URL_PATH_MORTY:-/morty/}"
 
-SEARX_URL="${PUBLIC_URL:-http://$(uname -n)/searx}"
-PUBLIC_URL_MORTY="$(echo "$SEARX_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}"
-if in_container; then
-    # container hostnames do not have a DNS entry, use primary IP
-    PUBLIC_URL_MORTY="$(url_replace_hostname "$PUBLIC_URL_MORTY" "$(primary_ip)")"
-fi
+PUBLIC_URL_MORTY="${PUBLIC_URL_MORTY:-$(echo "$SEARX_URL" |  sed -e's,^\(.*://[^/]*\).*,\1,g')${PUBLIC_URL_PATH_MORTY}}"
 
 # shellcheck disable=SC2034
 MORTY_TIMEOUT=5
@@ -229,6 +225,11 @@ install_all() {
         if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
             install_apache_site
         fi
+    elif nginx_is_installed; then
+        info_msg "nginx is installed on this host."
+        if ask_yn "Do you want to install a reverse proxy (ProxyPass)" Yn; then
+            install_nginx_site
+        fi
     fi
     info_searx
     if ask_yn "Add image and result proxy to searx settings.yml?" Yn; then
@@ -462,7 +463,9 @@ This installs a reverse proxy (ProxyPass) into nginx site (${NGINX_MORTY_SITE})"
 
     "${REPO_ROOT}/utils/searx.sh" install uwsgi
 
+    # shellcheck disable=SC2034
     SEARX_SRC=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_SRC)
+    # shellcheck disable=SC2034
     SEARX_URL_PATH=$("${REPO_ROOT}/utils/searx.sh" --getenv SEARX_URL_PATH)
     nginx_install_app "${NGINX_MORTY_SITE}"
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 9a73c58cc..48521f606 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -81,6 +81,11 @@ case $DIST_ID-$DIST_VERS in
         BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
         APACHE_PACKAGES="$APACHE_PACKAGES libapache2-mod-proxy-uwsgi"
         ;;
+    ubuntu-20.04)
+        # https://askubuntu.com/a/1224710
+        SEARX_PACKAGES="${SEARX_PACKAGES_debian} python-is-python3"
+        BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
+        ;;
     ubuntu-*|debian-*)
         SEARX_PACKAGES="${SEARX_PACKAGES_debian}"
         BUILD_PACKAGES="${BUILD_PACKAGES_debian}"
@@ -206,7 +211,12 @@ main() {
                 pyenv) create_pyenv ;;
                 searx-src) clone_searx ;;
                 settings) install_settings ;;
-                uwsgi) install_searx_uwsgi;;
+                uwsgi)
+                    install_searx_uwsgi
+                    if ! service_is_available "http://${SEARX_INTERNAL_HTTP}"; then
+                        err_msg "URL http://${SEARX_INTERNAL_HTTP} not available, check searx & uwsgi setup!"
+                    fi
+                    ;;
                 packages)
                     pkg_install "$SEARX_PACKAGES"
                     ;;
@@ -272,11 +282,6 @@ install_all() {
     rst_title "Install $SEARX_INSTANCE_NAME (service)"
     pkg_install "$SEARX_PACKAGES"
     wait_key
-    case $DIST_ID-$DIST_VERS in
-        fedora-*)
-            systemctl enable uwsgi
-            ;;
-    esac
     assert_user
     wait_key
     clone_searx
@@ -514,6 +519,7 @@ EOF
 install_searx_uwsgi() {
     rst_title "Install searx's uWSGI app (searx.ini)" section
     echo
+    install_uwsgi
     uWSGI_install_app "$SEARX_UWSGI_APP"
 }
 
@@ -575,7 +581,10 @@ EOF
 }
 
 set_result_proxy() {
-    info_msg "try to set result proxy ..."
+
+    # usage: set_result_proxy <URL> [<key>]
+
+    info_msg "try to set result proxy: $1"
     cp "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_PATH}.bak"
     _set_result_proxy "$1" "$2" > "${SEARX_SETTINGS_PATH}"
 }

From 30c0a0fb641f2248829a3ccc3fd6abe58a55c77f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 13 Apr 2020 13:00:03 +0200
Subject: [PATCH 108/125] doc: add missing command lines to nginx docs

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation-nginx.rst | 16 ++++++++++++++++
 1 file changed, 16 insertions(+)

diff --git a/docs/admin/installation-nginx.rst b/docs/admin/installation-nginx.rst
index 4ecc54066..5e32d9684 100644
--- a/docs/admin/installation-nginx.rst
+++ b/docs/admin/installation-nginx.rst
@@ -32,6 +32,22 @@ Install with nginx
    :local:
    :backlinks: entry
 
+----
+
+**Install** :ref:`nginx searx site` using :ref:`filtron.sh <filtron.sh overview>`
+
+.. code:: bash
+
+   $ sudo -H ./utils/filtron.sh nginx install
+
+**Install** :ref:`nginx searx site` using :ref:`morty.sh <morty.sh overview>`
+
+.. code:: bash
+
+   $ sudo -H ./utils/morty.sh nginx install
+
+----
+
 
 The nginx HTTP server
 =====================

From 5fc17de8617d0d5771578f38f62a9d4714054747 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Sun, 19 Apr 2020 12:34:37 +0200
Subject: [PATCH 109/125] docs: marginal fix of command's './utils/lxc.sh show
 suite' output

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/utils/lxc.sh.rst | 20 +++++++++++---------
 1 file changed, 11 insertions(+), 9 deletions(-)

diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
index 44ab79030..9ca9b6bc0 100644
--- a/docs/utils/lxc.sh.rst
+++ b/docs/utils/lxc.sh.rst
@@ -81,17 +81,19 @@ command.  To test instances from containers just open the URLs in your
 WEB-Browser::
 
   $ sudo ./utils/lxc.sh show suite | grep filtron
-  [searx-ubu1604]  INFO:  (eth0) filtron:    http://n.n.n.135:4004/
-  [searx-ubu1804]  INFO:  (eth0) filtron:    http://n.n.n.141:4004/
-  [searx-ubu1910]  INFO:  (eth0) filtron:    http://n.n.n.137:4004/
-  [searx-ubu2004]  INFO:  (eth0) filtron:    http://n.n.n.127:4004/
-  [searx-fedora31]  INFO:  (eth0) filtron:    http://n.n.n.18:4004/
-  [searx-archlinux]  INFO:  (eth0) filtron:    http://n.n.n.12:4004/
+  [searx-ubu1604]  INFO:  (eth0) filtron:    http://n.n.n.246:4004/ http://n.n.n.246/searx
+  [searx-ubu1804]  INFO:  (eth0) filtron:    http://n.n.n.147:4004/ http://n.n.n.147/searx
+  [searx-ubu1910]  INFO:  (eth0) filtron:    http://n.n.n.140:4004/ http://n.n.n.140/searx
+  [searx-ubu2004]  INFO:  (eth0) filtron:    http://n.n.n.18:4004/ http://n.n.n.18/searx
+  [searx-fedora31]  INFO:  (eth0) filtron:    http://n.n.n.46:4004/ http://n.n.n.46/searx
+  [searx-archlinux]  INFO:  (eth0) filtron:    http://n.n.n.32:4004/ http://n.n.n.32/searx
 
-To install a reverse proxy for filtron and morty use::
+To :ref:`install a nginx <installation nginx>` reverse proxy for filtron and
+morty use (or alternatively use :ref:`apache <installation apache>`)::
+
+    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh nginx install
+    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/morty.sh nginx install
 
-    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/filtron.sh apache install
-    sudo -H ./utils/lxc.sh cmd -- FORCE_TIMEOUT=0 ./utils/morty.sh apache install
 
 Running commands
 ================

From d21e0b0ef80402e6bb610ba086dc81ad93e451d5 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Wed, 22 Apr 2020 01:11:25 +0200
Subject: [PATCH 110/125] [fix] old images need an initial 'apt update'

see https://github.com/asciimoo/searx/pull/1803#issuecomment-616684353

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lib.sh | 6 ++++++
 1 file changed, 6 insertions(+)

diff --git a/utils/lib.sh b/utils/lib.sh
index eb55f356b..922227a89 100755
--- a/utils/lib.sh
+++ b/utils/lib.sh
@@ -1216,6 +1216,8 @@ uWSGI_disable_app() {
 # distro's package manager
 # ------------------------
 
+_apt_pkg_info_is_updated=0
+
 pkg_install() {
 
     # usage: TITEL='install foobar' pkg_install foopkg barpkg
@@ -1230,6 +1232,10 @@ pkg_install() {
     fi
     case $DIST_ID in
         ubuntu|debian)
+            if [[ $_apt_pkg_info_is_updated == 0 ]]; then
+                export _apt_pkg_info_is_updated=1
+                apt update
+            fi
             # shellcheck disable=SC2068
             apt-get install -m -y $@
             ;;

From 26a3a7d52359075bb96b39b26ed60430f2c667b9 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 28 Apr 2020 14:44:28 +0200
Subject: [PATCH 111/125] [docs] installation: add note to update OS before
 install searx

see: https://github.com/asciimoo/searx/pull/1803#issuecomment-618456661

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/installation.rst | 4 ++++
 1 file changed, 4 insertions(+)

diff --git a/docs/admin/installation.rst b/docs/admin/installation.rst
index bfc0742de..167c300fe 100644
--- a/docs/admin/installation.rst
+++ b/docs/admin/installation.rst
@@ -25,6 +25,10 @@ box <toolboxing>` as described below.
 Installation scripts
 ====================
 
+.. sidebar:: Update OS first!
+
+   To avoid unwanted side effects, update your OS before installing searx.
+
 The following will install a setup as shown in :ref:`architecture`.  First you
 need to get a clone.  The clone is only needed for the installation procedure
 and some maintenance tasks (alternatively you can create your own fork).

From ea3255835a259516c7a8b62eb319a338cebf7e9f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 28 Apr 2020 16:21:45 +0200
Subject: [PATCH 112/125] utils/morty.sh: set morty key to avoid service abuse

- https://github.com/asciimoo/searx/issues/1871#issuecomment-592459798

    make install all

generates random MORTY_KEY, install service with that key and sets option in the
searx settingy.yml file.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/morty.sh                                   | 5 ++++-
 utils/searx.sh                                   | 4 ++--
 utils/templates/lib/systemd/system/morty.service | 2 +-
 3 files changed, 7 insertions(+), 4 deletions(-)

diff --git a/utils/morty.sh b/utils/morty.sh
index 851b8864d..6b90db581 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -208,6 +208,9 @@ main() {
 }
 
 install_all() {
+
+    MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
+
     rst_title "Install $SERVICE_NAME (service)"
     assert_user
     wait_key
@@ -233,7 +236,7 @@ install_all() {
     fi
     info_searx
     if ask_yn "Add image and result proxy to searx settings.yml?" Yn; then
-        "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}"
+        "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
         "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
     fi
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 48521f606..7013ec750 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -584,7 +584,7 @@ set_result_proxy() {
 
     # usage: set_result_proxy <URL> [<key>]
 
-    info_msg "try to set result proxy: $1"
+    info_msg "try to set result proxy: '$1' ($2)"
     cp "${SEARX_SETTINGS_PATH}" "${SEARX_SETTINGS_PATH}.bak"
     _set_result_proxy "$1" "$2" > "${SEARX_SETTINGS_PATH}"
 }
@@ -593,7 +593,7 @@ _set_result_proxy() {
     local line
     local stage=0
     local url="    url: $1"
-    local key="    key: $2"
+    local key="    key: !!binary \"$2\""
     if [[ -z $2 ]]; then
        key=
     fi
diff --git a/utils/templates/lib/systemd/system/morty.service b/utils/templates/lib/systemd/system/morty.service
index d463c5097..25b676b51 100644
--- a/utils/templates/lib/systemd/system/morty.service
+++ b/utils/templates/lib/systemd/system/morty.service
@@ -10,7 +10,7 @@ Type=simple
 User=${SERVICE_USER}
 Group=${SERVICE_GROUP}
 WorkingDirectory=${SERVICE_HOME}
-ExecStart=${SERVICE_HOME}/go-apps/bin/morty -key '' -listen '${MORTY_LISTEN}' -timeout ${MORTY_TIMEOUT}
+ExecStart=${SERVICE_HOME}/go-apps/bin/morty -key '${MORTY_KEY}' -listen '${MORTY_LISTEN}' -timeout ${MORTY_TIMEOUT}
 
 Restart=always
 Environment=USER=${SERVICE_USER} HOME=${SERVICE_HOME} DEBUG=${SERVICE_ENV_DEBUG}

From 8829796f76bc74c584db1d2a83553ac0611372a0 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Tue, 28 Apr 2020 16:54:42 +0200
Subject: [PATCH 113/125] utils/morty.sh option new-key: set new morty key

A command to set new morty key in morty & searx service (settings.yml) and to
restart both services with new key::

  utils/morty.sh option new-key

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/morty.sh | 21 ++++++++++++++++++++-
 1 file changed, 20 insertions(+), 1 deletion(-)

diff --git a/utils/morty.sh b/utils/morty.sh
index 6b90db581..75bfeeedf 100755
--- a/utils/morty.sh
+++ b/utils/morty.sh
@@ -59,7 +59,7 @@ usage::
   $(basename "$0") activate   [service]
   $(basename "$0") deactivate [service]
   $(basename "$0") inspect    [service]
-  $(basename "$0") option     [debug-on|debug-off]
+  $(basename "$0") option     [debug-on|debug-off|new-key]
   $(basename "$0") apache     [install|remove]
   $(basename "$0") nginx      [install|remove]
   $(basename "$0") info       [searx]
@@ -79,6 +79,7 @@ inspect service
   show service status and log
 option
   set one of the available options
+  :new-key:   set new morty key
 apache : ${PUBLIC_URL_MORTY}
   :install: apache site with a reverse proxy (ProxyPass)
   :remove:  apache site ${APACHE_MORTY_SITE}
@@ -198,6 +199,7 @@ main() {
         option)
             sudo_or_exit
             case $2 in
+                new-key) set_new_key ;;
                 debug-on)  enable_debug ;;
                 debug-off)  disable_debug ;;
                 *) usage "$_usage"; exit 42;;
@@ -410,6 +412,23 @@ disable_debug() {
     set_service_env_debug false
 }
 
+
+set_new_key() {
+    rst_title "Set morty key"
+    echo
+
+    MORTY_KEY="$(head -c 32 /dev/urandom | base64)"
+    info_msg "morty key: '${MORTY_KEY}'"
+
+    warn_msg "this will need to reinstall services .."
+    MSG="${_Green}press any [${_BCyan}KEY${_Green}] to continue // stop with [${_BCyan}CTRL-C${_creset}]" wait_key
+
+    systemd_install_service "${SERVICE_NAME}" "${SERVICE_SYSTEMD_UNIT}"
+    "${REPO_ROOT}/utils/searx.sh" option result-proxy "${PUBLIC_URL_MORTY}" "${MORTY_KEY}"
+    "${REPO_ROOT}/utils/searx.sh" option image-proxy-on
+}
+
+
 install_apache_site() {
 
     rst_title "Install Apache site $APACHE_MORTY_SITE"

From 0973255aff6c7d1ea88147962ae04e9719423c7a Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 30 Apr 2020 17:49:26 +0200
Subject: [PATCH 114/125] [fix] fatal: repository '/root/searx' does not exist

The $SERVICE_USER (searx) needs read permission to clone repository.  The
installation script stops if permission is not granted.

- https://github.com/asciimoo/searx/pull/1803#issuecomment-616406528

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/searx.sh | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/utils/searx.sh b/utils/searx.sh
index 7013ec750..1499b928f 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -358,6 +358,9 @@ clone_is_available() {
 clone_searx() {
     rst_title "Clone searx sources" section
     echo
+    if ! sudo -i -u "$SERVICE_USER" ls -d "$REPO_ROOT" > /dev/null; then
+        die 42 "user '$SERVICE_USER' missed read permission: $REPO_ROOT"
+    fi
     SERVICE_HOME="$(sudo -i -u "$SERVICE_USER" echo \$HOME 2>/dev/null)"
     if [[ ! "${SERVICE_HOME}" ]]; then
         err_msg "to clone searx sources, user $SERVICE_USER hast to be created first"

From e50ccc99c09f7f56737d45f4aed15c48e91fb91f Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Mon, 4 May 2020 16:15:23 +0200
Subject: [PATCH 115/125] archlinux: add package 'inetutils' to boilerplate

Even it is a core component, some hoster do not have pre installed the
'inetutils' package.  We do need this package for the 'hostname' command.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/lxc.sh | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/utils/lxc.sh b/utils/lxc.sh
index a324bdaf2..a0688bc07 100755
--- a/utils/lxc.sh
+++ b/utils/lxc.sh
@@ -43,7 +43,7 @@ echo 'Set disable_coredump false' >> /etc/sudo.conf
 # shellcheck disable=SC2034
 archlinux_boilerplate="
 pacman -Syu --noconfirm
-pacman -S --noconfirm git curl wget sudo
+pacman -S --noconfirm inetutils git curl wget sudo
 echo 'Set disable_coredump false' >> /etc/sudo.conf
 "
 

From fcc5af66d8b4b364d98673d7c37e3b09373f4912 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 21 May 2020 15:16:49 +0200
Subject: [PATCH 116/125] [fix] disable debug in settings.yml by default

debug was accidentally enabled in commit cbc08fd

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 searx/settings.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/searx/settings.yml b/searx/settings.yml
index b0dee9fed..77cc4e087 100644
--- a/searx/settings.yml
+++ b/searx/settings.yml
@@ -1,5 +1,5 @@
 general:
-    debug : True # Debug mode, only for development
+    debug : False # Debug mode, only for development
     instance_name : "searx" # displayed name
 
 search:

From f576cadfba11dd96788f5c70e63fba4a70899584 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 21 May 2020 15:46:43 +0200
Subject: [PATCH 117/125] [fix] installation instructions: pip install -e searx
 (setup.py)

In the past we did not really install searx into the virtualenv.  This is a
problem, since entry-points and other python installation stuff is not trigger.

See discussion: https://github.com/asciimoo/searx/pull/1938#issuecomment-632056508

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/build-templates/searx.rst |  9 ++++++++-
 utils/searx.sh                 | 13 ++++++++++---
 2 files changed, 18 insertions(+), 4 deletions(-)

diff --git a/docs/build-templates/searx.rst b/docs/build-templates/searx.rst
index 964deb23d..5cb70e95f 100644
--- a/docs/build-templates/searx.rst
+++ b/docs/build-templates/searx.rst
@@ -112,8 +112,15 @@ ${fedora_build}
        $SEARX_PYENV/bin/python
        Python 3.8.1
 
+       # update pip's boilerplate ..
+       pip install -U pip
+       pip install -U setuptools
+       pip install -U wheel
+
+       # jump to searx's working tree and install searx into virtualenv
        (${SERVICE_USER})$ cd "$SEARX_SRC"
-       (${SERVICE_USER})$ ./manage.sh update_packages
+       (${SERVICE_USER})$ pip install -e .
+
 
 .. END manage.sh update_packages
 
diff --git a/utils/searx.sh b/utils/searx.sh
index 1499b928f..d55ec5dba 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -309,7 +309,10 @@ update_searx() {
 cd ${SEARX_SRC}
 git checkout -B "$GIT_BRANCH"
 git pull
-${SEARX_SRC}/manage.sh update_packages
+pip install -U pip
+pip install -U setuptools
+pip install -U wheel
+pip install -U -e .
 EOF
     install_settings
     uWSGI_restart "$SEARX_UWSGI_APP"
@@ -468,8 +471,12 @@ EOF
     wait_key
     info_msg "install needed python packages"
     tee_stderr 0.1 <<EOF | sudo -H -u "${SERVICE_USER}" -i 2>&1 |  prefix_stdout "$_service_prefix"
-pip install wheel
-${SEARX_SRC}/manage.sh update_packages
+pip install -U pip
+pip install -U setuptools
+pip install -U wheel
+pip install -U -e .
+cd ${SEARX_SRC}
+pip install -e .
 EOF
 }
 

From 6ff20cef7385d6827042f00d295f51bbc213afab Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:31:46 +0200
Subject: [PATCH 118/125] [fix] indentation of filtron's rules (json)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/filtron.rst                 | 179 +++++++++----------
 utils/templates/etc/filtron/rules.json | 228 +++++++++++++------------
 2 files changed, 200 insertions(+), 207 deletions(-)

diff --git a/docs/admin/filtron.rst b/docs/admin/filtron.rst
index 8bf641649..93e430b1f 100644
--- a/docs/admin/filtron.rst
+++ b/docs/admin/filtron.rst
@@ -57,100 +57,91 @@ of:
 
 .. code:: json
 
-   [
-     { "name": "search request",
-       "filters": [
-	 "Param:q",
-	 "Path=^(/|/search)$"
-       ],
-       "interval": "<time-interval-in-sec (int)>",
-       "limit": "<max-request-number-in-interval (int)>",
-       "subrules": [
-	 {
-	   "name": "roboagent limit",
-	   "interval": "<time-interval-in-sec (int)>",
-	   "limit": "<max-request-number-in-interval (int)>",
-	   "filters": [
-	     "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client)"
-	   ],
-	   "actions": [
-	     { "name": "log"},
-	     { "name": "block",
-	       "params": {
-		 "message": "Rate limit exceeded"
-	       }
-	     }
-	   ]
-	 },
-	 {
-	   "name": "botlimit",
-	   "limit": 0,
-	   "stop": true,
-	   "filters": [
-	     "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
-	   ],
-	   "actions": [
-	     { "name": "log"},
-	     { "name": "block",
-	       "params": {
-		 "message": "Rate limit exceeded"
-	       }
-	     }
-	   ]
-	 },
-	 {
-	   "name": "IP limit",
-	   "interval": "<time-interval-in-sec (int)>",
-	   "limit": "<max-request-number-in-interval (int)>",
-	   "stop": true,
-	   "aggregations": [
-	     "Header:X-Forwarded-For"
-	   ],
-	   "actions": [
-	     { "name": "log"},
-	     { "name": "block",
-	       "params": {
-		 "message": "Rate limit exceeded"
-	       }
-	     }
-	   ]
-	 },
-	 {
-	   "name": "rss/json limit",
-	   "interval": "<time-interval-in-sec (int)>",
-	   "limit": "<max-request-number-in-interval (int)>",
-	   "stop": true,
-	   "filters": [
-	     "Param:format=(csv|json|rss)"
-	   ],
-	   "actions": [
-	     { "name": "log"},
-	     { "name": "block",
-	       "params": {
-		 "message": "Rate limit exceeded"
-	       }
-	     }
-	   ]
-	 },
-	 {
-	   "name": "useragent limit",
-	   "interval": "<time-interval-in-sec (int)>",
-	   "limit": "<max-request-number-in-interval (int)>",
-	   "aggregations": [
-	     "Header:User-Agent"
-	   ],
-	   "actions": [
-	     { "name": "log"},
-	     { "name": "block",
-	       "params": {
-		 "message": "Rate limit exceeded"
-	       }
-	     }
-	   ]
-	 }
-       ]
-     }
-   ]
+    [
+        {
+            "name": "search request",
+            "filters": [
+                "Param:q",
+                "Path=^(/|/search)$"
+            ],
+            "interval": "<time-interval-in-sec (int)>"
+            "limit": "<max-request-number-in-interval (int)>",
+            "subrules": [
+                {
+                    "name": "missing Accept-Language",
+                    "filters": ["!Header:Accept-Language"],
+                    "limit": "<max-request-number-in-interval (int)>",
+                    "stop": true,
+                    "actions": [
+                        {"name":"log"},
+                        {"name": "block",
+                         "params": {"message": "Rate limit exceeded"}}
+                    ]
+                },
+                {
+                    "name": "suspiciously Connection=close header",
+                    "filters": ["Header:Connection=close"],
+                    "limit": "<max-request-number-in-interval (int)>",
+                    "stop": true,
+                    "actions": [
+                        {"name":"log"},
+                        {"name": "block",
+                         "params": {"message": "Rate limit exceeded"}}
+                    ]
+                },
+                {
+                    "name": "IP limit",
+                    "interval": "<time-interval-in-sec (int)>"
+                    "limit": "<max-request-number-in-interval (int)>",
+                    "stop": true,
+                    "aggregations": [
+                        "Header:X-Forwarded-For"
+                    ],
+                    "actions": [
+                        { "name": "log"},
+                        { "name": "block",
+                          "params": {
+                              "message": "Rate limit exceeded"
+                          }
+                        }
+                    ]
+                },
+                {
+                    "name": "rss/json limit",
+                    "filters": [
+                        "Param:format=(csv|json|rss)"
+                    ],
+                    "interval": "<time-interval-in-sec (int)>"
+                    "limit": "<max-request-number-in-interval (int)>",
+                    "stop": true,
+                    "actions": [
+                        { "name": "log"},
+                        { "name": "block",
+                          "params": {
+                              "message": "Rate limit exceeded"
+                          }
+                        }
+                    ]
+                },
+                {
+                    "name": "useragent limit",
+                    "interval": "<time-interval-in-sec (int)>"
+                    "limit": "<max-request-number-in-interval (int)>",
+                    "aggregations": [
+                        "Header:User-Agent"
+                    ],
+                    "actions": [
+                        { "name": "log"},
+                        { "name": "block",
+                          "params": {
+                              "message": "Rate limit exceeded"
+                          }
+                        }
+                    ]
+                }
+            ]
+        }
+    ]
 
 
 .. _filtron route request:
diff --git a/utils/templates/etc/filtron/rules.json b/utils/templates/etc/filtron/rules.json
index 8fbffa937..fff70fa8f 100644
--- a/utils/templates/etc/filtron/rules.json
+++ b/utils/templates/etc/filtron/rules.json
@@ -1,127 +1,129 @@
 [
     {
-	"name": "roboagent limit",
-	"filters": [
-	    "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)"
-	],
-	"limit": 0,
-	"stop": true,
-	"actions": [
-	    { "name": "log"},
-	    { "name": "block",
+        "name": "roboagent limit",
+        "filters": [
+            "Header:User-Agent=(curl|cURL|Wget|python-requests|Scrapy|FeedFetcher|Go-http-client|Ruby|UniversalFeedParser)"
+        ],
+        "limit": 0,
+        "stop": true,
+        "actions": [
+            { "name": "log"},
+            { "name": "block",
               "params": {
-		  "message": "Rate limit exceeded"
+                  "message": "Rate limit exceeded"
               }
-	    }
-	]
+            }
+        ]
     },
     {
-	"name": "botlimit",
-	"filters": [
-	    "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
-	],
-	"limit": 0,
-	"stop": true,
-	"actions": [
-	    { "name": "log"},
-	    { "name": "block",
+        "name": "botlimit",
+        "filters": [
+            "Header:User-Agent=(Googlebot|bingbot|Baiduspider|yacybot|YandexMobileBot|YandexBot|Yahoo! Slurp|MJ12bot|AhrefsBot|archive.org_bot|msnbot|MJ12bot|SeznamBot|linkdexbot|Netvibes|SMTBot|zgrab|James BOT)"
+        ],
+        "limit": 0,
+        "stop": true,
+        "actions": [
+            { "name": "log"},
+            { "name": "block",
               "params": {
-		  "message": "Rate limit exceeded"
+                  "message": "Rate limit exceeded"
               }
-	    }
-	]
+            }
+        ]
     },
-    { "name": "suspiciously frequent IP",
-      "filters": [],
-      "interval": 600,
-      "limit": 30,
-      "aggregations": [
-	  "Header:X-Forwarded-For"
+    {
+        "name": "suspiciously frequent IP",
+        "filters": [],
+        "interval": 600,
+        "limit": 30,
+        "aggregations": [
+            "Header:X-Forwarded-For"
       ],
-      "actions":[
-	  {"name":"log"}
+        "actions":[
+            {"name":"log"}
       ]
     },
-    { "name": "search request",
-      "filters": [
-	  "Param:q",
-	  "Path=^(/|/search)$"
-      ],
-      "interval": 61,
-      "limit": 999,
-      "subrules": [
-	  {
-	      "name": "missing Accept-Language",
-	      "filters": ["!Header:Accept-Language"],
-	      "limit": 0,
-	      "stop": true,
-	      "actions": [
-		  {"name":"log"},
-		  {"name": "block",
-		   "params": {"message": "Rate limit exceeded"}}
-	      ]
-	  },
-	  {
-              "name": "suspiciously Connection=close header",
-              "filters": ["Header:Connection=close"],
-              "limit": 0,
-              "stop": true,
-              "actions": [
-		  {"name":"log"},
-		  {"name": "block",
-		   "params": {"message": "Rate limit exceeded"}}
-              ]
-	  },
-	  {
-              "name": "IP limit",
-              "interval": 61,
-              "limit": 9,
-              "stop": true,
-              "aggregations": [
-		  "Header:X-Forwarded-For"
-              ],
-              "actions": [
-		  { "name": "log"},
-		  { "name": "block",
-		    "params": {
-			"message": "Rate limit exceeded"
-		    }
-		  }
-              ]
-	  },
-	  {
-              "name": "rss/json limit",
-              "filters": [
-		  "Param:format=(csv|json|rss)"
-              ],
-              "interval": 121,
-              "limit": 2,
-              "stop": true,
-              "actions": [
-		  { "name": "log"},
-		  { "name": "block",
-		    "params": {
-			"message": "Rate limit exceeded"
-		    }
-		  }
-              ]
-	  },
-	  {
-              "name": "useragent limit",
-              "interval": 61,
-              "limit": 199,
-              "aggregations": [
-		  "Header:User-Agent"
-              ],
-              "actions": [
-		  { "name": "log"},
-		  { "name": "block",
-		    "params": {
-			"message": "Rate limit exceeded"
-		    }
-		  }
-              ]
-	  }
-      ]
+    {
+        "name": "search request",
+        "filters": [
+            "Param:q",
+            "Path=^(/|/search)$"
+        ],
+        "interval": 61,
+        "limit": 999,
+        "subrules": [
+            {
+                "name": "missing Accept-Language",
+                "filters": ["!Header:Accept-Language"],
+                "limit": 0,
+                "stop": true,
+                "actions": [
+                    {"name":"log"},
+                    {"name": "block",
+                     "params": {"message": "Rate limit exceeded"}}
+                ]
+            },
+            {
+                "name": "suspiciously Connection=close header",
+                "filters": ["Header:Connection=close"],
+                "limit": 0,
+                "stop": true,
+                "actions": [
+                    {"name":"log"},
+                    {"name": "block",
+                     "params": {"message": "Rate limit exceeded"}}
+                ]
+            },
+            {
+                "name": "IP limit",
+                "interval": 61,
+                "limit": 9,
+                "stop": true,
+                "aggregations": [
+                    "Header:X-Forwarded-For"
+                ],
+                "actions": [
+                    { "name": "log"},
+                    { "name": "block",
+                      "params": {
+                          "message": "Rate limit exceeded"
+                      }
+                    }
+                ]
+            },
+            {
+                "name": "rss/json limit",
+                "filters": [
+                    "Param:format=(csv|json|rss)"
+                ],
+                "interval": 121,
+                "limit": 2,
+                "stop": true,
+                "actions": [
+                    { "name": "log"},
+                    { "name": "block",
+                      "params": {
+                          "message": "Rate limit exceeded"
+                      }
+                    }
+                ]
+            },
+            {
+                "name": "useragent limit",
+                "interval": 61,
+                "limit": 199,
+                "aggregations": [
+                    "Header:User-Agent"
+                ],
+                "actions": [
+                    { "name": "log"},
+                    { "name": "block",
+                      "params": {
+                          "message": "Rate limit exceeded"
+                      }
+                    }
+                ]
+            }
+        ]
     }
 ]

From 51428de0fea8c1de10c4caf07ef16ae6eb6e9b24 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:35:59 +0200
Subject: [PATCH 119/125] [fix] add missing latexmk package to debian's
 BUILD_PACKAGES

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 utils/searx.sh | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/utils/searx.sh b/utils/searx.sh
index d55ec5dba..4009fddb0 100755
--- a/utils/searx.sh
+++ b/utils/searx.sh
@@ -49,7 +49,8 @@ shellcheck"
 
 BUILD_PACKAGES_debian="\
 firefox graphviz imagemagick texlive-xetex librsvg2-bin
-texlive-latex-recommended texlive-extra-utils ttf-dejavu"
+texlive-latex-recommended texlive-extra-utils ttf-dejavu
+latexmk"
 
 # pacman packages
 SEARX_PACKAGES_arch="\

From 4f7a6c09dd1b799a53a25166b49d23adceccea7d Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:39:56 +0200
Subject: [PATCH 120/125] [fix] buildhosts documentation

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/buildhosts.rst | 2 +-
 docs/utils/lxc.sh.rst     | 4 ++--
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index 04698d8d2..b1e787398 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -17,7 +17,7 @@ Buildhosts
 To get best results from build, its recommend to install additional packages
 on build hosts (see :ref:`searx.sh`).::
 
-  sudo -H ./utils/searx.sh buildhost
+  sudo -H ./utils/searx.sh install buildhost
 
 This will install packages needed by searx:
 
diff --git a/docs/utils/lxc.sh.rst b/docs/utils/lxc.sh.rst
index 9ca9b6bc0..ae1412870 100644
--- a/docs/utils/lxc.sh.rst
+++ b/docs/utils/lxc.sh.rst
@@ -102,7 +102,7 @@ Running commands
 :ref:`toolboxing`.  By example: to setup a :ref:`buildhosts` and run the
 Makefile target ``test`` in the archlinux_ container::
 
-  sudo -H ./utils/lxc.sh cmd searx-archlinux ./utils/searx.sh buildhost
+  sudo -H ./utils/lxc.sh cmd searx-archlinux ./utils/searx.sh install buildhost
   sudo -H ./utils/lxc.sh cmd searx-archlinux make test
 
 
@@ -114,7 +114,7 @@ The installation procedure to set up a :ref:`build host<buildhosts>` takes its
 time.  Installation in all containers will take more time (time for another cup
 of coffee).::
 
-  sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh buildhost
+  sudo -H ./utils/lxc.sh cmd -- ./utils/searx.sh install buildhost
 
 To build (live) documentation inside a archlinux_ container::
 

From 15924bcb67680933f6ef71ddc3cd910fed62fbbd Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:44:22 +0200
Subject: [PATCH 121/125] [fix] buildprocess of the docs by adding
 sphinx-doc-prebuilds target

The sphinx-doc-prebuilds is the single point of definition for prerequisites
each doc build needs

BTW: remove $(LXC_ENV_FOLDER) offset from BOOKS_FOLDER and DOCS_FOLDER path.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 Makefile              | 17 ++++++++++-------
 utils/makefile.sphinx | 12 +++++++-----
 2 files changed, 17 insertions(+), 12 deletions(-)

diff --git a/Makefile b/Makefile
index aefd005f4..d10bd9dbf 100644
--- a/Makefile
+++ b/Makefile
@@ -77,19 +77,22 @@ run:  buildenv pyenvinstall
 # docs
 # ----
 
+sphinx-doc-prebuilds:: buildenv pyenvinstall prebuild-includes
+
 PHONY += docs
-docs:  buildenv pyenvinstall sphinx-doc prebuild-includes
+docs:  sphinx-doc-prebuilds sphinx-doc
 	$(call cmd,sphinx,html,docs,docs)
 
-PHONY += docs-live prebuild-includes
-docs-live:  buildenv pyenvinstall sphinx-live prebuild-includes
+PHONY += docs-live
+docs-live:  sphinx-doc-prebuilds sphinx-live
 	$(call cmd,sphinx_autobuild,html,docs,docs)
 
+PHONY += prebuild-includes
 prebuild-includes:
-	@mkdir -p $(DOCS_BUILD)/includes
-	@./utils/searx.sh doc | cat > $(DOCS_BUILD)/includes/searx.rst
-	@./utils/filtron.sh doc | cat > $(DOCS_BUILD)/includes/filtron.rst
-	@./utils/morty.sh doc | cat > $(DOCS_BUILD)/includes/morty.rst
+	$(Q)mkdir -p $(DOCS_BUILD)/includes
+	$(Q)./utils/searx.sh doc | cat > $(DOCS_BUILD)/includes/searx.rst
+	$(Q)./utils/filtron.sh doc | cat > $(DOCS_BUILD)/includes/filtron.rst
+	$(Q)./utils/morty.sh doc | cat > $(DOCS_BUILD)/includes/morty.rst
 
 
 $(GH_PAGES)::
diff --git a/utils/makefile.sphinx b/utils/makefile.sphinx
index fd4c3a6f1..1b0f42ccb 100644
--- a/utils/makefile.sphinx
+++ b/utils/makefile.sphinx
@@ -7,12 +7,12 @@ SPHINXOPTS  ?=
 SPHINXBUILD ?= $(PY_ENV_BIN)/sphinx-build
 SPHINX_CONF ?= conf.py
 
-DOCS_FOLDER = ./$(LXC_ENV_FOLDER)docs
+DOCS_FOLDER = ./docs
 DOCS_BUILD  = ./$(LXC_ENV_FOLDER)build/docs
 DOCS_DIST   = ./$(LXC_ENV_FOLDER)dist/docs
 GH_PAGES    ?= gh-pages
 
-BOOKS_FOLDER = ./$(LXC_ENV_FOLDER)docs
+BOOKS_FOLDER = ./docs
 BOOKS_DIST   = ./$(LXC_ENV_FOLDER)dist/books
 
 ifeq ($(KBUILD_VERBOSE),1)
@@ -56,11 +56,13 @@ docs-help:
 # requirements
 # ------------------------------------------------------------------------------
 
-sphinx-doc: $(PY_ENV)
+sphinx-doc-prebuilds:: $(PY_ENV)
+
+sphinx-doc: sphinx-doc-prebuilds
 	@echo "PYENV     installing Sphinx$(SPHINXVERS)"
 	$(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)'
 
-sphinx-live: $(PY_ENV)
+sphinx-live: sphinx-doc-prebuilds
 	@echo "PYENV     installing Sphinx$(SPHINXVERS)"
 	$(Q)$(PY_ENV_BIN)/pip install $(PIP_VERBOSE) 'Sphinx$(SPHINXVERS)' sphinx-autobuild
 
@@ -115,7 +117,7 @@ quiet_cmd_sphinx_clean = CLEAN     $@
 # targets
 # ------------------------------------------------------------------------------
 
-# build PDF of whole documentation in: $(DOCS_DIST)/pdf 
+# build PDF of whole documentation in: $(DOCS_DIST)/pdf
 
 PHONY += sphinx-pdf
 sphinx-pdf: sphinx-latex

From aa5f947157869f9d53eacd820cc9e5400c95132c Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:51:31 +0200
Subject: [PATCH 122/125] [enh] add 'kernel-include' reST-directive

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/conf.py      | 1 +
 docs/dev/reST.rst | 5 +++--
 2 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/docs/conf.py b/docs/conf.py
index 9e6d32931..1f79e4c14 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -66,6 +66,7 @@ extensions = [
     "sphinx_issues", # https://github.com/sloria/sphinx-issues/blob/master/README.rst
     "sphinxcontrib.jinja",  # https://github.com/tardyp/sphinx-jinja
     "sphinxcontrib.programoutput",  # https://github.com/NextThought/sphinxcontrib-programoutput
+    'linuxdoc.kernel_include',  # Implementation of the 'kernel-include' reST-directive.
     'linuxdoc.rstFlatTable',    # Implementation of the 'flat-table' reST-directive.
     'linuxdoc.kfigure',         # Sphinx extension which implements scalable image handling.
     "sphinx_tabs.tabs", # https://github.com/djungelorm/sphinx-tabs
diff --git a/docs/dev/reST.rst b/docs/dev/reST.rst
index 7b253d9ec..906a0e9af 100644
--- a/docs/dev/reST.rst
+++ b/docs/dev/reST.rst
@@ -325,8 +325,9 @@ Literal blocks
 
 The simplest form of :duref:`literal-blocks` is a indented block introduced by
 two colons (``::``).  For highlighting use :dudir:`highlight` or :ref:`reST
-code` directive.  To include literals from external files use directive
-:rst:dir:`literalinclude`.
+code` directive.  To include literals from external files use
+:rst:dir:`literalinclude` or :ref:`kernel-include <kernel-include-directive>`
+directive (latter one expands environment variables in the path name).
 
 .. _reST literal:
 

From bfef2c3b65aea8dd3485e0959e068d460577a841 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:52:45 +0200
Subject: [PATCH 123/125] [fix] buildprocess of docs in LXC (containers)

The $DOCS_BUILD variable (like all BUILD variables) in the lxc environment::

   sudo -H ./utils/lxc.sh cmd searx-archlinux make docs

is different from running build process in the HOST::

   make docs

with kernel-include directive we can use the environment variables in the reST
documents to address the correct file location of the include.

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/admin/buildhosts.rst         |  4 ++--
 docs/admin/installation-searx.rst | 14 +++++++-------
 docs/admin/installation-uwsgi.rst | 18 +++++++++---------
 docs/utils/filtron.sh.rst         |  8 ++++----
 docs/utils/morty.sh.rst           |  8 ++++----
 5 files changed, 26 insertions(+), 26 deletions(-)

diff --git a/docs/admin/buildhosts.rst b/docs/admin/buildhosts.rst
index b1e787398..a727d25b9 100644
--- a/docs/admin/buildhosts.rst
+++ b/docs/admin/buildhosts.rst
@@ -21,13 +21,13 @@ on build hosts (see :ref:`searx.sh`).::
 
 This will install packages needed by searx:
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START distro-packages
    :end-before: END distro-packages
 
 and packages needed to build docuemtation and run tests:
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START build-packages
    :end-before: END build-packages
 
diff --git a/docs/admin/installation-searx.rst b/docs/admin/installation-searx.rst
index d5c0063ee..f1d486021 100644
--- a/docs/admin/installation-searx.rst
+++ b/docs/admin/installation-searx.rst
@@ -17,7 +17,7 @@ universe repository.
 Install packages
 ================
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START distro-packages
    :end-before: END distro-packages
 
@@ -30,7 +30,7 @@ Install packages
 Create user
 ===========
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START create user
    :end-before: END create user
 
@@ -41,13 +41,13 @@ install searx & dependencies
 
 Start a interactive shell from new created user and clone searx:
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START clone searx
    :end-before: END clone searx
 
 In the same shell create *virtualenv*:
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START create virtualenv
    :end-before: END create virtualenv
 
@@ -55,7 +55,7 @@ To install searx's dependencies, exit the searx *bash* session you opened above
 and restart a new.  Before install, first check if your *virualenv* was sourced
 from the login (*~/.profile*):
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START manage.sh update_packages
    :end-before: END manage.sh update_packages
 
@@ -71,7 +71,7 @@ Create a copy of the :origin:`searx/settings.yml` configuration file in system's
 */etc* folder.  Configure like shown below -- replace ``searx@\$(uname -n)`` with
 a name of your choice -- *and/or* edit ``/etc/searx/settings.yml`` if necessary.
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START searx config
    :end-before: END searx config
 
@@ -82,7 +82,7 @@ To check your searx setup, optional enable debugging and start the *webapp*.
 Searx looks at the exported environment ``$SEARX_SETTINGS_PATH`` for a
 configuration file.
 
-.. include:: ../../build/docs/includes/searx.rst
+.. kernel-include:: $DOCS_BUILD/includes/searx.rst
    :start-after: START check searx installation
    :end-before: END check searx installation
 
diff --git a/docs/admin/installation-uwsgi.rst b/docs/admin/installation-uwsgi.rst
index 72498c0bf..ac4c463b9 100644
--- a/docs/admin/installation-uwsgi.rst
+++ b/docs/admin/installation-uwsgi.rst
@@ -104,21 +104,21 @@ restart the uwsgi application.
 
    .. group-tab:: Ubuntu / debian
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
          :start-after: START searx uwsgi-description ubuntu-20.04
          :end-before: END searx uwsgi-description ubuntu-20.04
 
 
    .. group-tab:: Arch Linux
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
          :start-after: START searx uwsgi-description arch
          :end-before: END searx uwsgi-description arch
 
 
    .. group-tab::  Fedora / RHEL
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
          :start-after: START searx uwsgi-description fedora
          :end-before: END searx uwsgi-description fedora
 
@@ -127,22 +127,22 @@ restart the uwsgi application.
 
    .. group-tab:: Ubuntu / debian
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
-         :language: ini
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+         :code: ini
          :start-after: START searx uwsgi-appini ubuntu-20.04
          :end-before: END searx uwsgi-appini ubuntu-20.04
 
    .. group-tab:: Arch Linux
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
-         :language: ini
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+         :code: ini
          :start-after: START searx uwsgi-appini arch
          :end-before: END searx uwsgi-appini arch
 
    .. group-tab::  Fedora / RHEL
 
-      .. literalinclude:: ../../build/docs/includes/searx.rst
-         :language: ini
+      .. kernel-include:: $DOCS_BUILD/includes/searx.rst
+         :code: ini
          :start-after: START searx uwsgi-appini fedora
          :end-before: END searx uwsgi-appini fedora
 
diff --git a/docs/utils/filtron.sh.rst b/docs/utils/filtron.sh.rst
index 6c83bdf78..86e3fa88a 100644
--- a/docs/utils/filtron.sh.rst
+++ b/docs/utils/filtron.sh.rst
@@ -41,7 +41,7 @@ into this user account:
 Create user
 ===========
 
-.. include:: ../../build/docs/includes/filtron.rst
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
    :start-after: START create user
    :end-before: END create user
 
@@ -49,7 +49,7 @@ Create user
 Install go
 ==========
 
-.. include:: ../../build/docs/includes/filtron.rst
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
    :start-after: START install go
    :end-before: END install go
 
@@ -61,11 +61,11 @@ Install :origin:`rules.json <utils/templates/etc/filtron/rules.json>` at
 ``/etc/filtron/rules.json`` (see :ref:`Sample configuration of filtron`) and
 install filtron software and systemd unit:
 
-.. include:: ../../build/docs/includes/filtron.rst
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
    :start-after: START install filtron
    :end-before: END install filtron
 
-.. include:: ../../build/docs/includes/filtron.rst
+.. kernel-include:: $DOCS_BUILD/includes/filtron.rst
    :start-after: START install systemd unit
    :end-before: END install systemd unit
 
diff --git a/docs/utils/morty.sh.rst b/docs/utils/morty.sh.rst
index 89629bea0..3bdf9bdd3 100644
--- a/docs/utils/morty.sh.rst
+++ b/docs/utils/morty.sh.rst
@@ -42,7 +42,7 @@ into this user account:
 Create user
 ===========
 
-.. include:: ../../build/docs/includes/morty.rst
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
    :start-after: START create user
    :end-before: END create user
 
@@ -50,7 +50,7 @@ Create user
 Install go
 ==========
 
-.. include:: ../../build/docs/includes/morty.rst
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
    :start-after: START install go
    :end-before: END install go
 
@@ -60,11 +60,11 @@ Install morty
 
 Install morty software and systemd unit:
 
-.. include:: ../../build/docs/includes/morty.rst
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
    :start-after: START install morty
    :end-before: END install morty
 
-.. include:: ../../build/docs/includes/morty.rst
+.. kernel-include:: $DOCS_BUILD/includes/morty.rst
    :start-after: START install systemd unit
    :end-before: END install systemd unit
 

From a5e82f98013eb76acc373c397bec7c694adad403 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 18:57:10 +0200
Subject: [PATCH 124/125] [fix] margial typo in docs/user/onf.py doc-string

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/user/conf.py | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/docs/user/conf.py b/docs/user/conf.py
index d4bdb576b..53ade4b63 100644
--- a/docs/user/conf.py
+++ b/docs/user/conf.py
@@ -1,5 +1,5 @@
 # -*- coding: utf-8; mode: python -*-
-"""Configuration for the CDB 15 Infrastruktur book
+"""Configuration for the Searx user handbook
 """
 project   = 'Searx User-HB'
 version   = release = VERSION_STRING

From 0616684baa58b86cac5df6546432d4372f965fe2 Mon Sep 17 00:00:00 2001
From: Markus Heiser <markus.heiser@darmarit.de>
Date: Thu, 18 Jun 2020 19:02:22 +0200
Subject: [PATCH 125/125] [fix] sphinx 3.1 CSS issues

See sphinx-doc project, PR 7838 & 7484 with elementary patch to the basic CSS:

- https://github.com/sphinx-doc/sphinx/issues/7838#issuecomment-646009605
- https://github.com/sphinx-doc/sphinx/pull/7484#issuecomment-646058972

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
---
 docs/_themes/searx/static/searx.css | 29 +++++++++++++++++++++++++++++
 1 file changed, 29 insertions(+)

diff --git a/docs/_themes/searx/static/searx.css b/docs/_themes/searx/static/searx.css
index ae742a4e0..0f2eff728 100644
--- a/docs/_themes/searx/static/searx.css
+++ b/docs/_themes/searx/static/searx.css
@@ -138,3 +138,32 @@ caption {
   caption-side: top;
   text-align: left;
 }
+
+/* bugs since sphinx 3.1
+
+See sphinx-doc project, PR 7838 & 7484 with elementary patch to the basic CSS:
+
+- https://github.com/sphinx-doc/sphinx/issues/7838#issuecomment-646009605
+- https://github.com/sphinx-doc/sphinx/pull/7484#issuecomment-646058972
+
+*/
+
+li > p:first-child {
+    margin-top: 0;
+}
+
+li > p:last-child {
+    margin-bottom: 0;
+}
+
+div.admonition dl {
+    margin-bottom: 0;
+}
+
+div.sidebar {
+    clear: none;
+}
+
+div.admonition, div.topic, pre {
+    clear: none;
+}