#!/bin/bash # Первая загрузка сервера, установленного с образа Bridge&Join. Спрашивает ключ продукта на консоли; # без ключа ставит ядро bj с образа (/var/lib/bj/bundle), а ключ вводится в браузере на первой # странице настройки. Пока bj не установлен, вопрос повторяется при каждой загрузке (с таймаутом), # адрес и подсказки всегда есть на экране входа (bj-welcome). set -u MARK=/var/lib/bj/.bj/installed-from-iso SERVER=https://lic.zetit.ru CHANNEL=stable BUNDLE=/var/lib/bj/bundle [ -s /etc/bj/channel ] && CHANNEL=$(tr -d ' \n' < /etc/bj/channel) # установка прошла: отметка и выключение первой загрузки (приветственный экран — после Enter, иначе он # сотрёт с экрана итог и пароли) finish_quiet() { mkdir -p "$(dirname "$MARK")" && date -Iseconds > "$MARK" systemctl disable bj-firstboot.service >/dev/null 2>&1 } finish_screen() { [ -x /usr/local/sbin/bj-welcome ] && /usr/local/sbin/bj-welcome; } urls() { ip -4 -o addr show scope global 2>/dev/null | awk '{split($4,a,"/"); printf " https://%s:8443%s\n", a[1], "'"$1"'"}'; } # Пока идут вопросы, сообщения загрузки на консоль не выводятся (ядро — только аварийные, # systemd — без строк «[ OK ] Started …»), иначе они перемешиваются с текстом на экране. quiet_console() { dmesg -n 1 2>/dev/null; kill -s RTMIN+21 1 2>/dev/null; sleep 2; } quiet_console # шрифт консоли установщика (кириллица, рамки, ✓ ▶ █): его кладёт образ; нет — экраны рисуются простыми символами FONT=/usr/local/share/bj/console.psf if [ -f "$FONT" ] && setfont "$FONT" 2>/dev/null; then export BJ_SCREEN_FONT=1; fi if [ "${BJ_SCREEN_FONT:-}" = 1 ]; then BOX_T='╔══'; BOX_B='╚══'; BOX_H='═'; else BOX_T='+=='; BOX_B='+=='; BOX_H='='; fi LOG=/var/log/bj/firstboot-install.log SCREEN=/usr/local/sbin/bj-firstboot-screen ACC_DONE=/var/lib/bj/.bj/os-accounts-set ACCOUNTS="" genpw() { local p=""; while [ ${#p} -lt 12 ]; do p="$p$(head -c 48 /dev/urandom | tr -dc 'ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnpqrstuvwxyz23456789')"; done; printf '%s-%s-%s' "${p:0:4}" "${p:4:4}" "${p:8:4}"; } askpw() { # $1 — логин, $2 — предложенный пароль; печатает итоговый пароль while true; do printf ' Пароль для %s: %s\n Enter — принять, или введите свой (не короче 10 символов): ' "$1" "$2" > /dev/tty stty -echo < /dev/tty; read -r P1 < /dev/tty; stty echo < /dev/tty; printf '\n' > /dev/tty [ -z "$P1" ] && { printf '%s' "$2"; return; } [ ${#P1} -lt 10 ] && { printf ' Короче 10 символов, ещё раз.\n' > /dev/tty; continue; } printf ' Повторите: ' > /dev/tty; stty -echo < /dev/tty; read -r P2 < /dev/tty; stty echo < /dev/tty; printf '\n' > /dev/tty [ "$P1" = "$P2" ] && { printf '%s' "$P1"; return; } printf ' Не совпало, ещё раз.\n' > /dev/tty done } setup_accounts() { [ -f "$ACC_DONE" ] && return id user_bj >/dev/null 2>&1 || useradd -m -s /bin/bash -c "Пользователь сервера bj" -G adm,systemd-journal user_bj id root_bj >/dev/null 2>&1 && usermod -aG sudo root_bj clear printf '\n Bridge&Join: учётные записи сервера\n ────────────────────────────────────────────────────────────\n' printf ' root_bj — администратор сервера (команды через sudo)\n user_bj — обычный вход (журналы, без прав администратора)\n' printf ' Работа с bj — в браузере; эти учётные записи нужны для обслуживания ОС.\n\n' printf ' Шаг 1 из 2 — пароли. Для каждой учётной записи предложен пароль:\n' printf ' • Enter — оставить предложенный (на следующем экране его нужно будет записать или сфотографировать);\n' printf ' • или наберите свой (не короче 10 символов, при вводе не отображается) и Enter.\n\n' PA=$(askpw root_bj "$(genpw)"); PU=$(askpw user_bj "$(genpw)") printf 'root_bj:%s\nuser_bj:%s\n' "$PA" "$PU" | chpasswd mkdir -p "$(dirname "$ACC_DONE")" && date -Iseconds > "$ACC_DONE" ACCOUNTS=$(printf ' root_bj (администратор, sudo) пароль: %s\n user_bj (обычный вход) пароль: %s\n' "$PA" "$PU") unset PA PU clear printf '\n Bridge&Join: учётные записи сервера\n' show_accounts confirm_accounts } # пароли учётных записей — крупно, с просьбой сфотографировать или записать: нигде не сохраняются show_accounts() { [ -n "$ACCOUNTS" ] || return 0 printf '\n %s СФОТОГРАФИРУЙТЕ ИЛИ ЗАПИШИТЕ ЭТИ ПАРОЛИ %s\n\n%s\n\n' "$BOX_T" "$(printf '%*s' 24 '' | tr ' ' "$BOX_H")" "$ACCOUNTS" printf ' %s они показаны только здесь и нигде не сохраняются %s\n' "$BOX_B" "$(printf '%*s' 14 '' | tr ' ' "$BOX_H")" printf ' Вход в консоль — для обслуживания сервера; работа с bj — в браузере. Сменить пароль: passwd\n\n' } # пока не нажат Enter — экран не меняется (без таймера: пароли не должны пропасть, пока их не записали) confirm_accounts() { [ -n "$ACCOUNTS" ] || return 0 read -r -p " Записали или сфотографировали пароли? Нажмите Enter. " _ < /dev/tty || true } # установка bj: экран хода (bj-firstboot-screen), весь вывод — в журнал $LOG; не вышло — ещё одна попытка: # на первой загрузке что-то могло не успеть запуститься, пройденные шаги повторяются безопасно install_bj() { mkdir -p /var/log/bj for try in 1 2; do if command -v python3 >/dev/null 2>&1 && [ -x "$SCREEN" ]; then python3 "$SCREEN" --log "$LOG" -- /usr/local/sbin/bj-installer install "$@" && return 0 else /usr/local/sbin/bj-installer install "$@" 2>&1 | tee -a "$LOG" [ "${PIPESTATUS[0]}" = 0 ] && return 0 fi if [ "$try" = 1 ]; then printf '\n Не получилось с первого раза — через 10 секунд попробую ещё раз.\n' sleep 10 fi done printf '\n Подробный журнал установки: %s\n' "$LOG" return 1 } setup_accounts # постоянный адрес: DHCP может выдать серверу другой адрес, и bj «потеряется» (только Ubuntu/netplan) if [ ! -f /var/lib/bj/.bj/network-asked ] && [ -x /usr/local/sbin/bj-network ] && command -v netplan >/dev/null; then mkdir -p /var/lib/bj/.bj && date -Iseconds > /var/lib/bj/.bj/network-asked printf '\n Сейчас адрес сервера: %s\n' "$(ip -4 -o addr show scope global 2>/dev/null | awk '{print $4}' | tr '\n' ' ')" # ответ цифрой: консоль начинает в английской раскладке, «д» без переключения не набрать read -t 60 -r -p " Задать постоянный адрес (рекомендуется)? 1 — да, Enter — нет: " ANS < /dev/tty || ANS="" case "$ANS" in 1*|[дДyYlL]*) /usr/local/sbin/bj-network < /dev/tty ;; esac fi clear printf '\n Bridge&Join: установка на сервер\n ────────────────────────────────────────────────────────────\n' printf ' Шаг 2 из 2 — установка bj. Система установлена, осталось поставить bj.\n\n' if [ -f "$BUNDLE/manifest.json" ]; then printf ' Ключ продукта (BJ-XXXX-XXXX-XXXX) есть в портале клиента %s/portal/.\n' "$SERVER" printf ' Вводить его здесь необязательно: нажмите Enter — ядро bj установится с образа,\n а ключ вы введёте в браузере на первой странице настройки (там его удобно вставить).\n\n' else printf ' Нужен ключ продукта (BJ-XXXX-XXXX-XXXX): он есть в портале клиента %s/portal/\n\n' "$SERVER" fi for i in $(seq 1 30); do curl -fsS -m 5 -o /dev/null "$SERVER/healthz" && break [ "$i" = 1 ] && printf ' Жду сеть и связь с %s…\n' "$SERVER" sleep 2 done while true; do CODE="" if [ -f "$BUNDLE/manifest.json" ]; then read -t 120 -r -p " Ключ продукта (Enter — установить с образа, ключ ввести в браузере): " CODE || true else read -t 120 -r -p " Ключ продукта (Enter — установить позже): " CODE || true fi CODE=$(printf '%s' "${CODE:-}" | tr -d ' ' | tr '[:lower:]' '[:upper:]') if [ -z "$CODE" ]; then if [ -f "$BUNDLE/manifest.json" ]; then if install_bj --bundle "$BUNDLE" --server "$SERVER" --channel "$CHANNEL"; then finish_quiet # адрес и код первичной настройки — на экране установки выше show_accounts read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true finish_screen exit 0 fi show_accounts printf ' Установка bj не завершилась. Она повторится при следующей загрузке, или сейчас вручную:\n sudo bj-installer install --bundle %s\n\n' "$BUNDLE" read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true exit 0 fi show_accounts printf '\n Установка bj отложена. Вопрос повторится при следующей загрузке; вручную:\n sudo bj-installer install --code КЛЮЧ-ПРОДУКТА\n\n' read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true exit 0 fi if install_bj --code "$CODE" --server "$SERVER" --channel "$CHANNEL"; then finish_quiet show_accounts read -r -p " Нажмите Enter, чтобы перейти к экрану входа. " _ < /dev/tty || true finish_screen exit 0 fi show_accounts printf ' Установка не завершилась. Проверьте ключ продукта и сеть и попробуйте ещё раз.\n\n' done