Files
Bridge-and-Join-s/deploy/linux/install-validata.sh
zuevav 9737c787f9 feat: живой цикл M2M с НРД + мастер установки ключа на флешку
Инфраструктура M2M (живой обмен с НРД через ИШ):
- обработка M2MTransferResponse: ERROR(M2Mxx) → заявка Отклонена, сохранение
  ответа; INFO → ждём Decision; идемпотентность поллера
- fallback-корреляция ответов с нулевым GUID (M2M14/M2M17) по FIFO
- сырой XML ответа НРД в карточке заявки (для пересылки в ТП)
- тестовый пакет роботу приведён к эталону m2m_robot_samples (CostInfo=Yes,
  4 бумаги, IsolationStatus, DocumentSeries=сценарий); override паспорта
- редирект из теста сразу в карточку заявки

Мастер установки ключа Валидаты на флешку (admin/setup/keywizard):
- пошаговый: загрузка .7z+пароль → выбор флешки → запись → справочник
  сертификатов (CRL) → перезапуск+проверка ИШ → готово
- привилегированный воркер (bj-keymedia) в host-namespace через файл-обмен,
  bj-server остаётся в песочнице
- сохранение структуры профиля архива (spr<N>), перечисление съёмных USB

Прочее:
- пакет-доказательство для ТП НРД + форма регистрации участника M2M
- эталонные образцы робота (DOC/m2m_robot_samples)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-19 00:03:21 +03:00

