Ключ: «У меня уже есть подготовленная флешка — использовать ключ с неё» — без записи на флешку

- Флешка с готовым ключом (например, с другого сервера bj) используется как есть:
  bj-keymedia-install use-media ничего на неё не пишет — справочник для ИШ
  пересобирается в копии на диске, из неё — профиль ИШ; этапы и кнопки — «использовать
  ключ с флешки», «на неё ничего не записывается».
- Ключевая флешка подключается к ИШ постоянно и только на чтение, как на рабочем
  сервере: строка fstab по UUID на /var/lib/igate/keymedia с автомонтированием
  (прежний fstab — копией), ссылка реестра ключей igate на флешку. И при обычной
  установке (запись архива) — тоже: на свежем сервере Валидата иначе не находила ключ.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi
This commit is contained in:
zuevav
2026-10-06 18:28:08 +03:00
parent 0a0408a3cc
commit dd59c47dc0
4 changed files with 152 additions and 6 deletions
+68
View File
@@ -274,6 +274,66 @@ func (h *setupHandlers) renderKeyWizard(w http.ResponseWriter, r *http.Request)
render(w, h.tpl.a.keyWizard, *d)
}
// keyWizardUseMedia — «использовать ключ с подготовленной флешки как есть»: на флешку ничего не пишется.
// Справочник для ИШ собирается из её копии на диске, флешка подключается к ИШ постоянно и только на чтение.
func (h *setupHandlers) keyWizardUseMedia(w http.ResponseWriter, r *http.Request, staging string) {
h.keyWiz.mu.Lock()
dev, prof := h.keyWiz.MediaDevice, h.keyWiz.MediaProfile
h.keyWiz.mu.Unlock()
found := false
for _, d := range listFlashDrives() {
found = found || d.Device == dev
}
if dev == "" || !found {
h.keyWizFlash(w, r, "Флешка с ключом не найдена — вставьте её и нажмите «Использовать ключ с флешки» ещё раз.")
return
}
contour := r.FormValue("contour")
if contour != "prod" {
contour = "test3"
}
ctx, cancel := context.WithTimeout(r.Context(), 3*time.Minute)
defer cancel()
res, err := runKeymediaOp(ctx, "use-media", map[string]string{"staging": staging, "profile": prof, "target_dev": dev, "channel": contourChannel(contour)})
h.keyWiz.mu.Lock()
if err != nil || res["ok"] != true {
msg := errStr(err)
if e, ok := res["error"].(string); ok {
msg = e
}
h.keyWiz.set(1, "error", "Ключ с флешки не подключён: "+msg)
h.keyWiz.mu.Unlock()
h.keyWizFlash(w, r, "Ключ с флешки не подключён: "+msg)
return
}
if p, ok := res["profile"].(string); ok {
h.keyWiz.Profile = p
}
rebuild, _ := res["rebuild"].(string)
igate, _ := res["igate"].(string)
h.keyWiz.set(1, "ok", "Флешка "+dev+" подключена к ИШ только для чтения — на неё ничего не записано")
h.keyWiz.set(2, "ok", fmt.Sprintf("Справочник для ИШ собран на диске (%s); профиль «%s» %s", rebuildRu(rebuild), h.keyWiz.Profile, igateRu(igate)))
h.keyWiz.set(3, "active", "")
h.keyWiz.mu.Unlock()
h.reactivateCryptoProfile(ctx)
ishOK, ishMsg := h.restartAndVerifyISH(ctx)
h.keyWiz.mu.Lock()
if !ishOK {
h.keyWiz.set(3, "error", "ИШ не подтвердил готовность: "+ishMsg)
h.keyWiz.mu.Unlock()
h.keyWizFlash(w, r, "Ключ подключён, но ИШ не готов: "+ishMsg)
return
}
h.keyWiz.set(3, "ok", "ИШ перезапущен и работает: "+ishMsg)
h.keyWiz.set(4, "ok", "Ключ с флешки подключён — проверьте подключения на странице «Статус»")
h.keyWiz.Done = true
h.keyWiz.mu.Unlock()
_ = os.RemoveAll(staging)
refreshCertStore(h.rc)
http.Redirect(w, r, keyWizBack(r)+"flash="+urlQ("Готово! Ключ с флешки подключён к ИШ как есть — флешка не менялась."), http.StatusSeeOther)
}
// keyWizardFromMedia — POST /admin/setup/keywizard/from-media: ключ уже на флешке (подготовленной bj раньше
// или с ключом и справочником от НРД). Флешка только читается: ключ и справочник копируются во временный
// каталог — дальше обычная установка на эту же флешку под тем же именем профиля (с резервной копией флешки).
@@ -316,6 +376,10 @@ func (h *setupHandlers) keyWizardFromMedia(w http.ResponseWriter, r *http.Reques
h.keyWiz.reset()
h.keyWiz.StagingID, h.keyWiz.VDK = id, vdk
h.keyWiz.MediaDevice, h.keyWiz.MediaProfile = drive.Device, prof
// флешка уже подготовлена: используется как есть — этапы называются по-другому, записи на флешку нет
h.keyWiz.Steps[0].Title = "Ключ найден на подготовленной флешке"
h.keyWiz.Steps[1].Title = "Флешка подключается как есть — на неё ничего не записывается"
h.keyWiz.Steps[2].Title = "Справочник сертификатов для ИШ — на диске сервера"
h.keyWiz.set(0, "ok", fmt.Sprintf("Ключ найден на флешке %s: %s, справочник %s%s", fallback(drive.Model, drive.Device), vdk, spr, prefixed(", профиль ", prof)))
h.keyWiz.set(1, "active", "")
h.keyWiz.mu.Unlock()
@@ -416,6 +480,10 @@ func (h *setupHandlers) keyWizardInstall(w http.ResponseWriter, r *http.Request)
return
}
staging := filepath.Join("/var/lib/bj/media/iso", id)
if r.FormValue("as_is") == "1" {
h.keyWizardUseMedia(w, r, staging)
return
}
// Выбор флешки и имя профиля из формы.
profileName := strings.TrimSpace(r.FormValue("profile_name"))
+9
View File
@@ -463,6 +463,15 @@ func TestDumpOnboardingPages(t *testing.T) {
h.renderWizard(w, httptest.NewRequest(http.MethodGet, fmt.Sprintf("/admin/wizard?step=%d", step), nil))
_ = os.WriteFile(filepath.Join(dir, fmt.Sprintf("wizard-%02d.html", step)), w.Body.Bytes(), 0o644)
}
// шаг «Ключ подписи»: ключ найден на подготовленной флешке — используется как есть
h.keyWiz = newKeyWizardState()
h.keyWiz.StagingID, h.keyWiz.VDK, h.keyWiz.MediaDevice, h.keyWiz.MediaProfile = "media-test", "CKJCI7AYNIOSNLXK.vdk", "/dev/sdb1", "PrUser1046"
h.keyWiz.Steps[0].Title, h.keyWiz.Steps[1].Title = "Ключ найден на подготовленной флешке", "Флешка подключается как есть — на неё ничего не записывается"
h.keyWiz.set(0, "ok", "Ключ найден на флешке USB2FlashStorage: CKJCI7AYNIOSNLXK.vdk, справочник spr1046, профиль PrUser1046")
wk := httptest.NewRecorder()
h.renderWizard(wk, httptest.NewRequest(http.MethodGet, "/admin/wizard?step=8", nil))
_ = os.WriteFile(filepath.Join(dir, "wizard-08-media.html"), wk.Body.Bytes(), 0o644)
h.keyWiz = nil
// шаг QUIK после проверки подключения — выбор «Как bj работает с QUIK»
q := rc.Snapshot().Quik
q.Server, q.User, q.Verified = "91.209.122.88", "BROKER_88", true
@@ -42,6 +42,29 @@
</form>
</div>
</div>
{{else if and $s.StagingID $s.MediaDevice}}
{{/* ключ с подготовленной флешки — используется как есть, на флешку ничего не пишется */}}
<div class="card">
<h2>Шаг 2 — использовать ключ с флешки</h2>
<p class="muted">На флешке уже подготовленный ключ: <code>{{fallbackTpl $s.VDK "—"}}</code>{{if $s.MediaProfile}}, профиль <code>{{$s.MediaProfile}}</code>{{end}}.
bj подключит его к шлюзу НРД <b>как есть</b>: на флешку ничего не записывается — справочник для ИШ собирается на диске сервера, флешка подключается только для чтения.</p>
<form method="post" action="/admin/setup/keywizard/install" style="margin-top:12px;display:grid;gap:12px;max-width:640px"
onsubmit="this.querySelector('button[type=submit]').disabled=true;this.querySelector('button[type=submit]').textContent='Подключаю… (до пары минут)';">
{{template "keyWizRet" .}}
<input type="hidden" name="as_is" value="1">
<div>
<label style="font-weight:600;display:block;margin-bottom:6px">Для какого контура НРД этот ключ</label>
{{$c := .SuggestContour}}{{if not $c}}{{$c = .WantContour}}{{end}}{{if not $c}}{{$c = currentContour}}{{end}}
{{if .SuggestIssuer}}<p class="muted" style="font-size:12px;margin:0 0 4px">Сертификат от «{{.SuggestIssuer}}» — это {{if eq .SuggestContour "test3"}}тестовый{{else}}боевой{{end}} контур.</p>{{end}}
<label style="display:flex;gap:8px;align-items:center;font-weight:400"><input type="radio" name="contour" value="test3" style="width:auto" {{if ne $c "prod"}}checked{{end}}> Тестовый TEST3</label>
<label style="display:flex;gap:8px;align-items:center;font-weight:400"><input type="radio" name="contour" value="prod" style="width:auto" {{if eq $c "prod"}}checked{{end}}> Боевой</label>
</div>
<button type="submit" class="btn btn-ok">Использовать ключ с флешки</button>
</form>
<form method="post" action="/admin/setup/keywizard/reset" style="margin-top:8px">{{template "keyWizRet" .}}
<button type="submit" class="btn btn-secondary">Отмена</button>
</form>
</div>
{{else if $s.StagingID}}
{{/* Архив загружен — выбор флешки + запись */}}
<div class="card">
@@ -107,7 +130,7 @@
<button type="submit" class="btn btn-ok">Загрузить и распаковать</button>
</form>
<div style="margin-top:18px;padding-top:14px;border-top:1px solid var(--border)">
<p class="muted" style="margin:0 0 8px"><b>Ключ уже на флешке</b> (готовили раньше или получили с ключом и справочником): bj возьмёт ключ и справочник с флешки — она только читается — и подключит их к шлюзу НРД. Архив не нужен.</p>
<p class="muted" style="margin:0 0 8px"><b>У меня уже есть подготовленная флешка</b> — использовать ключ с неё (например, флешка с другого сервера bj). Флешка используется как есть: bj её только читает, ничего на неё не записывает. Архив не нужен.</p>
{{if .Drives}}
<form method="post" action="/admin/setup/keywizard/from-media" style="display:grid;gap:8px;max-width:640px">
{{template "keyWizRet" .}}
@@ -118,7 +141,7 @@
<span class="muted" style="font-size:12px">{{$d.Device}}{{if $d.IsKeymedia}} · <b style="color:var(--accent)">сейчас ключевой носитель ИШ</b>{{end}}</span></span>
</label>
{{end}}
<button type="submit" class="btn btn-secondary" onclick="this.textContent='Читаю флешку…'">Взять ключ с флешки</button>
<button type="submit" class="btn btn-secondary" onclick="this.textContent='Читаю флешку…'">Использовать ключ с флешки</button>
</form>
{{else}}<p class="muted" style="font-size:13px;margin:0">Флешка не найдена — вставьте её в сервер и обновите страницу.</p>{{end}}
</div>