From 9b963eb0b94561f6a070b28880207efe1c0eaf8b Mon Sep 17 00:00:00 2001 From: zuevav Date: Thu, 24 Sep 2026 15:29:42 +0300 Subject: [PATCH] =?UTF-8?q?=D0=9E=D0=B1=D1=80=D0=B0=D0=B7=20=D0=BD=D0=B0?= =?UTF-8?q?=20Ubuntu=2022.04=20LTS=20(=D0=B0=D0=B2=D1=82=D0=BE=D1=83=D1=81?= =?UTF-8?q?=D1=82=D0=B0=D0=BD=D0=BE=D0=B2=D0=BA=D0=B0)=20=D1=81=D0=BE=20?= =?UTF-8?q?=D0=B2=D1=81=D1=82=D1=80=D0=BE=D0=B5=D0=BD=D0=BD=D0=BE=D0=B9=20?= =?UTF-8?q?=D0=A1=D0=9A=D0=97=D0=98=20=C2=AB=D0=92=D0=B0=D0=BB=D0=B8=D0=B4?= =?UTF-8?q?=D0=B0=D1=82=D0=B0=20=D0=9A=D0=BB=D0=B8=D0=B5=D0=BD=D1=82=20L?= =?UTF-8?q?=C2=BB:=20=D1=81=D0=B1=D0=BE=D1=80=D1=89=D0=B8=D0=BA=20build-is?= =?UTF-8?q?o-ubuntu.sh,=20=D1=88=D0=B0=D0=B3=20=D1=83=D1=81=D1=82=D0=B0?= =?UTF-8?q?=D0=BD=D0=BE=D0=B2=D1=89=D0=B8=D0=BA=D0=B0=20=C2=AB=D0=A1=D0=9A?= =?UTF-8?q?=D0=97=D0=98=20=D0=92=D0=B0=D0=BB=D0=B8=D0=B4=D0=B0=D1=82=D0=B0?= =?UTF-8?q?=C2=BB=20(=D0=B8=D0=B7=20=D0=BA=D0=BE=D0=BC=D0=BF=D0=BB=D0=B5?= =?UTF-8?q?=D0=BA=D1=82=D0=B0=20=D0=BE=D0=B1=D1=80=D0=B0=D0=B7=D0=B0,=20?= =?UTF-8?q?=D1=81=20=D1=81=D0=BE=D0=B2=D0=BC=D0=B5=D1=81=D1=82=D0=B8=D0=BC?= =?UTF-8?q?=D0=BE=D0=B9=20libldap=202.4),=20=D1=83=D1=81=D1=82=D0=B0=D0=BD?= =?UTF-8?q?=D0=BE=D0=B2=D1=89=D0=B8=D0=BA=20=D0=BF=D1=80=D0=B8=D0=BD=D0=B8?= =?UTF-8?q?=D0=BC=D0=B0=D0=B5=D1=82=20Ubuntu=2022.04?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014FsEKyJrYF69Cj9ZXzFLRi --- cmd/bj-installer/main.go | 35 +++++++++++++++- deploy/iso/bj-firstboot | 2 +- deploy/iso/build-iso-ubuntu.sh | 70 ++++++++++++++++++++++++++++++++ deploy/iso/ubuntu/user-data.tmpl | 35 ++++++++++++++++ 4 files changed, 140 insertions(+), 2 deletions(-) create mode 100755 deploy/iso/build-iso-ubuntu.sh create mode 100644 deploy/iso/ubuntu/user-data.tmpl diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index aaa9244..ca42150 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -144,6 +144,7 @@ func (in *installer) run() error { all := []step{ {"precheck", "Проверка системы", in.stepPrecheck}, {"packages", "Системные пакеты", in.stepPackages}, + {"validata", "СКЗИ «Валидата Клиент L»", in.stepValidata}, {"system", "Пользователь bj и каталоги", in.stepSystem}, {"license", "Активация лицензии", in.stepLicense}, {"database", "База данных PostgreSQL", in.stepDatabase}, @@ -203,9 +204,10 @@ func (in *installer) stepPrecheck() (string, error) { name := pick(osr["PRETTY_NAME"], id+" "+ver) switch { case id == "debian" && strings.HasPrefix(ver, "13"): + case id == "ubuntu" && strings.HasPrefix(ver, "22.04"): case id == "astra" || strings.Contains(like, "astra") || strings.Contains(strings.ToLower(name), "astra"): default: - return "", fmt.Errorf("поддерживаются Debian 13 и Astra Linux, а на сервере %s", name) + return "", fmt.Errorf("поддерживаются Ubuntu 22.04, Debian 13 и Astra Linux, а на сервере %s", name) } if runtime.GOARCH != "amd64" { return "", fmt.Errorf("нужна архитектура x86_64, а на сервере %s", runtime.GOARCH) @@ -667,6 +669,37 @@ func (in *installer) componentsFromBundle() (string, error) { return detail, nil } +// validataDir — пакеты «Валидата Клиент L», положенные образом ISO (zpki, zsdk и совместимая libldap-2.4-2). +const validataDir = "/var/lib/bj/validata" + +// stepValidata — ставит СКЗИ «Валидата Клиент L» из комплекта образа, если её ещё нет. +func (in *installer) stepValidata() (string, error) { + if out, err := run("dpkg-query", "-W", "-f", "${Version}", "zpki"); err == nil && strings.TrimSpace(out) != "" { + return "уже установлена, версия " + strings.TrimSpace(out), nil + } + debs, _ := filepath.Glob(in.P(validataDir) + "/*.deb") + if len(debs) == 0 { + return "не входит в комплект этой установки — поставьте по инструкции «Справка → Криптография»", nil + } + if in.root != "" { + return "проверка в песочнице: пакеты " + strings.Join(debs, ", "), nil + } + ld, _ := run("sh", "-c", "ldconfig -p 2>/dev/null | grep -c 'libldap_r-2.4.so.2' || true") + var args []string + for _, d := range debs { + if strings.Contains(filepath.Base(d), "libldap") && strings.TrimSpace(ld) != "0" && strings.TrimSpace(ld) != "" { + continue // в системе уже есть libldap 2.4 + } + args = append(args, d) + } + env := append(os.Environ(), "DEBIAN_FRONTEND=noninteractive") + if out, err := runEnv(env, "apt-get", append([]string{"install", "-y", "-qq", "--no-install-recommends"}, args...)...); err != nil { + return "", fmt.Errorf("установка Валидаты: %v\n%s", err, tail(out, 12)) + } + ver, _ := run("dpkg-query", "-W", "-f", "${Version}", "zpki") + return "установлена с образа, версия " + strings.TrimSpace(ver), nil +} + func (in *installer) stepServices() (string, error) { var files []string err := fs.WalkDir(deploy.Files, ".", func(p string, d fs.DirEntry, err error) error { diff --git a/deploy/iso/bj-firstboot b/deploy/iso/bj-firstboot index 8f3fe79..8ba82ca 100755 --- a/deploy/iso/bj-firstboot +++ b/deploy/iso/bj-firstboot @@ -52,7 +52,7 @@ show_accounts() { setup_accounts clear printf '\n Bridge&Join: установка на сервер\n ────────────────────────────────────────────────────────────\n' -printf ' Debian установлен. Осталось установить bj.\n\n' +printf ' Система установлена. Осталось установить bj.\n\n' if [ -f "$BUNDLE/manifest.json" ]; then printf ' Ключ продукта (BJ-XXXX-XXXX-XXXX) есть в портале клиента %s/portal/.\n' "$SERVER" printf ' Вводить его здесь необязательно: нажмите Enter — ядро bj установится с образа,\n а ключ вы введёте в браузере на первой странице настройки (там его удобно вставить).\n\n' diff --git a/deploy/iso/build-iso-ubuntu.sh b/deploy/iso/build-iso-ubuntu.sh new file mode 100755 index 0000000..d9d3839 --- /dev/null +++ b/deploy/iso/build-iso-ubuntu.sh @@ -0,0 +1,70 @@ +#!/bin/sh +# Сборка образа ISO Bridge&Join на основе Ubuntu 22.04 LTS Server (автоустановка). +# +# deploy/iso/build-iso-ubuntu.sh <версия> <каталог релиза> <каталог Валидаты> [канал] [выходной.iso] +# +# Каталог релиза — dist с подписанным manifest.json (в образ кладётся ядро bj: установка без ключа продукта). +# Каталог Валидаты — zpki/zsdk (ПК «Валидата Клиент L» для Linux, fs.moex.com ClientL_Other.zip) +# и libldap-2.4-2 из Ubuntu 20.04 (нужна zpki, в 22.04 её нет). Ubuntu 22.04 — ОС, на которой +# «Валидата Клиент L» ставится официально (Debian 13 её установщик не принимает). +# Меню загрузки (grub, BIOS и UEFI): по умолчанию — установка Bridge&Join с автоответами. +set -eu +SRC=${1:?ubuntu-22.04.x-live-server-amd64.iso} +INSTALLER=${2:?файл bj-installer} +VERSION=${3:?версия образа} +BUNDLE=${4:?каталог релиза с manifest.json} +VALIDATA=${5:?каталог с zpki/zsdk/libldap-2.4-2} +CHANNEL=${6:-stable} +OUT=${7:-bj-ubuntu22-$VERSION.iso} +HERE=$(cd "$(dirname "$0")" && pwd) +command -v xorriso >/dev/null || { echo "нужен xorriso"; exit 1; } +WORK=$(mktemp -d); trap 'rm -rf "$WORK"' EXIT +mkdir -p "$WORK/bj/bundle" "$WORK/bj/validata" "$WORK/nocloud" "$WORK/grub" + +install -m 0755 "$INSTALLER" "$WORK/bj/bj-installer" +install -m 0755 "$HERE/bj-firstboot" "$WORK/bj/bj-firstboot" +cp "$HERE/bj-firstboot.service" "$WORK/bj/" +install -m 0755 "$HERE/../linux/sbin/bj-welcome" "$WORK/bj/bj-welcome" +cp "$HERE/../systemd/bj-welcome.service" "$HERE/../systemd/bj-welcome.timer" "$WORK/bj/" +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") +[ -n "$FILES" ] || { echo "в релизе нет компонентов ядра"; exit 1; } +for f in $FILES; do cp "$BUNDLE/$f" "$WORK/bj/bundle/"; done + +# Валидата +n=0; for p in zpki zsdk libldap-2.4-2; do + f=$(ls "$VALIDATA"/${p}_*.deb "$VALIDATA"/${p}-*.deb 2>/dev/null | grep -E 'amd64\.deb$' | head -1 || true) + [ -n "$f" ] || { echo "в $VALIDATA нет пакета $p"; exit 1; } + cp "$f" "$WORK/bj/validata/"; n=$((n+1)) +done + +# автоответы (пароль root_bj — случайный и нигде не сохраняется: настоящий задаёт bj-firstboot) +HASH=$(openssl passwd -6 "$(head -c 32 /dev/urandom | base64)") +sed "s#@@PASSHASH@@#$HASH#" "$HERE/ubuntu/user-data.tmpl" > "$WORK/nocloud/user-data" +: > "$WORK/nocloud/meta-data" + +# меню загрузки: первый пункт — установка Bridge&Join +xorriso -osirrox on -indev "$SRC" -extract /boot/grub/grub.cfg "$WORK/grub/grub.orig" >/dev/null 2>&1 +{ + echo "set default=0" + echo "set timeout=10" + echo "loadfont unicode" + echo "menuentry --hotkey=b 'Install Bridge&Join $VERSION (Ubuntu 22.04)' {" + echo " set gfxpayload=keep" + echo " linux /casper/vmlinuz autoinstall ds=nocloud\;s=/cdrom/nocloud/ ---" + echo " initrd /casper/initrd" + echo "}" + sed '/^set timeout/d;/^loadfont/d' "$WORK/grub/grub.orig" +} > "$WORK/grub/grub.cfg" + +rm -f "$OUT" +xorriso -indev "$SRC" -outdev "$OUT" -boot_image any replay \ + -volid "BJ_UBUNTU22" \ + -map "$WORK/bj" /bj -map "$WORK/nocloud" /nocloud \ + -map "$WORK/grub/grub.cfg" /boot/grub/grub.cfg \ + -end >/dev/null 2>"$WORK/xorriso.log" || { cat "$WORK/xorriso.log"; exit 1; } +echo "Образ собран: $OUT ($(du -h "$OUT" | cut -f1)), канал $CHANNEL; ядро: $FILES; Валидата: $n пакета" diff --git a/deploy/iso/ubuntu/user-data.tmpl b/deploy/iso/ubuntu/user-data.tmpl new file mode 100644 index 0000000..b115129 --- /dev/null +++ b/deploy/iso/ubuntu/user-data.tmpl @@ -0,0 +1,35 @@ +#cloud-config +# Автоустановка Ubuntu 22.04 для образа Bridge&Join (deploy/iso/build-iso-ubuntu.sh). +# Спрашиваются только сеть (если DHCP нет — задать вручную) и диск (подтверждение стирания). +# Учётная запись root_bj (sudo) создаётся с неизвестным никому паролем — настоящие пароли +# root_bj и user_bj задаёт bj-firstboot при первой загрузке и показывает один раз. +autoinstall: + version: 1 + interactive-sections: + - network + - storage + locale: ru_RU.UTF-8 + keyboard: + layout: us,ru + toggle: grp:alt_shift_toggle + timezone: Europe/Moscow + identity: + hostname: bj-server + realname: "Администратор сервера bj" + username: root_bj + password: "@@PASSHASH@@" + ssh: + install-server: true + allow-pw: true + storage: + layout: + name: direct + updates: security + late-commands: + - mkdir -p /target/var/lib/bj /target/etc/bj /target/usr/local/sbin + - cp -r /cdrom/bj/bundle /target/var/lib/bj/bundle + - cp -r /cdrom/bj/validata /target/var/lib/bj/validata + - install -m 0755 /cdrom/bj/bj-installer /cdrom/bj/bj-firstboot /cdrom/bj/bj-welcome /target/usr/local/sbin/ + - install -m 0644 /cdrom/bj/bj-firstboot.service /cdrom/bj/bj-welcome.service /cdrom/bj/bj-welcome.timer /target/etc/systemd/system/ + - install -m 0644 /cdrom/bj/channel /target/etc/bj/channel + - curtin in-target --target=/target -- systemctl enable bj-firstboot.service bj-welcome.service bj-welcome.timer