install-dependencies.sh 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. #!/usr/bin/env bash
  2. set -Eeuo pipefail
  3. export PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
  4. export HOME=${HOME:-/root}
  5. export GOPATH=${GOPATH:-/var/cache/debtik/go}
  6. export GOCACHE=${GOCACHE:-/var/cache/debtik/go-build}
  7. src=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)
  8. mode=${1:-install}
  9. [[ $# -le 1 && ( $mode == install || $mode == --check ) ]] || { echo 'Usage: install-dependencies.sh [--check]' >&2; exit 2; }
  10. packages=(
  11. bash coreutils grep gawk sed findutils util-linux systemd procps kmod
  12. ca-certificates curl git openssh-client tar python3 openssl tzdata
  13. iproute2 bridge-utils iptables ipset conntrack wireguard-tools
  14. keepalived bird2 dnsmasq isc-dhcp-client
  15. ppp pppoe pptp-linux pptpd xl2tpd
  16. charon-systemd strongswan-swanctl libstrongswan-standard-plugins
  17. libcharon-extra-plugins libcharon-extauth-plugins
  18. iputils-ping iputils-tracepath traceroute bind9-dnsutils mtr-tiny
  19. )
  20. installed() { [[ $(dpkg-query -W -f='${db:Status-Status}' "$1" 2>/dev/null || :) == installed ]]; }
  21. required=$(awk '$1=="toolchain" {sub(/^go/,"",$2); print $2}' "$src/go.mod")
  22. if [[ -z $required ]]; then required=$(awk '$1=="go" {print $2}' "$src/go.mod"); fi
  23. [[ $required =~ ^[0-9]+\.[0-9]+(\.[0-9]+)?$ ]] || { echo 'Invalid Go version in go.mod' >&2; exit 1; }
  24. go_version_ok() {
  25. local actual
  26. actual=$(GOTOOLCHAIN=local "$1" version 2>/dev/null | awk '{sub(/^go/,"",$3);print $3}') || return 1
  27. [[ -n $actual && $(printf '%s\n%s\n' "$required" "$actual" | sort -V | head -n1) == "$required" ]]
  28. }
  29. check_dependencies() {
  30. local missing=0 package binary go_cmd
  31. for package in "${packages[@]}"; do
  32. if ! installed "$package"; then echo "Missing package: $package" >&2; missing=1; fi
  33. done
  34. if ! installed ifupdown && ! installed ifupdown2; then echo 'Missing ifupdown or ifupdown2' >&2; missing=1; fi
  35. 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
  36. if ! command -v "$binary" >/dev/null; then echo "Missing command: $binary" >&2; missing=1; fi
  37. done
  38. go_cmd=$(command -v go || :)
  39. if [[ -x /opt/debtik/go/bin/go ]]; then go_cmd=/opt/debtik/go/bin/go; fi
  40. if [[ -z $go_cmd ]] || ! go_version_ok "$go_cmd"; then echo "Missing Go >= $required" >&2; missing=1; fi
  41. if (( missing )); then return 1; fi
  42. echo "All DebTik dependencies available (Go >= $required, including RSTP)."
  43. }
  44. if [[ $mode == --check ]]; then check_dependencies; exit; fi
  45. [[ $EUID -eq 0 ]] || { echo 'Run as root' >&2; exit 1; }
  46. command -v apt-get >/dev/null || { echo 'Debian apt-get required' >&2; exit 1; }
  47. install -d -m 0700 /var/lib/debtik/update
  48. # Reuse the installer's inherited lock, or acquire it for a standalone repair.
  49. if [[ $(readlink /proc/self/fd/9 2>/dev/null || :) != /var/lib/debtik/update/lock ]]; then exec 9>/var/lib/debtik/update/lock; fi
  50. flock -n 9 || { echo 'An update or installation is already running' >&2; exit 1; }
  51. policy_created=false
  52. work=''
  53. cleanup() {
  54. if [[ -n $work ]]; then rm -rf "$work"; fi
  55. if [[ $policy_created == true ]]; then rm -f /usr/sbin/policy-rc.d; fi
  56. }
  57. trap cleanup EXIT
  58. if [[ ! -e /usr/sbin/policy-rc.d && ! -L /usr/sbin/policy-rc.d ]]; then
  59. printf '#!/bin/sh\nexit 101\n' > /usr/sbin/policy-rc.d
  60. chmod 0755 /usr/sbin/policy-rc.d
  61. policy_created=true
  62. fi
  63. export DEBIAN_FRONTEND=noninteractive NEEDRESTART_MODE=l
  64. apt-get update
  65. # Proxmox normally uses ifupdown2; retain that implementation.
  66. if ! installed ifupdown && ! installed ifupdown2; then packages+=(ifupdown); fi
  67. apt-get install -y --no-install-recommends "${packages[@]}"
  68. work=$(mktemp -d)
  69. if ! command -v mstpd >/dev/null || ! command -v mstpctl >/dev/null; then
  70. candidate=$(apt-cache policy mstpd | awk '/Candidate:/ {print $2}')
  71. if [[ -n $candidate && $candidate != '(none)' ]]; then
  72. apt-get install -y --no-install-recommends mstpd
  73. else
  74. apt-get install -y --no-install-recommends gcc make libc6-dev autoconf automake libtool pkg-config
  75. # Upstream release 0.2.0, pinned to a commit and verified archive digest.
  76. mstpd_commit=8995e5d11ee89f2251734e34cea711bb5a5bb911
  77. curl --fail --location --retry 3 "https://codeload.github.com/mstpd/mstpd/tar.gz/$mstpd_commit" -o "$work/mstpd.tar.gz"
  78. printf '%s %s\n' f965e715c82640484f89fcf11cd85dbd86c325dcf42ef85bd15173d9a9aa45b3 "$work/mstpd.tar.gz" | sha256sum -c -
  79. mkdir "$work/mstpd"
  80. tar --strip-components=1 -C "$work/mstpd" -xzf "$work/mstpd.tar.gz"
  81. (
  82. cd "$work/mstpd"
  83. # The archive has no .git metadata; supply its verified revision label.
  84. sed -i 's/^PACKAGE_BUILD=.*/PACKAGE_BUILD=8995e5d/' configure.ac
  85. ./autogen.sh
  86. ./configure --prefix=/usr/local --exec-prefix=/usr/local --sysconfdir=/etc --localstatedir=/var
  87. make -j2 mstpd mstpctl
  88. )
  89. install -m 0755 "$work/mstpd/mstpd" /usr/local/sbin/mstpd.next
  90. install -m 0755 "$work/mstpd/mstpctl" /usr/local/sbin/mstpctl.next
  91. mv -f /usr/local/sbin/mstpd.next /usr/local/sbin/mstpd
  92. mv -f /usr/local/sbin/mstpctl.next /usr/local/sbin/mstpctl
  93. install -d -m 0755 /usr/local/share/doc/debtik-mstpd
  94. install -m 0644 "$work/mstpd/LICENSE" /usr/local/share/doc/debtik-mstpd/LICENSE
  95. printf '%s\n' "$mstpd_commit" > /usr/local/share/doc/debtik-mstpd/commit
  96. fi
  97. fi
  98. # Start only the daemon. DebTik explicitly attaches its configured RSTP bridges;
  99. # upstream global bridge-stp/ifupdown hooks are not installed by the source build.
  100. if ! systemctl cat mstpd.service >/dev/null 2>&1; then
  101. mstpd_binary=$(command -v mstpd)
  102. cat > /etc/systemd/system/mstpd.service <<UNIT
  103. [Unit]
  104. Description=Spanning Tree Protocol daemon for DebTik
  105. Before=proxmox-router.service
  106. [Service]
  107. Type=simple
  108. ExecStart=$mstpd_binary -d
  109. Restart=on-failure
  110. RestartSec=2
  111. [Install]
  112. WantedBy=multi-user.target
  113. UNIT
  114. fi
  115. install -d -m 0755 /etc/systemd/system/proxmox-router.service.d
  116. cat > /etc/systemd/system/proxmox-router.service.d/mstpd.conf <<'UNIT'
  117. [Unit]
  118. Wants=mstpd.service
  119. After=mstpd.service
  120. UNIT
  121. systemctl daemon-reload
  122. systemctl enable --now mstpd.service
  123. systemctl is-active --quiet mstpd.service
  124. go_cmd=$(command -v go || :)
  125. if [[ -x /opt/debtik/go/bin/go ]]; then go_cmd=/opt/debtik/go/bin/go; fi
  126. if [[ -z $go_cmd ]] || ! go_version_ok "$go_cmd"; then
  127. case $(uname -m) in x86_64) arch=amd64;; aarch64) arch=arm64;; *) echo 'Unsupported CPU' >&2; exit 1;; esac
  128. archive="go${required}.linux-${arch}.tar.gz"
  129. curl --fail --location --retry 3 "https://go.dev/dl/$archive" -o "$work/$archive"
  130. curl --fail --location --retry 3 'https://go.dev/dl/?mode=json&include=all' -o "$work/go-releases.json"
  131. digest=$(python3 - "$work/go-releases.json" "$archive" <<'PYTHON'
  132. import json, sys
  133. with open(sys.argv[1]) as f:
  134. releases = json.load(f)
  135. for release in releases:
  136. for item in release['files']:
  137. if item['filename'] == sys.argv[2]:
  138. print(item['sha256'])
  139. raise SystemExit(0)
  140. raise SystemExit('Go archive is absent from the official release manifest')
  141. PYTHON
  142. )
  143. [[ $digest =~ ^[a-f0-9]{64}$ ]] || { echo 'Invalid Go archive checksum' >&2; exit 1; }
  144. printf '%s %s\n' "$digest" "$work/$archive" | sha256sum -c -
  145. mkdir "$work/go-stage"
  146. tar -C "$work/go-stage" -xzf "$work/$archive"
  147. install -d -m 0755 /opt/debtik
  148. rm -rf /opt/debtik/go.previous
  149. if [[ -d /opt/debtik/go ]]; then mv /opt/debtik/go /opt/debtik/go.previous; fi
  150. if ! mv "$work/go-stage/go" /opt/debtik/go; then
  151. if [[ -d /opt/debtik/go.previous ]]; then mv /opt/debtik/go.previous /opt/debtik/go; fi
  152. exit 1
  153. fi
  154. fi
  155. check_dependencies