diff --git a/cmd/bj-installer/main.go b/cmd/bj-installer/main.go index 3c5a136..3304fed 100644 --- a/cmd/bj-installer/main.go +++ b/cmd/bj-installer/main.go @@ -300,8 +300,9 @@ func (in *installer) stepPackages() (string, error) { withJava := append(pkgs, javaPkg(debs)) args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, debs...) if _, err := runEnv(env, "apt-get", append(args, withJava...)...); err == nil { + smb := in.stepSamba(env, debs) _ = os.RemoveAll(in.P(debsDir)) - return strings.Join(withJava, ", ") + " (с образа, без загрузки)", nil + return strings.Join(withJava, ", ") + " (с образа, без загрузки)" + smb, nil } } if out, err := runEnv(env, "apt-get", "update", "-qq"); err != nil { @@ -312,7 +313,28 @@ func (in *installer) stepPackages() (string, error) { if out, err := runEnv(env, "apt-get", args...); err != nil { return "", fmt.Errorf("apt-get install: %v\n%s", err, tail(out, 12)) } - return strings.Join(pkgs, ", "), nil + return strings.Join(pkgs, ", ") + in.stepSamba(env, nil), nil +} + +// sambaPkgs — для папок обмена шлюза НРД по сети (Настройка → НРД → «Папки шлюза по сети»): Samba и вход +// через домен (winbind). Ставятся заранее, чтобы в закрытом контуре папки открывались без интернета. +var sambaPkgs = []string{"samba", "winbind", "libnss-winbind"} + +// stepSamba — поставить Samba необязательной частью: не вышло (нет на диске образа и нет сети) — установка bj +// продолжается, Samba поставится при первом открытии папок. Службы выключены, пока папки не открыты в bj: +// иначе сервер сразу отвечал бы по SMB. +func (in *installer) stepSamba(env []string, debs []string) string { + if in.root != "" { + return "" + } + args := append([]string{"install", "-y", "-qq", "--no-install-recommends"}, debs...) + if _, err := runEnv(env, "apt-get", append(args, sambaPkgs...)...); err != nil { + return "; Samba не установлена — поставится при первом открытии папок шлюза по сети" + } + for _, u := range []string{"smbd", "nmbd", "winbind", "samba-ad-dc"} { + _, _ = run("systemctl", "disable", "--now", u) + } + return ", " + strings.Join(sambaPkgs, ", ") + " (службы выключены до открытия папок)" } func (in *installer) stepSystem() (string, error) { diff --git a/deploy/iso/build-ubuntu-updates-layer.sh b/deploy/iso/build-ubuntu-updates-layer.sh index 293014b..b446729 100755 --- a/deploy/iso/build-ubuntu-updates-layer.sh +++ b/deploy/iso/build-ubuntu-updates-layer.sh @@ -19,7 +19,7 @@ OUT=${2:?выходной каталог} [ "$(id -u)" = 0 ] || { echo "нужен root"; exit 1; } for c in mksquashfs xorriso; do command -v $c >/dev/null || { echo "нужен $c"; exit 1; }; done # пакеты — как в bj-installer (stepPackages); Java 17 — служба подписи собрана под неё (в 22.04 по умолчанию 11) -BJ_PKGS="ca-certificates curl postgresql postgresql-client openjdk-17-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip" +BJ_PKGS="ca-certificates curl postgresql postgresql-client openjdk-17-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip samba winbind libnss-winbind" LAYER=ubuntu-server-minimal.ubuntu-server.bj.squashfs mkdir -p "$OUT" diff --git a/deploy/iso/preseed.cfg b/deploy/iso/preseed.cfg index 0f9695c..81bce0d 100644 --- a/deploy/iso/preseed.cfg +++ b/deploy/iso/preseed.cfg @@ -59,7 +59,7 @@ 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 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip sudo +d-i pkgsel/include string ca-certificates curl postgresql postgresql-client default-jre-headless pcscd python3 smbclient gnome-keyring dbus xvfb xauth libsecret-tools unzip sudo samba winbind libnss-winbind d-i pkgsel/upgrade select safe-upgrade popularity-contest popularity-contest/participate boolean false diff --git a/deploy/linux/sbin/bj-igate-admin b/deploy/linux/sbin/bj-igate-admin index db1d977..945010e 100755 --- a/deploy/linux/sbin/bj-igate-admin +++ b/deploy/linux/sbin/bj-igate-admin @@ -330,7 +330,7 @@ SQL CONF=/etc/samba/smb.conf; B='# >>> bj: папки шлюза НРД'; E='# <<< bj: папки шлюза НРД' GB='# >>> bj: вход через домен'; GE='# <<< bj: вход через домен' on=$(j enabled); su=$(j user); sp=$(j password); mode=$(j mode); vu=$(j valid) - manual=false; [ -f "$CONF" ] && sed "/^$B/,/^$E/d" "$CONF" | grep -q '^\[igate_exchange\]' && manual=true + manual=false; [ -f "$CONF" ] && sed "/^$B/,/^$E/d" "$CONF" | grep -qE '^\[igate_exchange(_test|_prod)?\]' && manual=true dmode=simple; [ -f "$CONF" ] && grep -q "^$GB" "$CONF" && dmode=domain if [ "${on:-}" = status ] || [ -z "${on:-}" ]; then mine=false; [ -f "$CONF" ] && grep -q "^$B" "$CONF" && mine=true @@ -347,8 +347,8 @@ SQL [ "$manual" = true ] && err "папка igate_exchange уже открыта вручную в $CONF — bj её не меняет; чтобы управлять из bj, уберите её из smb.conf" if [ "$on" != true ]; then [ -f "$CONF" ] && sed -i "/^$B/,/^$E/d" "$CONF" - systemctl reload smbd >/dev/null 2>&1 || true - ok "доступ к папкам шлюза по сети закрыт"; exit 0 + systemctl disable --now smbd nmbd >/dev/null 2>&1 || true # папок bj нет — сервер не отвечает по SMB + ok "доступ к папкам шлюза по сети закрыт, Samba выключена"; exit 0 fi [ -n "$mode" ] || mode=$dmode if ! command -v smbd >/dev/null 2>&1; then @@ -398,7 +398,7 @@ SQL [ -f "$CONF" ] && sed -i "/^$B/,/^$E/d" "$CONF" { echo "$B" for c in test3 prod; do d=$(exchange_dir "$c"); [ -d "$d" ] || continue - n=igate_exchange; [ "$c" = prod ] && n=igate_exchange_prod + n=igate_exchange_test; [ "$c" = prod ] && n=igate_exchange # боевой — без суффикса, тестовый — _test printf '[%s]\n comment = Шлюз НРД: папки обмена %s (INBOX/OUTBOX/SENT/ERRORS)\n path = %s\n browseable = yes\n read only = no\n valid users = %s\n force user = %s\n force group = %s\n' "$n" "$c" "$d" "$valid" "$SVC_USER" "$SVC_USER" done echo "$E"; } >> "$CONF" @@ -407,8 +407,13 @@ SQL sed -i -E 's/^([[:space:]]*)map to guest[[:space:]]*=[[:space:]]*bad user.*$/\1map to guest = never/I' "$CONF" testparm -s "$CONF" >/dev/null 2>&1 || { sed -i "/^$B/,/^$E/d" "$CONF"; err "smb.conf после правки не проходит проверку (testparm) — правка отменена"; } systemctl enable smbd >/dev/null 2>&1; systemctl restart smbd >/dev/null 2>&1 || err "служба smbd не перезапустилась" - if [ "$dmode" = domain ]; then ok "${note}папки шлюза открыты по сети для сотрудников домена с доступом к bj: \\\\$(hostname -I | awk '{print $1}')\\igate_exchange" - else ok "${note}папки шлюза открыты по сети для $su: \\\\$(hostname -I | awk '{print $1}')\\igate_exchange"; fi ;; + ip=$(hostname -I | awk '{print $1}'); where="" + for n in $(sed -n "/^$B/,/^$E/p" "$CONF" | sed -n 's/^\[\(.*\)\]$/\1/p'); do + t="боевой"; case "$n" in *_test) t="тестовый TEST3" ;; esac + where="$where${where:+, }\\\\$ip\\$n ($t)" + done + if [ "$dmode" = domain ]; then ok "${note}папки шлюза открыты по сети для сотрудников домена с доступом к bj: $where" + else ok "${note}папки шлюза открыты по сети для $su: $where"; fi ;; *) err "неизвестная команда: $cmd" ;; esac diff --git a/internal/lkgateway/igateshare.go b/internal/lkgateway/igateshare.go index d9811ab..5d73b63 100644 --- a/internal/lkgateway/igateshare.go +++ b/internal/lkgateway/igateshare.go @@ -203,6 +203,23 @@ func igateShareSync(ctx context.Context, rc *RuntimeConfig) (string, error) { if on, _ := st["on"].(bool); !on { return "папки закрыты — сверять нечего", nil } + if man, _ := st["manual"].(bool); man { + return "папки открыты вручную — bj их не меняет", nil + } + // прежние имена (тестовый — igate_exchange, боевой — igate_exchange_prod) → новые (_test и без суффикса) + if sh, _ := st["shares"].([]any); igateShareOldNames(sh) { + p := map[string]string{"enabled": "true", "mode": "simple"} + if m, _ := st["mode"].(string); m == "domain" { + valid, err := igateShareValid(rc.Snapshot().Auth) + if err != nil { + return "", err + } + p = map[string]string{"enabled": "true", "mode": "domain", "valid": valid} + } else { + p["user"], _ = st["user"].(string) + } + return igateShareApply(ctx, p, "папки переименованы: тестовый — igate_exchange_test, боевой — igate_exchange") + } if m, _ := st["mode"].(string); m != "domain" { return "простой режим — доступ по одному логину", nil } @@ -213,7 +230,11 @@ func igateShareSync(ctx context.Context, rc *RuntimeConfig) (string, error) { if cur, _ := st["user"].(string); cur == valid { return "список доступа не изменился", nil } - extra, _ := json.Marshal(map[string]string{"enabled": "true", "mode": "domain", "valid": valid}) + return igateShareApply(ctx, map[string]string{"enabled": "true", "mode": "domain", "valid": valid}, "доступ к папкам шлюза обновлён: "+valid) +} + +func igateShareApply(ctx context.Context, p map[string]string, done string) (string, error) { + extra, _ := json.Marshal(p) res, err := runKeymediaOp(ctx, "igate-share", map[string]string{"extra": string(extra)}) if err != nil { return "", err @@ -222,8 +243,18 @@ func igateShareSync(ctx context.Context, rc *RuntimeConfig) (string, error) { e, _ := res["error"].(string) return "", fmt.Errorf("%s", e) } - log.Printf("igate-share: доступ к папкам шлюза обновлён: %s", valid) - return "доступ обновлён: " + valid, nil + log.Printf("igate-share: %s", done) + return done, nil +} + +// igateShareOldNames — папки открыты по прежней схеме имён (до 07.10.2026: боевой был igate_exchange_prod). +func igateShareOldNames(shares []any) bool { + for _, x := range shares { + if n, _ := x.(string); n == "igate_exchange_prod" { + return true + } + } + return false } // igateShareSyncSoon — после изменения групп или сотрудников: сверить доступ к папкам в фоне (раз в несколько @@ -255,10 +286,12 @@ func igateShareSyncSoon(rc *RuntimeConfig) { func (s *Server) igateShareSyncLoop(ctx context.Context) { t := time.NewTicker(15 * time.Minute) defer t.Stop() + first := time.After(30 * time.Second) // после запуска (обновления) — сразу, а не через 15 минут for { select { case <-ctx.Done(): return + case <-first: case <-t.C: } c, cancel := context.WithTimeout(ctx, 2*time.Minute) diff --git a/internal/lkgateway/igateshare_test.go b/internal/lkgateway/igateshare_test.go index 315f160..46246c2 100644 --- a/internal/lkgateway/igateshare_test.go +++ b/internal/lkgateway/igateshare_test.go @@ -30,3 +30,12 @@ func TestIgateShareValid(t *testing.T) { t.Fatal("пустой список доступа должен быть ошибкой") } } + +func TestIgateShareOldNames(t *testing.T) { + if !igateShareOldNames([]any{"igate_exchange", "igate_exchange_prod"}) { + t.Fatal("прежняя схема имён не распознана") + } + if igateShareOldNames([]any{"igate_exchange_test", "igate_exchange"}) { + t.Fatal("новая схема принята за прежнюю") + } +} diff --git a/internal/lkgateway/web/templates/part_igate.html b/internal/lkgateway/web/templates/part_igate.html index 48bfa44..f074ade 100644 --- a/internal/lkgateway/web/templates/part_igate.html +++ b/internal/lkgateway/web/templates/part_igate.html @@ -190,7 +190,7 @@ function addrs(d){ var hosts=[d.ip||location.hostname]; if(d.host && d.host!==hosts[0]) hosts.push(d.host); var names=(d.shares&&d.shares.length)?d.shares:['igate_exchange'], rows=[]; names.forEach(function(n){ hosts.forEach(function(h){ var p='\\\\'+h+'\\'+n; - rows.push('
'+esc(p)+''+(n.indexOf('prod')>=0?'боевой контур':'тестовый контур TEST3')+(h===d.host?' · по имени сервера':'')+''+esc(p)+''+(/_test$/.test(n)?'тестовый контур TEST3':'боевой контур')+(h===d.host?' · по имени сервера':'')+'