Browse Source

[mod] script to build & install a redis instance

A script to build & install a simple & isolated redis service, dedicated to
SearXNG and connected via Unix socket.

    $ ./manage redis.help
    redis.:
      devpkg    : install essential packages to compile redis
      build     : build redis binaries at /800GBPCIex4/share/SearXNG/dist/redis/6.2.6/amd64
      install   : create user (searxng-redis) and install systemd service (searxng-redis)
      remove    : delete user (searxng-redis) and remove service (searxng-redis)
      shell     : start bash interpreter from user searxng-redis
      src       : clone redis source code to <path> and checkput 6.2.6
      useradd   : create user (searxng-redis) at /usr/local/searxng-redis
      userdel   : delete user (searxng-redis)
      addgrp    : add <user> to group (searxng-redis)
      rmgrp     : remove <user> from group (searxng-redis)

Signed-off-by: Markus Heiser <markus.heiser@darmarit.de>
Markus Heiser 3 years ago
parent
commit
fe8b88c8a4
3 changed files with 397 additions and 3 deletions
  1. 7 3
      manage
  2. 348 0
      utils/lib_redis.sh
  3. 42 0
      utils/templates/lib/systemd/system/searxng-redis.service

+ 7 - 3
manage

@@ -17,6 +17,9 @@ source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_static.sh"
 # shellcheck source=utils/lib_go.sh
 # shellcheck source=utils/lib_go.sh
 source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_go.sh"
 source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_go.sh"
 
 
+# shellcheck source=utils/lib_redis.sh
+source "$(dirname "${BASH_SOURCE[0]}")/utils/lib_redis.sh"
+
 # config
 # config
 
 
 PYOBJECTS="searx"
 PYOBJECTS="searx"
@@ -74,9 +77,10 @@ docker.:
 gecko.driver:
 gecko.driver:
   download & install geckodriver if not already installed (required for
   download & install geckodriver if not already installed (required for
   robot_tests)
   robot_tests)
-EOF
-    nvm.help
-    cat <<EOF
+redis:
+  build     : build redis binaries at $(redis._get_dist)
+  install   : create user (${REDIS_USER}) and install systemd service (${REDIS_SERVICE_NAME})
+  help      : show more redis commands
 node.:
 node.:
   env       : download & install npm dependencies locally
   env       : download & install npm dependencies locally
   clean     : drop locally npm installations
   clean     : drop locally npm installations

+ 348 - 0
utils/lib_redis.sh

