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:
zuevav
2026-06-19 00:03:21 +03:00
parent 6e503433d4
commit 9737c787f9
110 changed files with 10771 additions and 1690 deletions
+79
View File
@@ -0,0 +1,79 @@
# Артефактория Bridge-and-Join-s
Сервис раздачи релизов и обновлений (#18). Клиенты (bj-server auto-update,
install.sh) скачивают **подписанный** манифест канала, проверяют подпись
зашитым публичным ключом и обновляют компоненты.
## Компоненты
- `internal/release` — формат манифеста + подпись Ed25519 (sign/verify, хеши).
- `cmd/bj-release` — инструмент издателя: генерация ключей, сборка и подпись манифеста.
- `cmd/bj-artifactory` — HTTP-сервер раздачи манифеста и артефактов.
- `deploy/artifactory/` — nginx (TLS) + systemd unit.
## Модель доверия
Один корневой Ed25519-ключ. Приватный (`signing.priv`) держит издатель в
секрете (НЕ в git). Публичный (`signing.pub`) зашивается в bj-server и в
install.sh. Манифест подписывается целиком — клиент проверяет подпись ДО
доверия версиям и хешам артефактов, затем сверяет sha256 каждого скачанного
файла с манифестом.
## Релизный цикл (издатель)
```bash
# 1. Однократно — сгенерировать ключи подписи (приватный хранить в секрете!)
bj-release keygen -out ./keys/signing
# → keys/signing.priv (секрет), keys/signing.pub
# Публичный base64 из вывода — зашить в bj-server (auto-update, #20)
# 2. Собрать артефакты релиза в каталог
mkdir -p dist/stable
cp bj-server crypto-service.jar dist/stable/
cp deploy/linux/install-validata.sh deploy/ish/configure-ish.sql dist/stable/
# 3. Собрать + подписать манифест
bj-release build -dir dist/stable -version 1.0.0 -channel stable \
-key keys/signing.priv -keyid main -out dist/stable/manifest.json \
-notes "Первый релиз"
# 4. Выложить каталог в хранилище артефактории
rsync -a dist/stable/ server:/var/lib/bj-artifactory/releases/stable/
```
## Сервер
```bash
bj-artifactory --addr 127.0.0.1:8090 --root /var/lib/bj-artifactory/releases
```
Раскладка хранилища:
```
releases/
stable/
manifest.json ← подписанный SignedManifest
bj-server
crypto-service.jar
install-validata.sh
configure-ish.sql
beta/
manifest.json
...
```
## HTTP API
| Метод | Путь | Ответ |
|---|---|---|
| GET | `/v1/<channel>/manifest.json` | подписанный манифест канала |
| GET | `/v1/<channel>/files/<name>` | артефакт по имени |
| GET | `/healthz` | `ok` |
За TLS-reverse-proxy (`nginx.conf`). Прод: `updates.example.com` → 127.0.0.1:8090.
## Дальше
- **#19 License-сервер** — манифест/обновления гейтятся годовым ключом.
- **#20 Auto-update в bj-server** — горутина: качает манифест канала, проверяет
подпись, сравнивает версии, atomic-replace бинарей, systemctl restart.
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Bridge-and-Join-s — Artifactory (раздача релизов и обновлений)
After=network-online.target
Wants=network-online.target
[Service]
Type=simple
User=bj-updates
Group=bj-updates
ExecStart=/opt/bj-artifactory/bj-artifactory --addr 127.0.0.1:8090 --root /var/lib/bj-artifactory/releases
Restart=on-failure
RestartSec=5
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadOnlyPaths=/var/lib/bj-artifactory
PrivateTmp=true
[Install]
WantedBy=multi-user.target
+44
View File
@@ -0,0 +1,44 @@
# nginx.conf — reverse-proxy для bj-artifactory с TLS.
# Раздаёт релизы и обновления bj-server по HTTPS.
#
# Установка: положить в /etc/nginx/sites-available/, заменить server_name
# и пути сертификатов, выпустить TLS через certbot, symlink в sites-enabled.
#
# updates.example.com → bj-artifactory на 127.0.0.1:8090
#
# bj-artifactory запускается как systemd-сервис (см. artifactory.service).
server {
listen 80;
server_name updates.example.com;
# Редирект на HTTPS (кроме ACME-челленджа certbot).
location /.well-known/acme-challenge/ { root /var/www/certbot; }
location / { return 301 https://$host$request_uri; }
}
server {
listen 443 ssl http2;
server_name updates.example.com;
ssl_certificate /etc/letsencrypt/live/updates.example.com/fullchain.pem;
ssl_certificate_key /etc/letsencrypt/live/updates.example.com/privkey.pem;
ssl_protocols TLSv1.2 TLSv1.3;
ssl_prefer_server_ciphers on;
# Манифесты маленькие — не кэшируем агрессивно (быстрое распространение релизов).
location /v1/ {
proxy_pass http://127.0.0.1:8090;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
# Артефакты могут быть крупными (jar ~20МБ) — без буферизации тела.
proxy_buffering off;
client_max_body_size 0;
}
location /healthz {
proxy_pass http://127.0.0.1:8090;
}
# Всё остальное — 404.
location / { return 404; }
}
+1
View File
@@ -0,0 +1 @@
TEST3 GOST|TEST3GOST|WSL|t
+132
View File
@@ -0,0 +1,132 @@
-- configure-ish.sql — автонастройка ИШ НРД без GUI.
--
-- Снято как эталон с рабочей GUI-конфигурации (deploy/ish/params-reference.txt)
-- и параметризовано. Воспроизводит то, что оператор делал бы мышкой в
-- igate.exe (Avalonia): PostgreSQL + Web API + WSL-канал TEST3-GOST.
--
-- Применяется к свежей БД ИШ ПОСЛЕ того как схема создана через
-- `igate-cli --data <dir>` (он накатывает EF-миграции при первом подключении).
--
-- Подстановки (заменяются установщиком через psql -v):
-- :channel_name — отображаемое имя канала, напр. 'TEST3 GOST'
-- :channel_code — локальный код канала, напр. 'TEST3GOST'
-- :wsl_endpoint — URL службы WSL НРД (TEST3-GOST)
-- :crypto_profile — имя профиля Валидаты ('moex')
-- :repository_code— код депонента из письма НРД ('MC0079200000')
-- :exchange_dir — рабочая папка обмена ('/var/lib/igate/exchange')
-- :web_port — порт Web API ('8090')
--
-- Пример:
-- psql -h 127.0.0.1 -U igate -d igate \
-- -v channel_name="'TEST3 GOST'" -v channel_code="'TEST3GOST'" \
-- -v wsl_endpoint="'https://gost-t3.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo'" \
-- -v crypto_profile="'moex'" -v repository_code="'MC0079200000'" \
-- -v exchange_dir="'/var/lib/igate/exchange'" -v web_port="'8090'" \
-- -f configure-ish.sql
BEGIN;
-- Чистим прежнюю конфигурацию (идемпотентность)
DELETE FROM parameters;
DELETE FROM channels;
-- --- Глобальные параметры: Web API (КРИТИЧНО — runEngineOnStartApp=True,
-- иначе движок не стартует в headless-режиме и Kestrel не поднимается) ---
INSERT INTO parameters(name, value, chanel_id) VALUES
('runEngineOnStartApp', 'True', NULL),
('server.useServer', 'True', NULL),
('server.host', 'localhost', NULL),
('server.port', :web_port, 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', 'False', NULL),
('cleanAtTime', '00:30:00', NULL),
('cleanWhenLarger', '1024', NULL),
('cleanVacuum', 'False', NULL),
('storePeriod', '15', NULL),
('archiveAutomatically', 'False', NULL);
-- --- WSL-канал ---
-- ВАЖНО: ИШ резолвит канал по СОСТАВНОМУ коду = <код канала> + <код депонента>
-- (так формирует ИШ-GUI: TEST3 + MC0413600000 = TEST3MC0413600000). С коротким
-- кодом ИШ падает 'more than one Channel' и admin API не видит канал.
INSERT INTO channels(name, code, type, enable)
VALUES (:channel_name, :channel_code || :repository_code, 'WSL', true);
-- Параметры канала привязываем к его id (находим по составному коду)
INSERT INTO parameters(name, value, chanel_id)
SELECT n, v, c.id FROM channels c, (VALUES
('enable', 'True'),
('wslEndpoint', :wsl_endpoint),
('cryptography.type', 'GOST'),
('cryptography.profile', :crypto_profile),
('cryptography.pincode', ''),
('cryptography.clientCertificateSerialNumber', ''),
('repositoryCode', :repository_code),
('fetchInterval', '00:01:00'),
('attemptInterval', '30000'),
('sendAttempts', '3'),
('maxPartSize', '500'),
('loadOldMessagesDepth', '3'),
('isIncomingEnabled', 'True'),
('isOutgoingEnabled', 'True'),
('isTransitTerminalChannel', 'False'),
('useDirectories', 'True'),
('dir', :exchange_dir),
('inboxDirName', 'INBOX'),
('outboxDirName', 'OUTBOX'),
('sentDirName', 'SENT'),
('errorDirName', 'ERRORS'),
('archive1042sDirName', :exchange_dir || '/Archives1042S'),
('enableLockFile', 'True'),
('enableAutoResponse', 'True'),
('enable1042ReportProcessing', 'True'),
('RenameOutgoingFiles', 'True'),
('generateReceivedPackageInfo', 'True'),
('generateSentPackageInfo', 'False'),
('moveReceiptsToSentFolder', 'False'),
('applyAddHashOfPackageToFolder', 'False'),
('ignorePackageDirectoryStructure', 'False'),
('checkReceivedPackageNsdSign', 'False'),
('checkReceivedPackageSenderSign', 'False'),
('autoUpdateTransitMember', 'False'),
('automaticcalyLoadCrls', 'False'),
('autoInPkgReportOffload', 'False'),
('monitoringThreshold', '00:00:10'),
-- Пустые параметры для полного соответствия эталону GUI (движок ожидает
-- их наличие; отсутствие части может дать «Invalid value» при старте).
('autoLoadCrlsTime', ''),
('fetchThreadCount', ''),
('forceCryPackageEncryption', ''),
('inPkgReportDirectory', ''),
('inPkgReportOffloadInterval', ''),
('maxPackagesPerJob', ''),
('nsdCertificateSerialNumbers', ''),
('pkiDecryptMode', ''),
('pkiEncryptMode', ''),
('pkiSignMode', ''),
('pkiVerifyMode', ''),
('receiveProcThreadCount', ''),
('sendProcThreadCount', ''),
('updateTransitMemberListTime', '')
) AS p(n, v)
WHERE c.code = :channel_code || :repository_code;
COMMIT;
\echo 'ИШ настроен. Перезапустите igate-svc: systemctl restart igate'
+83
View File
@@ -0,0 +1,83 @@
archiveAtTime||NULL
archiveAutomatically|False|NULL
archiveRecordsOlderThan||NULL
archiveWhenLarger||NULL
cleanAtTime|00:30:00|NULL
cleanAutomatically|False|NULL
cleanVacuum|False|NULL
cleanWhenLarger|1024|NULL
enableDbLogging|False|NULL
httpTimeout||NULL
packageBackupFolder||NULL
runEngineOnStartApp|False|NULL
server.authentication.enable|False|NULL
server.authentication.password||NULL
server.authentication.userName||NULL
server.certificate.file.password||NULL
server.certificate.file.path||NULL
server.certificate.storage|File|NULL
server.certificate.store.location|CurrentUser|NULL
server.certificate.store.name|My|NULL
server.host|localhost|NULL
server.port|8090|NULL
server.scheme|Http|NULL
server.useServer|True|NULL
storePeriod|15|NULL
wsl.httpsMode|Auto|NULL
wsl.maxConnsPerServer|4|NULL
wsl.proxy.address||NULL
wsl.proxy.mode|None|NULL
wsl.proxy.password||NULL
wsl.proxy.port|0|NULL
wsl.proxy.username||NULL
applyAddHashOfPackageToFolder|False|33
archive1042sDirName|/var/lib/igate/exchange/Archives1042S|33
attemptInterval|30000|33
autoInPkgReportOffload|False|33
autoLoadCrlsTime||33
automaticcalyLoadCrls|False|33
autoUpdateTransitMember|False|33
checkReceivedPackageNsdSign|False|33
checkReceivedPackageSenderSign|False|33
cryptography.clientCertificateSerialNumber||33
cryptography.pincode||33
cryptography.profile|My|33
cryptography.type|GOST|33
dir|/var/lib/igate/exchange|33
enable|True|33
enable1042ReportProcessing|True|33
enableAutoResponse|True|33
enableLockFile|True|33
errorDirName|ERRORS|33
fetchInterval|00:01:00|33
fetchThreadCount||33
forceCryPackageEncryption||33
generateReceivedPackageInfo|True|33
generateSentPackageInfo|False|33
ignorePackageDirectoryStructure|False|33
inboxDirName|INBOX|33
inPkgReportDirectory||33
inPkgReportOffloadInterval||33
isIncomingEnabled|True|33
isOutgoingEnabled|True|33
isTransitTerminalChannel|False|33
loadOldMessagesDepth|3|33
maxPackagesPerJob||33
maxPartSize|500|33
monitoringThreshold|00:00:10|33
moveReceiptsToSentFolder|False|33
nsdCertificateSerialNumbers||33
outboxDirName|OUTBOX|33
pkiDecryptMode||33
pkiEncryptMode||33
pkiSignMode||33
pkiVerifyMode||33
receiveProcThreadCount||33
RenameOutgoingFiles|True|33
repositoryCode|MC0079200000|33
sendAttempts|3|33
sendProcThreadCount||33
sentDirName|SENT|33
updateTransitMemberListTime||33
useDirectories|True|33
wslEndpoint|https://gost-t3.nsd.ru/onyx-ms/OnyxEdoWSService/OnyxEdo|33
+71
View File
@@ -0,0 +1,71 @@
# Лицензирование Bridge-and-Join-s (#19)
Годовые лицензии с офлайн-проверкой по подписи Ed25519 + опциональный
онлайн-отзыв.
## Компоненты
- `internal/license` — формат лицензии + подпись/проверка (offline).
- `cmd/bj-license` — издательский CLI: keygen, issue, verify.
- `cmd/bj-license-server` — онлайн-реестр отзывов (revocation).
- Интеграция в bj-server: `internal/lkgateway/licensecheck.go` — проверка
лицензии + гейт обновлений; UI раздел «Лицензия».
## Модель
Лицензия — самодостаточный подписанный токен: `payload.signature.keyid`.
bj-server проверяет подпись зашитым публичным ключом и срок **офлайн**
работает без связи с сервером. Online-сервер нужен только для отзыва.
**Гейт обновлений:** если лицензирование включено (есть публичный ключ),
авто-обновление (#20) выполняется только при валидной лицензии с фичей
`updates`. Без лицензирования (публичный ключ не зашит) — открытый режим,
гейты не действуют (бесплатная редакция / разработка).
## Издателю
```bash
# 1. Ключи лицензий (однократно; приватный — в секрете!)
bj-license keygen -out ./keys/license
# публичный base64 — зашить в bj-server (DefaultLicensePublicKey)
# 2. Выпустить годовую лицензию клиенту
bj-license issue -tenant "ООО Ромашка" -plan pro -days 365 \
-features updates,web-cabinet -key ./keys/license.priv -keyid main
# → выводит ключ payload.signature.keyid — отдать клиенту
# 3. Проверить
bj-license verify -key-file license.key -pub ./keys/license.pub
```
Планы: `free` (без фич), `pro` (перечисленные features), `enterprise`
(всё включено). Фичи: `updates`, `web-cabinet`, …
## Клиенту (on-prem bj-server)
Админ → Настройка → **Лицензия** → вставить ключ → «Активировать».
Проверка офлайн; статус (организация, план, срок, обновления) виден сразу.
## Онлайн-отзыв (опционально)
```bash
bj-license-server --addr :8091 --revoked /var/lib/bj-license/revoked.json
```
`revoked.json` — JSON-массив отозванных license ID:
```json
["28db4973-fde8-434c-b102-e83623eede2c"]
```
`GET /v1/check?id=<id>``{"revoked":true|false}`. Перечитывается раз в
минуту. В проде заменить файл на PostgreSQL + admin API выпуска/отзыва.
## Зашивка публичного ключа в релиз
```bash
go build -ldflags "\
-X .../lkgateway.DefaultLicensePublicKey=<base64-pub> \
-X .../lkgateway.DefaultUpdatePublicKey=<base64-pub-артефактории> \
-X .../lkgateway.BuildVersion=1.0.0" -o bj-server ./cmd/bj-server/
```
+388
View File
@@ -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 "================================================================"
@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="windows-1251"?>
<!--
АНКЕТА УЧАСТНИКА СЕРВИСА M2M (M2MTransferParticipantForm)
Назначение: регистрация нашего депозитарного кода в справочнике участников
M2M на стороне НРД. Без этой регистрации сервис МОСТ отклоняет запросы с
кодом M2M14 «Код ЭДО НРД отправителя отсутствует в справочнике участников M2M».
ВНИМАНИЕ:
1. Перед отправкой замените все значения ЗАПОЛНИТЬ_* на реальные реквизиты
организации. Это юридические данные — заполняет уполномоченное лицо.
2. Файл должен быть в кодировке windows-1251 (как объявлено в прологе).
Наш редактор хранит его в UTF-8 для удобства — перекодируйте перед
отправкой: iconv -f utf8 -t cp1251 form.xml > form.win1251.xml
3. Известное значение уже подставлено: депозитарный код MC0413600000
(выдан НРД для тестового контура TEST3, период 21.05.202601.09.2026).
4. Схема: DOC/M2MSchemas_260408/M2MTransferParticipantForm.xsd
-->
<pf:M2MTransferParticipantForm
xmlns:m2m="http://nsd.ru/schemas/m2m/types"
xmlns:pf="http://nsd.ru/schemas/m2m/participant/form"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://nsd.ru/schemas/m2m/participant/form M2MTransferParticipantForm.xsd">
<!-- Дата/время формирования анкеты по МСК. T — латиница, МСК — кириллица. -->
<pf:CreationTimestamp>2026-06-17T12:00:00(МСК)</pf:CreationTimestamp>
<pf:Participant>
<!-- ИНН организации (10 цифр для ЮЛ). -->
<m2m:INN>ЗАПОЛНИТЬ_ИНН</m2m:INN>
<m2m:Names>
<m2m:Rus>
<!-- Полное наименование по уставу. -->
<m2m:FullName>ЗАПОЛНИТЬ_ПОЛНОЕ_НАИМЕНОВАНИЕ</m2m:FullName>
<!-- Сокращённое наименование (необязательно). -->
<m2m:ShortName>ЗАПОЛНИТЬ_СОКРАЩЁННОЕ</m2m:ShortName>
<!-- Отображаемое короткое имя (показывается контрагенту). -->
<m2m:DisplayName>ЗАПОЛНИТЬ_ОТОБРАЖАЕМОЕ</m2m:DisplayName>
</m2m:Rus>
<!-- Английский блок необязателен; заполните, если есть. -->
<m2m:Eng>
<m2m:FullName>ЗАПОЛНИТЬ_FULL_NAME_EN</m2m:FullName>
<m2m:DisplayName>ЗАПОЛНИТЬ_DISPLAY_EN</m2m:DisplayName>
</m2m:Eng>
</m2m:Names>
<!-- Депозитарное место: наш код участника в НРД. УЖЕ ЗАПОЛНЕНО. -->
<m2m:DepositoryPlace>
<m2m:ParticipantCode>MC0413600000</m2m:ParticipantCode>
</m2m:DepositoryPlace>
<!-- Брокерское место — только если выступаем брокером. Иначе удалить блок. -->
<!--
<m2m:BrokerPlace>
<m2m:ParticipantCode>ЗАПОЛНИТЬ_БРОКЕРСКИЙ_КОД</m2m:ParticipantCode>
</m2m:BrokerPlace>
-->
</pf:Participant>
</pf:M2MTransferParticipantForm>
+58
View File
@@ -0,0 +1,58 @@
# Регистрация в справочнике участников M2M (НРД)
Закрывает блокер заявок с кодом **M2M14** — «Код ЭДО НРД отправителя
отсутствует в справочнике участников M2M». Технически наш контур (ИШ, СКЗИ,
канал, REST) работает; не хватает только регистрации нашего депозитарного
кода в справочнике сервиса МОСТ на стороне НРД.
## Что известно
| Параметр | Значение |
|-------------------------|---------------------------------------|
| Депозитарный код | `MC0413600000` |
| Тестовый контур | TEST3 (ГОСТ-криптография) |
| Период тестирования | 21.05.2026 — 01.09.2026 |
| Счёт (account_id) | `HL171004001C` |
| Раздел (section_id) | `36MC0413600000F00` |
## Что нужно заполнить (юридические реквизиты организации)
В файле `M2MTransferParticipantForm.example.xml` замените:
- `ЗАПОЛНИТЬ_ИНН` — ИНН организации (10 цифр для ЮЛ);
- `ЗАПОЛНИТЬ_ПОЛНОЕ_НАИМЕНОВАНИЕ` — полное наименование по уставу;
- `ЗАПОЛНИТЬ_СОКРАЩЁННОЕ` / `ЗАПОЛНИТЬ_ОТОБРАЖАЕМОЕ` — сокращённое и
отображаемое имя;
- английский блок `<m2m:Eng>` — при наличии, иначе удалить;
- `<m2m:BrokerPlace>` — только если выступаем брокером, иначе оставить
закомментированным.
Депозитарный код `MC0413600000` уже подставлен.
## Подготовка файла к отправке
Файл хранится в UTF-8 для удобства, а НРД ждёт **windows-1251** (как объявлено
в прологе XML). Перекодируйте перед отправкой:
```bash
iconv -f utf8 -t cp1251 M2MTransferParticipantForm.example.xml \
> M2MTransferParticipantForm.win1251.xml
```
(Опционально) проверьте по схеме, если установлен xmllint:
```bash
xmllint --noout \
--schema ../../DOC/M2MSchemas_260408/M2MTransferParticipantForm.xsd \
M2MTransferParticipantForm.win1251.xml
```
## Куда отправлять
Письмо на **M2MOST@nsd.ru** (служба сервиса МОСТ M2M), приложив заполненную
анкету. Текст письма — в `email-draft.txt`.
После того как НРД внесёт код `MC0413600000` в справочник участников M2M,
тестовый робот начнёт отвечать `M2MTransferDecision` вместо `M2MTransferResponse`
с ошибкой M2M14 — и заявки в bj-server будут доходить до статуса
«Подтверждена/Отклонена» по решению контрагента, а не «Отклонена (M2M14)».
+29
View File
@@ -0,0 +1,29 @@
Кому: M2MOST@nsd.ru
Тема: Регистрация участника сервиса M2M (тестовый контур TEST3, код MC0413600000)
Добрый день!
Просим зарегистрировать нашу организацию в справочнике участников сервиса
M2M (МОСТ) для тестового контура TEST3.
При отправке тестовых запросов M2MTransferRequest роботом возвращается
M2MTransferResponse со статусом ERROR и кодом M2M14 «Код ЭДО НРД отправителя
отсутствует в справочнике участников M2M». Технически интеграция настроена
(Интеграционный шлюз, ГОСТ-криптография, REST-обмен работают), требуется
только внесение нашего депозитарного кода в справочник участников M2M.
Реквизиты для регистрации:
- Депозитарный код участника: MC0413600000
- Тестовый контур: TEST3 (ГОСТ-криптография)
- Период тестирования: 21.05.2026 — 01.09.2026
- Полное наименование организации: ЗАПОЛНИТЬ
- ИНН: ЗАПОЛНИТЬ
Заполненная анкета участника (M2MTransferParticipantForm) во вложении.
После регистрации просим подтвердить — повторим тестовый сценарий с роботом.
С уважением,
ЗАПОЛНИТЬ_ФИО, ЗАПОЛНИТЬ_ДОЛЖНОСТЬ
ЗАПОЛНИТЬ_ОРГАНИЗАЦИЯ
ЗАПОЛНИТЬ_КОНТАКТ (телефон / e-mail)
@@ -0,0 +1,119 @@
<?xml version="1.0" encoding="windows-1251"?>
<M2MTransferRequest xmlns="http://nsd.ru/schemas/m2m/request">
<Header xmlns="http://nsd.ru/schemas/m2m/request">
<GUID xmlns="http://nsd.ru/schemas/m2m/types">b26440be-8a1e-4403-a35e-bc9df0da4a33</GUID>
<CreationTimestamp xmlns="http://nsd.ru/schemas/m2m/types">2026-06-18T13:13:50(МСК)</CreationTimestamp>
<SenderCode xmlns="http://nsd.ru/schemas/m2m/types">MC0413600000</SenderCode>
<ReceiverCode xmlns="http://nsd.ru/schemas/m2m/types">MC0012500000</ReceiverCode>
<CostInfo xmlns="http://nsd.ru/schemas/m2m/types">
<Yes xmlns="http://nsd.ru/schemas/m2m/types">
<Code xmlns="http://nsd.ru/schemas/m2m/types">MC0413600000</Code>
</Yes>
</CostInfo>
</Header>
<Data xmlns="http://nsd.ru/schemas/m2m/request">
<IsM2M xmlns="http://nsd.ru/schemas/m2m/types">true</IsM2M>
<InvestorInformation xmlns="http://nsd.ru/schemas/m2m/types">
<LastName xmlns="http://nsd.ru/schemas/m2m/types">Петров</LastName>
<FirstName xmlns="http://nsd.ru/schemas/m2m/types">Пётр</FirstName>
<MiddleName xmlns="http://nsd.ru/schemas/m2m/types">Петрович</MiddleName>
<IdentityDocument xmlns="http://nsd.ru/schemas/m2m/types">
<DocumentType xmlns="http://nsd.ru/schemas/m2m/types">21</DocumentType>
<DocumentSeries xmlns="http://nsd.ru/schemas/m2m/types">2001</DocumentSeries>
<DocumentNumber xmlns="http://nsd.ru/schemas/m2m/types">111111</DocumentNumber>
</IdentityDocument>
</InvestorInformation>
<TransferringDepository xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7702165310</INN>
</TransferringDepository>
<ReceivingDepository xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7722061076</INN>
</ReceivingDepository>
<TransferredSecurities xmlns="http://nsd.ru/schemas/m2m/types">
<Security xmlns="http://nsd.ru/schemas/m2m/types">
<ReferenceId xmlns="http://nsd.ru/schemas/m2m/types">M2M20260618RGVNK</ReferenceId>
<SecurityCode xmlns="http://nsd.ru/schemas/m2m/types">RU0007661625</SecurityCode>
<SecurityDetails xmlns="http://nsd.ru/schemas/m2m/types">
<ISIN xmlns="http://nsd.ru/schemas/m2m/types">RU0007661625</ISIN>
</SecurityDetails>
<Quantity xmlns="http://nsd.ru/schemas/m2m/types">
<Whole xmlns="http://nsd.ru/schemas/m2m/types">1</Whole>
</Quantity>
<SettlementAccount xmlns="http://nsd.ru/schemas/m2m/types">
<SettlementRequisites xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7702070139</INN>
</SettlementRequisites>
<SettlementLocation xmlns="http://nsd.ru/schemas/m2m/types">
<DeponentCode xmlns="http://nsd.ru/schemas/m2m/types">DP100200</DeponentCode>
<AccountId xmlns="http://nsd.ru/schemas/m2m/types">31MC0010000000A01</AccountId>
<SectionId xmlns="http://nsd.ru/schemas/m2m/types">A001</SectionId>
</SettlementLocation>
</SettlementAccount>
<IsolationStatus xmlns="http://nsd.ru/schemas/m2m/types">SGDN</IsolationStatus>
</Security>
<Security xmlns="http://nsd.ru/schemas/m2m/types">
<ReferenceId xmlns="http://nsd.ru/schemas/m2m/types">M2M20260618G5DW6</ReferenceId>
<SecurityCode xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JP5V6</SecurityCode>
<SecurityDetails xmlns="http://nsd.ru/schemas/m2m/types">
<ISIN xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JP5V6</ISIN>
</SecurityDetails>
<Quantity xmlns="http://nsd.ru/schemas/m2m/types">
<Whole xmlns="http://nsd.ru/schemas/m2m/types">1</Whole>
</Quantity>
<SettlementAccount xmlns="http://nsd.ru/schemas/m2m/types">
<SettlementRequisites xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7702070139</INN>
</SettlementRequisites>
<SettlementLocation xmlns="http://nsd.ru/schemas/m2m/types">
<DeponentCode xmlns="http://nsd.ru/schemas/m2m/types">DP100200</DeponentCode>
<AccountId xmlns="http://nsd.ru/schemas/m2m/types">31MC0010000000A01</AccountId>
<SectionId xmlns="http://nsd.ru/schemas/m2m/types">A001</SectionId>
</SettlementLocation>
</SettlementAccount>
<IsolationStatus xmlns="http://nsd.ru/schemas/m2m/types">SGDN</IsolationStatus>
</Security>
<Security xmlns="http://nsd.ru/schemas/m2m/types">
<ReferenceId xmlns="http://nsd.ru/schemas/m2m/types">M2M20260618CTDHY</ReferenceId>
<SecurityCode xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JPKH7</SecurityCode>
<SecurityDetails xmlns="http://nsd.ru/schemas/m2m/types">
<ISIN xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JPKH7</ISIN>
</SecurityDetails>
<Quantity xmlns="http://nsd.ru/schemas/m2m/types">
<Whole xmlns="http://nsd.ru/schemas/m2m/types">1</Whole>
</Quantity>
<SettlementAccount xmlns="http://nsd.ru/schemas/m2m/types">
<SettlementRequisites xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7702070139</INN>
</SettlementRequisites>
<SettlementLocation xmlns="http://nsd.ru/schemas/m2m/types">
<DeponentCode xmlns="http://nsd.ru/schemas/m2m/types">DP100200</DeponentCode>
<AccountId xmlns="http://nsd.ru/schemas/m2m/types">31MC0010000000A01</AccountId>
<SectionId xmlns="http://nsd.ru/schemas/m2m/types">A001</SectionId>
</SettlementLocation>
</SettlementAccount>
<IsolationStatus xmlns="http://nsd.ru/schemas/m2m/types">SGDN</IsolationStatus>
</Security>
<Security xmlns="http://nsd.ru/schemas/m2m/types">
<ReferenceId xmlns="http://nsd.ru/schemas/m2m/types">M2M20260618HQZ1Q</ReferenceId>
<SecurityCode xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JPGP8</SecurityCode>
<SecurityDetails xmlns="http://nsd.ru/schemas/m2m/types">
<ISIN xmlns="http://nsd.ru/schemas/m2m/types">RU000A0JPGP8</ISIN>
</SecurityDetails>
<Quantity xmlns="http://nsd.ru/schemas/m2m/types">
<Whole xmlns="http://nsd.ru/schemas/m2m/types">1</Whole>
</Quantity>
<SettlementAccount xmlns="http://nsd.ru/schemas/m2m/types">
<SettlementRequisites xmlns="http://nsd.ru/schemas/m2m/types">
<INN xmlns="http://nsd.ru/schemas/m2m/types">7702070139</INN>
</SettlementRequisites>
<SettlementLocation xmlns="http://nsd.ru/schemas/m2m/types">
<DeponentCode xmlns="http://nsd.ru/schemas/m2m/types">DP100200</DeponentCode>
<AccountId xmlns="http://nsd.ru/schemas/m2m/types">31MC0010000000A01</AccountId>
<SectionId xmlns="http://nsd.ru/schemas/m2m/types">A001</SectionId>
</SettlementLocation>
</SettlementAccount>
<IsolationStatus xmlns="http://nsd.ru/schemas/m2m/types">SGDN</IsolationStatus>
</Security>
</TransferredSecurities>
</Data>
</M2MTransferRequest>
@@ -0,0 +1,9 @@
<?xml version="1.0" encoding="windows-1251" standalone="yes"?>
<M2MTransferResponse xmlns:ns2="http://nsd.ru/schemas/m2m/types" xmlns:ns3="http://nsd.ru/schemas/m2m/response">
<ns3:GUID>00000000-0000-0000-0000-000000000000</ns3:GUID>
<ns3:StatusCode>ERROR</ns3:StatusCode>
<ns3:Response>
<ns2:Code>M2M14</ns2:Code>
<ns2:Text>Код ЭДО НРД отправителя сообщения отсутствует в справочнике участников M2M</ns2:Text>
</ns3:Response>
</M2MTransferResponse>
@@ -0,0 +1,55 @@
ПАКЕТ ДЛЯ ТЕХПОДДЕРЖКИ НРД (сервис MOEX МОСТ / M2M)
====================================================
Цель: показать НРД, ЧТО мы отправляем роботу и КАКОЙ ответ получаем, чтобы
поддержка однозначно поняла суть обращения и подтвердила/выполнила регистрацию
нашего кода в справочнике участников M2M тестового контура.
НАШИ РЕКВИЗИТЫ
-------------
Депозитарный код (= Код ЭДО НРД отправителя): MC0413600000
Тестовый контур: TEST3 (ГОСТ-криптография)
Канал ИШ: TEST3MC0413600000
Период тестирования: 21.05.2026 — 01.09.2026
ЧТО МЫ ОТПРАВЛЯЕМ → файл 01_outgoing_M2MTransferRequest.xml
--------------------------------------------------------------
Тип документа: M2MTransferRequest (#M2MTR)
GUID запроса: b26440be-8a1e-4403-a35e-bc9df0da4a33
CreationTimestamp: 2026-06-18T13:13:50(МСК)
SenderCode: MC0413600000 (наш код ЭДО НРД)
ReceiverCode: MC0012500000 (тестовый робот НРД)
Сценарий робота: 2001 (DocumentSeries), «принять все бумаги»
Бумаг в пакете: 4
Пакет подписан Интеграционным шлюзом (ИШ) сертификатом УЦ МБ.
ЧТО ОТВЕЧАЕТ НРД → файл 02_incoming_M2MTransferResponse.xml
--------------------------------------------------------------
Тип документа: M2MTransferResponse (#M2MER), подпись НРД — VALID
GUID: 00000000-0000-0000-0000-000000000000 (нулевой)
StatusCode: ERROR
Код: M2M14
Текст: «Код ЭДО НРД отправителя сообщения отсутствует в
справочнике участников M2M»
СУТЬ ОБРАЩЕНИЯ И ВОПРОС К НРД
----------------------------
Технически обмен работает: запрос доходит до сервиса МОСТ, НРД его принимает,
проверяет нашу подпись и отвечает. Отказ — на уровне справочника участников:
наш код ЭДО MC0413600000 в справочнике участников M2M (тестовый контур) пока
отсутствует.
Просим:
1) подтвердить, что отправителем M2M для нашего контура должен выступать
именно депозитарный код MC0413600000 (либо сообщить корректный код);
2) зарегистрировать наш код в справочнике участников M2M тестового контура,
чтобы робот (MC0012500000) распознавал отправителя и возвращал
M2MTransferDecision вместо ошибки M2M14.
Для поиска нашего запроса на стороне НРД: GUID b26440be-8a1e-4403-a35e-bc9df0da4a33
(ответ M2M14 приходит с нулевым GUID, поэтому указываем GUID исходного запроса).
ВЛОЖЕНИЯ
--------
01_outgoing_M2MTransferRequest.xml — наш запрос (то, что мы отправляем)
02_incoming_M2MTransferResponse.xml — ответ НРД (M2M14)
@@ -0,0 +1,44 @@
Кому: M2MOST@nsd.ru
Копия: soed@nsd.ru
Тема: M2M Автотестирование (МОСТ): ошибка M2M14 — регистрация кода MC0413600000 в справочнике участников
Добрый день!
Проводим автотестирование сервиса MOEX МОСТ (перевод M2M) с роботом в тестовом
контуре TEST3. Технически обмен настроен и работает: запрос доходит до сервиса,
НРД проверяет подпись и отвечает. Однако робот возвращает отказ:
StatusCode: ERROR
Код: M2M14
Текст: «Код ЭДО НРД отправителя сообщения отсутствует в справочнике
участников M2M»
Наши реквизиты:
- Депозитарный код (Код ЭДО НРД отправителя): MC0413600000
- Тестовый контур: TEST3, ГОСТ-криптография
- Период тестирования: 21.05.2026 — 01.09.2026
- GUID нашего запроса для поиска на вашей стороне:
b26440be-8a1e-4403-a35e-bc9df0da4a33
(ответ M2M14 приходит с нулевым GUID, поэтому указываем GUID запроса)
Во вложении — фактический обмен, чтобы предметно видеть, что мы отправляем и
что получаем в ответ:
1) 01_outgoing_M2MTransferRequest.xml — наш запрос роботу (MC0012500000),
SenderCode=MC0413600000, сценарий 2001;
2) 02_incoming_M2MTransferResponse.xml — ваш ответ с кодом M2M14.
Просим:
1) подтвердить, что отправителем M2M для нашего контура должен выступать
именно депозитарный код MC0413600000 (либо сообщить корректный код);
2) зарегистрировать наш код в справочнике участников M2M тестового контура,
чтобы робот распознавал отправителя и возвращал M2MTransferDecision
вместо ошибки M2M14.
Если для этого требуется уточнить/дополнить онлайн-заявку на участие в
тестировании систем НРД (система МОСТ, тип «M2M Автотестирование») — подскажите,
пожалуйста, что именно поправить.
С уважением,
ЗАПОЛНИТЬ_ФИО, ЗАПОЛНИТЬ_ДОЛЖНОСТЬ
ЗАПОЛНИТЬ_ОРГАНИЗАЦИЯ
ЗАПОЛНИТЬ_КОНТАКТ (телефон / e-mail)
+35
View File
@@ -0,0 +1,35 @@
[Unit]
Description=Bridge-and-Join-s — Crypto sidecar (Java + Валидата Клиент L)
Documentation=https://git.zetit.ru/zuevav/Bridge-and-Join-s
Before=bj-server.service
After=network-online.target pcscd.service
Wants=network-online.target
[Service]
Type=simple
User=bj
Group=bj
RuntimeDirectory=bj
RuntimeDirectoryMode=0750
Environment=BJ_CRYPTO_SOCKET=/run/bj/crypto.sock
Environment=BJ_CRYPTO_PROVIDER=validata
Environment=LD_LIBRARY_PATH=/opt/Validata/VDCSP/lib/amd64
ExecStart=/usr/bin/java \
-Djava.library.path=/opt/Validata/VDCSP/lib/amd64 \
-jar /opt/bj/crypto-service.jar
Restart=on-failure
RestartSec=5
StandardOutput=append:/var/log/bj/crypto-service.log
StandardError=append:/var/log/bj/crypto-service.err
NoNewPrivileges=true
ProtectSystem=strict
ProtectHome=true
ReadWritePaths=/run/bj /var/log/bj
PrivateTmp=true
[Install]
WantedBy=multi-user.target