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>
This commit is contained in:
Executable
+388
@@ -0,0 +1,388 @@
|
||||
#!/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 "================================================================"
|
||||
Reference in New Issue
Block a user