Начнём не с учётной записи, а с одного действия. Верстальщик Анна должна положить на сервер готовый архив с макетами. Больше ей ничего не нужно: смотреть чужие материалы, переименовывать файлы коллеги или удалять старые версии она не должна. Если сформулировать задачу так, настройка почти пишется сама: нужен пользователь, у которого есть путь для загрузки результата и ровно те права, что требуются этому пути. Общий логин «contractor» с паролем в переписке этому описанию не отвечает: в нём невозможно отделить одного исполнителя от другого, а срок доступа приходится помнить в голове.
Дальше разберём сценарий с двумя исполнителями. Анна сдаёт макеты, ожидаемый объём результата около 6 ГБ. Борис монтирует видеоролик и принесёт около 8 ГБ. Эти числа условные: в своей задаче подставьте размер реальных результатов, число версий, которые подрядчик может загрузить до приёмки, и объём исходников, если вы тоже кладёте их на сервер.
Один исполнитель — один виртуальный пользователь
SFTPGo строится на виртуальных аккаунтах: пользователь существует внутри сервиса и не обязан иметь учётную запись в операционной системе. Это ключевое для нашей задачи. Подрядчик получает доступ к файловому сервису, а не к оболочке сервера, и временный аккаунт не превращается в доступ к ОС. Права назначаются по пользователю и по каталогу, а аутентификация поддерживает и пароль, и публичный ключ (SFTPGo: Features).
Заведите двух пользователей, например anna_layout и boris_video. Имя лучше делать говорящим: потом вы будете читать его в журнале, и фраза «пользователь upload1» через месяц ничего не скажет. Для обоих разумно выбрать вход по персональному публичному ключу: исполнитель присылает только публичную часть, закрытая остаётся у него, и вам не приходится придумывать и пересылать пароль. Если подрядчик работает через браузер и ключей не держит, подойдёт пароль, но тогда он должен быть уникальным для этого аккаунта.
Теперь о правах. SFTPGo различает отдельные операции: просмотр списка, загрузку, перезапись, скачивание, удаление и другие (SFTPGo: Features). Эта детализация и нужна, чтобы выдать «только положить результат», а не «всё внутри папки». Сформулируем желаемое для каждого:
| Исполнитель | Что должен делать | Что разрешаем | Что не даём |
|---|---|---|---|
| Анна (макеты) | Получить бриф, сдать архив | Список, скачивание в папке с материалами; загрузка в папку сдачи | Удаление, перезапись чужого |
| Борис (видео) | Взять исходники, сдать ролик | Список и скачивание исходников; загрузка в папку результата | Удаление, доступ к папке Анны |
Отдельно подумайте про перезапись. Если подрядчику разрешена загрузка, но запрещена перезапись, то повторная отправка файла с тем же именем будет отклонена, и вам придётся договориться о схеме имён версий, например maket_v1.zip, maket_v2.zip. Это неудобно, зато каждая загруженная версия остаётся в журнале и в хранилище. Если же вы допускаете, что исполнитель будет заменять свой же файл, перезапись можно разрешить именно в его папке сдачи, не распространяя на остальное.
Два виртуальных пути — две разные картины мира
Виртуальная папка в SFTPGo сопоставляет виртуальный путь реальному пути или другому storage backend (SFTPGo: Virtual Folders). Для подрядчика важно, что настоящая структура диска ему не видна: он видит только те пути, которые вы ему показали.
Представим, что на сервере лежат каталоги /srv/handoff/anna-out, /srv/handoff/boris-out и общий /srv/handoff/briefs с материалами. Каждому пользователю сопоставляем два виртуальных пути:
- у Анны
/briefведёт на реальный каталог с брифом, а/delivery— наanna-out; - у Бориса
/sourceведёт на каталог исходников, а/delivery— наboris-out.
Одинаковое имя /delivery у обоих — осознанное решение: инструкция для исполнителя звучит одинаково («кладите в delivery»), но физически эти пути разные. Анна, войдя, видит свой /delivery и не знает, что рядом есть boris-out. Борис, соответственно, наоборот.
Теперь нюанс, на котором легко споткнуться: пересечения. Документация предупреждает, что пересекающиеся или вложенные mapped paths могут нарушить расчёт квоты, и советует избегать такого пересечения, если квоты применяются (SFTPGo: Virtual Folders). Поэтому не делайте anna-out подкаталогом общей папки, которая сама сопоставлена другому пути. Раскладывайте каталоги рядом, как соседние ветки, а не как матрёшку.
Второй нюанс касается учёта места. Квота виртуальной папки может считаться отдельно, а может входить в квоту пользователя; приватную папку, включённую в пользовательскую квоту, не следует произвольно делить между пользователями (SFTPGo: Virtual Folders). Для нашей схемы это означает простое правило: папка сдачи принадлежит одному исполнителю. Общим можно сделать каталог с брифами, откуда все только читают, но тогда он не должен быть тем, чем «питается» чья-то личная квота.
Проверка на чужом файле и на собственном результате
Настроенные права ничего не стоят, пока вы не попробовали ими воспользоваться как подрядчик. Проверка занимает десять минут и состоит из двух коротких сценариев, которые нужно прогнать от лица каждого исполнителя.
Первый — «чужой файл». Положите в служебный каталог любой тестовый файл, который у исполнителя должен быть виден только на чтение, например бриф. Войдите под Анной и попытайтесь: удалить его, перезаписать, переименовать. Все три попытки должны закончиться отказом. Затем скачайте тот же файл: если по замыслу чтение разрешено, скачивание пройдёт. Теперь попробуйте добраться до папки Бориса: ввести путь вручную, подняться на уровень выше. Ничего, кроме собственных виртуальных путей, видно быть не должно.
Второй — «собственный результат». Загрузите в /delivery небольшой файл. Затем проверьте остальное: можно ли его удалить, можно ли загрузить файл с тем же именем повторно. Ответ должен совпасть с тем, что вы решили заранее. Если вы разрешили только загрузку, то и повторная отправка, и удаление должны быть отклонены; если дали перезапись, повтор пройдёт.
Эту проверку полезно записать в виде короткой таблицы ожиданий и фактов и пройти по обоим пользователям. Расхождение между ожиданием и фактом — самая частая находка: например, права, заданные на уровне пользователя, оказались шире, чем вы хотели задать для конкретного пути. Лучше увидеть это сейчас, чем при передаче файлов в боевом режиме.
И ещё одна оговорка. Правила SFTPGo действуют на операции, которые идут через сервис. Если кто-то с доступом к самому серверу правит каталоги напрямую, обходя SFTPGo, ни права подрядчика, ни квоты в этом не участвуют. Поэтому и административный доступ к серверу держите отдельно от подрядческого.
Нужен сервер под эту задачу?
Разверните VPS MAATRIX за пару минут: NVMe, AMD EPYC, root-доступ, локации Россия, США, Франция и Великобритания. Оплата картой РФ и по СБП.
Квоты: три разных числа, которые нельзя путать
Здесь у многих возникает приятная иллюзия: выставил квоту 8 ГБ — и сервер «зарезервировал» восемь гигабайт. Нет. Квота — это учётный лимит, который не создаёт новое место. SFTPGo документирует квоты по объёму и по числу файлов, а также отдельные ограничения скорости загрузки и скачивания и лимиты сессий (SFTPGo: Features). Лимит может отказать подрядчику в загрузке сверх нормы, но сам диск от этого не станет больше.
Поэтому различайте три величины:
- Квота пользователя — сколько всего может занять этот аккаунт.
- Квота папки — сколько может занять конкретная виртуальная папка; она учитывается отдельно или входит в пользовательскую.
- Свободное место на диске — то, что реально есть под файловой системой, где лежат каталоги.
Теперь расчёт на нашем примере. Ожидаемый результат Анны — 6 ГБ, Бориса — 8 ГБ. Вместе серверу нужно как минимум 14 ГБ под сами файлы. Но диск держит не только их: туда же ложатся операционная система, программа SFTPGo, журнал передач и всё остальное, что вы на нём запустили. Плюс нужен запас на случай, что исполнитель загрузит промежуточную версию, а потом финальную. Итого формула такая: 14 ГБ результатов + место под ОС + место под журнал + выбранный вами запас. Размер запаса, ОС и журнала подставьте свои: он зависит от образа системы, от того, сколько версий вы готовы терпеть, и от того, как долго храните журналы.
Теперь квоты. Для Анны разумно задать квоту чуть выше ожидаемых 6 ГБ — например, с запасом на одну дополнительную версию, если вы это допускаете. Для Бориса — выше 8 ГБ. Но сумма квот не должна превышать того, что вы готовы отдать подрядчикам с реального диска. Если вы выдали одному 10 ГБ, другому 12 ГБ, а свободно только 15 ГБ, то обе квоты по отдельности «в порядке», а вместе они обещают больше, чем есть. Первый же заполненный до предела исполнитель оставит второго без места, и ошибка будет выглядеть как проблема самого диска, а не лимита.
Поэтому проверка делается в два прохода: сначала каждая квота против ожидаемого результата своего исполнителя, затем сумма квот против свободного места. Если к этому добавляется копия результатов, учитывайте её по реально выбранному месту хранения: если она лежит на том же диске, она съедает то же свободное место и должна войти в расчёт; если вы выносите копию на отдельный носитель или в другое хранилище, считайте ёмкость уже того места, а не нынешнего диска.
Здесь же появляется практический ответ на вопрос о размере VPS. Нужный вам объём диска вытекает из расчёта выше, а не из названия тарифа. На VPS MAATRIX с NVMe, процессорами AMD EPYC и root-доступом диск начинается с 40 ГБ на тарифе Ba, далее 80 ГБ на Uab, 160 ГБ на Heka, 240 ГБ на Maat и 320 ГБ на Djed. Сравните с вашим итогом: 14 ГБ результатов плюс система, журнал и запас. Если после сложения вы укладываетесь с хорошим остатком, ёмкость подойдёт; если исполнителей станет больше или результаты вырастут, пересчитайте по тем же правилам. Заявленные ресурсы — это параметры тарифа, а не обещание, что любая нагрузка на нём пройдёт, поэтому окончательное решение принимайте по своим объёмам и по тому, сколько вы реально видите на диске.
Если вам интересно, как устроены квоты на уровне файловой системы, а не сервиса, посмотрите отдельный материал: дисковые квоты для пользователей и контейнеров. Это другой уровень контроля, и он не заменяет учёт внутри SFTPGo, но помогает понять границу между тем и другим.
Скорость и число сессий
Раз уж речь о ресурсах: SFTPGo умеет ограничивать скорость загрузки и скачивания и число одновременных сессий (SFTPGo: Features). Для подрядчика с гигабитным каналом и видеофайлом на 8 ГБ это не мелочь. Без лимита одна загрузка способна занять канал, которым пользуются и другие сервисы на том же сервере. Ограничение скорости и разумный лимит сессий на аккаунт делают поведение предсказуемым: два исполнителя сдают результат одновременно, и ни один из них не вытесняет остальной трафик. Конкретные значения подбирайте под ваш канал и под то, что ещё работает на машине.
Срок входа и действующие сессии
Подрядчик работает неделю — значит, и доступ должен жить неделю. У пользователя SFTPGo для этого есть поле expiration_date. Это Unix timestamp в миллисекундах, и по схеме пользователя истёкший аккаунт не может войти. Значение 0 означает отсутствие срока (SFTPGo: official OpenAPI user schema).
Две типичные ошибки. Первая — записать в поле секунды вместо миллисекунд. Тогда число окажется на три порядка меньше, и дата уедет в 1970-е: аккаунт будет считаться давно истёкшим и вход не получится с первого дня. Вторая — оставить 0 «на потом». Это ровно «срока нет», и временный пользователь превращается в постоянного. Чтобы получить правильное значение, возьмите нужный момент в секундах Unix и умножьте на 1000. Например, если момент окончания выражается числом 1800000000 секунд, в поле должно стоять 1800000000000.
Теперь про границу. Описание поля говорит о том, что истёкший аккаунт не может войти, то есть о проверке нового входа. Обещания мгновенно оборвать уже открытую сессию в нём нет. Практический вывод: если Борис вошёл за минуту до окончания срока и продолжает большую загрузку, не рассчитывайте, что соединение разорвётся ровно в назначенный момент. Закладывайте срок с запасом на завершение передачи и держите в голове два действия: срок закрывает вход, а за уже начатой сессией вы следите сами. Если сессия должна прекратиться немедленно, это отдельное решение администратора, а не следствие даты в поле.
По той же причине выбирайте срок под реальную работу, а не «на всякий случай». Если исполнитель вправе сдавать результат в пределах недели, ставьте окончание на конец последнего дня плюс небольшой запас, но не месяц. Лишний срок — это лишнее окно для входа с давно забытым ключом.
Приёмка по журналу передачи
Когда исполнитель написал, что всё загружено, верить на слово необязательно. Журналы SFTPGo — это поток JSON, где поле sender различает виды событий. Записи о передачах фиксируют загрузку или скачивание, имя пользователя, путь к файлу, размер в байтах, время, идентификатор соединения, протокол и ошибку, если она была (SFTPGo: Logs).
Для приёмки это почти готовый протокол. Отберите записи о загрузке по имени пользователя и сопоставьте их с ожиданиями:
- путь файла лежит в
/delivery, а не где-то ещё; - размер в байтах совпадает с тем, что заявил исполнитель, и с размером файла на диске;
- в записи нет поля ошибки, а если оно есть, передача не считается завершённой;
- время загрузки укладывается в срок, который вы выдали.
Если Анна говорит, что отправила архив на 6 ГБ, а в журнале запись с размером в несколько мегабайт и ошибкой, разговор получается коротким и предметным: обрыв связи, повторите. Идентификатор соединения помогает связать несколько записей одной сессии, а протокол показывает, пришёл ли файл через SFTP или другим способом.
Но будьте точны в выводах. Журнал содержит идентификатор учётной записи, а не доказательство личности человека за экраном. Запись «пользователь boris_video загрузил файл» говорит о том, что кто-то вошёл под этим аккаунтом с его ключом или паролем. Это достаточно для приёмки результата, но это не удостоверение личности; поэтому персональные ключи и отказ от общих логинов — часть задачи, а не формальность.
В SFTPGo есть Web Client, Event Manager и события передач (SFTPGo: Features). Наличие этих механизмов ещё не означает, что вам придёт уведомление о загрузке: правила нужно создать самим. Пока этого нет, приёмка идёт ручным просмотром журнала. Если же вы настроите реакцию на события передач, проверьте её на тестовом файле так же, как проверяли права, прежде чем на неё полагаться.
Закрыть вход и сохранить результат
Финальный шаг короткий, но именно его чаще всего откладывают. Результат принят, записи в журнале сходятся. Теперь нужно сделать две разные вещи и не перепутать порядок.
Сначала сохраните результат. Скопируйте принятые файлы Анны и Бориса из папок сдачи туда, где они будут жить дальше, и проверьте, что размеры совпадают с теми, что значились в журнале. Если копия идёт на тот же диск, вспомните расчёт про свободное место: два исполнителя на 14 ГБ плюс копия — это уже 28 ГБ под файлы. Если на другое хранилище, считайте ёмкость там.
Затем закройте вход. Для Анны, чья работа завершена, срок уже можно проставить в прошлое либо отключить аккаунт, чтобы новый вход не проходил. Убедитесь, что попытка подключиться под anna_layout заканчивается отказом, и убедитесь, что у неё нет открытых соединений. Помните: срок входа не обещает мгновенного разрыва уже открытой сессии, поэтому смотрите на действующие подключения отдельно. Борис, у которого работа ещё идёт, остаётся со своим сроком и своей папкой, и от закрытия аккаунта Анны у него ничего не меняется: пути, права и квоты у них не пересекались.
В итоге картина получается такой, какой мы и хотели с самого начала. Анна положила архив на 6 ГБ в свой /delivery, чужих файлов не видела и тронуть не могла. Её вход закрыт, ключ больше ничего не открывает, а принятый результат лежит у вас с записью в журнале о том, кто, когда и сколько загрузил.