diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index 36ff6d2..7623d6b 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -634,7 +634,12 @@ func (in *installer) componentsFromBundle() (string, error) { default: src := filepath.Join(in.bundle, art.File) if _, err := os.Stat(src); err != nil { - return "", fmt.Errorf("в релизе нет файла %s", art.File) + if c.Name == "bj-server" { + return "", fmt.Errorf("в релизе нет файла %s", art.File) + } + // в образ кладётся не всё: недостающее поставит первое обновление после ввода ключа + skipped = append(skipped, c.Title+" (поставится обновлением)") + continue } body, err := os.ReadFile(src) if err != nil { diff --git a/deploy/iso/build-iso-ubuntu.sh b/deploy/iso/build-iso-ubuntu.sh index 6471743..ce1d14b 100755 --- a/deploy/iso/build-iso-ubuntu.sh +++ b/deploy/iso/build-iso-ubuntu.sh @@ -31,7 +31,7 @@ echo "$CHANNEL" > "$WORK/bj/channel"; echo "$VERSION" > "$WORK/bj/version" # ядро bj: подписанный манифест + bj-server, bj-component-install [ -s "$BUNDLE/manifest.json" ] || { echo "в $BUNDLE нет manifest.json"; exit 1; } cp "$BUNDLE/manifest.json" "$WORK/bj/bundle/" -FILES=$(python3 -c 'import json,sys,base64;s=json.load(open(sys.argv[1]));m=json.loads(base64.b64decode(s["payload"]));print(" ".join(a["file"] for a in m["artifacts"] if a["name"] in ("bj-server","bj-component-install")))' "$BUNDLE/manifest.json") +FILES=$(python3 -c 'import json,sys,base64;s=json.load(open(sys.argv[1]));m=json.loads(base64.b64decode(s["payload"]));print(" ".join(a["file"] for a in m["artifacts"] if a["name"] in ("bj-server","bj-component-install","bj-installer","crypto-service.jar")))' "$BUNDLE/manifest.json") [ -n "$FILES" ] || { echo "в релизе нет компонентов ядра"; exit 1; } for f in $FILES; do cp "$BUNDLE/$f" "$WORK/bj/bundle/"; done diff --git a/internal/lkgateway/setup.go b/internal/lkgateway/setup.go index dab39bf..4209fd9 100644 --- a/internal/lkgateway/setup.go +++ b/internal/lkgateway/setup.go @@ -238,30 +238,21 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { d.Done.NSD = s.NSD.IGWBaseURL != "" && s.NSD.Profile != "" d.Done.TestRun = s.LastTest != nil || (!d.DevMode && d.Done.NSD) // вне режима разработки тестовой заявки нет - // Определяем текущий шаг. - step := 1 + // Мастер идёт по порядку: каждый шаг показывается, даже если он уже выполнен (установщиком, + // лицензией) — на нём написано, что сделано, и пользователь сам нажимает «Далее». Без ?step + // продолжаем со следующего за последним пройденным шагом. + progress := wizardProgress() + step := progress + 1 if v := r.URL.Query().Get("step"); v != "" { if n, err := strconv.Atoi(v); err == nil && n >= 1 && n <= 7 { step = n + if n-1 > progress { + setWizardProgress(n - 1) + } } - } else { - // Авто: первый незавершённый. - switch { - case !d.Done.License: - step = 1 - case !d.Done.Postgres: - step = 2 - case !d.Done.Depo: - step = 3 - case !d.Done.Crypto: - step = 4 - case !d.Done.Certs: - step = 5 - case !d.Done.NSD: - step = 6 - default: - step = 7 - } + } + if step > 7 { + step = 7 } d.Step = step if step == 1 && d.Done.License { @@ -273,35 +264,26 @@ func (h *setupHandlers) renderWizard(w http.ResponseWriter, r *http.Request) { } cancel() } - d.AutoDone = wizardAutoDone(&d) render(w, h.tpl.a.wizard, d) } -// wizardAutoDone — пояснения к шагам до текущего, которые уже выполнены: мастер переходит к первому -// незавершённому шагу сам, и без пояснения непонятно, почему предыдущие отмечены галочкой. -func wizardAutoDone(d *WizardData) []string { - var out []string - if d.Step > 1 && d.Done.License { - out = append(out, "1. Лицензия — активирована: "+d.Lic.Tenant+".") +// wizardProgressFile — номер последнего пройденного шага мастера (переход «Далее» на шаг N +// означает, что шаги до N пройдены). +const wizardProgressFile = "/var/lib/bj/.bj/wizard-progress" + +func wizardProgress() int { + b, err := os.ReadFile(wizardProgressFile) + if err != nil { + return 0 } - if d.Step > 2 && d.Done.Postgres { - t := "2. База bj — готова" - if d.PGSummary != "" { - t += " (" + d.PGSummary + ")" - } - out = append(out, t+": её создал установщик, настраивать ничего не нужно.") + n, _ := strconv.Atoi(strings.TrimSpace(string(b))) + return n +} + +func setWizardProgress(n int) { + if err := os.WriteFile(wizardProgressFile, []byte(strconv.Itoa(n)+"\n"), 0o600); err != nil { + log.Printf("wizard: прогресс мастера не сохранён: %v", err) } - if d.Step > 3 && d.Done.Depo { - if !d.DepoLicensed { - out = append(out, "3. Учёт DEPO — не нужен: модуль обмена с DEPO и QUIK не входит в лицензию. Когда модуль добавят, шаг станет доступен.") - } else { - out = append(out, "3. Учёт DEPO — подключение уже сохранено ("+d.Settings.Fancy.Server+").") - } - } - if d.Step > 4 && d.Done.Crypto && d.ValidataVer != "" { - out = append(out, "4. Валидата — установлена вместе с bj (версия "+d.ValidataVer+").") - } - return out } // dsnSummary — «host:port, база name» из строки подключения PostgreSQL, без логина и пароля. diff --git a/internal/lkgateway/web/templates/admin_wizard.html b/internal/lkgateway/web/templates/admin_wizard.html index 3f62de2..db80274 100644 --- a/internal/lkgateway/web/templates/admin_wizard.html +++ b/internal/lkgateway/web/templates/admin_wizard.html @@ -41,11 +41,7 @@
-{{if .AutoDone}}
-

Уже готово — эти шаги пропущены, делать там ничего не нужно:

- {{range .AutoDone}}

✓ {{.}}

{{end}} -

Открыть любой шаг можно кнопками «← К шагу» внизу.

-
{{end}} + {{/* ============= ШАГ 1: Лицензия ============= */}} {{if eq .Step 1}} @@ -57,7 +53,7 @@ {{if .Done.License}}

✓ Лицензия активна: {{.Lic.Tenant}}, план {{.Lic.Plan}}, модули: {{if .Lic.Features}}{{range $i, $f := .Lic.Features}}{{if $i}}, {{end}}{{$f}}{{end}}{{else}}только ядро{{end}}; {{.Lic.Message}}

{{end}} {{with .Update}}{{if .HasUpdate}}
-

Есть обновление: версия {{.Available}}

+

На сервере лицензий есть что поставить (выпуск {{.Available}})

Образ установил ту версию bj, с которой был собран. На сервере лицензий есть более новая — с исправлениями ядра, установщика и служб. Поставьте её сейчас, до настройки: bj перезапустится примерно на 10 секунд, затем обновите страницу.
{{.Message}}

@@ -107,7 +103,7 @@ {{/* ============= ШАГ 1: PostgreSQL ============= */}} {{if eq .Step 2}}
-

Шаг 2. PostgreSQL

+

Шаг 2. База данных bj (PostgreSQL)

База самого bj: журнал заявок и поручений, история обмена с НРД и DEPO, пользователи.

{{if .Done.Postgres}}

✓ База готова{{if .PGSummary}}: {{.PGSummary}}{{end}}. Её создал установщик — ничего делать не нужно, переходите к следующему шагу.

{{end}} @@ -137,7 +133,7 @@
- + ← К шагу 1 {{if .Done.Postgres}}К шагу 3 →{{else}}{{end}}
{{if not .Done.Postgres}}

Пропустить (буду работать в режиме in-memory — без сохранения сделок)

{{end}} @@ -151,7 +147,7 @@

СКЗИ «Валидата Клиент L» проверяет подписи НРД на квитанциях и сообщениях и нужно для работы с ключами и сертификатами (шаг 5).

{{if .ValidataVer}}

✓ Валидата установлена вместе с bj: версия {{.ValidataVer}}, каталог /opt/Validata/VDCSP. Ничего настраивать на этом шаге не нужно.

-

bj работает с Валидатой через свою службу криптографии bj-crypto{{if eq .CryptoSvc "active"}} — она запущена{{else}} — она запустится, когда на шаге 5 будут загружены ключи и сертификаты{{end}}.

+

bj работает с Валидатой через свою службу криптографии bj-crypto{{if eq .CryptoSvc "active"}} — она запущена{{else}} — сейчас она не запущена: служба ставится обновлением с сервера лицензий (шаг 1, кнопка «Обновить сейчас»), а работает с ключом, установленным на шаге 5{{end}}.

{{else}}

Валидата на этом сервере не найдена.

@@ -174,41 +170,22 @@ {{if eq .Step 5}}

Шаг 5. Сертификаты

-

Импортируйте сертификаты вашей организации и сертификаты УЦ НРД (для проверки квитанций).

+

Здесь две части: ключ и сертификат вашей организации (ставите вы) и сертификаты НРД и удостоверяющих центров (bj ведёт их сам).

-
- Что говорят документы НРД (DOC/Инструккия M2M.pdf, стр. 11, 16-19): -
    -
  • Наши пакеты должны быть подписаны сертификатом УЦ МБ (Удостоверяющий центр Московской Биржи).
  • -
  • В режиме ИШ НРД: подписывает сам ИШ — наш ключ настраивается в ИШ, не здесь. Bj-server нужен только для проверки квитанций НРД и (опц.) расшифровки 4BROKER01.
  • -
  • В режиме прямого ONYX без ИШ: bj-server подписывает сам — нужен наш ключ с приватной частью.
  • -
- Что куда загружать: -
    -
  • Корневой сертификат УЦ МБ (ca.moex.com) — для проверки цепочки нашей подписи и подписей контрагентов.
  • -
  • Корневой и подписной УЦ НРД (nsd.ru/workflow/system/cryptography/) — для проверки квитанций от НРД.
  • -
  • Наш сертификат с приватным ключом (только если без ИШ) — для подписи пакетов и расшифровки 4BROKER01.
  • -
-

Полный регламент PKI — в «Правилах ЭДО НРД» и «Руководстве по установке ИШ» (nsd.ru/ru/documents/workflow/) — в наших PDF этого не описано.

+

1. Ключ и сертификат организации

+
Что это. Ключ ЭП, которым подписываются сообщения в НРД, и его сертификат (выдаёт УЦ Московской биржи). Архив ключа с паролем вам передаёт НРД/УЦ.
+ Как поставить. Мастер установки ключа: загрузить архив → запись на носитель → справочник сертификатов → проверка подписи Валидатой.
+ Открыть мастер установки ключа → + +

2. Сертификаты НРД и УЦ — автоматически

+

✓ Автоматическое обновление {{if .Settings.NSDCerts.Manual}}выключено (включается в «Настройка → Криптография»){{else}}включено{{end}}. Делать на этом шаге ничего не нужно.

+
+ Как это работает. Действующие сертификаты НРД и удостоверяющих центров Московской биржи поставляются вместе с bj и приезжают с обновлениями. Каждые 6 часов bj сверяет их со справочником сертификатов ИШ: добавляет недостающие, а заменённые НРД убирает строго после срока, который НРД объявил в уведомлении. Перед каждым изменением делается резервная копия и проверка подписи, при ошибке всё откатывается.
+ Тестовые и боевые. Сертификат попадает только в справочник своего контура: боевые (издатель «Корневой УЦ») — только к боевому ключу, тестовые (издатель «Тестовый УЦ») — только к тестовому. Поэтому смешать контуры нельзя.
+ Откуда берутся. НРД и Московская биржа публикуют сертификаты в постоянных местах: комплект ГОСТ МБ, НКЦ и НРД — https://fs.moex.com/cdp/sert/GOST.zip, сертификаты УЦ — https://fs.moex.com/cdp/. Когда там появляется новый сертификат, мы добавляем его в комплект bj, и он приезжает к вам с обновлением.
+ Состояние, ручная проверка и выключение — «Настройка → Криптография → Сертификаты НРД в справочнике ИШ».
- -

Авто-загрузка сертификатов УЦ НРД

-

Самый простой способ — добавить прямые URL .cer-файлов УЦ НРД (с nsd.ru/workflow/system/cryptography/) и включить авто-обновление. Раз в сутки система перепроверит и сохранит изменённые сертификаты в /var/lib/bj/ca-certs/.

- - - -
- -
- -
- - {{if not .Settings.CACerts.LastFetch.IsZero}}Последнее обновление: {{.Settings.CACerts.LastFetch.Format "02.01.2006 15:04:05"}}{{end}} -
+ {{if .Settings.NSDCerts.LastRows}}

Последняя сверка: {{.Settings.NSDCerts.LastRun.Format "02.01.2006 15:04"}}{{if .Settings.NSDCerts.LastProfile}}, профиль ИШ {{.Settings.NSDCerts.LastProfile}}{{end}}.

{{else}}

Первая сверка пройдёт, когда будут настроены ключ и шлюз НРД (шаг 6).

{{end}} {{if .Certs}}

Установленные сертификаты ({{len .Certs}})

@@ -227,7 +204,7 @@ {{else}} -

Пока сертификаты не импортированы.

+

Ключ организации ещё не установлен. Можно пропустить этот шаг и вернуться к нему, когда будет архив ключа.

{{end}}
@@ -268,7 +245,7 @@

-

Депозитарные реквизиты организации в НРД. {{if .Settings.Fancy.Server}}Их можно взять из базы DEPO (Fancy): кнопка «Определить из DEPO» подставит найденное в пустые поля — проверьте и сохраните.{{else}}Если учёт ведётся в Fancy DEPO — сначала настройте подключение к базе в разделе Обмен Fancy, тогда реквизиты подставятся из DEPO автоматически; иначе введите их из договора с НРД.{{end}}

+

Депозитарные реквизиты организации в НРД. {{if .Settings.Fancy.Server}}Их можно взять из базы DEPO (Fancy): кнопка «Определить из DEPO» подставит найденное в пустые поля — проверьте и сохраните.{{else}}Если учёт ведётся в Fancy DEPO — сначала подключите базу DEPO на шаге 3, тогда реквизиты подставятся из DEPO автоматически; иначе введите их из договора с НРД.{{end}}

{{if .Settings.Fancy.Server}}{{end}} {{if .Suggest}}{{if .Suggest.AccountID}}
Найдено в DEPO: счёт {{.Suggest.AccountID}}, код {{.Suggest.DeponentCode}}, раздел {{.Suggest.SectionID}}{{if .Suggest.OrgName}}; организация {{.Suggest.OrgName}}, ИНН {{.Suggest.OrgINN}}{{end}}.{{range .Suggest.Notes}}
⚠ {{.}}{{end}}
{{end}}{{end}}
@@ -339,7 +316,7 @@

Итоговая сводка

- + {{if devOn}}{{end}}
PostgreSQL{{if .Done.Postgres}}настроен{{else}}in-memory{{end}}
Крипто-провайдер{{if .Done.Crypto}}{{.Settings.Crypto.Provider}}{{else}}не настроен{{end}}
Крипто-провайдер{{if .Done.Crypto}}Валидата Клиент L{{if .ValidataVer}} {{.ValidataVer}}{{end}}{{else}}не настроен{{end}}
Сертификатов установлено{{len .Certs}}
ИШ НРД{{if .Done.NSD}}{{.Settings.NSD.Profile}}{{else}}{{if devOn}}mock-режим{{else}}не настроен{{end}}{{end}}
Тестовый прогон{{if .Done.TestRun}}пройден{{else}}не запускался{{end}}