#!/bin/bash # ############################################################################### # Copyright (C) 2020 Simon Adlem, G7RZU # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software Foundation, # Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ############################################################################### set -e BASE_URL='https://gitlab.hacknix.net/hacknix/FreeDMR/-/raw/master' prompt_value() { prompt_text="$1" default_value="$2" if [ ! -r /dev/tty ] then echo "Interactive input is required unless installer values are supplied as environment variables." >&2 exit 2 fi if [ -n "$default_value" ] then printf '%s [%s]: ' "$prompt_text" "$default_value" >/dev/tty else printf '%s: ' "$prompt_text" >/dev/tty fi read -r entered_value &2 exit 2 fi } build_data_gateway_relationships() { count="$1" base_id="$2" key="$3" relationships='[' number=1 while [ "$number" -le "$count" ] do if [ "$count" -eq 1 ] then server_id="$base_id" else server_id=$((base_id + number)) fi bind_port=$((62030 + number)) peer_port=$((62040 + number)) if [ "$number" -gt 1 ] then relationships="${relationships}," fi relationships="${relationships}{\"link_id\":\"hdstack-${number}\",\"bind_host\":\"0.0.0.0\",\"bind_port\":${bind_port},\"peer_host\":\"freedmr\",\"peer_port\":${peer_port},\"peer_network_id\":${server_id},\"direct_source_server_id\":${server_id},\"key\":\"${key}\"}" number=$((number + 1)) done printf '%s]' "$relationships" } CPU_COUNT=$(getconf _NPROCESSORS_ONLN 2>/dev/null) || CPU_COUNT=2 if ! is_unsigned_integer "$CPU_COUNT" || [ "$CPU_COUNT" -lt 1 ] then CPU_COUNT=2 fi DEFAULT_HDSTACK=$((CPU_COUNT - 1)) if [ "$DEFAULT_HDSTACK" -lt 1 ] then DEFAULT_HDSTACK=1 elif [ "$DEFAULT_HDSTACK" -gt 5 ] then DEFAULT_HDSTACK=5 fi echo 'FreeDMR Docker installer' echo echo 'Assigned Server ID, or your own DMR ID if you do not have one.' HDSTACK_BASEID="${HDSTACK_BASEID:-}" if [ -z "$HDSTACK_BASEID" ] then HDSTACK_BASEID=$(prompt_value 'Main/aggregator server ID' '') fi validate_server_id 'Main/aggregator server ID' "$HDSTACK_BASEID" echo echo 'Enabling HDStack allows FreeDMR to use more than one CPU core and support more than 100 HBP connections.' HDSTACK_COUNT="${HDSTACK:-}" if [ -z "$HDSTACK_COUNT" ] then HDSTACK_COUNT=$(prompt_value 'Number of HDStack workers (1-5)' "$DEFAULT_HDSTACK") fi if ! is_unsigned_integer "$HDSTACK_COUNT" || [ "$HDSTACK_COUNT" -lt 1 ] || [ "$HDSTACK_COUNT" -gt 5 ] then echo 'HDSTACK must be an integer from 1 to 5.' >&2 exit 2 fi if [ $((HDSTACK_BASEID + HDSTACK_COUNT)) -gt 4294967295 ] then echo 'Main/aggregator ID plus worker IDs exceed the supported ID range.' >&2 exit 2 fi HDSTACK_BRIDGE_VALUE="${HDSTACK_BRIDGE:-}" if [ -z "$HDSTACK_BRIDGE_VALUE" ] then HDSTACK_BRIDGE_VALUE=$(prompt_value 'Enable the separate bridge server? (y/N)' 'N') fi case "$HDSTACK_BRIDGE_VALUE" in 1|y|Y|yes|YES) HDSTACK_BRIDGE_VALUE=1 ;; 0|n|N|no|NO) HDSTACK_BRIDGE_VALUE=0 ;; *) echo 'HDSTACK_BRIDGE must be 0 or 1 (or yes/no).' >&2 exit 2 ;; esac HDSTACK_BRIDGE_ID="${HDSTACK_BRIDGE_ID:-0}" if [ "$HDSTACK_BRIDGE_VALUE" -eq 1 ] then if [ "$HDSTACK_COUNT" -eq 1 ] then echo 'The separate bridge process requires HDSTACK to be 2..5.' >&2 exit 2 fi if [ "$HDSTACK_BRIDGE_ID" -eq 0 ] 2>/dev/null then echo echo 'The bridge server requires its own assigned server ID.' HDSTACK_BRIDGE_ID=$(prompt_value 'Bridge server ID' '') fi validate_server_id 'Bridge server ID' "$HDSTACK_BRIDGE_ID" if [ "$HDSTACK_BRIDGE_ID" -ge "$HDSTACK_BASEID" ] && [ "$HDSTACK_BRIDGE_ID" -le $((HDSTACK_BASEID + HDSTACK_COUNT)) ] then echo 'Bridge server ID must not duplicate the aggregator or a worker ID.' >&2 exit 2 fi fi echo echo 'The separate Data Gateway provides D-APRS and future packet-data services.' FREEDMR_GATEWAY_APRS_LOGIN_CALL="${FREEDMR_GATEWAY_APRS_LOGIN_CALL:-}" if [ -z "$FREEDMR_GATEWAY_APRS_LOGIN_CALL" ] then FREEDMR_GATEWAY_APRS_LOGIN_CALL=$(prompt_value 'APRS-IS login callsign' '') fi case "$FREEDMR_GATEWAY_APRS_LOGIN_CALL" in ''|*[!A-Za-z0-9-]*) echo 'APRS-IS login callsign may contain only letters, digits and a hyphen.' >&2 exit 2 ;; esac if [ "${#FREEDMR_GATEWAY_APRS_LOGIN_CALL}" -gt 9 ] then echo 'APRS-IS login callsign must not exceed 9 characters.' >&2 exit 2 fi FREEDMR_GATEWAY_APRS_PASSCODE="${FREEDMR_GATEWAY_APRS_PASSCODE:-}" if [ -z "$FREEDMR_GATEWAY_APRS_PASSCODE" ] then FREEDMR_GATEWAY_APRS_PASSCODE=$(prompt_value 'APRS-IS passcode' '') fi if ! is_unsigned_integer "$FREEDMR_GATEWAY_APRS_PASSCODE" then echo 'APRS-IS passcode must be numeric.' >&2 exit 2 fi FREEDMR_GATEWAY_FBP_KEY="${FREEDMR_GATEWAY_FBP_KEY:-}" if [ -z "$FREEDMR_GATEWAY_FBP_KEY" ] then FREEDMR_GATEWAY_FBP_KEY=$(od -An -N10 -tx1 /dev/urandom | tr -d ' \n') fi case "$FREEDMR_GATEWAY_FBP_KEY" in ''|*[!A-Za-z0-9._-]*) echo 'Data Gateway FBP key may contain only letters, digits, dot, underscore and hyphen.' >&2 exit 2 ;; esac if [ "${#FREEDMR_GATEWAY_FBP_KEY}" -gt 20 ] then echo 'Data Gateway FBP key must not exceed 20 characters.' >&2 exit 2 fi FREEDMR_GATEWAY_FBP_RELATIONSHIPS=$(build_data_gateway_relationships \ "$HDSTACK_COUNT" "$HDSTACK_BASEID" "$FREEDMR_GATEWAY_FBP_KEY") echo echo 'Installing required packages and Docker Community Edition...' for package in docker.io docker-compose docker-doc docker-buildx podman-docker containerd runc do if dpkg-query -W -f='${Status}' "$package" 2>/dev/null | grep -q 'ok installed' then apt-get -y remove "$package" fi done apt-get -y update apt-get -y install ca-certificates curl install -m 0755 -d /etc/apt/keyrings curl -fsSL https://download.docker.com/linux/debian/gpg \ -o /etc/apt/keyrings/docker.asc chmod a+r /etc/apt/keyrings/docker.asc cat < /etc/apt/sources.list.d/docker.sources Types: deb URIs: https://download.docker.com/linux/debian Suites: $(. /etc/os-release && echo "$VERSION_CODENAME") Components: stable Architectures: $(dpkg --print-architecture) Signed-By: /etc/apt/keyrings/docker.asc EOF apt-get -y update apt-get -y install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin echo 'Configuring Docker...' cat < /etc/docker/daemon.json { "userland-proxy": false, "experimental": true, "log-driver": "json-file", "log-opts": { "max-size": "10m", "max-file": "3" } } EOF systemctl restart docker echo 'Installing FreeDMR configuration...' mkdir -p /etc/freedmr/acme.sh /etc/freedmr/certs /etc/freedmr/json chmod -R 755 /etc/freedmr chown 54000:54000 /etc/freedmr/json curl -fsSL "$BASE_URL/FreeDMR.cfg" -o /etc/freedmr/freedmr.cfg sed -i "0,/^SERVER_ID:/s/^SERVER_ID:.*/SERVER_ID: $HDSTACK_BASEID/" /etc/freedmr/freedmr.cfg sed -i '/^\[GLOBAL\]/,/^\[/ s/^DATA_GATEWAY:.*/DATA_GATEWAY: True/' /etc/freedmr/freedmr.cfg sed -i '/^\[DATA-GATEWAY\]/,/^\[/ s/^ENABLED:.*/ENABLED: True/' /etc/freedmr/freedmr.cfg sed -i "/^\[DATA-GATEWAY\]/,/^\[/ s|^PASSPHRASE:.*|PASSPHRASE: $FREEDMR_GATEWAY_FBP_KEY|" /etc/freedmr/freedmr.cfg sed -i '/^\[DATA-GATEWAY\]/,/^\[/ s/^TARGET_IP:.*/TARGET_IP: freedmr-data-gateway/' /etc/freedmr/freedmr.cfg if [ ! -e /etc/freedmr/freedmr-bridge.cfg ] then curl -fsSL "$BASE_URL/docker-configs/freedmr-bridge.cfg" -o /etc/freedmr/freedmr-bridge.cfg fi if [ "$HDSTACK_BRIDGE_VALUE" -eq 1 ] then sed -i "0,/^SERVER_ID:/s/^SERVER_ID:.*/SERVER_ID: $HDSTACK_BRIDGE_ID/" /etc/freedmr/freedmr-bridge.cfg fi if [ ! -e /etc/freedmr/rules-bridge.py ] then curl -fsSL "$BASE_URL/docker-configs/rules-bridge.py" -o /etc/freedmr/rules-bridge.py fi curl -fsSL "$BASE_URL/docker-configs/docker-compose.yml" -o /etc/freedmr/docker-compose.yml sed -i "s|^[[:space:]]*#*- HDSTACK=.*| - HDSTACK=$HDSTACK_COUNT|" /etc/freedmr/docker-compose.yml sed -i "s|^[[:space:]]*#*- HDSTACK_BASEID=.*| - HDSTACK_BASEID=$HDSTACK_BASEID|" /etc/freedmr/docker-compose.yml sed -i "s|^[[:space:]]*- HDSTACK_BRIDGE=.*| - HDSTACK_BRIDGE=$HDSTACK_BRIDGE_VALUE|" /etc/freedmr/docker-compose.yml sed -i "s|__FREEDMR_GATEWAY_FBP_RELATIONSHIPS__|$FREEDMR_GATEWAY_FBP_RELATIONSHIPS|" /etc/freedmr/docker-compose.yml sed -i "s|__FREEDMR_GATEWAY_APRS_LOGIN_CALL__|$FREEDMR_GATEWAY_APRS_LOGIN_CALL|" /etc/freedmr/docker-compose.yml sed -i "s|__FREEDMR_GATEWAY_APRS_PASSCODE__|$FREEDMR_GATEWAY_APRS_PASSCODE|" /etc/freedmr/docker-compose.yml chown -R 54000:54000 /etc/freedmr if [ -e /etc/cron.daily/lastheard ] then chmod 755 /etc/cron.daily/lastheard fi echo 'Tuning network stack...' cat < /etc/sysctl.conf net.core.rmem_default=134217728 net.core.rmem_max=134217728 net.core.wmem_max=134217728 net.core.netdev_max_backlog=250000 net.netfilter.nf_conntrack_udp_timeout=15 net.netfilter.nf_conntrack_udp_timeout_stream=35 EOF /usr/sbin/sysctl -p echo 'Starting the FreeDMR container...' cd /etc/freedmr docker compose up -d echo 'Read /etc/freedmr/docker-compose.yml and docs/hdstack.md before adding optional services or custom links.' echo 'FreeDMR setup complete.'