Первичная настройка, часть 4: HTTPS веб-интерфейса (:8443, самоподписанный сертификат на имя и адреса сервера, свой сертификат, перенаправление браузера с HTTP); сервер времени из настроек (ntpd/chrony/timesyncd — bj-system-admin); постоянный адрес — bj-network (netplan try с откатом), вопрос при первой загрузке с образа; раздел «Сервер: HTTPS, время, сеть»; HTTPS в итоговой проверке; адреса https:// на экране установщика и консоли

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-05 16:43:23 +03:00
parent 4332e3309b
commit 765d7b5994
17 changed files with 582 additions and 26 deletions
+2
View File
@@ -13,6 +13,8 @@ process_one(){
component-install) out=$(/usr/local/sbin/bj-component-install "$staging" 2>&1) ;;
# интеграционный шлюз НРД: состояние, установка, канал контура (TEST3 | боевой); параметры — JSON в extra
igate-status|igate-install|igate-contour) out=$(BJ_EXTRA="$extra" /usr/local/sbin/bj-igate-admin "${action#igate-}" 2>&1) ;;
# системные настройки сервера: серверы времени (NTP)
system-ntp) out=$(BJ_EXTRA="$extra" /usr/local/sbin/bj-system-admin ntp 2>&1) ;;
# резервное копирование и восстановление (настройки bj → bj-backup-admin); параметры — JSON в extra
backup-config|backup-test|backup-run|backup-list|backup-verify|backup-restore) out=$(BJ_EXTRA="$extra" /usr/local/sbin/bj-backup-admin "${action#backup-}" 2>&1) ;;
# Перезапуск службы модуля из админки. Список закрытый: чужую службу
+36
View File
@@ -0,0 +1,36 @@
#!/bin/bash
# bj-network — постоянный адрес сервера bj (на консоли сервера, под sudo). Ubuntu (netplan): спрашивает
# адрес, шлюз и DNS и применяет через «netplan try» — если связь пропала и подтверждения нет 120 секунд,
# прежние настройки возвращаются сами. Запускается и при первой загрузке с образа (bj-firstboot).
set -u
[ "$(id -u)" = 0 ] || { echo "запустите через sudo: sudo bj-network"; exit 1; }
command -v netplan >/dev/null || { echo "netplan не найден: на этой системе сеть настраивается её средствами (/etc/network/interfaces)."; exit 1; }
IF=$(ip -4 route show default 2>/dev/null | awk '{print $5; exit}')
[ -n "$IF" ] || IF=$(ip -o link show | awk -F': ' '$2!="lo"{print $2; exit}')
CUR=$(ip -4 -o addr show dev "$IF" scope global 2>/dev/null | awk '{print $4; exit}')
GW=$(ip -4 route show default 2>/dev/null | awk '{print $3; exit}')
DNS=$(resolvectl dns "$IF" 2>/dev/null | sed 's/.*: *//' | tr ' ' ',' )
printf '\n Сеть сервера: интерфейс %s, адрес %s, шлюз %s, DNS %s\n' "$IF" "${CUR:-нет}" "${GW:-нет}" "${DNS:-нет}"
printf ' Постоянный адрес нужен, чтобы адрес bj не менялся (DHCP может выдать другой).\n\n'
ask() { local v; read -r -p " $1 [$2]: " v; printf '%s' "${v:-$2}"; }
ADDR=$(ask "Адрес с маской (например 10.10.10.30/24)" "${CUR:-}")
[[ "$ADDR" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}/[0-9]{1,2}$ ]] || { echo " адрес — вида 10.10.10.30/24"; exit 1; }
GW2=$(ask "Шлюз" "${GW:-}")
[[ "$GW2" =~ ^[0-9]{1,3}(\.[0-9]{1,3}){3}$ ]] || { echo " шлюз — вида 10.10.10.1"; exit 1; }
DNS2=$(ask "DNS через запятую" "${DNS:-$GW2}")
F=/etc/netplan/60-bj-static.yaml
[ -f "$F" ] && cp -a "$F" "$F.bak"
{
printf 'network:\n version: 2\n ethernets:\n %s:\n dhcp4: false\n addresses: [%s]\n routes:\n - to: default\n via: %s\n nameservers:\n addresses: [%s]\n' "$IF" "$ADDR" "$GW2" "$DNS2"
} > "$F"
chmod 600 "$F"
# прежние файлы с DHCP для этого интерфейса — выключаем, чтобы не было двух адресов
for f in /etc/netplan/*.yaml; do [ "$f" = "$F" ] && continue; grep -q "$IF" "$f" 2>/dev/null && mv "$f" "$f.disabled-by-bj"; done
echo " Применяю. Если связь пропадёт — через 120 секунд вернутся прежние настройки."
if netplan try --timeout 120; then
echo " ✓ Адрес ${ADDR%/*} закреплён. Веб-вход: https://${ADDR%/*}:8443/"
[ -x /usr/local/sbin/bj-welcome ] && /usr/local/sbin/bj-welcome
else
for f in /etc/netplan/*.disabled-by-bj; do [ -f "$f" ] && mv "$f" "${f%.disabled-by-bj}"; done
rm -f "$F"; netplan apply; echo " Настройки не приняты — оставлен прежний адрес."
fi
+46
View File
@@ -0,0 +1,46 @@
#!/bin/bash
# bj-system-admin — системные настройки сервера bj из веб-интерфейса (через root-демон bj-keymedia).
# ntp — серверы времени (BJ_EXTRA: "ntp1.example.ru ntp2.example.ru"): ntpd, chrony или systemd-timesyncd —
# что стоит на сервере. Время важно для подписей и обмена с НРД.
set -uo pipefail
q() { python3 -c "import json,sys;print(json.dumps(sys.argv[1],ensure_ascii=False))" "$1"; }
ok() { echo "{\"ok\":true,\"output\":$(q "$1")}"; }
err() { echo "{\"ok\":false,\"error\":$(q "$1")}"; exit 0; }
cmd=${1:-}
case "$cmd" in
ntp)
servers=$(printf '%s' "${BJ_EXTRA:-}" | tr ',;' ' ' | xargs)
[ -n "$servers" ] || err "не указан сервер времени"
for s in $servers; do [[ "$s" =~ ^[A-Za-z0-9][A-Za-z0-9.-]{0,252}$ ]] || err "недопустимое имя сервера времени: $s"; done
if systemctl is-active --quiet ntp || systemctl is-active --quiet ntpd || [ -f /etc/ntp.conf ] && ! systemctl is-active --quiet chrony && ! systemctl is-active --quiet systemd-timesyncd; then
conf=/etc/ntp.conf; [ -f "$conf" ] || err "нет /etc/ntp.conf"
cp -a "$conf" "$conf.bak-bj" 2>/dev/null
python3 - "$conf" $servers <<'PY' || err "не удалось изменить /etc/ntp.conf"
import sys,re
p,servers=sys.argv[1],sys.argv[2:]
out=[]
for l in open(p).read().split("\n"):
if l.startswith("server ") and l.rstrip().endswith("# bj"): continue # прежние наши
if re.match(r"^(server|pool) ",l): l="#bj# "+l # чужие — выключаем
out.append(l)
out+=[f"server {s} iburst # bj" for s in servers]
open(p,"w").write("\n".join(out).rstrip("\n")+"\n")
PY
svc=ntp; systemctl is-active --quiet ntpd && svc=ntpd
systemctl restart "$svc" || err "служба $svc не перезапустилась"
ok "серверы времени: $servers (служба $svc)"
elif systemctl is-active --quiet chrony || [ -d /etc/chrony ]; then
d=/etc/chrony/sources.d; mkdir -p "$d"
: > "$d/bj.sources"; for s in $servers; do echo "server $s iburst" >> "$d/bj.sources"; done
grep -q '^sourcedir /etc/chrony/sources.d' /etc/chrony/chrony.conf 2>/dev/null || echo 'sourcedir /etc/chrony/sources.d' >> /etc/chrony/chrony.conf
systemctl restart chrony || err "служба chrony не перезапустилась"
ok "серверы времени: $servers (служба chrony)"
else
mkdir -p /etc/systemd/timesyncd.conf.d
printf '[Time]\nNTP=%s\n' "$servers" > /etc/systemd/timesyncd.conf.d/bj.conf
timedatectl set-ntp true >/dev/null 2>&1
systemctl restart systemd-timesyncd || err "служба systemd-timesyncd не перезапустилась"
ok "серверы времени: $servers (служба systemd-timesyncd)"
fi ;;
*) err "неизвестная команда: $cmd" ;;
esac
+2 -1
View File
@@ -10,10 +10,11 @@ SETUP=/var/lib/bj/.bj/setup.json
USERS=/var/lib/bj/.bj/users.json
FIRSTRUN=/var/lib/bj/.bj/first-run.json
PORT=8080
TLSPORT=8443 # веб-вход по HTTPS (сертификат сервера — браузер попросит подтвердить исключение)
HOST=$(hostname 2>/dev/null || echo bj)
ADDRS=$(ip -4 -o addr show scope global 2>/dev/null | awk '{split($4,a,"/"); print a[1]}' | tr '\n' ' ')
[ -z "$ADDRS" ] && ADDRS="<адрес не получен: проверьте сеть>"
url() { for a in $ADDRS; do case "$a" in \<*) printf ' http://%s:%s%s\n' "<IP сервера>" "$PORT" "$1";; *) printf ' http://%s:%s%s\n' "$a" "$PORT" "$1";; esac; done; }
url() { for a in $ADDRS; do case "$a" in \<*) printf ' https://%s:%s%s\n' "<IP сервера>" "$TLSPORT" "$1";; *) printf ' https://%s:%s%s\n' "$a" "$TLSPORT" "$1";; esac; done; }
lic=""
if [ -r "$SETUP" ]; then
lic=$(python3 - "$SETUP" 2>/dev/null <<'PY'