Безопасность ERPNext: двухфакторная аутентификация, SSL, права системы и обновления
Базовая защита ERPNext держится на пяти вещах: двухфакторная аутентификация и политика паролей в System Settings, SSL на домене, закрытые наружу базы данных и Redis, ограниченный доступ к Server Script и Print Format, копия с проверенным восстановлением перед обновлением. Ниже чек-лист с критерием готовности.
С чего начать защиту ERPNext: пять настроек, которые закрывают основное
Первым делом я не покупаю ничего и не ставлю новых программ. ERPNext построен на Frappe, и значительная часть защиты лежит в настройках самой системы, куда обычно никто не заглядывает после установки. Пять пунктов ниже закрывают основные риски для небольшой компании, а остальное разбираю в следующих разделах. Для каждого пункта я записываю критерий готовности, по которому можно ответить «сделано» или «нет» без споров. Этот чек-лист я использую при приёмке внедрений, в том числе в проектах ERPNext под ключ, а ниже даю разбор настроек по исходникам Frappe.
Принцип такой: безопасность это не разовое действие, а чек-лист, который повторяют после каждого обновления и при каждом уходе сотрудника. Если вы администрируете систему сами, распечатайте пять пунктов и пройдите их сегодня. Если нет, передайте тому, кто отвечает за сервер, и попросите отметить критерий напротив каждого.
Одно замечание о границах. Эта статья про настройки ERPNext и окружения; общие вопросы вроде защиты периметра и мониторинга серверов раскрыты в материалах по информационной безопасности. Хранение данных и соблюдение законодательства при self-hosted остаются зоной ответственности владельца системы, 152-ФЗ я здесь упоминаю только мимоходом.
- 2FA включена для администраторов и бухгалтерии (System Settings, блок двухфакторной аутентификации). Критерий: вход без одноразового кода невозможен.
- Политика паролей и срок сессии заданы (System Settings). Критерий: минимальный балл пароля не ниже 3, простой сессии не больше рабочего дня.
- SSL на домене, порты базы данных и Redis наружу не опубликованы. Критерий: с внешней сети отвечает только 443, и браузер не показывает предупреждений.
- Server Script отключены или доступны только System Manager; правка Print Format только у System Manager. Критерий: у остальных ролей нет права менять форматы и скрипты.
- Полная копия и проверенное восстановление перед каждым обновлением. Критерий: пробное восстановление выполнено на тестовой копии и данные открылись.
Как включить двухфакторную аутентификацию и ужесточить пароли и сессии
Все эти параметры лежат в одном месте: System Settings. Я проверил их названия и значения по умолчанию в исходниках Frappe версии 15, чтобы вы не искали по памяти. Двухфакторная аутентификация включается флагом «Enable Two Factor Auth». Метод выбирается полем «Two Factor Authentication method» с вариантами «OTP App», «SMS» и «Email». Описание поля говорит, что выбранный метод используется всеми пользователями: переключить его на отдельную роль в этом блоке нельзя. По умолчанию стоит приложение-аутентификатор, и это хороший выбор, потому что не зависит от почты и SMS.
Рядом лежат связанные настройки. «OTP Issuer Name» по умолчанию равен «Frappe Framework», его стоит заменить на название вашей компании, иначе в приложении-аутентификаторе у всех сотрудников будет запись с чужим именем. «Bypass Two Factor Auth for users who login from restricted IP Address» позволяет не спрашивать код у пользователей, для которых ограничен адрес входа: полезно для офисной сети, но каждое исключение это дыра, поэтому включайте его осознанно. Сначала проверьте всё на отдельной учётной записи, а не на единственном администраторе: если вы заблокируете себя, вернуть доступ придётся через консоль сервера.
Теперь пароли и сессии. Политика паролей включена по умолчанию («Enable Password Policy»), а «Minimum Password Score» принимает значения 2, 3 или 4, и по умолчанию стоит 2: этого мало, я ставлю 3. «Force User to Reset Password» задаёт срок в днях. Лимит неудачных попыток управляется полями «Allow Consecutive Login Attempts» (по умолчанию 10) и «Allow Login After Fail» (по умолчанию 60 секунд): после десяти ошибок вход блокируется на минуту, это защита от перебора, но не панацея. Срок простоя сессии, поле «Session Expiry (idle timeout)», по умолчанию равен 170 часам, то есть больше недели: для финансовой системы я сокращаю до 8–12 часов. «Allow only one session per user» ограничивает вход с нескольких устройств, с исключением для мобильных.
Что ещё я проверяю в этом блоке. «Login with email link» в v15 включён по умолчанию: по ссылке из письма можно войти без пароля, и если почтовый ящик сотрудника взломан, система окажется под угрозой. Если этот способ вам не нужен, отключите его. «Logout All Sessions on Password Reset» включён по умолчанию, и это правильно. На карточке пользователя есть поля «Restrict IP», «Login After» и «Login Before»: ограничение адреса и часов входа, полезное для привилегированных учёток. Для интеграций заведите отдельного пользователя с ключом API (api_key и api_secret) и узкой ролью, а не используйте ключи администратора; журнал запросов включается флагом «Log API Requests».
SSL и периметр: что открыто наружу и что должно быть закрыто
Вход по паролю без SSL недопустим: пароль и сессия идут открытым текстом. На классической установке через bench сертификат Let's Encrypt выпускает команда bench setup lets-encrypt. Её аргумент это имя сайта, а опция --custom-domain задаёт домен; опция -n (--non-interactive) перезапускает nginx и запускает certbot без вопросов, но по справке её не стоит использовать с первой попытки. Перед выпуском DNS должен указывать на сервер, а конфигурация nginx быть настроена:
sudo bench setup nginx
sudo bench setup lets-encrypt site1.example.com --custom-domain erp.example.comДополнительный домен сначала привязывают к сайту (bench setup add-domain), и только потом выпускают на него сертификат. Как ваша версия bench ведёт себя с несколькими сайтами на одном сервере, проверьте на тестовом домене. Срок сертификата стоит держать под автоматическим контролем, иначе вы узнаете о просрочке от пользователей.
В Docker-развёртывании сертификатом обычно занимается обратный прокси: в документации frappe_docker для схем с Traefik и nginx-proxy обязательна переменная LETSENCRYPT_EMAIL. Там же есть переменные портов публикации HTTP_PUBLISH_PORT и HTTPS_PUBLISH_PORT. Есть и тревожный пункт: переменная DB_PASSWORD (пароль root базы данных MariaDB или Postgres) по умолчанию равна «123». Менять этот пароль надо до первого запуска и хранить в менеджере секретов, а не в файле .env в репозитории. Про хранение паролей смотрите материал про корпоративный менеджер паролей.
Что должно быть закрыто. Из интернета должен быть виден только порт 443 (и 80 для перенаправления). Порты базы данных, Redis и внутренний порт приложения наружу публиковать нельзя. В Docker это отдельная ловушка: опубликованный порт контейнера может быть доступен снаружи вопреки правилам межсетевого экрана на хосте, об этом у меня есть разбор про Docker и ufw. Проверка простая: со стороннего компьютера просканируйте внешний адрес и убедитесь, что отвечает только то, что должно. От перебора паролей на уровне сервера помогает fail2ban.
Опасные места Frappe: Server Script, Print Format и загрузка файлов
Три функции Frappe сами по себе делают систему гибкой, а в чужих руках опасны. Первая: Server Script, то есть код на Python, который выполняется на сервере и создаётся из интерфейса. Начиная с версии 15 они отключены по умолчанию, чтобы не подвергать риску общие окружения. Включение делается осознанно, на уровне всего bench:
bench set-config -g server_script_enabled trueПосле этого создавать скрипты сможет тот, у кого есть соответствующее право. Я включаю их только там, где без них нельзя, например для динамического согласующего в маршруте (самосогласование, кстати, скрипта не требует: его отключает флажок Allow Self Approval на переходе Workflow), и даю право только администратору системы, с обязательной проверкой на тестовом стенде.
Вторая: Print Format. Поле HTML допускает произвольную разметку и скрипты, и форматы исполняются в браузерах пользователей, которые их открывают. В схеме DocType право создавать и менять форматы есть у роли System Manager, а остальным по умолчанию доступен только выбор формата. Простое правило: обычному сотруднику право редактировать печатные формы не нужно, ему достаточно права Print. Третья: загрузка файлов. В System Settings есть настройки, которые я проверяю на каждом проекте: «Max File Size (MB)», «Allowed File Extensions» (список разрешённых расширений, по одному в строке), «Strip EXIF tags from uploaded images», «Max Zip Extract Size (MB)» и «Allow Guests to Upload Files». Последнюю нужно держать выключенной, если у вас нет публичных форм с вложениями.
Ещё одна опасность не техническая, а организационная: права. Одной роли мало, чтобы разграничить объекты. Если сотруднику выдали роль с доступом к типу документа и не создали User Permission, он увидит все записи этого типа. Поэтому прорабу назначают User Permission на его проекты, а проверку я всегда делаю под тестовым пользователем: заходим как прораб и убеждаемся, что чужие объекты не видны. Подробнее про эту механику читайте в материалах о правах доступа и ролях; сюда стоит добавить регламент отзыва доступов при увольнении, например по чек-листу отзыва доступов.
Обновления как часть безопасности: фиксированные версии и тест на стенде
Обновления в ERPNext это одновременно защита и риск. С одной стороны, исправления безопасности приходят только в новых версиях. С другой, отката версии базы данных нет: можно только накатывать вперёд или восстанавливаться из резервной копии. Поэтому я требую два условия до любого обновления боевой базы. Первое: версии зафиксированы, то есть вы знаете, какая версия стоит сейчас и что вы ставите. Второе: на тестовой копии (staging) обновление уже прошло, и под ролями прораба, снабженца и бухгалтера проверен вход и основные сценарии.
Копия перед обновлением делается штатной командой, а восстановление проверяется на другом сайте. Файлы тоже нужны, поэтому добавляйте --with-files:
bench --site site1.example.com backup --with-files
bench --site test.example.com restore /path/to/database.sql.gz \
--with-public-files /path/to/files.tar \
--with-private-files /path/to/private-files.tarКоманда обновления bench update по умолчанию делает резервные копии сайтов, а флаг --no-backup отключает их, и справка прямо называет это нежелательным на боевой системе. Команда restore предупреждает о попытке «понизить» версию: флаг --force отключает эти проверки, и я его не использую.
Две настройки, о которых забывают. «Number of Backups» в System Settings по умолчанию равно 3: старые копии удаляются автоматически, и если вы делаете копию раз в сутки, то глубина хранения три дня. Для боевой системы этого мало, и копии лучше выносить на отдельный носитель. «Encrypt Backups» шифрует копии, но тогда вы должны хранить ключ отдельно: без него восстановление невозможно, а при восстановлении используется опция --encryption-key. О правиле резервного копирования и проверке восстановления у меня есть отдельный материал, например про правило 3-2-1. Доработки в ERPNext должны жить в отдельном приложении и выгружаться через fixtures, а не правкой ядра: иначе обновление их стирает.
Как «Лифт-Сервис Норд» на 30 рабочих мест проводила аудит настроек
Условный пример. Сервисная компания «Лифт-Сервис Норд», 30 рабочих мест: диспетчеры, инженеры, снабжение, бухгалтерия, руководители. ERPNext развернут в Docker на арендованном сервере, три года без изменений настроек безопасности. Директор задал вопрос, который я слышу чаще всего: «Нас можно взломать?» Мы ответили чек-листом, а не словами.
Аудит занял один рабочий день и шёл по пяти пунктам. Сначала мы посмотрели System Settings: двухфакторная аутентификация выключена, минимальный балл пароля 2, срок простоя сессии стоял по умолчанию. Дальше снаружи просканировали адрес: порт 443 отвечал, лишних портов не нашли, но при осмотре конфигурации контейнеров обнаружилось, что пароль базы данных остался значением из примера и хранился в файле в открытом виде. Затем проверили роли: у двух диспетчеров была роль System Manager «на всякий случай», хотя им нужен только просмотр. Потом посмотрели резервные копии: настройка «Number of Backups» осталась по умолчанию, восстановление ни разу не проверяли.
Что сделали за две недели. Включили 2FA сначала для администраторов и бухгалтерии, потом для остальных, с кодами восстановления у директора. Подняли минимальный балл пароля до 3, а срок простоя сессии сократили до рабочего дня. Сменили пароль базы и перенесли его в менеджер секретов. Сняли роль System Manager у диспетчеров, создали им роль с правом чтения и печати. Отключили вход по ссылке из письма. Настроили ежедневные копии на отдельный носитель и провели пробное восстановление на тестовом сайте, после чего вошли под ролями прораба, снабженца и бухгалтера. Ни одна из этих мер не требовала доработок кода.
Что осталось на ближайший год в виде регламента: ежемесячно проверка срока SSL и учётных записей уволенных, ежеквартально пробное восстановление и ревизия ролей, раз в год оценка перехода на новую мажорную версию на staging, после каждого обновления тест входа под ключевыми ролями. Показать, как выглядят эти настройки в System Settings, могу на демостенде по запросу.
- Ежемесячно: срок SSL, учётные записи уволенных, лишние сессии.
- Ежеквартально: пробное восстановление копии, ревизия ролей и User Permission.
- Раз в год: оценка перехода на новую мажорную версию на staging.
- После каждого обновления: вход под ролями прораба, снабженца и бухгалтера, проверка печатных форм.
Частые вопросы
Как включить двухфакторную аутентификацию в ERPNext?
В System Settings включите «Enable Two Factor Auth» и выберите метод: «OTP App», «SMS» или «Email». Метод общий для всех пользователей. Сначала проверьте включение на отдельной учётной записи и сохраните резервный способ входа для администратора, чтобы не заблокировать себя. Проверьте также «OTP Issuer Name».
Нужен ли SSL для ERPNext и как его настроить?
Да, вход по паролю без SSL недопустим. На классической установке есть команда bench setup lets-encrypt для сайта с опцией --custom-domain, перед этим должен быть настроен DNS и nginx. В Docker сертификатом обычно занимается обратный прокси, например Traefik с переменной LETSENCRYPT_EMAIL. Срок сертификата держите под мониторингом.
Почему Server Script в ERPNext по умолчанию выключены?
Начиная с Frappe v15 серверные скрипты из интерфейса отключены, потому что это код, выполняемый на сервере. Включаются командой bench set-config -g server_script_enabled true на весь bench. Включайте осознанно, только для доверенных администраторов и после проверки на тестовом стенде.
Кому можно давать правку Print Format в ERPNext?
Только доверенным ролям уровня System Manager: поле HTML формата допускает разметку и скрипты, и они исполняются в браузере пользователя. Обычному сотруднику право редактировать печатные формы не нужно, ему достаточно права Print. Проверяйте права под тестовым пользователем.
Нужно ли часто обновлять ERPNext ради безопасности?
Нужно планово и осознанно. Версии фиксируют, перед обновлением делают полную копию (bench backup --with-files) и прогоняют на staging. Отката версии базы нет: возможно только накатывать вперёд или восстанавливаться из копии. Обновление без копии и стенда само по себе риск для данных.
Источники
- Исходники Frappe v15: System Settings (GitHub) — Проверены поля и значения по умолчанию: enable_two_factor_auth, two_factor_method (OTP App, SMS, Email), otp_issuer_name, session_expiry (170:00), minimum_password_score (2), allow_consecutive_login_attempts (10), allow_login_after_fail (60), backup_limit (3), encrypt_backup, allow_guests_to_upload_files, allowed_file_extensions, max_file_size, login_with_email_link. https://github.com/frappe/frappe/blob/version-15/frappe/core/doctype/system_settings/system_settings.json
- Документация Frappe: Server Script — Проверено: с v15 серверные скрипты отключены по умолчанию, включение командой bench set-config -g server_script_enabled true. https://docs.frappe.io/framework/user/en/desk/scripting/server-script
- Исходники bench и frappe: команды (GitHub) — Проверены: bench setup lets-encrypt (опции --custom-domain, --non-interactive), bench update (--no-backup, --reset), bench backup --with-files, bench restore (--with-public-files, --with-private-files, --force, --encryption-key). https://github.com/frappe/bench/blob/develop/bench/commands/setup.py
- Документация frappe_docker: переменные окружения — Проверены LETSENCRYPT_EMAIL, DB_PASSWORD (по умолчанию 123), HTTP_PUBLISH_PORT, HTTPS_PUBLISH_PORT. https://github.com/frappe/frappe_docker/blob/main/docs/02-setup/04-env-variables.md
- Главы 08 и 09 базы знаний ITfresh — Раздел «Системная безопасность», «Обновления и миграция», User Permissions; примеры конкретной 2FA в главе отсутствовали и сверены по System Settings. projects/erpnext-kb-20261001/kb/09_ustanovka_ekspluataciya.md
