Пост

Собираем Samba NAS в Proxmox LXC. Часть 5: Публичная read-only шара

Создаём публичную read-only Samba-шару Public в Proxmox LXC: гостевой доступ, SMB3, безопасные права и bash-скрипт.

Собираем Samba NAS в Proxmox LXC. Часть 5: Публичная read-only шара

Задача

В предыдущих частях мы сделали:

  • [share] — основную рабочую шару;
  • [secure] — защищённую шару;
  • audit для [secure].

Теперь добавим публичную read-only шару:

1
\\nas\public

Она нужна для файлов, которые можно читать всем:

  • инструкции;
  • ISO-образы;
  • драйверы;
  • публичные документы;
  • общие материалы;
  • файлы для быстрой передачи внутри LAN.

Требования:

  • доступ без пароля;
  • только чтение;
  • отдельный каталог /srv/samba/public;
  • безопасные права;
  • не перезатирать текущий smb.conf;
  • проверить конфиг перед перезапуском.

[public] — это не место для рабочих документов. Всё, что требует контроля доступа, должно лежать в [share] или [secure].


Архитектура

После этой части структура будет такой:

1
2
3
4
/srv/samba
├── public   # guest read-only
├── share    # user writable
└── secure   # user writable + encryption + audit

Важный момент про guest-доступ

Для guest-шары в Samba обычно нужен глобальный параметр:

1
map to guest = Bad User

Он говорит Samba: если пришёл неизвестный пользователь, можно сопоставить его с guest-учёткой.

Но сам guest-доступ должен быть разрешён только в конкретной шаре:

1
2
3
[public]
   guest ok = yes
   read only = yes

Так мы не делаем весь сервер анонимным, а разрешаем guest только в [public].


Скрипт add-public-smb-share.sh

Скрипт:

  • создаёт каталог /srv/samba/public;
  • выставляет 755;
  • добавляет [public];
  • проверяет наличие map to guest = Bad User;
  • делает backup;
  • проверяет testparm;
  • перезапускает Samba.

Создай файл:

1
nano add-public-smb-share.sh

Вставь:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
#!/usr/bin/env bash
set -Eeuo pipefail

# ============================================================
# SMB на bash. Часть 5. Public
# Добавление read-only guest-шары [public]
# ============================================================

SHARE_NAME="${SHARE_NAME:-public}"
PUBLIC_DIR="${PUBLIC_DIR:-/srv/samba/public}"
SMB_CONF="${SMB_CONF:-/etc/samba/smb.conf}"
GUEST_USER="${GUEST_USER:-nobody}"
GUEST_GROUP="${GUEST_GROUP:-nogroup}"

require_root() {
  if [[ "${EUID}" -ne 0 ]]; then
    echo "Ошибка: запусти скрипт от root"
    exit 1
  fi
}

check_requirements() {
  if [[ ! -f "${SMB_CONF}" ]]; then
    echo "Ошибка: не найден ${SMB_CONF}"
    echo "Сначала выполни часть 2 и создай базовую Samba-конфигурацию."
    exit 1
  fi

  if ! id "${GUEST_USER}" &>/dev/null; then
    echo "Ошибка: пользователь ${GUEST_USER} не найден"
    exit 1
  fi

  if ! getent group "${GUEST_GROUP}" >/dev/null; then
    echo "Группа ${GUEST_GROUP} не найдена, пробуем использовать nobody"
    GUEST_GROUP="nobody"
  fi
}

prepare_dir() {
  echo "==> Создаём каталог ${PUBLIC_DIR}"
  mkdir -p "${PUBLIC_DIR}"
  chown "${GUEST_USER}:${GUEST_GROUP}" "${PUBLIC_DIR}"
  chmod 0755 "${PUBLIC_DIR}"
}

backup_config() {
  local backup="${SMB_CONF}.bak.public.$(date +%F_%H-%M-%S)"
  echo "==> Делаем backup ${backup}"
  cp "${SMB_CONF}" "${backup}"
}

