| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156 |
- #!/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 kmod
- 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 modprobe; 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
|