#!/bin/sh # Сборка образа ISO Bridge&Join на основе Ubuntu 22.04 LTS Server (автоустановка). # # deploy/iso/build-iso-ubuntu.sh <версия> <каталог релиза> <каталог Валидаты> [канал] [выходной.iso] # # LAYER=<каталог> — слой обновлений Ubuntu и пакеты bj из build-ubuntu-updates-layer.sh: установка # ставит систему уже с обновлениями и не качает PostgreSQL/Java из сети. Без LAYER — как в Ubuntu 22.04.5. # # Каталог релиза — 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","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 # слой обновлений Ubuntu (необязательно) LAYER=${LAYER:-} LNAME=ubuntu-server-minimal.ubuntu-server.bj LMAP="" LRM="" if [ -n "$LAYER" ]; then for f in $LNAME.squashfs $LNAME.size $LNAME.manifest; do [ -s "$LAYER/$f" ] || { echo "в $LAYER нет $f"; exit 1; }; done mkdir -p "$WORK/bj/debs"; cp "$LAYER"/debs/*.deb "$WORK/bj/debs/" # источник установки «Ubuntu Server + обновления Bridge&Join» — по умолчанию; слои minimal и # ubuntu-server под ним установщик подключает сам по цепочке имён xorriso -osirrox on -indev "$SRC" -extract /casper/install-sources.yaml "$WORK/is.orig" >/dev/null 2>&1 { sed 's/^ default: true$/ default: false/;s/^- default: true$/- default: false/' "$WORK/is.orig" echo "- default: true" echo " description:" echo " en: Ubuntu Server with updates applied when the Bridge&Join image was built." echo " id: ubuntu-server-bj" echo " locale_support: locale-only" echo " name:" echo " en: Ubuntu Server (Bridge&Join, updated)" echo " path: $LNAME.squashfs" echo " size: $(( $(cat "$LAYER/$LNAME.size") + $(awk '/path: ubuntu-server-minimal.ubuntu-server.squashfs/{f=1} f&&/size:/{print $2; exit}' "$WORK/is.orig") ))" echo " type: fsimage-layered" echo " variant: server" } > "$WORK/install-sources.yaml" # Ядро и прошивки новее — в слое; их старые копии в репозитории образа (и ядро HWE, которое # мы не ставим) не нужны: установка не будет их брать, пакет linux-generic уже стоит в слое. LRM="/pool/main/l/linux-firmware /pool/main/l/linux-hwe-6.8 /pool/main/l/linux-meta-hwe-6.8" LMAP="-map $LAYER/$LNAME.squashfs /casper/$LNAME.squashfs -map $LAYER/$LNAME.size /casper/$LNAME.size -map $LAYER/$LNAME.manifest /casper/$LNAME.manifest -map $WORK/install-sources.yaml /casper/install-sources.yaml" fi # Валидата 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 "}" # пункт с ядром HWE убираем — его файлы из образа удаляются sed '/^set timeout/d;/^loadfont/d' "$WORK/grub/grub.orig" | awk '/^menuentry "Ubuntu Server with the HWE kernel"/{skip=1} skip&&/^}/{skip=0;next} !skip' } > "$WORK/grub/grub.cfg" # md5sum.txt: установщик Ubuntu при загрузке сверяет файлы образа по этому списку (casper-md5check); # несовпадение или отсутствующий файл роняет подсистему Integrity. Убираем удалённые файлы HWE, # пересчитываем изменённые и добавляем наши. xorriso -osirrox on -indev "$SRC" -extract /md5sum.txt "$WORK/md5.orig" >/dev/null 2>&1 { grep -vE '\./pool/main/l/linux-(firmware|hwe-6\.8|meta-hwe-6\.8)/' "$WORK/md5.orig" | grep -vE '\./casper/(hwe-vmlinuz|hwe-initrd|ubuntu-server-minimal\.ubuntu-server\.installer\.generic-hwe\.squashfs|install-sources\.yaml)$|\./boot/grub/grub\.cfg$' if [ -n "$LAYER" ]; then echo "$(md5sum < "$WORK/install-sources.yaml" | cut -d' ' -f1) ./casper/install-sources.yaml" for f in $LNAME.squashfs $LNAME.size $LNAME.manifest; do echo "$(md5sum < "$LAYER/$f" | cut -d' ' -f1) ./casper/$f"; done else grep -E '\./casper/install-sources\.yaml$' "$WORK/md5.orig" fi echo "$(md5sum < "$WORK/grub/grub.cfg" | cut -d' ' -f1) ./boot/grub/grub.cfg" (cd "$WORK" && find bj nocloud -type f | sort | while read -r f; do echo "$(md5sum < "$f" | cut -d' ' -f1) ./$f"; done) } > "$WORK/md5sum.txt" 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 \ -map "$WORK/md5sum.txt" /md5sum.txt $LMAP \ -rm_r /casper/hwe-vmlinuz /casper/hwe-initrd /casper/ubuntu-server-minimal.ubuntu-server.installer.generic-hwe.squashfs $LRM -- \ -end >/dev/null 2>"$WORK/xorriso.log" || { cat "$WORK/xorriso.log"; exit 1; } echo "Образ собран: $OUT ($(du -h "$OUT" | cut -f1)), канал $CHANNEL; ядро: $FILES; Валидата: $n пакета; слой обновлений: ${LAYER:-нет}"