Папки шлюза по сети — два режима: простой (один логин) или через домен (группы и сотрудники bj)

- «Через домен»: сервер bj подключается к домену AD (winbind, один раз — учётной записью администратора домена, bj её не сохраняет); папки igate_exchange(_prod) открыты группам с ролью в bj и доменным сотрудникам bj — своим входом в Windows; краткое имя домена bj берёт из AD (crossRef nETBIOSName).
- Список доступа bj сверяет сам: после изменения групп, сотрудников и настроек входа (через несколько секунд) и раз в 15 минут.
- «Простой»: как было — один логин и пароль Samba; переход с домена выключает вход через домен (учётная запись компьютера в домене остаётся).
- bj-igate-admin share: режимы simple/domain, join (realm, workgroup, admin), проверка списка доступа на недопустимые символы, состояние — mode/joined/realm.

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-07 09:01:46 +03:00
parent 6aa5d60797
commit 9553bd6e22
8 changed files with 426 additions and 45 deletions
+53 -13
View File
@@ -324,9 +324,14 @@ SQL
# папки обмена шлюза по сети (SMB): для программ организации, которые кладут/забирают файлы через ИШ. bj это не
# нужно. Блок общих папок в smb.conf — между метками bj; заведённые вручную [igate_exchange] не трогаем.
share)
# Папки обмена шлюза по сети (Samba). Два режима:
# simple — один логин и пароль (пользователь Samba, файлы пишутся от имени шлюза);
# domain — сервер в домене AD (winbind): входят сотрудники и группы домена с доступом к bj (список даёт bj).
CONF=/etc/samba/smb.conf; B='# >>> bj: папки шлюза НРД'; E='# <<< bj: папки шлюза НРД'
on=$(j enabled); su=$(j user); sp=$(j password)
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
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
u=$( [ "$mine" = true ] && sed -n "/^$B/,/^$E/p" "$CONF" | sed -n 's/^ *valid users *= *//p' | head -1 )
@@ -335,7 +340,9 @@ SQL
ip=$(hostname -I 2>/dev/null | awk '{print $1}'); hn=$(hostname -s 2>/dev/null)
sh=""; [ "$mine" = true ] && for n in $(sed -n "/^$B/,/^$E/p" "$CONF" | sed -n 's/^\[\(.*\)\]$/\1/p'); do sh="$sh${sh:+,}$(q "$n")"; done
[ "$manual" = true ] && [ -z "$sh" ] && sh=$(q igate_exchange)
echo "{\"ok\":true,\"on\":$( [ "$mine" = true ] || [ "$manual" = true ] && echo true || echo false),\"manual\":$manual,\"user\":$(q "$u"),\"smbd\":$(q "$(systemctl is-active smbd 2>/dev/null)"),\"ip\":$(q "$ip"),\"host\":$(q "$hn"),\"shares\":[$sh]}"; exit 0
realm=$( [ "$dmode" = domain ] && sed -n "/^$GB/,/^$GE/p" "$CONF" | sed -n 's/^ *realm *= *//p' | head -1 )
joined=false; [ "$dmode" = domain ] && command -v net >/dev/null 2>&1 && net ads testjoin >/dev/null 2>&1 && joined=true
echo "{\"ok\":true,\"on\":$( [ "$mine" = true ] || [ "$manual" = true ] && echo true || echo false),\"manual\":$manual,\"mode\":$(q "$dmode"),\"joined\":$joined,\"realm\":$(q "$realm"),\"user\":$(q "$u"),\"smbd\":$(q "$(systemctl is-active smbd 2>/dev/null)"),\"ip\":$(q "$ip"),\"host\":$(q "$hn"),\"shares\":[$sh]}"; exit 0
fi
[ "$manual" = true ] && err "папка igate_exchange уже открыта вручную в $CONF — bj её не меняет; чтобы управлять из bj, уберите её из smb.conf"
if [ "$on" != true ]; then
@@ -343,24 +350,56 @@ SQL
systemctl reload smbd >/dev/null 2>&1 || true
ok "доступ к папкам шлюза по сети закрыт"; exit 0
fi
[[ "$su" =~ ^[a-z][a-z0-9_-]{2,31}$ ]] || err "логин — латинские строчные буквы, цифры, _ и -, от 3 символов, начинается с буквы"
case "$su" in root|igate|bj|postgres) err "логин $su занят системой — выберите другой" ;; esac
[ -n "$mode" ] || mode=$dmode
if ! command -v smbd >/dev/null 2>&1; then
DEBIAN_FRONTEND=noninteractive apt-get install -y -q samba >/dev/null 2>&1 || err "не удалось установить Samba (apt-get install samba) — нужен доступ к репозиторию"
fi
id "$su" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin "$su" || err "пользователь $su не создан"
if [ -n "$sp" ]; then
[ ${#sp} -ge 8 ] || err "пароль — не короче 8 символов"
printf '%s\n%s\n' "$sp" "$sp" | smbpasswd -s -a "$su" >/dev/null 2>&1 || err "пароль для $su не задан (smbpasswd)"
smbpasswd -e "$su" >/dev/null 2>&1
elif ! pdbedit -L 2>/dev/null | grep -q "^$su:"; then
err "задайте пароль для $su"
note=""
if [ "$mode" = domain ]; then
# подключение сервера к домену — один раз: учётная запись администратора домена не сохраняется
if [ "$(j join)" = 1 ]; then
realm=$(j realm | tr '[:lower:]' '[:upper:]'); wg=$(j workgroup | tr '[:lower:]' '[:upper:]'); au=$(j admin); ap=$(j adminpw)
[[ "$realm" =~ ^[A-Z0-9][A-Z0-9.-]+$ ]] || err "домен не задан — подключите домен в bj: Настройка → Пользователи"
[[ "$wg" =~ ^[A-Z0-9][A-Z0-9_-]{0,14}$ ]] || err "не определено краткое имя домена (NetBIOS)"
[ -n "$au" ] && [ -n "$ap" ] || err "нужны логин и пароль администратора домена"
command -v winbindd >/dev/null 2>&1 || DEBIAN_FRONTEND=noninteractive apt-get install -y -q winbind libnss-winbind >/dev/null 2>&1 || err "не удалось установить winbind (apt-get install winbind) — нужен доступ к репозиторию"
cp -p "$CONF" "$CONF.bj-before-join" 2>/dev/null
sed -i "/^$GB/,/^$GE/d" "$CONF"
{ echo "$GB"
printf '[global]\n workgroup = %s\n realm = %s\n security = ads\n kerberos method = secrets and keytab\n winbind refresh tickets = yes\n idmap config * : backend = tdb\n idmap config * : range = 3000-7999\n idmap config %s : backend = rid\n idmap config %s : range = 10000-999999\n map to guest = never\n' "$wg" "$realm" "$wg" "$wg"
echo "$GE"; } >> "$CONF"
# блок домена — раньше папок: папки дописываются после него
if grep -q "^$B" "$CONF"; then blk=$(sed -n "/^$B/,/^$E/p" "$CONF"); sed -i "/^$B/,/^$E/d" "$CONF"; printf '%s\n' "$blk" >> "$CONF"; fi
testparm -s "$CONF" >/dev/null 2>&1 || { cp -p "$CONF.bj-before-join" "$CONF"; err "smb.conf с параметрами домена не проходит проверку (testparm) — правка отменена"; }
out=$(PASSWD="$ap" net ads join -U "$au" 2>&1) || { cp -p "$CONF.bj-before-join" "$CONF"; systemctl restart smbd >/dev/null 2>&1; err "сервер не подключён к домену: $(printf '%s' "$out" | tail -2 | tr '\n' ' ')"; }
systemctl enable winbind >/dev/null 2>&1; systemctl restart winbind >/dev/null 2>&1
net ads testjoin >/dev/null 2>&1 || err "сервер подключён, но проверка подключения к домену не прошла (net ads testjoin)"
note="сервер подключён к домену $realm; "; dmode=domain
fi
[ "$dmode" = domain ] || err "сервер ещё не подключён к домену — нажмите «Подключить сервер к домену»"
[ -n "$vu" ] || err "в bj нет групп и сотрудников домена с доступом — добавьте их в Настройка → Пользователи"
case "$vu" in *$'\n'*|*'['*|*']'*|*';'*|*'#'*|*'%'*) err "недопустимые символы в списке доступа" ;; esac
valid="$vu"
else
[[ "$su" =~ ^[a-z][a-z0-9_-]{2,31}$ ]] || err "логин — латинские строчные буквы, цифры, _ и -, от 3 символов, начинается с буквы"
case "$su" in root|igate|bj|postgres) err "логин $su занят системой — выберите другой" ;; esac
id "$su" >/dev/null 2>&1 || useradd --system --no-create-home --shell /usr/sbin/nologin "$su" || err "пользователь $su не создан"
if [ -n "$sp" ]; then
[ ${#sp} -ge 8 ] || err "пароль — не короче 8 символов"
printf '%s\n%s\n' "$sp" "$sp" | smbpasswd -s -a "$su" >/dev/null 2>&1 || err "пароль для $su не задан (smbpasswd)"
smbpasswd -e "$su" >/dev/null 2>&1
elif ! pdbedit -L 2>/dev/null | grep -q "^$su:"; then
err "задайте пароль для $su"
fi
# простой режим — без домена: вход учётной записью Samba
if [ "$dmode" = domain ]; then sed -i "/^$GB/,/^$GE/d" "$CONF"; systemctl disable --now winbind >/dev/null 2>&1; note="вход через домен выключен (учётная запись компьютера в домене осталась); "; fi
valid="$su"
fi
[ -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
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" "$su" "$SVC_USER" "$SVC_USER"
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"
# Windows сначала пробует войти текущей учётной записью; с «map to guest = bad user» Samba пускает её гостем,
@@ -368,7 +407,8 @@ 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 не перезапустилась"
ok "папки шлюза открыты по сети для $su: \\\\$(hostname -I | awk '{print $1}')\\igate_exchange" ;;
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 ;;
*) err "неизвестная команда: $cmd" ;;
esac