389 lines
17 KiB
Bash
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/usr/bin/env bash
# install-validata.sh — установка АПК «Валидата Клиент L» под bj-crypto.
#
# Поддерживаемые ОС:
# - Debian 11 / 12 (основная, бесплатная)
# - Astra Linux SE 1.7 (платная, для регуляторно-обязанных)
# - Astra Linux CE 1.8 (бесплатная)
# - Ubuntu 22.04 / 24.04 (с предупреждением)
#
# Что делает:
# 1. Ставит зависимости (pcscd, libpcsclite, libgtk-3, libldap, p7zip, execstack)
# 2. Ставит zpki + zsdk deb-пакеты Валидаты
# 3. execstack -c libvdcsp.so (исправление GNU_STACK с RWE на RW)
# 4. Создаёт системного пользователя bj (если ещё нет)
# 5. Кладёт 5 systemd drop-ins (pcscd no-autoexit + 3×bj-crypto + 1×bj-server)
# 6. Создаёт /opt/Validata/VDCSP/etc/spki.ini (Валидата с ним капризничает)
# 7. Дописывает заголовочную секцию в pki1.conf
# 8. Включает pcscd в режиме always-on (без socket-активации) — Валидата
# ожидает постоянно живой демон, иначе ловит 0x8010001D
# 9. Ставит udev-rule + systemd-mount unit для авто-mount USB-флешек с .vdk
# в /var/lib/bj/usb/<label>/ с владельцем bj — это убирает необходимость
# пробрасывать /media/<gui-user>/
# 10. systemctl daemon-reload + enable/start bj-crypto + bj-server
#
# Идемпотентный — повторный запуск ничего не ломает.
#
# Запуск:
# sudo bash install-validata.sh [path-to-validata-zpki.deb]
#
# Если путь не передан — ищет:
# ./ClientL_Other/zpki-*.deb
# /opt/bj/src/dist/validata/*.deb
# ~/Загрузки/ClientL_Other/*.deb
# ~/Downloads/ClientL_Other/*.deb
set -euo pipefail
# --------------------------------------------------------------------- #
# Логирование
# --------------------------------------------------------------------- #
log() { echo -e "\033[1;34m[validata-install]\033[0m $*"; }
ok() { echo -e "\033[1;32m[validata-install OK]\033[0m $*"; }
warn() { echo -e "\033[1;33m[validata-install WARN]\033[0m $*" >&2; }
fail() { echo -e "\033[1;31m[validata-install FAIL]\033[0m $*" >&2; exit 1; }
[ "$EUID" -eq 0 ] || fail "Запускать от root (sudo)"
# --------------------------------------------------------------------- #
# 1. Детект ОС
# --------------------------------------------------------------------- #
DISTRO=""
DISTRO_VERSION=""
if [ -r /etc/os-release ]; then
. /etc/os-release
case "$ID" in
astra) DISTRO=astra; DISTRO_VERSION="${VERSION_ID:-unknown}";;
debian) DISTRO=debian; DISTRO_VERSION="${VERSION_ID:-unknown}";;
ubuntu) DISTRO=ubuntu; DISTRO_VERSION="${VERSION_ID:-unknown}";;
*) DISTRO="$ID"; DISTRO_VERSION="${VERSION_ID:-unknown}";;
esac
fi
case "$DISTRO" in
debian|astra) ok "ОС: $PRETTY_NAME ($DISTRO $DISTRO_VERSION) — поддерживается";;
ubuntu) warn "ОС: $PRETTY_NAME — поддерживается на свой страх (Validata в формуляре нет)";;
*) warn "ОС: $PRETTY_NAME — не проверена, продолжаю на свой страх";;
esac
# --------------------------------------------------------------------- #
# 2. Поиск deb-пакетов Валидаты
# --------------------------------------------------------------------- #
ZPKI_DEB=""
ZSDK_DEB=""
if [ -n "${1:-}" ] && [ -f "$1" ]; then
# Конкретный файл передан аргументом
ZPKI_DEB="$1"
# zsdk ищем рядом
ZSDK_DEB="$(dirname "$1")/$(basename "$1" | sed 's/zpki/zsdk/')"
else
for d in \
./ClientL_Other \
./ClientL_ALSE \
/opt/bj/src/dist/validata \
/home/*/Загрузки/ClientL_Other \
/home/*/Загрузки/ClientL_ALSE \
/home/*/Downloads/ClientL_Other \
/home/*/Downloads/ClientL_ALSE \
/tmp/ClientL_Other; do
[ -d "$d" ] || continue
cand_zpki=$(find "$d" -maxdepth 1 -name "zpki-*.amd64.deb" 2>/dev/null | head -1)
cand_zsdk=$(find "$d" -maxdepth 1 -name "zsdk-*.amd64.deb" 2>/dev/null | head -1)
if [ -n "$cand_zpki" ]; then
ZPKI_DEB="$cand_zpki"
ZSDK_DEB="$cand_zsdk"
break
fi
done
fi
[ -n "$ZPKI_DEB" ] || fail "Не найден zpki-*.amd64.deb. Скачайте https://fs.moex.com/cdp/po/ClientL_Other.zip и распакуйте рядом со скриптом или в /opt/bj/src/dist/validata/"
log "Найдено:"
log " zpki: $ZPKI_DEB"
[ -n "$ZSDK_DEB" ] && log " zsdk: $ZSDK_DEB" || warn " zsdk не найден — будет пропущен"
# --------------------------------------------------------------------- #
# 3. Системные зависимости
# --------------------------------------------------------------------- #
log "Обновляю apt-кеш и ставлю зависимости..."
apt-get update -qq
# Базовые зависимости (одинаковые на Debian/Astra)
DEPS=(
libgtk-3-0
libpcsclite1
libccid
pcscd
libcurl4
libkrb5-3
libgssapi-krb5-2
libsasl2-modules
libsasl2-modules-gssapi-mit
execstack
p7zip-full
util-linux
uuid-runtime
)
# libldap-2.4-2 в Bullseye/Astra; в Bookworm уже libldap-2.5-0.
# Зависимость в zpki жёстко на 2.4-2 → используем --force-depends на этом этапе
# и ставим libldap-2.5-0 как замену (ABI совместим в нашем use-case).
if apt-cache show libldap-2.4-2 >/dev/null 2>&1; then
DEPS+=(libldap-2.4-2)
USE_FORCE=0
else
DEPS+=(libldap-2.5-0)
USE_FORCE=1
warn "libldap-2.4-2 недоступен → ставлю libldap-2.5-0 и буду форсить --force-depends при установке zpki"
fi
apt-get install -y --no-install-recommends "${DEPS[@]}"
ok "Зависимости установлены"
# --------------------------------------------------------------------- #
# 4. Установка Валидаты
# --------------------------------------------------------------------- #
log "Ставлю zpki..."
if [ "$USE_FORCE" = "1" ]; then
dpkg --force-depends -i "$ZPKI_DEB"
else
dpkg -i "$ZPKI_DEB" || { apt-get -f install -y; dpkg -i "$ZPKI_DEB"; }
fi
if [ -n "$ZSDK_DEB" ]; then
log "Ставлю zsdk..."
dpkg -i "$ZSDK_DEB" || { apt-get -f install -y; dpkg -i "$ZSDK_DEB"; }
fi
[ -d /opt/Validata/VDCSP/lib/amd64 ] || fail "Валидата не установилась в /opt/Validata — проверьте dpkg -L zpki"
ok "Валидата в /opt/Validata/VDCSP"
# --------------------------------------------------------------------- #
# 5. execstack libvdcsp.so (GNU_STACK RWE → RW)
# --------------------------------------------------------------------- #
log "execstack -c libvdcsp.so (требование Валидаты)..."
if execstack -q /opt/Validata/VDCSP/lib/amd64/libvdcsp.so 2>/dev/null | grep -q '^X'; then
execstack -c /opt/Validata/VDCSP/lib/amd64/libvdcsp.so
ok "executable-stack снят"
else
ok "executable-stack уже снят"
fi
# --------------------------------------------------------------------- #
# 6. Системный пользователь bj
# --------------------------------------------------------------------- #
if ! id bj >/dev/null 2>&1; then
log "Создаю системного пользователя bj..."
useradd --system --create-home --home-dir /var/lib/bj --shell /bin/bash bj
ok "Пользователь bj создан (home=/var/lib/bj)"
else
ok "Пользователь bj уже есть"
fi
install -d -o bj -g bj -m 0755 /var/lib/bj/usb
install -d -o bj -g bj -m 0700 /var/lib/bj/.Validata
install -d -o bj -g bj -m 0700 /var/lib/bj/.Validata/vdkeys
install -d -o bj -g bj -m 0755 /var/lib/bj/profiles
install -d -o bj -g bj -m 0755 /var/log/bj
# --------------------------------------------------------------------- #
# 7. pcscd: убираем --auto-exit и socket-активацию
# --------------------------------------------------------------------- #
log "Настраиваю pcscd как always-on демон..."
install -d /etc/systemd/system/pcscd.service.d
cat >/etc/systemd/system/pcscd.service.d/no-autoexit.conf <<'EOF'
[Unit]
# Отвязываем сервис от сокет-юнита, чтобы можно было держать его постоянно живым
Requires=
After=
Sockets=
[Service]
# Убираем --auto-exit — Валидата ожидает постоянно живой pcscd, иначе
# получает 0x8010001D «Диспетчер ресурсов смарт-карт не выполняется»
# при попытке найти ключевой носитель (.vdk файл выглядит для неё как
# виртуальная смарт-карта)
ExecStart=
ExecStart=/usr/sbin/pcscd --foreground
EOF
ok "pcscd drop-in: /etc/systemd/system/pcscd.service.d/no-autoexit.conf"
# --------------------------------------------------------------------- #
# 8. bj-crypto drop-ins
# --------------------------------------------------------------------- #
log "Кладу drop-ins для bj-crypto..."
install -d /etc/systemd/system/bj-crypto.service.d
cat >/etc/systemd/system/bj-crypto.service.d/validata-paths.conf <<'EOF'
[Service]
# Валидата ищет pki1.conf в текущей рабочей директории — работаем оттуда
WorkingDirectory=/opt/Validata/VDCSP/etc
# Валидата пишет в /opt/Validata/VDCSP/etc/pki1.conf при инициализации
# профиля. ProtectSystem=strict делает /opt read-only — открываем точечно.
ReadWritePaths=/opt/Validata/VDCSP/etc
ReadWritePaths=/var/lib/bj
EOF
cat >/etc/systemd/system/bj-crypto.service.d/usb-access.conf <<'EOF'
[Service]
# Без этого PrivateTmp + ProtectSystem закроет /media и /var/lib/bj/usb,
# а нам нужно туда смотреть в поисках .vdk на флешке.
ReadOnlyPaths=/media
ReadOnlyPaths=/var/lib/bj/usb
EOF
cat >/etc/systemd/system/bj-crypto.service.d/share-crysvc.conf <<'EOF'
[Service]
# Валидата общается с криптодрайвером (vdcrysvc) через Unix-сокет
# /tmp/.crysvc.sock — но PrivateTmp=true даёт нам приватный /tmp.
# Прокидываем именно этот сокет внутрь нашего namespace.
PrivateTmp=true
BindPaths=/tmp/.crysvc.sock:/tmp/.crysvc.sock
EOF
ok "bj-crypto drop-ins: validata-paths.conf, usb-access.conf, share-crysvc.conf"
# --------------------------------------------------------------------- #
# 9. bj-server drop-in
# --------------------------------------------------------------------- #
log "Кладу drop-in для bj-server..."
install -d /etc/systemd/system/bj-server.service.d
cat >/etc/systemd/system/bj-server.service.d/pki1conf.conf <<'EOF'
[Service]
# bj-server при импорте профиля дописывает секцию в pki1.conf.
# ProtectSystem=strict закрывает /opt — открываем точечно.
ReadWritePaths=/opt/Validata/VDCSP/etc
EOF
ok "bj-server drop-in: pki1conf.conf"
# --------------------------------------------------------------------- #
# 10. spki.ini — Валидата требует, при отсутствии mkstores падает
# --------------------------------------------------------------------- #
SPKI=/opt/Validata/VDCSP/etc/spki.ini
if [ ! -f "$SPKI" ]; then
log "Создаю $SPKI (Валидата без него падает в mkstores/zpki1utl)..."
cat >"$SPKI" <<'EOF'
[store]
count = 0
[Parameters]
PkiLdapTimeout = 10
PkiHttpTimeout = 60
EOF
chmod 644 "$SPKI"
ok "spki.ini создан"
else
ok "spki.ini уже есть"
fi
# --------------------------------------------------------------------- #
# 11. pki1.conf — делаем доступным для записи группе bj
# --------------------------------------------------------------------- #
PKI1=/opt/Validata/VDCSP/etc/pki1.conf
if [ -f "$PKI1" ]; then
chgrp bj "$PKI1"
chmod g+w "$PKI1"
ok "pki1.conf: group=bj, g+w"
if ! grep -q "^# --- bj-server: BEGIN ---" "$PKI1"; then
printf '\n# --- bj-server: BEGIN ---\n# Секции профилей дописываются автоматически при импорте через /admin/setup.\n# --- bj-server: END ---\n' >> "$PKI1"
ok "В pki1.conf добавлены маркеры bj-server"
fi
fi
# --------------------------------------------------------------------- #
# 12. udev-rule для авто-mount USB с .vdk
# --------------------------------------------------------------------- #
log "Ставлю udev-rule для авто-mount USB → /var/lib/bj/usb/..."
cat >/etc/udev/rules.d/99-bj-usb.rules <<'EOF'
# Авто-mount USB-флешек в /var/lib/bj/usb/<label> с владельцем bj.
# Применяется только к USB-устройствам (SUBSYSTEMS=="usb") с файловой
# системой. Mountpoint выбирается по метке тома или UUID.
ACTION=="add", KERNEL=="sd?[0-9]", SUBSYSTEMS=="usb", \
ENV{ID_FS_TYPE}!="", \
ENV{SYSTEMD_WANTS}="bj-usb-mount@$env{ID_FS_UUID}.service", \
ENV{SYSTEMD_USER_WANTS}=""
ACTION=="remove", KERNEL=="sd?[0-9]", SUBSYSTEMS=="usb", \
ENV{ID_FS_TYPE}!="", \
ENV{SYSTEMD_WANTS}="bj-usb-umount@$env{ID_FS_UUID}.service"
EOF
# Систэмный template-сервис, который монтирует и umonтирует
cat >/etc/systemd/system/bj-usb-mount@.service <<'EOF'
[Unit]
Description=Mount USB %i to /var/lib/bj/usb/%i for bj
DefaultDependencies=no
After=local-fs.target
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/bash -c 'mkdir -p /var/lib/bj/usb/%i && /usr/bin/mount -o uid=$(id -u bj),gid=$(id -g bj),fmask=0133,dmask=0022 UUID=%i /var/lib/bj/usb/%i'
ExecStop=/usr/bin/umount /var/lib/bj/usb/%i || true
EOF
cat >/etc/systemd/system/bj-usb-umount@.service <<'EOF'
[Unit]
Description=Umount USB %i from /var/lib/bj/usb/%i
DefaultDependencies=no
[Service]
Type=oneshot
ExecStart=/usr/bin/bash -c '/usr/bin/umount /var/lib/bj/usb/%i 2>/dev/null; /usr/bin/rmdir /var/lib/bj/usb/%i 2>/dev/null; true'
EOF
udevadm control --reload-rules
udevadm trigger
ok "udev-rule + systemd-mount шаблон установлены"
# --------------------------------------------------------------------- #
# 13. Установка bj-crypto.service unit (если его ещё нет — берём из репы)
# --------------------------------------------------------------------- #
SELF_DIR="$(cd "$(dirname "$0")" && pwd)"
REPO_UNIT="$SELF_DIR/../systemd/bj-crypto.service"
if [ ! -f /etc/systemd/system/bj-crypto.service ] && [ -f "$REPO_UNIT" ]; then
log "Устанавливаю /etc/systemd/system/bj-crypto.service из репы..."
install -m 0644 "$REPO_UNIT" /etc/systemd/system/bj-crypto.service
ok "bj-crypto.service установлен"
fi
# --------------------------------------------------------------------- #
# 14. daemon-reload + старт сервисов
# --------------------------------------------------------------------- #
log "systemctl daemon-reload..."
systemctl daemon-reload
log "Отключаю pcscd.socket (его подменяет наш drop-in always-on)..."
systemctl disable pcscd.socket 2>/dev/null || true
systemctl stop pcscd.socket 2>/dev/null || true
log "Запускаю pcscd..."
systemctl enable pcscd
systemctl restart pcscd
if [ -f /etc/systemd/system/bj-crypto.service ]; then
log "Запускаю bj-crypto..."
systemctl enable bj-crypto
systemctl restart bj-crypto
fi
# --------------------------------------------------------------------- #
# 15. Финальная проверка
# --------------------------------------------------------------------- #
echo
echo "================================================================"
echo " Валидата установлена, окружение настроено"
echo "================================================================"
for svc in pcscd vdcrysvc bj-crypto; do
if systemctl is-active --quiet "$svc" 2>/dev/null; then
echo "$svc — active"
else
echo "$svc — НЕ active (проверьте journalctl -u $svc)"
fi
done
echo
echo " Дальнейшие шаги:"
echo " 1. Подключите USB с .vdk → авто-маунт в /var/lib/bj/usb/<UUID>/"
echo " 2. Откройте /admin/setup в bj-server"
echo " 3. Загрузите .7z с профилем → bj-server сам всё извлечёт и импортирует"
echo " 4. Нажмите «Активировать профиль»"
echo "================================================================"