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:
@@ -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.
|
||||
@@ -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
|
||||
@@ -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; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
TEST3 GOST|TEST3GOST|WSL|t
|
||||
@@ -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'
|
||||
@@ -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
|
||||
@@ -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/
|
||||
```
|
||||
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 "================================================================"
|
||||
@@ -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.2026–01.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>
|
||||
@@ -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)».
|
||||
@@ -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)
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user