|
@@ -0,0 +1,156 @@
|
|
|
|
|
+#!/usr/bin/env bash
|
|
|
|
|
+set -Eeuo pipefail
|
|
|
|
|
+export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
|
|
|
|
|
+export HOME=${HOME:-/root}
|
|
|
|
|
+export GOPATH=${GOPATH:-/var/cache/debtik/go}
|
|
|
|
|
+export GOCACHE=${GOCACHE:-/var/cache/debtik/go-build}
|
|
|
|
|
+src=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
|
|
|
|
|
+mode=${1:-install}
|
|
|
|
|
+[[ $# -le 1 && ( $mode == install || $mode == --check ) ]] || { echo 'Usage: install-dependencies.sh [--check]' >&2; exit 2; }
|
|
|
|
|
+packages=(
|
|
|
|
|
+ bash coreutils grep gawk sed findutils util-linux systemd procps
|
|
|
|
|
+ ca-certificates curl git openssh-client tar python3 openssl tzdata
|
|
|
|
|
+ iproute2 bridge-utils iptables ipset conntrack wireguard-tools
|
|
|
|
|
+ keepalived bird2 dnsmasq isc-dhcp-client
|
|
|
|
|
+ ppp pppoe pptp-linux pptpd xl2tpd
|
|
|
|
|
+ charon-systemd strongswan-swanctl libstrongswan-standard-plugins
|
|
|
|
|
+ libcharon-extra-plugins libcharon-extauth-plugins
|
|
|
|
|
+ iputils-ping iputils-tracepath traceroute bind9-dnsutils mtr-tiny
|
|
|
|
|
+)
|
|
|
|
|
+installed() { [[ $(dpkg-query -W -f='${db:Status-Status}' "$1" 2>/dev/null || :) == installed ]]; }
|
|
|
|
|
+required=$(awk '$1=="toolchain" {sub(/^go/,"",$2); print $2}' "$src/go.mod")
|
|
|
|
|
+if [[ -z $required ]]; then required=$(awk '$1=="go" {print $2}' "$src/go.mod"); fi
|
|
|
|
|
+[[ $required =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]] || { echo 'Invalid Go version in go.mod' >&2; exit 1; }
|
|
|
|
|
+go_version_ok() {
|
|
|
|
|
+ local actual
|
|
|
|
|
+ actual=$(GOTOOLCHAIN=local "$1" version 2>/dev/null | awk '{sub(/^go/,"",$3);print $3}') || return 1
|
|
|
|
|
+ [[ -n $actual && $(printf '%s\n%s\n' "$required" "$actual" | sort -V | head -n1) == "$required" ]]
|
|
|
|
|
+}
|
|
|
|
|
+check_dependencies() {
|
|
|
|
|
+ local missing=0 package binary go_cmd
|
|
|
|
|
+ for package in "${packages[@]}"; do
|
|
|
|
|
+ if ! installed "$package"; then echo "Missing package: $package" >&2; missing=1; fi
|
|
|
|
|
+ done
|
|
|
|
|
+ if ! installed ifupdown && ! installed ifupdown2; then echo 'Missing ifupdown or ifupdown2' >&2; missing=1; fi
|
|
|
|
|
+ for binary in ssh ip bridge brctl iptables ip6tables iptables-save ip6tables-save iptables-restore ip6tables-restore ipset conntrack wg wg-quick keepalived bird birdc dnsmasq dhclient pppd pon poff pppoe-server pptp pptpd xl2tpd swanctl ping tracepath traceroute dig mtr mstpd mstpctl systemctl systemd-run hostnamectl timedatectl pkill flock python3; do
|
|
|
|
|
+ if ! command -v "$binary" >/dev/null; then echo "Missing command: $binary" >&2; missing=1; fi
|
|
|
|
|
+ done
|
|
|
|
|
+ go_cmd=$(command -v go || :)
|
|
|
|
|
+ if [[ -x /opt/debtik/go/bin/go ]]; then go_cmd=/opt/debtik/go/bin/go; fi
|
|
|
|
|
+ if [[ -z $go_cmd ]] || ! go_version_ok "$go_cmd"; then echo "Missing Go >= $required" >&2; missing=1; fi
|
|
|
|
|
+ if (( missing )); then return 1; fi
|
|
|
|
|
+ echo "All DebTik dependencies available (Go >= $required, including RSTP)."
|
|
|
|
|
+}
|
|
|
|
|
+if [[ $mode == --check ]]; then check_dependencies; exit; fi
|
|
|
|
|
+[[ $EUID -eq 0 ]] || { echo 'Run as root' >&2; exit 1; }
|
|
|
|
|
+command -v apt-get >/dev/null || { echo 'Debian apt-get required' >&2; exit 1; }
|
|
|
|
|
+install -d -m 0700 /var/lib/debtik/update
|
|
|
|
|
+# Reuse the installer's inherited lock, or acquire it for a standalone repair.
|
|
|
|
|
+if [[ $(readlink /proc/self/fd/9 2>/dev/null || :) != /var/lib/debtik/update/lock ]]; then exec 9>/var/lib/debtik/update/lock; fi
|
|
|
|
|
+flock -n 9 || { echo 'An update or installation is already running' >&2; exit 1; }
|
|
|
|
|
+policy_created=false
|
|
|
|
|
+work=''
|
|
|
|
|
+cleanup() {
|
|
|
|
|
+ if [[ -n $work ]]; then rm -rf "$work"; fi
|
|
|
|
|
+ if [[ $policy_created == true ]]; then rm -f /usr/sbin/policy-rc.d; fi
|
|
|
|
|
+}
|
|
|
|
|
+trap cleanup EXIT
|
|
|
|
|
+if [[ ! -e /usr/sbin/policy-rc.d && ! -L /usr/sbin/policy-rc.d ]]; then
|
|
|
|
|
+ printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d
|
|
|
|
|
+ chmod 0755 /usr/sbin/policy-rc.d
|
|
|
|
|
+ policy_created=true
|
|
|
|
|
+fi
|
|
|
|
|
+export DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=l
|
|
|
|
|
+apt-get update
|
|
|
|
|
+# Proxmox normally uses ifupdown2; retain that implementation.
|
|
|
|
|
+if ! installed ifupdown && ! installed ifupdown2; then packages+=(ifupdown); fi
|
|
|
|
|
+apt-get install -y --no-install-recommends "${packages[@]}"
|
|
|
|
|
+work=$(mktemp -d)
|
|
|
|
|
+if ! command -v mstpd >/dev/null || ! command -v mstpctl >/dev/null; then
|
|
|
|
|
+ candidate=$(apt-cache policy mstpd | awk '/Candidate:/ {print $2}')
|
|
|
|
|
+ if [[ -n $candidate && $candidate != '(none)' ]]; then
|
|
|
|
|
+ apt-get install -y --no-install-recommends mstpd
|
|
|
|
|
+ else
|
|
|
|
|
+ apt-get install -y --no-install-recommends gcc make libc6-dev autoconf automake libtool pkg-config
|
|
|
|
|
+ # Upstream release 0.2.0, pinned to a commit and verified archive digest.
|
|
|
|
|
+ mstpd_commit=8995e5d11ee89f2251734e34cea711bb5a5bb911
|
|
|
|
|
+ curl --fail --location --retry 3 "https://codeload.github.com/mstpd/mstpd/tar.gz/$mstpd_commit" -o "$work/mstpd.tar.gz"
|
|
|
|
|
+ printf '%s %s\n' f965e715c82640484f89fcf11cd85dbd86c325dcf42ef85bd15173d9a9aa45b3 "$work/mstpd.tar.gz" | sha256sum -c -
|
|
|
|
|
+ mkdir "$work/mstpd"
|
|
|
|
|
+ tar --strip-components=1 -C "$work/mstpd" -xzf "$work/mstpd.tar.gz"
|
|
|
|
|
+ (
|
|
|
|
|
+ cd "$work/mstpd"
|
|
|
|
|
+ # The archive has no .git metadata; supply its verified revision label.
|
|
|
|
|
+ sed -i 's/^PACKAGE_BUILD=.*/PACKAGE_BUILD=8995e5d/' configure.ac
|
|
|
|
|
+ ./autogen.sh
|
|
|
|
|
+ ./configure --prefix=/usr/local --exec-prefix=/usr/local --sysconfdir=/etc --localstatedir=/var
|
|
|
|
|
+ make -j2 mstpd mstpctl
|
|
|
|
|
+ )
|
|
|
|
|
+ install -m 0755 "$work/mstpd/mstpd" /usr/local/sbin/mstpd.next
|
|
|
|
|
+ install -m 0755 "$work/mstpd/mstpctl" /usr/local/sbin/mstpctl.next
|
|
|
|
|
+ mv -f /usr/local/sbin/mstpd.next /usr/local/sbin/mstpd
|
|
|
|
|
+ mv -f /usr/local/sbin/mstpctl.next /usr/local/sbin/mstpctl
|
|
|
|
|
+ install -d -m 0755 /usr/local/share/doc/debtik-mstpd
|
|
|
|
|
+ install -m 0644 "$work/mstpd/LICENSE" /usr/local/share/doc/debtik-mstpd/LICENSE
|
|
|
|
|
+ printf '%s\n' "$mstpd_commit" > /usr/local/share/doc/debtik-mstpd/commit
|
|
|
|
|
+ fi
|
|
|
|
|
+fi
|
|
|
|
|
+# Start only the daemon. DebTik explicitly attaches its configured RSTP bridges;
|
|
|
|
|
+# upstream global bridge-stp/ifupdown hooks are not installed by the source build.
|
|
|
|
|
+if ! systemctl cat mstpd.service >/dev/null 2>&1; then
|
|
|
|
|
+ mstpd_binary=$(command -v mstpd)
|
|
|
|
|
+ cat > /etc/systemd/system/mstpd.service <<UNIT
|
|
|
|
|
+[Unit]
|
|
|
|
|
+Description=Spanning Tree Protocol daemon for DebTik
|
|
|
|
|
+Before=proxmox-router.service
|
|
|
|
|
+[Service]
|
|
|
|
|
+Type=simple
|
|
|
|
|
+ExecStart=$mstpd_binary -d
|
|
|
|
|
+Restart=on-failure
|
|
|
|
|
+RestartSec=2
|
|
|
|
|
+[Install]
|
|
|
|
|
+WantedBy=multi-user.target
|
|
|
|
|
+UNIT
|
|
|
|
|
+fi
|
|
|
|
|
+install -d -m 0755 /etc/systemd/system/proxmox-router.service.d
|
|
|
|
|
+cat > /etc/systemd/system/proxmox-router.service.d/mstpd.conf <<'UNIT'
|
|
|
|
|
+[Unit]
|
|
|
|
|
+Wants=mstpd.service
|
|
|
|
|
+After=mstpd.service
|
|
|
|
|
+UNIT
|
|
|
|
|
+systemctl daemon-reload
|
|
|
|
|
+systemctl enable --now mstpd.service
|
|
|
|
|
+systemctl is-active --quiet mstpd.service
|
|
|
|
|
+
|
|
|
|
|
+go_cmd=$(command -v go || :)
|
|
|
|
|
+if [[ -x /opt/debtik/go/bin/go ]]; then go_cmd=/opt/debtik/go/bin/go; fi
|
|
|
|
|
+if [[ -z $go_cmd ]] || ! go_version_ok "$go_cmd"; then
|
|
|
|
|
+ case $(uname -m) in x86_64) arch=amd64;; aarch64) arch=arm64;; *) echo 'Unsupported CPU' >&2; exit 1;; esac
|
|
|
|
|
+ archive="go${required}.linux-${arch}.tar.gz"
|
|
|
|
|
+ curl --fail --location --retry 3 "https://go.dev/dl/$archive" -o "$work/$archive"
|
|
|
|
|
+ curl --fail --location --retry 3 'https://go.dev/dl/?mode=json&include=all' -o "$work/go-releases.json"
|
|
|
|
|
+ digest=$(python3 - "$work/go-releases.json" "$archive" <<'PYTHON'
|
|
|
|
|
+import json, sys
|
|
|
|
|
+with open(sys.argv[1]) as f:
|
|
|
|
|
+ releases = json.load(f)
|
|
|
|
|
+for release in releases:
|
|
|
|
|
+ for item in release['files']:
|
|
|
|
|
+ if item['filename'] == sys.argv[2]:
|
|
|
|
|
+ print(item['sha256'])
|
|
|
|
|
+ raise SystemExit(0)
|
|
|
|
|
+raise SystemExit('Go archive is absent from the official release manifest')
|
|
|
|
|
+PYTHON
|
|
|
|
|
+ )
|
|
|
|
|
+ [[ $digest =~ ^[a-f0-9]{64}$ ]] || { echo 'Invalid Go archive checksum' >&2; exit 1; }
|
|
|
|
|
+ printf '%s %s\n' "$digest" "$work/$archive" | sha256sum -c -
|
|
|
|
|
+ mkdir "$work/go-stage"
|
|
|
|
|
+ tar -C "$work/go-stage" -xzf "$work/$archive"
|
|
|
|
|
+ install -d -m 0755 /opt/debtik
|
|
|
|
|
+ rm -rf /opt/debtik/go.previous
|
|
|
|
|
+ if [[ -d /opt/debtik/go ]]; then mv /opt/debtik/go /opt/debtik/go.previous; fi
|
|
|
|
|
+ if ! mv "$work/go-stage/go" /opt/debtik/go; then
|
|
|
|
|
+ if [[ -d /opt/debtik/go.previous ]]; then mv /opt/debtik/go.previous /opt/debtik/go; fi
|
|
|
|
|
+ exit 1
|
|
|
|
|
+ fi
|
|
|
|
|
+fi
|
|
|
|
|
+check_dependencies
|