Первичная настройка, повторная проверка: копии без ключевых носителей СКЗИ (--one-file-system, без keymedia), без отпечатка железа и списка компонентов; восстановление на новый сервер — пароль базы bj из копии, база и пользователь ИШ; ключ для контура без канала запоминается и подключается при настройке канала; первый настроенный канал становится рабочим контуром; справочник сертификатов перечитывается после ключа и смены контура; шлюз НРД — шаг 7, ключ — шаг 8 (ключ ставится в профиль ИШ); подсказка контура ключа по сертификату; ИШ: параметры работающего шлюза не перезаписываются; без чужого кода отправителя и получателя по умолчанию; прокси для выхода в интернет; установка с образа без интернета; HTTPS — новый сертификат при смене адреса; bj-network — без cloud-init; проверка готовности: справочник участников, сетевые доступы, учётные записи, последний обмен с НРД

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-05 17:33:10 +03:00
parent 765d7b5994
commit 10305a095e
25 changed files with 634 additions and 95 deletions
+9 -3
View File
@@ -33,10 +33,16 @@ runuser -u postgres -- pg_dump -Fc bj > "$W/bj.pgdump" || fail "дамп баз
if runuser -u postgres -- psql -Atc "select 1 from pg_database where datname='igate'" 2>/dev/null | grep -q 1; then
runuser -u postgres -- pg_dump -Fc igate > "$W/igate.pgdump" || fail "дамп базы igate"
fi
tarok -C / -czf "$W/bj-data.tar.gz" --exclude=etc/bj/backup-smb.cred var/lib/bj/.bj etc/bj || fail "архив данных bj"
# Не копируем: пароль хранилища; то, что принадлежит этой машине (отпечаток железа, список установленных
# компонентов — на новом сервере они свои); ключевые носители (флешка ключа монтируется в
# /var/lib/igate/keymedia): ключи СКЗИ не должны уходить на сетевое хранилище — при восстановлении носитель
# подключают заново. --one-file-system — не заходить ни в какие смонтированные носители.
tarok -C / -czf "$W/bj-data.tar.gz" --one-file-system --exclude=etc/bj/backup-smb.cred --exclude=etc/bj/hw-id \
--exclude=var/lib/bj/.bj/components.json var/lib/bj/.bj etc/bj || fail "архив данных bj"
if [ -d /var/lib/igate ]; then
tarok -C / -czf "$W/igate-data.tar.gz" --warning=no-file-changed --exclude='var/lib/igate/.cache' \
--exclude='var/lib/igate/NSD Integration Gateway/logs*' --exclude='var/lib/igate/logs-cli' var/lib/igate || fail "архив шлюза НРД"
tarok -C / -czf "$W/igate-data.tar.gz" --one-file-system --warning=no-file-changed --exclude='var/lib/igate/.cache' \
--exclude='var/lib/igate/keymedia/*' --exclude='var/lib/igate/NSD Integration Gateway/logs*' \
--exclude='var/lib/igate/logs-cli' var/lib/igate || fail "архив шлюза НРД"
fi
cat > "$W/backup-info.txt" <<INFO
Bridge&Join — резервная копия сервера $(hostname) от $(date '+%d.%m.%Y %H:%M:%S')
+39 -4
View File
@@ -129,13 +129,43 @@ PY
LC_ALL=C PGOPTIONS='-c lc_messages=C' runuser -u postgres -- pg_restore --clean --if-exists -d bj < "$W/bj.pgdump" >/dev/null 2>"$W/pg.err"
# «does not exist» при --clean на пустой базе — не ошибка; остальное — ошибка
if grep -i "error" "$W/pg.err" | grep -vqi "does not exist"; then die "база bj: $(grep -i error "$W/pg.err" | grep -vi 'does not exist' | head -1)"; fi
if [ -s "$W/igate.pgdump" ] && runuser -u postgres -- psql -Atc "select 1 from pg_database where datname='igate'" | grep -q 1; then
if [ -s "$W/igate.pgdump" ]; then
st db "восстановление базы шлюза НРД"
# новый сервер: ИШ ещё не ставили — готовим пользователя, роль, базу и доступ без пароля (как ставит
# bj-igate-admin), чтобы данные и каналы ИШ вернулись; сам пакет ИШ потом ставится на шаге «Шлюз НРД»
id igate >/dev/null 2>&1 || useradd --system --home-dir /var/lib/igate --create-home --shell /usr/sbin/nologin igate
runuser -u postgres -- psql -tAc "select 1 from pg_roles where rolname='igate'" | grep -q 1 || runuser -u postgres -- psql -qc "create role igate login"
runuser -u postgres -- psql -lqt | cut -d'|' -f1 | grep -qw igate || runuser -u postgres -- createdb -O igate igate
hba=$(runuser -u postgres -- psql -tAc "show hba_file")
if [ -f "$hba" ] && ! grep -q '^# bj-igate-trust' "$hba"; then
cp -a "$hba" "$hba.bak-bj-igate"
{ printf '# bj-igate-trust\nlocal igate igate trust\nhost igate igate 127.0.0.1/32 trust\nhost igate igate ::1/128 trust\n'; cat "$hba.bak-bj-igate"; } > "$hba"
runuser -u postgres -- psql -qtAc "select pg_reload_conf()" >/dev/null
fi
LC_ALL=C PGOPTIONS='-c lc_messages=C' runuser -u postgres -- pg_restore --clean --if-exists -d igate < "$W/igate.pgdump" >/dev/null 2>&1
fi
st files "восстановление данных bj и шлюза НРД"
tar -C / -xzf "$W/bj-data.tar.gz" || die "распаковка данных bj"
[ -s "$W/igate-data.tar.gz" ] && { tar -C / -xzf "$W/igate-data.tar.gz" || die "распаковка данных шлюза НРД"; }
# своё у этой машины (отпечаток железа, установленные компоненты) — сохраняем поверх копии; ключевые
# носители из старых копий не раскладываем: ключ СКЗИ — только на своём носителе
mkdir -p "$W/keep"; cp -a /var/lib/bj/.bj/components.json /etc/bj/hw-id "$W/keep/" 2>/dev/null
tar -C / -xzf "$W/bj-data.tar.gz" --exclude=etc/bj/hw-id --exclude=var/lib/bj/.bj/components.json || die "распаковка данных bj"
[ -f "$W/keep/components.json" ] && cp -a "$W/keep/components.json" /var/lib/bj/.bj/components.json
[ -f "$W/keep/hw-id" ] && cp -a "$W/keep/hw-id" /etc/bj/hw-id
# пароль пользователя базы bj — как в восстановленных настройках: установщик нового сервера задал свой
# случайный пароль, и без этого bj не подключился бы к своей базе (ушёл бы в хранение в памяти)
pw=$(python3 - <<'PY'
import json,urllib.parse
try:
d=json.load(open("/var/lib/bj/.bj/setup.json")).get("postgres",{}).get("dsn","")
u=urllib.parse.urlparse(d)
if u.username=="bj" and u.password and u.hostname in ("127.0.0.1","localhost"): print(urllib.parse.unquote(u.password))
except Exception: pass
PY
)
if [ -n "$pw" ]; then
printf "ALTER ROLE bj WITH PASSWORD '%s';\n" "$(printf '%s' "$pw" | sed "s/'/''/g")" | runuser -u postgres -- psql -q >/dev/null 2>&1 || die "не удалось выставить пароль базы bj из копии"
fi
[ -s "$W/igate-data.tar.gz" ] && { tar -C / -xzf "$W/igate-data.tar.gz" --exclude='var/lib/igate/keymedia/*' || die "распаковка данных шлюза НРД"; }
# копия хранилища в восстановленных настройках есть, а пароль в неё не входит — берём тот, с которым
# восстанавливали, и возвращаем расписание копий
if [ -r "$CONF" ]; then
@@ -148,7 +178,12 @@ PY
systemctl start bj-server >/dev/null 2>&1
systemctl is-enabled bj-backup.timer >/dev/null 2>&1 && systemctl start bj-backup.timer >/dev/null 2>&1
for u in bj-npr bj-lk; do systemctl is-enabled "$u" >/dev/null 2>&1 && systemctl start "$u" >/dev/null 2>&1; done
had_igate=0; [ -s "$W/igate-data.tar.gz" ] && had_igate=1
rm -rf "$W" /run/bj-restore.conf
st done "восстановлено из копии $NAME" ;;
if [ "$had_igate" = 1 ] && [ ! -x /opt/nsd/igate/igate-svc/igate-svc ]; then
st done "восстановлено из копии $NAME. Шлюз НРД на этом сервере не установлен — установите его в мастере (шаг «Шлюз НРД»): настройки и каналы из копии подхватятся; ключ — подключите носитель и установите мастером ключа"
else
st done "восстановлено из копии $NAME"
fi ;;
*) err "неизвестная команда: $cmd" ;;
esac
+27 -18
View File
@@ -114,9 +114,11 @@ PY
st deps "системные пакеты для работы ИШ без графики"
need=""
for p in gnome-keyring dbus xvfb xauth libsecret-tools unzip curl; do dpkg -s "$p" >/dev/null 2>&1 || need="$need $p"; done
px=$(j proxy) # прокси организации (из настроек bj) — для apt и скачивания с nsd.ru
aptpx=(); [ -n "$px" ] && aptpx=(-o "Acquire::http::Proxy=$px" -o "Acquire::https::Proxy=$px")
if [ -n "$need" ]; then
DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends $need || \
{ DEBIAN_FRONTEND=noninteractive apt-get update -qq && DEBIAN_FRONTEND=noninteractive apt-get install -y -qq --no-install-recommends $need; } || die "не удалось установить пакеты:$need"
DEBIAN_FRONTEND=noninteractive apt-get "${aptpx[@]}" install -y -qq --no-install-recommends $need || \
{ DEBIAN_FRONTEND=noninteractive apt-get "${aptpx[@]}" update -qq && DEBIAN_FRONTEND=noninteractive apt-get "${aptpx[@]}" install -y -qq --no-install-recommends $need; } || die "не удалось установить пакеты:$need"
fi
st package "дистрибутив ИШ"
@@ -125,7 +127,7 @@ PY
v=$(printf '%s' "$ver" | tr . -)
url="https://www.nsd.ru/media/integratsionnyj-shlyuz/$v/igate-$v-amd64.zip"
echo "скачивание $url"
curl -fsSL -m 600 -A 'Mozilla/5.0 (X11; Linux x86_64) bj-installer' -H 'Accept: */*' -o "$W/igate.zip" "$url" || die "не удалось скачать дистрибутив ИШ $ver с сайта НРД ($url)"
curl -fsSL -m 600 ${px:+-x "$px"} -A 'Mozilla/5.0 (X11; Linux x86_64) bj-installer' -H 'Accept: */*' -o "$W/igate.zip" "$url" || die "не удалось скачать дистрибутив ИШ $ver с сайта НРД ($url)"
file="$W/igate.zip"
fi
case "$file" in
@@ -200,6 +202,8 @@ StartLimitBurst=8
User=$SVC_USER
WorkingDirectory=$HOME_DIR
# самоочистка перед стартом: осиротевшие сокеты, keyring, X-локи — иначе перезапуски срываются
# каталог сеанса igate (XDG_RUNTIME_DIR): обычно его создаёт systemd-logind (linger), но без него keyring не стартует
ExecStartPre=+/usr/bin/install -d -o $SVC_USER -g $SVC_USER -m 700 /run/user/$uid
ExecStartPre=-/bin/bash -c 'rm -f /tmp/CoreFxPipe_NSD.IGate.* $HOME_DIR/.local/share/keyrings/login.keyring; find /tmp -maxdepth 1 -name ".X9*-lock" -delete 2>/dev/null; true'
ExecStart=/usr/bin/dbus-run-session -- $WRAPPER
Restart=on-failure
@@ -215,28 +219,31 @@ EOF
run_as_igate_keyring "timeout 90 $IGATE_DIR/igate-cli/igate-cli --data $HOME_DIR </dev/null >/dev/null 2>&1; true"
psqlq -c "select to_regclass('public.parameters') is not null" | grep -q t || die "ИШ не создал схему базы (igate-cli). Журнал: $LOG"
fi
# глобальные параметры: Web API на localhost:8090 без авторизации и запуск движка без графики
# Общие параметры ИШ. Обязательные для работы без графики и для bj (движок при старте, Web API на
# localhost:8090 без авторизации) — выставляем всегда; остальные — только если их ещё нет: настройки ИШ,
# который у клиента уже работает (очистка, хранение, прокси), не трогаем. Значения по умолчанию — эталон GUI.
psqlq <<'SQL' || die "не записаны параметры ИШ"
DELETE FROM parameters WHERE chanel_id IS NULL AND name IN ('runEngineOnStartApp','server.useServer','server.host','server.port','server.scheme',
'server.authentication.enable','server.authentication.userName','server.authentication.password','server.certificate.storage',
'server.certificate.store.location','server.certificate.store.name','server.certificate.file.path','server.certificate.file.password',
'wsl.httpsMode','wsl.maxConnsPerServer','wsl.proxy.mode','wsl.proxy.address','wsl.proxy.port','wsl.proxy.username','wsl.proxy.password',
'enableDbLogging','cleanAutomatically','cleanAtTime','cleanWhenLarger','cleanVacuum','storePeriod','archiveAutomatically');
INSERT INTO parameters(name, value, chanel_id) VALUES
('runEngineOnStartApp','True',NULL),('server.useServer','True',NULL),('server.host','localhost',NULL),('server.port','8090',NULL),
('server.scheme','Http',NULL),('server.authentication.enable','False',NULL),('server.authentication.userName','',NULL),
('server.authentication.password','',NULL),('server.certificate.storage','File',NULL),('server.certificate.store.location','CurrentUser',NULL),
('server.certificate.store.name','My',NULL),('server.certificate.file.path','',NULL),('server.certificate.file.password','',NULL),
('wsl.httpsMode','Auto',NULL),('wsl.maxConnsPerServer','4',NULL),('wsl.proxy.mode','None',NULL),('wsl.proxy.address','',NULL),
('wsl.proxy.port','0',NULL),('wsl.proxy.username','',NULL),('wsl.proxy.password','',NULL),('enableDbLogging','False',NULL),
('cleanAutomatically','True',NULL),('cleanAtTime','00:30:00',NULL),('cleanWhenLarger','1024',NULL),('cleanVacuum','False',NULL),
('storePeriod','30',NULL),('archiveAutomatically','False',NULL);
CREATE TEMP TABLE must(n text, v text);
INSERT INTO must VALUES ('runEngineOnStartApp','True'),('server.useServer','True'),('server.host','localhost'),
('server.port','8090'),('server.scheme','Http'),('server.authentication.enable','False');
CREATE TEMP TABLE dflt(n text, v text);
INSERT INTO dflt VALUES ('server.authentication.userName',''),('server.authentication.password',''),('server.certificate.storage','File'),
('server.certificate.store.location','CurrentUser'),('server.certificate.store.name','My'),('server.certificate.file.path',''),
('server.certificate.file.password',''),('wsl.httpsMode','Auto'),('wsl.maxConnsPerServer','4'),('wsl.proxy.mode','None'),
('wsl.proxy.address',''),('wsl.proxy.port','0'),('wsl.proxy.username',''),('wsl.proxy.password',''),('enableDbLogging','False'),
('cleanAutomatically','False'),('cleanAtTime','00:30:00'),('cleanWhenLarger','1024'),('cleanVacuum','False'),('storePeriod','15'),
('archiveAutomatically','False');
UPDATE parameters p SET value = m.v FROM must m WHERE p.chanel_id IS NULL AND p.name = m.n;
INSERT INTO parameters(name, value, chanel_id)
SELECT x.n, x.v, NULL FROM (SELECT * FROM must UNION ALL SELECT * FROM dflt) x
WHERE NOT EXISTS (SELECT 1 FROM parameters p WHERE p.chanel_id IS NULL AND p.name = x.n);
SQL
st start "запуск ИШ"
systemctl enable igate >/dev/null 2>&1
systemctl restart igate || die "служба igate не запустилась"
wait_engine 120 || die "ИШ запущен, но движок не перешёл в Running за 2 минуты. Журнал ИШ: $DATA_DIR/logs-svc/log.txt"
case "$(j file)" in /var/lib/bj/igate-upload/*) rm -f "$(j file)" ;; esac # загруженный архив больше не нужен
st done "ИШ $(igate_version) установлен и работает; дальше — канал контура (TEST3 или боевой)" ;;
contour)
@@ -247,6 +254,8 @@ SQL
[[ "$dep" =~ ^[A-Z]{2}[0-9]{10}$ ]] || err "депозитарный код — вида MC0000000000"
[[ -z "$prof" || "$prof" =~ ^[A-Za-z0-9_-]+$ ]] || err "недопустимое имя профиля Валидаты"
[ -x "$IGATE_DIR/igate-svc/igate-svc" ] || err "ИШ не установлен"
# ключ этого контура ставили раньше, чем канал: профиль запомнен установкой ключа
if [ -z "$prof" ] && [ -s "$HOME_DIR/.bj-profile-$CODE" ]; then prof=$(tr -cd 'A-Za-z0-9_-' < "$HOME_DIR/.bj-profile-$CODE"); fi
DIR=$(exchange_dir "$contour"); install -d -o "$SVC_USER" -g "$SVC_USER" -m 755 "$DIR"
psqlq -v code="$CODE$dep" -v name="$NAME" -v ep="$EP" -v fp="$FP" -v typ="$TYPE" -v dep="$dep" -v prof="$prof" -v dir="$DIR" <<'SQL' || err "канал не записан в базу ИШ"
BEGIN;
+9 -1
View File
@@ -199,7 +199,15 @@ open(ini,"w").write("\n".join(L))
PY
fi
# подключить профиль к ИШ: cryptography.profile в БД igate (канал 33)
if [ "$IGATE_RES" = "ok" ]; then
nchan=1
if [ "$IGATE_RES" = "ok" ] && [ -n "$CHAN_PREFIX" ]; then
nchan=$(su -s /bin/sh postgres -c "psql -d igate -tAc \"select count(*) from channels where code like '${CHAN_PREFIX}%'\"" 2>/dev/null | tr -d '[:space:]')
fi
if [ "$IGATE_RES" = "ok" ] && [ "${nchan:-0}" = 0 ]; then
# канала этого контура в ИШ ещё нет: профиль запоминаем — bj-igate-admin подключит его при настройке канала
printf '%s\n' "$PROFILE" > "/var/lib/igate/.bj-profile-$CHAN_PREFIX"; chown igate:igate "/var/lib/igate/.bj-profile-$CHAN_PREFIX" 2>/dev/null
IGATE_RES="pending-channel"
elif [ "$IGATE_RES" = "ok" ]; then
# только канал контура этого ключа (BJ_CHANNEL); старый bj без контура — все каналы, как раньше. Канал с ключом включаем.
if su -s /bin/sh postgres -c "psql -d igate -tAc \"update parameters set value='$PROFILE' where name='cryptography.profile' and $(chan_where); update channels set enable=true where id in (select chanel_id from parameters where name='cryptography.profile' and value='$PROFILE'); update parameters set value='True' where name='enable' and chanel_id in (select chanel_id from parameters where name='cryptography.profile' and value='$PROFILE')\"" >/dev/null 2>&1; then
IGATE_RES="connected"
+4 -1
View File
@@ -26,11 +26,14 @@ F=/etc/netplan/60-bj-static.yaml
chmod 600 "$F"
# прежние файлы с DHCP для этого интерфейса — выключаем, чтобы не было двух адресов
for f in /etc/netplan/*.yaml; do [ "$f" = "$F" ] && continue; grep -q "$IF" "$f" 2>/dev/null && mv "$f" "$f.disabled-by-bj"; done
# на Ubuntu с образа сетью управляет cloud-init: без запрета при перезагрузке он может вернуть DHCP
CI=/etc/cloud/cloud.cfg.d/99-bj-disable-network-config.cfg
[ -d /etc/cloud/cloud.cfg.d ] && printf 'network: {config: disabled}\n' > "$CI"
echo " Применяю. Если связь пропадёт — через 120 секунд вернутся прежние настройки."
if netplan try --timeout 120; then
echo " ✓ Адрес ${ADDR%/*} закреплён. Веб-вход: https://${ADDR%/*}:8443/"
[ -x /usr/local/sbin/bj-welcome ] && /usr/local/sbin/bj-welcome
else
for f in /etc/netplan/*.disabled-by-bj; do [ -f "$f" ] && mv "$f" "${f%.disabled-by-bj}"; done
rm -f "$F"; netplan apply; echo " Настройки не приняты — оставлен прежний адрес."
rm -f "$F" "$CI"; netplan apply; echo " Настройки не приняты — оставлен прежний адрес."
fi