#!/bin/bash
# Первый запуск готового моста: Debian 12+/Ubuntu 22.04+, systemd, amd64/arm64.
# Перед запуском изучите файл. Закрытых ключей выпуска здесь нет.
set -euo pipefail
umask 077
[[ $(id -u) == 0 ]] || { echo 'Запустите установщик с sudo.' >&2; exit 1; }
[[ -d /run/systemd/system ]] || { echo 'Нужна Linux-система с systemd.' >&2; exit 1; }
source /etc/os-release
[[ "$ID" == debian || "$ID" == ubuntu ]] || { echo 'Этот выпуск поддерживает Debian и Ubuntu.' >&2; exit 1; }
case "$(uname -m)" in
 x86_64) ARCH=amd64; EXPECTED='692cf5c2e5791aef7378f36337b1197d31fadfb63684e0a93ee50f7d68adcbb2' ;;
 aarch64|arm64) ARCH=arm64; EXPECTED='74a818201825d22a638e1693abcad9b1fbb67108134f1bfe3a80e7189c081b77' ;;
 *) echo 'Нужен 64-разрядный мост amd64 или arm64.' >&2; exit 1 ;;
esac
ORIGIN='https://control-dev.doctorstroy.ru'
VERSION='1.1.0'
if [[ -e /etc/ds-bridge/release.json || -e /opt/ds-bridge/current ]]; then
 echo 'Мост уже установлен. Обновляйте его через ds-bridge-update.service, сохраняя ключи и привязку.' >&2; exit 1
fi
apt-get update -qq
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq ca-certificates curl ffmpeg avahi-daemon
id ds-bridge >/dev/null 2>&1 || useradd --system --home-dir /var/lib/ds-bridge --shell /usr/sbin/nologin ds-bridge
install -d -m 0755 /opt/ds-bridge /etc/ds-bridge
install -d -m 0700 -o ds-bridge -g ds-bridge /var/lib/ds-bridge
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
curl --proto '=https' --tlsv1.2 --fail --silent --show-error --max-time 120 "$ORIGIN/releases/bridge/$VERSION/ds-bridge-maintain-linux-$ARCH" -o "$WORK/maintain"
printf '%s  %s\n' "$EXPECTED" "$WORK/maintain" | sha256sum --check --status
chmod 0700 "$WORK/maintain"
cat > /etc/ds-bridge/release.json <<JSON
{"origin":"$ORIGIN","manifest_url":"$ORIGIN/releases/bridge/$VERSION/release-linux-$ARCH.json","public_key":"zuUC8lz221MH3QlDCHzm03N3Py6CovGIgQdk8cg1b50","root":"/opt/ds-bridge"}
JSON
cat > /etc/ds-bridge/agent.env <<'ENV'
DS_BRIDGE_API=https://control-dev.doctorstroy.ru/api
DS_BRIDGE_SERVER_PUBKEY=OkE-ExVPm1dvC2rpPLO3aOAUWMgkNkjSqfZiYTkX3uM
DS_BRIDGE_RELEASE_PUBKEY=zuUC8lz221MH3QlDCHzm03N3Py6CovGIgQdk8cg1b50
DS_BRIDGE_STATE_DIR=/var/lib/ds-bridge
DS_BRIDGE_GO2RTC_CONFIG=/var/lib/ds-bridge/go2rtc.yaml
DS_BRIDGE_HEALTH_ADDR=127.0.0.1:1985
ENV
# Версия встроена в подписанный исполняемый файл. При обновлении конфигурация и привязка сохраняются.
cat > /var/lib/ds-bridge/go2rtc.yaml <<'YAML'
log:
  level: fatal
api:
  listen: 127.0.0.1:1984
rtsp:
  listen: 127.0.0.1:8554
webrtc:
  listen: ''
streams: {}
YAML
chown ds-bridge:ds-bridge /var/lib/ds-bridge/go2rtc.yaml
chmod 0600 /etc/ds-bridge/agent.env /var/lib/ds-bridge/go2rtc.yaml
cat > /etc/systemd/system/ds-bridge-agent.service <<'UNIT'
[Unit]
Description=DoctorStroy camera bridge
After=network-online.target ds-go2rtc.service
Wants=network-online.target
Requires=ds-go2rtc.service
[Service]
User=ds-bridge
Group=ds-bridge
EnvironmentFile=/etc/ds-bridge/agent.env
ExecStart=/opt/ds-bridge/current/bridge-agent
Restart=on-failure
RestartSec=5
KillMode=control-group
TimeoutStopSec=15
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectProc=invisible
ReadWritePaths=/var/lib/ds-bridge
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
AmbientCapabilities=CAP_NET_BIND_SERVICE
CapabilityBoundingSet=CAP_NET_BIND_SERVICE
UMask=0077
[Install]
WantedBy=multi-user.target
UNIT
cat > /etc/systemd/system/ds-go2rtc.service <<'UNIT'
[Unit]
Description=DoctorStroy local camera media service
After=network-online.target
Wants=network-online.target
[Service]
User=ds-bridge
Group=ds-bridge
ExecStart=/opt/ds-bridge/current/go2rtc -config /var/lib/ds-bridge/go2rtc.yaml
Restart=on-failure
RestartSec=3
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
ProtectProc=invisible
ReadWritePaths=/var/lib/ds-bridge
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX AF_NETLINK
CapabilityBoundingSet=
UMask=0077
[Install]
WantedBy=multi-user.target
UNIT
cat > /etc/systemd/system/ds-bridge-update.service <<'UNIT'
[Unit]
Description=Verify and update DoctorStroy bridge with automatic rollback
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/opt/ds-bridge/current/ds-bridge-maintain apply --config /etc/ds-bridge/release.json
TimeoutStartSec=40min
NoNewPrivileges=true
PrivateTmp=true
ProtectSystem=strict
ProtectHome=true
ProtectKernelTunables=true
ProtectKernelModules=true
ProtectControlGroups=true
RestrictAddressFamilies=AF_INET AF_INET6 AF_UNIX
ReadWritePaths=/opt/ds-bridge
CapabilityBoundingSet=
UMask=0077
UNIT
cat > /etc/systemd/system/ds-bridge-update.timer <<'UNIT'
[Unit]
Description=Check signed DoctorStroy bridge releases
[Timer]
OnBootSec=10min
OnUnitActiveSec=1h
RandomizedDelaySec=10min
Persistent=true
[Install]
WantedBy=timers.target
UNIT
systemctl daemon-reload
"$WORK/maintain" apply --config /etc/ds-bridge/release.json
cat > /etc/ds-bridge/release.json <<JSON
{"origin":"$ORIGIN","manifest_url":"$ORIGIN/releases/bridge/latest-linux-$ARCH.json","public_key":"zuUC8lz221MH3QlDCHzm03N3Py6CovGIgQdk8cg1b50","root":"/opt/ds-bridge"}
JSON
systemctl enable ds-go2rtc.service ds-bridge-agent.service ds-bridge-update.timer
systemctl start avahi-daemon.service ds-bridge-update.timer
# Название задаётся только этому новому мосту, существующие объекты не затрагиваются.
echo 'Готово. В одной сети откройте http://ИМЯ-ИЛИ-IP-МОСТА, введите код из объекта → Камеры → Камера с мостом.'
echo 'Адреса моста:'
hostname -I
