feat(iso): образ Bridge&Join на основе Debian 13 netinst

- deploy/iso/build-iso.sh: пересборка официального netinst (xorriso, загрузочные записи
  BIOS и UEFI сохраняются), пункт по умолчанию «Установить Bridge&Join», текстовый установщик
- preseed.cfg: русская локаль, Москва, DHCP, SSH и пакеты для bj; спрашиваются только
  пароли, диск и подтверждение стирания
- bj-firstboot: при первой загрузке на экране сервера спрашивает ключ продукта и запускает
  bj-installer; адрес первичной настройки и код остаются на экране входа

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
zuevav
2026-09-16 00:48:56 +03:00
parent 4135435b51
commit d8dee07213
4 changed files with 207 additions and 0 deletions
+42
View File
@@ -0,0 +1,42 @@
#!/bin/bash
# Первая загрузка сервера, установленного с образа Bridge&Join: на экране сервера спрашивает ключ
# продукта и запускает bj-installer. После успешной установки служба выключается; адрес первичной
# настройки и одноразовый код остаются на экране входа (/etc/issue.d/bj-setup.issue).
set -u
MARK=/var/lib/bj/.bj/installed-from-iso
SERVER=https://lic.zetit.ru
CHANNEL=stable
[ -s /etc/bj/channel ] && CHANNEL=$(tr -d ' \n' < /etc/bj/channel)
finish() {
mkdir -p "$(dirname "$MARK")" && date -Iseconds > "$MARK"
systemctl disable bj-firstboot.service >/dev/null 2>&1
}
clear
printf '\n Bridge&Join: установка на сервер\n ────────────────────────────────────────────────\n\n'
printf ' Debian установлен. Осталось установить bj.\n'
printf ' Нужен ключ продукта (BJ-XXXX-XXXX-XXXX): он есть в портале клиента %s/portal/\n\n' "$SERVER"
for i in $(seq 1 60); do
curl -fsS -m 5 -o /dev/null "$SERVER/healthz" && break
[ "$i" = 1 ] && printf ' Жду сеть и связь с %s…\n' "$SERVER"
sleep 2
done
while true; do
read -r -p " Ключ продукта (пусто — установить позже): " CODE
CODE=$(printf '%s' "$CODE" | tr -d ' ' | tr '[:lower:]' '[:upper:]')
if [ -z "$CODE" ]; then
printf '\n Установка bj отложена. Позже выполните: sudo bj-installer install --code КЛЮЧ-ПРОДУКТА\n\n'
finish
sleep 3
exit 0
fi
if /usr/local/sbin/bj-installer install --code "$CODE" --server "$SERVER" --channel "$CHANNEL"; then
finish
read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _
exit 0
fi
printf '\n Установка не завершилась. Проверьте ключ продукта и сеть и попробуйте ещё раз.\n\n'
done
+21
View File
@@ -0,0 +1,21 @@
[Unit]
Description=Bridge&Join: установка bj при первой загрузке (ключ продукта на экране сервера)
After=network-online.target systemd-user-sessions.service
Wants=network-online.target
Before=getty@tty1.service
ConditionPathExists=!/var/lib/bj/.bj/installed-from-iso
[Service]
Type=oneshot
ExecStart=/usr/local/sbin/bj-firstboot
StandardInput=tty
StandardOutput=tty
StandardError=tty
TTYPath=/dev/tty1
TTYReset=yes
TTYVHangup=yes
TimeoutSec=0
RemainAfterExit=no
[Install]
WantedBy=multi-user.target
+73
View File
@@ -0,0 +1,73 @@
#!/bin/sh
# Сборка образа ISO Bridge&Join на основе официального Debian 13 netinst.
#
# deploy/iso/build-iso.sh <debian-13.x-amd64-netinst.iso> <bj-installer> <версия> [канал] [выходной.iso]
#
# В образ добавляются: файл ответов установщику (preseed.cfg), bj-installer и служба первой загрузки.
# Загрузочное меню BIOS (isolinux) и UEFI (grub) по умолчанию запускает установку с этими ответами.
# Загрузочные записи исходного образа сохраняются (xorriso -boot_image any replay).
# Нужен xorriso. Проверка исходного образа — по SHA256SUMS с cdimage.debian.org.
set -eu
SRC=${1:?исходный debian-13.x-amd64-netinst.iso}
INSTALLER=${2:?файл bj-installer}
VERSION=${3:?версия образа, например 2026.10.1}
CHANNEL=${4:-stable}
OUT=${5:-bj-debian13-$VERSION.iso}
HERE=$(cd "$(dirname "$0")" && pwd)
command -v xorriso >/dev/null || { echo "нужен xorriso: apt-get install xorriso"; exit 1; }
WORK=$(mktemp -d)
trap 'rm -rf "$WORK"' EXIT
mkdir -p "$WORK/bj" "$WORK/preseed" "$WORK/isolinux" "$WORK/grub"
cp "$HERE/preseed.cfg" "$WORK/preseed/bj.cfg"
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/bj-firstboot.service"
echo "$CHANNEL" > "$WORK/bj/channel"
echo "$VERSION" > "$WORK/bj/version"
BOOT="auto=true priority=high preseed/file=/cdrom/preseed/bj.cfg language=ru country=RU locale=ru_RU.UTF-8 keyboard-configuration/xkb-keymap=ru"
# BIOS: пункт по умолчанию в isolinux
xorriso -osirrox on -indev "$SRC" -extract /isolinux/txt.cfg "$WORK/isolinux/txt.orig" >/dev/null 2>&1
KERNEL=$(awk '/^\s*kernel/{print $2; exit}' "$WORK/isolinux/txt.orig")
INITRD=$(grep -o 'initrd=[^ ]*' "$WORK/isolinux/txt.orig" | head -1 | cut -d= -f2)
{
echo "default bj"
echo "label bj"
echo " menu label ^Установить Bridge&Join $VERSION (Debian 13)"
echo " menu default"
echo " kernel $KERNEL"
echo " append vga=788 initrd=$INITRD $BOOT --- quiet"
sed '/menu default/d;/^default /d' "$WORK/isolinux/txt.orig"
} > "$WORK/isolinux/txt.cfg"
xorriso -osirrox on -indev "$SRC" -extract /isolinux/isolinux.cfg "$WORK/isolinux/isolinux.cfg" >/dev/null 2>&1
sed -i 's/^timeout .*/timeout 100/; s/^prompt .*/prompt 0/' "$WORK/isolinux/isolinux.cfg"
grep -q '^timeout' "$WORK/isolinux/isolinux.cfg" || echo "timeout 100" >> "$WORK/isolinux/isolinux.cfg"
# UEFI: первый пункт в grub
xorriso -osirrox on -indev "$SRC" -extract /boot/grub/grub.cfg "$WORK/grub/grub.orig" >/dev/null 2>&1
# текстовый установщик (без gtk), как в BIOS: установка идёт без графической среды
LINUX=$(awk '/^\s*linux\s/ && $2 !~ /gtk/ {print $2; exit}' "$WORK/grub/grub.orig")
INITRDG=$(awk '/^\s*initrd\s/ && $2 !~ /gtk/ {print $2; exit}' "$WORK/grub/grub.orig")
{
echo "set default=0"
echo "set timeout=10"
echo "menuentry --hotkey=i 'Установить Bridge&Join $VERSION (Debian 13)' {"
echo " set background_color=black"
echo " linux $LINUX vga=788 $BOOT --- quiet"
echo " initrd $INITRDG"
echo "}"
sed '/^set default/d;/^set timeout/d' "$WORK/grub/grub.orig"
} > "$WORK/grub/grub.cfg"
rm -f "$OUT"
xorriso -indev "$SRC" -outdev "$OUT" -boot_image any replay \
-volid "BJ_DEBIAN13" \
-map "$WORK/preseed" /preseed \
-map "$WORK/bj" /bj \
-map "$WORK/isolinux/txt.cfg" /isolinux/txt.cfg \
-map "$WORK/isolinux/isolinux.cfg" /isolinux/isolinux.cfg \
-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"
+71
View File
@@ -0,0 +1,71 @@
#_preseed_V1
# Ответы установщику Debian 13 для образа Bridge&Join (deploy/iso/build-iso.sh).
# Спрашиваются только пароли, диск (если их несколько) и подтверждение стирания; остальное задано здесь.
# После установки служба bj-firstboot на экране сервера спросит ключ продукта и установит bj.
# Язык, клавиатура, время
d-i debian-installer/language string ru
d-i debian-installer/country string RU
d-i debian-installer/locale string ru_RU.UTF-8
d-i localechooser/supported-locales multiselect ru_RU.UTF-8, en_US.UTF-8
d-i keyboard-configuration/xkb-keymap select ru
d-i keyboard-configuration/toggle select Alt+Shift
d-i clock-setup/utc boolean true
d-i time/zone string Europe/Moscow
d-i clock-setup/ntp boolean true
# Сеть: DHCP на первом подключённом интерфейсе
d-i netcfg/choose_interface select auto
d-i netcfg/get_hostname string bj-server
d-i netcfg/get_domain string
d-i netcfg/hostname string bj-server
d-i hw-detect/load_firmware boolean true
# Зеркало пакетов
d-i mirror/country string manual
d-i mirror/http/hostname string deb.debian.org
d-i mirror/http/directory string /debian
d-i mirror/http/proxy string
d-i apt-setup/cdrom/set-first boolean false
d-i apt-setup/non-free-firmware boolean true
d-i apt-setup/services-select multiselect security, updates
# Учётные записи: пароль root и администратор ОС задаются при установке
d-i passwd/root-login boolean true
d-i passwd/make-user boolean true
d-i passwd/user-fullname string Администратор
d-i passwd/username string bjadmin
# Разметка: весь выбранный диск, одна файловая система; диск и стирание подтверждаются при установке
d-i partman-auto/method string regular
d-i partman-auto/choose_recipe select atomic
d-i partman-auto-lvm/guided_size string max
d-i partman-partitioning/confirm_write_new_label boolean true
d-i partman/choose_partition select finish
d-i partman/default_filesystem string ext4
d-i partman-md/confirm boolean true
d-i partman-lvm/device_remove_lvm boolean true
d-i partman-md/device_remove_md boolean true
# Пакеты: без графики; SSH и всё, что нужно bj, ставится сразу
tasksel tasksel/first multiselect standard, ssh-server
d-i pkgsel/include string ca-certificates curl postgresql postgresql-client default-jre-headless pcscd python3 sudo
d-i pkgsel/upgrade select safe-upgrade
popularity-contest popularity-contest/participate boolean false
# Загрузчик
d-i grub-installer/only_debian boolean true
d-i grub-installer/with_other_os boolean false
d-i grub-installer/bootdev string default
# Установщик bj и служба первой загрузки (файлы лежат в /bj на образе)
d-i preseed/late_command string \
install -m 0755 /cdrom/bj/bj-installer /target/usr/local/sbin/bj-installer; \
install -m 0755 /cdrom/bj/bj-firstboot /target/usr/local/sbin/bj-firstboot; \
install -m 0644 /cdrom/bj/bj-firstboot.service /target/etc/systemd/system/bj-firstboot.service; \
mkdir -p /target/etc/bj; \
if [ -f /cdrom/bj/channel ]; then install -m 0644 /cdrom/bj/channel /target/etc/bj/channel; fi; \
in-target systemctl enable bj-firstboot.service
d-i finish-install/reboot_in_progress note
d-i cdrom-detect/eject boolean true