@@ -0,0 +1,348 @@
+#!/usr/bin/env bash
+# -*- coding: utf-8; mode: sh indent-tabs-mode: nil -*-
+# SPDX-License-Identifier: AGPL-3.0-or-later
+#
+# Tools to build and install redis [1] binaries & packages.
+#
+# [1] https://redis.io/download#installation
+#
+# 1. redis.devpkg (sudo)
+# 2. redis.build
+# 3. redis.install (sudo)
+#
+# systemd commands::
+#
+#    sudo -H systemctl status searxng-redis
+#    sudo -H journalctl -u searxng-redis
+#    sudo -H journalctl --vacuum-size=1M
+#
+# Test socket connection from client (local user)::
+#
+#    $ sudo -H ./manage redis.addgrp "${USER}"
+#    # logout & login to get member of group
+#    $ groups
+#    ... searxng-redis ...
+#    $ source /usr/local/searxng-redis/.redis_env
+#    $ which redis-cli
+#    /usr/local/searxng-redis/.local/bin/redis-cli
+#
+#    $ redis-cli -s /usr/local/searxng-redis/redis.sock
+#    redis /usr/local/searxng-redis/redis.sock> set foo bar
+#    OK
+#    redis /usr/local/searxng-redis/redis.sock> get foo
+#    "bar"
+#    [CTRL-D]
+
+
+# shellcheck disable=SC2091
+# shellcheck source=utils/lib.sh
+. /dev/null
+
+REDIS_GIT_URL="https://github.com/redis/redis.git"
+REDIS_GIT_TAG="${REDIS_GIT_TAG:-6.2.6}"
+
+REDIS_USER="searxng-redis"
+REDIS_HOME="/usr/local/${REDIS_USER}"
+REDIS_HOME_BIN="${REDIS_HOME}/.local/bin"
+REDIS_ENV="${REDIS_HOME}/.redis_env"
+
+REDIS_SERVICE_NAME="searxng-redis"
+REDIS_SYSTEMD_UNIT="${SYSTEMD_UNITS}/${REDIS_SERVICE_NAME}.service"
+
+# binaries to compile & install
+REDIS_INSTALL_EXE=(redis-server redis-benchmark redis-cli)
+# link names of redis-server binary
+REDIS_LINK_EXE=(redis-sentinel redis-check-rdb redis-check-aof)
+
+REDIS_CONF="${REDIS_HOME}/redis.conf"
+REDIS_CONF_TEMPLATE=$(cat <<EOF
+# Note that in order to read the configuration file, Redis must be
+# started with the file path as first argument:
+#
+# ./redis-server /path/to/redis.conf
+
+# bind 127.0.0.1 -::1
+protected-mode yes
+
+# Accept connections on the specified port, default is 6379 (IANA #815344).
+# If port 0 is specified Redis will not listen on a TCP socket.
+port 0
+
+# Specify the path for the Unix socket that will be used to listen for
+# incoming connections.
+
+unixsocket ${REDIS_HOME}/run/redis.sock
+unixsocketperm 770
+
+# The working directory.
+dir ${REDIS_HOME}/run
+
+# If you run Redis from upstart or systemd, Redis can interact with your
+# supervision tree.
+supervised auto
+
+pidfile ${REDIS_HOME}/run/redis.pid
+
+# log to the system logger
+syslog-enabled yes
+EOF
+)
+
+redis.help(){
+    cat <<EOF
+redis.:
+  devpkg    : install essential packages to compile redis
+  build     : build redis binaries at $(redis._get_dist)
+  install   : create user (${REDIS_USER}) and install systemd service (${REDIS_SERVICE_NAME})
+  remove    : delete user (${REDIS_USER}) and remove service (${REDIS_SERVICE_NAME})
+  shell     : start bash interpreter from user ${REDIS_USER}
+  src       : clone redis source code to <path> and checkput ${REDIS_GIT_TAG}
+  useradd   : create user (${REDIS_USER}) at ${REDIS_HOME}
+  userdel   : delete user (${REDIS_USER})
+  addgrp    : add <user> to group (${REDIS_USER})
+  rmgrp     : remove <user> from group (${REDIS_USER})
+EOF
+}
+
+redis.devpkg() {
+
+    # Uses OS package manager to install the essential packages to build and
+    # compile sources
+
+    sudo_or_exit
+
+    case ${DIST_ID} in
+        ubuntu|debian)
+            pkg_install git build-essential
+            ;;
+        arch)
+            pkg_install git base-devel
+            ;;
+        fedora)
+            pkg_install git @development-tools
+            ;;
+        centos)
+            pkg_install git
+            yum groupinstall "Development Tools" -y
+            ;;
+        *)
+            err_msg "$DIST_ID-$DIST_VERS: No rules to install development tools from OS."
+            return 42
+            ;;
+    esac
+}
+
+redis.build() {
+
+    # usage: redis.build
+
+    rst_title "get redis sources" section
+    redis.src "${CACHE}/redis"
+
+    if ! required_commands gcc nm make gawk; then
+        sudo -H "$0" redis.devpkg
+    fi
+
+    rst_title "compile redis sources" section
+
+    pushd "${CACHE}/redis" &>/dev/null
+
+    if ask_yn "Do you run 'make distclean' first'?" Ny; then
+        $(bash.cmd) -c "make distclean" 2>&1 | prefix_stdout
+    fi
+
+    $(bash.cmd) -c "make" 2>&1 | prefix_stdout
+    if ask_yn "Do you run 'make test'?" Ny; then
+        $(bash.cmd) -c "make test" | prefix_stdout
+    fi
+
+    popd &>/dev/null
+
+    tee_stderr 0.1 <<EOF | $(bash.cmd) 2>&1 |  prefix_stdout
+mkdir -p "$(redis._get_dist)"
+cd "${CACHE}/redis/src"
+cp ${REDIS_INSTALL_EXE[@]} "$(redis._get_dist)"
+EOF
+    info_msg "redis binaries available at $(redis._get_dist)"
+}
+
+
+redis.install() {
+    sudo_or_exit
+    (
+        set -e
+        redis.useradd
+        redis._install_bin
+        redis._install_conf
+        redis._install_service
+    )
+    dump_return $?
+}
+
+redis.remove() {
+    sudo_or_exit
+    (
+        set -e
+        redis._remove_service
+        redis.userdel
+    )
+    dump_return $?
+}
+
+redis.shell() {
+    interactive_shell "${REDIS_USER}"
+}
+
+redis.src() {
+
+    # usage: redis.src "${CACHE}/redis"
+
+    local dest="${1:-${CACHE}/redis}"
+
+    if [ -d "${dest}" ] ; then
+        info_msg "already cloned: $dest"
+        tee_stderr 0.1 <<EOF | $(bash.cmd) 2>&1 | prefix_stdout
+cd "${dest}"
+git fetch --all
+git reset --hard tags/${REDIS_GIT_TAG}
+EOF
+    else
+        tee_stderr 0.1 <<EOF | $(bash.cmd) 2>&1 | prefix_stdout
+mkdir -p "$(dirname "$dest")"
+cd "$(dirname "$dest")"
+git clone "${REDIS_GIT_URL}" "${dest}"
+EOF
+        tee_stderr 0.1 <<EOF | $(bash.cmd) 2>&1 | prefix_stdout
+cd "${dest}"
+git checkout tags/${REDIS_GIT_TAG} -b "build-branch"
+EOF
+    fi
+}
+
+redis.useradd(){
+
+    # usage: redis.useradd
+
+    rst_title "add user ${REDIS_USER}" section
+    echo
+    sudo_or_exit
+
+    # create user account
+    tee_stderr 0.5 <<EOF | sudo -H bash | prefix_stdout
+useradd --shell /bin/bash --system \
+ --home-dir "${REDIS_HOME}" \
+ --comment 'user that runs a redis instance' "${REDIS_USER}"
+mkdir -p "${REDIS_HOME}"
+chown -R "${REDIS_USER}:${REDIS_USER}" "${REDIS_HOME}"
+groups "${REDIS_USER}"
+EOF
+
+    # create App-ENV and add source it in the .profile
+    tee_stderr 0.5 <<EOF | sudo -H -u "${REDIS_USER}" bash | prefix_stdout
+mkdir -p "${REDIS_HOME_BIN}"
+echo "export PATH=${REDIS_HOME_BIN}:\\\$PATH" > "${REDIS_ENV}"
+grep -qFs -- 'source "${REDIS_ENV}"' ~/.profile || echo 'source "${REDIS_ENV}"' >> ~/.profile
+EOF
+}
+
+redis.userdel() {
+    sudo_or_exit
+    drop_service_account "${REDIS_USER}"
+    groupdel "${REDIS_USER}" 2>&1 | prefix_stdout || true
+}
+
+redis.addgrp() {
+
+    # usage: redis.addgrp <user>
+
+    [[ -z $1 ]] && die_caller 42 "missing argument <user>"
+    sudo -H gpasswd -a "$1" "${REDIS_USER}"
+}
+
+redis.rmgrp() {
+
+    # usage: redis.rmgrp <user>
+
+    [[ -z $1 ]] && die_caller 42 "missing argument <user>"
+    sudo -H gpasswd -d "$1" "${REDIS_USER}"
+
+}
+
+
+# private redis. functions
+# ------------------------
+
+redis._install_bin() {
+    local src
+    src="$(redis._get_dist)"
+    (
+        set -e
+        for redis_exe in "${REDIS_INSTALL_EXE[@]}"; do
+            install -v -o "${REDIS_USER}" -g "${REDIS_USER}" \
+                 "${src}/${redis_exe}" "${REDIS_HOME_BIN}"
+        done
+
+        pushd "${REDIS_HOME_BIN}" &> /dev/null
+        for redis_exe in "${REDIS_LINK_EXE[@]}"; do
+            info_msg "link redis-server --> ${redis_exe}"
+            sudo -H -u "${REDIS_USER}" ln -sf redis-server "${redis_exe}"
+        done
+        popd &> /dev/null
+
+    )
+}
+
+redis._install_conf() {
+        sudo -H -u "${REDIS_USER}" bash <<EOF
+mkdir -p "${REDIS_HOME}/run"
+echo '${REDIS_CONF_TEMPLATE}' > "${REDIS_CONF}"
+EOF
+}
+
+redis._install_service() {
+    systemd_install_service "${REDIS_SERVICE_NAME}" "${REDIS_SYSTEMD_UNIT}"
+}
+
+redis._remove_service() {
+    systemd_remove_service "${REDIS_SERVICE_NAME}" "${REDIS_SYSTEMD_UNIT}"
+}
+
+redis._get_dist() {
+    if [ -z "${REDIS_DIST}" ]; then
+        echo "${REPO_ROOT}/dist/redis/${REDIS_GIT_TAG}/$(redis._arch)"
+    else
+        echo "${REDIS_DIST}"
+    fi
+}
+
+redis._arch() {
+    local ARCH
+    case "$(command uname -m)" in
+        "x86_64") ARCH=amd64 ;;
+        "aarch64") ARCH=arm64 ;;
+        "armv6" | "armv7l") ARCH=armv6l ;;
+        "armv8") ARCH=arm64 ;;
+        .*386.*) ARCH=386 ;;
+        ppc64*) ARCH=ppc64le ;;
+    *)  die 42 "ARCH is unknown: $(command uname -m)" ;;
+    esac
+    echo "${ARCH}"
+}
+
+# TODO: move this to the right place ..
+
+bash.cmd(){
+
+    # print cmd to get a bash in a non-root mode, even if we are in a sudo
+    # context.
+
+    local user="${USER}"
+    local bash_cmd="bash"
+
+    if [ -n "${SUDO_USER}" ] && [ "root" != "${SUDO_USER}" ] ; then
+        user="${SUDO_USER}"
+        bash_cmd="sudo -H -u ${SUDO_USER} bash"
+    fi
+
+    printf "%s" "${bash_cmd}"
+}

