#!/usr/bin/env bash # upgrade-ish-v107.sh — полное автоматическое обновление ИШ до версии 107 на # Astra Linux (amd64) в headless-режиме (сервер без графической сессии). # # ИШ 107 принёс изменения, из-за которых он «из коробки» не стартует как # systemd-служба без графики. Этот скрипт решает ВСЕ слои (проверено 19.08.2026, # см. память ish-v107-upgrade): # 1. Установка .deb (dpkg). # 2. gnome-keyring для секретов (v107 хранит пароли в OS SecretStore/libsecret). # 3. Виртуальный дисплей Xvfb (v107 igate-svc тянет GTK/GVFS). # 4. Параметры канала WSL для v107 (короткий URL, TLS-отпечаток GOST, DN, chain, # M2M) — через configure-ish-v107.sql. # 5. Самоочистка stale-состояния (ExecStartPre) — иначе рестарты флакуют. # # Конфигурация ИШ живёт в PostgreSQL и ПЕРЕЖИВАЕТ обновление. # # Использование: # sudo ./upgrade-ish-v107.sh /путь/к/igate-107-0-930-amd64.zip # Опционально переменные окружения: # SVC_USER (по умолчанию igate) — пользователь службы ИШ # IGATE_DB (по умолчанию igate) — имя БД PostgreSQL # CHAN_ID (по умолчанию auto) — id канала в channels (auto = найти WSL) # KEYRING_PASS_FILE (/var/lib/igate/.keyring-pass) — файл пароля keyring # set -euo pipefail ZIP="${1:?укажите путь к igate-107-*-amd64.zip}" SVC_USER="${SVC_USER:-igate}" IGATE_DB="${IGATE_DB:-igate}" IGATE_DIR="/opt/nsd/igate" WRAPPER="/usr/local/bin/igate-run.sh" # вне /opt — переживает переустановку .deb DROPIN_DIR="/etc/systemd/system/igate.service.d" SQL_DIR="$(cd "$(dirname "$0")" && pwd)" STAMP="$(date +%Y%m%d-%H%M%S)" WORK="$(mktemp -d)"; trap 'rm -rf "$WORK"' EXIT log(){ echo -e "\n=== $* ==="; } [ "$(id -u)" = 0 ] || { echo "нужен root (sudo)"; exit 1; } SVC_UID="$(id -u "$SVC_USER")" KEYRING_PASS_FILE="${KEYRING_PASS_FILE:-/var/lib/$SVC_USER/.keyring-pass}" HOME_DIR="$(getent passwd "$SVC_USER" | cut -d: -f6)" log "0/9 Проверка зависимостей" for bin in dpkg gnome-keyring-daemon dbus-run-session xvfb-run loginctl psql; do command -v "$bin" >/dev/null || { echo "нет $bin — установите (gnome-keyring, dbus-x11, xvfb, libsecret-tools, postgresql-client)"; exit 1; } done echo "все зависимости на месте" log "1/9 Распаковка дистрибутива" unzip -o -q "$ZIP" -d "$WORK" DEB="$(ls "$WORK"/*.deb 2>/dev/null | head -1)" [ -n "$DEB" ] || { echo "в архиве нет .deb"; exit 1; } echo "пакет: $(basename "$DEB") версия: $(dpkg-deb -f "$DEB" Version)" log "2/9 Бэкап установки, конфига службы и БД" mkdir -p "/var/backups/ish-pre107-$STAMP" BK="/var/backups/ish-pre107-$STAMP" [ -d "$IGATE_DIR" ] && tar czf "$BK/opt-nsd-igate.tar.gz" -C "$(dirname "$IGATE_DIR")" "$(basename "$IGATE_DIR")" 2>/dev/null || true [ -d "$DROPIN_DIR" ] && cp -a "$DROPIN_DIR" "$BK/dropin" || true ( cd /tmp && sudo -u postgres pg_dump -d "$IGATE_DB" -Fc | cat > "$BK/igate-db.dump" ) 2>/dev/null || echo " (пропуск дампа БД)" echo "$BK" > /var/backups/ish-last-backup.txt echo "бэкап: $BK" log "3/9 Остановка службы и очистка stale-состояния" systemctl stop igate 2>/dev/null || true pkill -9 -u "$SVC_USER" -f igate-svc 2>/dev/null || true pkill -9 -u "$SVC_USER" -f Xvfb 2>/dev/null || true rm -f /tmp/CoreFxPipe_NSD.IGate.* 2>/dev/null || true log "4/9 Установка пакета (.deb)" echo "igate igate/service boolean false" | debconf-set-selections echo "igate igate/shortcut boolean false" | debconf-set-selections DEBIAN_FRONTEND=noninteractive dpkg -i --force-confnew "$DEB" || apt-get -f install -y || true echo "версия установлена: $(python3 -c "import json;print([k for k in json.load(open('$IGATE_DIR/igate/igate.deps.json'))['libraries'] if k.lower().startswith('igate/')][0])" 2>/dev/null)" log "5/9 Подготовка keyring (пароль + linger для XDG_RUNTIME_DIR)" loginctl enable-linger "$SVC_USER" if [ ! -f "$KEYRING_PASS_FILE" ]; then umask 077 # локальный пароль keyring (только для сервисного хранилища секретов ИШ) head -c 18 /dev/urandom | base64 | tr -dc 'A-Za-z0-9' | head -c 20 > "$KEYRING_PASS_FILE" chown "$SVC_USER":"$SVC_USER" "$KEYRING_PASS_FILE"; chmod 600 "$KEYRING_PASS_FILE" echo "создан $KEYRING_PASS_FILE" else echo "используется существующий $KEYRING_PASS_FILE" fi # сбросить возможно несовместимый старый login.keyring — wrapper создаст заново rm -f "$HOME_DIR/.local/share/keyrings/login.keyring" 2>/dev/null || true log "6/9 Установка wrapper $WRAPPER" cat > "$WRAPPER" </dev/null)" export GNOME_KEYRING_CONTROL SSH_AUTH_SOCK cd "\$HOME" exec xvfb-run -a $IGATE_DIR/igate-svc/igate-svc EOF chmod +x "$WRAPPER"; echo "wrapper установлен" log "7/9 Настройка systemd (drop-in с самоочисткой и авто-рестартом)" mkdir -p "$DROPIN_DIR" cat > "$DROPIN_DIR/override.conf" </dev/null; true' ExecStart= ExecStart=/usr/bin/dbus-run-session -- $WRAPPER Restart=on-failure RestartSec=8 EOF systemctl daemon-reload log "8/9 Параметры канала v107 (SQL) + миграция паролей" CHAN_ID="${CHAN_ID:-auto}" if [ "$CHAN_ID" = auto ]; then CHAN_ID="$(cd /tmp && sudo -u postgres psql -d "$IGATE_DB" -tAc "select id from channels where type='WSL' order by id limit 1" 2>/dev/null | tr -d '[:space:]')" fi [ -n "$CHAN_ID" ] || { echo "не найден WSL-канал в channels — укажите CHAN_ID вручную"; exit 1; } echo "канал id=$CHAN_ID" if [ -f "$SQL_DIR/configure-ish-v107.sql" ]; then ( cd /tmp && sudo -u postgres psql -d "$IGATE_DB" -v chan_id="$CHAN_ID" -f "$SQL_DIR/configure-ish-v107.sql" ) else echo " ⚠️ configure-ish-v107.sql не найден рядом со скриптом — параметры канала НЕ применены" fi # Утилита миграции v107 (флаг миграции); финальный Console.ReadKey в headless # безвреден — миграция сохраняется до него. if [ -x "$IGATE_DIR/igate-mig/igate-mig" ]; then sudo -u "$SVC_USER" bash -c ' export HOME='"$HOME_DIR"' XDG_DATA_HOME='"$HOME_DIR"'/.local/share XDG_RUNTIME_DIR=/run/user/'"$SVC_UID"' dbus-run-session -- bash -c " eval \$(printf %s\\\\n \"\$(cat '"$KEYRING_PASS_FILE"')\" | gnome-keyring-daemon --replace --unlock --components=secrets,pkcs11 2>/dev/null) echo \"\" | timeout 30 '"$IGATE_DIR"'/igate-mig/igate-mig --data '"$HOME_DIR"' >/dev/null 2>&1 || true "' 2>/dev/null || true echo "igate-mig выполнен (флаг миграции)" fi log "9/9 Запуск и проверка" systemctl start igate OK=0 for i in $(seq 1 8); do sleep 5 if ss -tlnp 2>/dev/null | grep -q ':8090'; then OK=1; break; fi systemctl is-active --quiet igate || break done VER="$(python3 -c "import json;print([k for k in json.load(open('$IGATE_DIR/igate/igate.deps.json'))['libraries'] if k.lower().startswith('igate/')][0])" 2>/dev/null || true)" STATE="$(curl -s http://localhost:8090/api/admin/engine/state 2>/dev/null || true)" echo "версия: $VER" echo "engine/state: ${STATE:-нет ответа}" echo "8090: $(ss -tlnp 2>/dev/null | grep -c ':8090') слушатель" if [ "$OK" = 1 ] && [ "$STATE" = '"Running"' ]; then echo -e "\n✅ ИШ 107 запущен и работает." else cat < $DROPIN_DIR/override.conf && systemctl daemon-reload && systemctl start igate MSG fi