ensure_global_guest_mapping() {
  if grep -qE '^[[:space:]]*map to guest[[:space:]]*=' "${SMB_CONF}"; then
    echo "==> map to guest уже настроен"
    return
  fi

  echo "==> Добавляем map to guest = Bad User в [global]"

  awk '
    BEGIN { in_global = 0; added = 0 }
    /^\[global\]/ {
      in_global = 1
      print
      next
    }
    in_global && /^\[/ && added == 0 {
      print "   map to guest = Bad User"
      added = 1
      in_global = 0
    }
    { print }
    END {
      if (in_global && added == 0) {
        print "   map to guest = Bad User"
      }
    }
  ' "${SMB_CONF}" > /tmp/smb.conf.public

  mv /tmp/smb.conf.public "${SMB_CONF}"
}

ensure_share_not_exists() {
  if grep -qE "^\[${SHARE_NAME}\]" "${SMB_CONF}"; then
    echo "Шара [${SHARE_NAME}] уже существует — пропускаем"
    exit 0
  fi
}

append_share() {
  echo "==> Добавляем шару [${SHARE_NAME}]"

  cat >> "${SMB_CONF}" <<EOF

[${SHARE_NAME}]
   path = ${PUBLIC_DIR}
   guest ok = yes
   public = yes
   read only = yes
   writable = no
   browseable = yes

   force user = ${GUEST_USER}
   force group = ${GUEST_GROUP}
EOF
}

validate_config() {
  echo "==> Проверяем Samba config"
  testparm -s "${SMB_CONF}" >/dev/null
}

restart_samba() {
  echo "==> Перезапускаем Samba"
  systemctl restart smbd
}

print_summary() {
  local host
  host="$(hostname -f 2>/dev/null || hostname)"

  echo
  echo "Готово."
  echo "Публичная read-only шара создана:"
  echo "  //${host}/${SHARE_NAME}"
  echo
  echo "Каталог:"
  echo "  ${PUBLIC_DIR}"
  echo
  echo "Проверка:"
  echo "  smbclient //SERVER_IP/${SHARE_NAME} -N -m SMB3"
}

main() {
  require_root
  check_requirements
  prepare_dir
  backup_config
  ensure_global_guest_mapping
  ensure_share_not_exists
  append_share
  validate_config
  restart_samba
  print_summary
}

main "$@"

Запуск

1
2
chmod +x add-public-smb-share.sh
sudo ./add-public-smb-share.sh

Добавим тестовый файл

1
2
echo "Hello from public Samba share" | sudo tee /srv/samba/public/readme.txt
sudo chmod 0644 /srv/samba/public/readme.txt

Проверка с Linux

Список шар:

1
smbclient -L //SERVER_IP -N -m SMB3

Подключение к public без пароля:

1
smbclient //SERVER_IP/public -N -m SMB3

Внутри:

1
2
3
smb: \> ls
smb: \> get readme.txt
smb: \> put test.txt

Команда put test.txt должна завершиться ошибкой, потому что шара read-only.


Проверка с Windows

Открыть в проводнике:

1
\\SERVER_IP\public

Или подключить как диск:

net use P: \\SERVER_IP\public /user:guest ""

Почему public должна быть read-only

Публичная writable-шара — это почти всегда проблема.

Если разрешить запись без пароля, любой клиент в сети сможет:

  • загрузить мусор;
  • удалить файлы;
  • положить вредоносный файл;
  • заполнить диск;
  • устроить хаос в каталоге.

Поэтому для публичного доступа лучше использовать принцип:

1
2
read-only по сети
write только локально администратором

Если нужно место для обмена файлами, лучше сделать отдельную authenticated-шару, например [upload], а не превращать [public] в общую корзину.


Возможные ошибки

NT_STATUS_ACCESS_DENIED при подключении без пароля

Проверь:

1
testparm -s | grep -E 'map to guest|guest ok|public'

В конфиге должны быть:

1
2
map to guest = Bad User
guest ok = yes

Видно шару, но нельзя читать файлы

Проверь права:

1
2
ls -ld /srv/samba/public
ls -la /srv/samba/public

Исправить:

1
2
3
sudo chown -R nobody:nogroup /srv/samba/public
sudo chmod 0755 /srv/samba/public
sudo find /srv/samba/public -type f -exec chmod 0644 {} \;

Если в системе нет nogroup, используй:

1
sudo chown -R nobody:nobody /srv/samba/public

Итог

Мы добавили публичную read-only шару:

1
\\nas\public

Она:

  • доступна без пароля;
  • работает только на чтение;
  • отделена от рабочих и защищённых данных;
  • не ломает текущие [share] и [secure];
  • подходит для общих файлов внутри локальной сети.

В следующей части займёмся самым важным для NAS — immutable backups на уровне Proxmox/ZFS.

Авторский пост защищен лицензией CC BY 4.0 .

Популярные теги