+ 42 - 0
utils/templates/lib/systemd/system/searxng-redis.service

@@ -0,0 +1,42 @@
+[Unit]
+
+Description=SearXNG redis service
+After=syslog.target
+After=network.target
+Documentation=https://redis.io/documentation
+
+[Service]
+
+Type=simple
+User=${REDIS_USER}
+Group=${REDIS_USER}
+WorkingDirectory=${REDIS_HOME}
+Restart=always
+TimeoutStopSec=0
+
+Environment=USER=${REDIS_USER} HOME=${REDIS_HOME}
+ExecStart=${REDIS_HOME_BIN}/redis-server ${REDIS_CONF}
+ExecPaths=${REDIS_HOME_BIN}
+
+LimitNOFILE=65535
+NoNewPrivileges=true
+PrivateDevices=yes
+
+# ProtectSystem=full
+ProtectHome=yes
+ReadOnlyDirectories=/
+ReadWritePaths=-${REDIS_HOME}/run
+
+UMask=007
+PrivateTmp=yes
+
+MemoryDenyWriteExecute=true
+ProtectKernelModules=true
+ProtectKernelTunables=true
+ProtectControlGroups=true
+RestrictRealtime=true
+RestrictNamespaces=true
+
+[Install]
+
+WantedBy=multi-user.target