124 lines
7.9 KiB
Markdown
124 lines
7.9 KiB
Markdown
# mcimport — массовое создание ящиков mailcow из CSV
|
||
|
||
Однобинарный инструмент (stdlib-only Go, embedded веб-морда) для провижининга
|
||
почтовых ящиков mailcow через API. Импорт файла → предпросмотр с валидацией →
|
||
потоковый журнал создания.
|
||
|
||

|
||
|
||
## Возможности
|
||
|
||
- **Проверка подключения** к API (домены/ящики читаются, чтобы подтвердить ключ и allow-list).
|
||
- **Выбор домена**: после проверки подключения можно выбрать домен из списка mailcow —
|
||
тогда в CSV достаточно логина без `@домена` (email собирается как `логин@выбранный_домен`).
|
||
- **Предпросмотр**: разбор CSV, структурная валидация (email, пароль, дубликаты),
|
||
плюс — если задан ключ — сверка с живым mailcow: пропуск существующих ящиков и
|
||
отсев несуществующих доменов.
|
||
- **Импорт** с потоковым (NDJSON) журналом в реальном времени: по строке на ящик,
|
||
разбор ответа mailcow (`success`/`danger`), счётчики, выгрузка лога в CSV.
|
||
|
||
## Формат CSV
|
||
|
||
Заголовок обязателен. Разделитель — точка с запятой (`;`); также распознаются
|
||
`,` и таб. BOM снимается, кодировка — UTF-8. Готовый образец можно скачать в UI
|
||
(ссылка «Скачать пример CSV») или по `GET /api/example`.
|
||
|
||
С выбранным в UI доменом достаточно логина:
|
||
|
||
```csv
|
||
login;password;name
|
||
ivanov;S3cretPass!;Иван Иванов
|
||
petrov;An0therPass!;Пётр Петров
|
||
```
|
||
|
||
Без выбора домена — полный email в каждой строке:
|
||
|
||
```csv
|
||
email;password;name
|
||
ivanov@bildme.ru;S3cretPass!;Иван Иванов
|
||
petrov@amega.kz;An0therPass!;Пётр Петров
|
||
```
|
||
|
||
Колонки (регистр/пробелы/подчёркивания не важны, есть RU-алиасы):
|
||
|
||
| колонка | обяз. | примечание |
|
||
|----------------------|-------|--------------------------------------------------------|
|
||
| `email` / `login` | да* | при выбранном домене — логин без `@`; иначе полный email или пара `localpart` + `domain` |
|
||
| `password` | да | <8 символов → предупреждение |
|
||
| `name` | нет | ФИО; по умолчанию = localpart |
|
||
| `quota` | нет | МиБ; иначе «квота по умолчанию» |
|
||
| `active` | нет | `1`/`0`, по умолчанию `1` |
|
||
|
||
## Запуск
|
||
|
||
### Бинарь
|
||
```bash
|
||
CGO_ENABLED=0 go build -ldflags="-s -w" -o mcimport .
|
||
MAILCOW_URL=https://mail.dom.ru ./mcimport
|
||
# открыть http://localhost:8080
|
||
```
|
||
|
||
### Docker
|
||
```bash
|
||
docker compose up -d --build
|
||
# морда на 127.0.0.1:8080 — ходите через SSH-туннель:
|
||
# ssh -L 8080:127.0.0.1:8080 user@host
|
||
```
|
||
|
||
## Переменные окружения
|
||
|
||
| переменная | по умолчанию | назначение |
|
||
|------------------------|-------------------------|---------------------------------------------------|
|
||
| `MAILCOW_URL` | — | базовый URL (можно ввести в UI) |
|
||
| `MAILCOW_API_KEY` | — | Read-Write ключ; если задан — не вводится в UI |
|
||
| `DEFAULT_QUOTA` | `10240` | квота по умолчанию, МиБ |
|
||
| `LISTEN` | `:8080` | адрес прослушивания |
|
||
| `INSECURE_SKIP_VERIFY` | `false` | отключить проверку TLS (внутренний CA) |
|
||
|
||
## Что нужно на стороне mailcow
|
||
|
||
1. UI → `Configuration → Access → Edit administrator details → API`:
|
||
создать ключ с правами **Read-Write**.
|
||
2. В `API_ALLOW_FROM` добавить IP, с которого mcimport ходит в API
|
||
(egress-IP контейнера/хоста). Иначе — отказ доступа.
|
||
3. Убедиться, что лимит «Максимум почтовых ящиков» у домена и квоты домена
|
||
покрывают импортируемую пачку.
|
||
|
||
## Общая квота домена (частая ошибка `mailbox_quota_left_exceeded`)
|
||
|
||
mailcow не даст создать ящик, если `Σ выделенных квот ящиков домена` превысит
|
||
**Domain quota** (общую квоту домена). Это не про квоту одного ящика — ставить
|
||
её в `0` (безлимит) не нужно. Предпросмотр (при заданном ключе) заранее считает
|
||
нехватку и **останавливает** импорт с подсказкой, до какого значения поднять
|
||
Domain quota.
|
||
|
||
Поднять квоту домена: mailcow UI → домен → «Domain quota», либо один вызов API:
|
||
|
||
```bash
|
||
curl -sk -X POST https://mail.dom.ru/api/v1/edit/domain \
|
||
-H "X-API-Key: $KEY" -H "Content-Type: application/json" \
|
||
-d '{"items":["a27.kz"],"attr":{"quota":"307200"}}' # 300 ГБ (МиБ)
|
||
```
|
||
|
||
После правки — в UI кнопка **«Повторить проверку»** (перепроверить без загрузки
|
||
файла) или **«Повторить неудавшиеся»** в журнале (досоздать только упавшие ящики).
|
||
|
||
## Замечания по безопасности
|
||
|
||
- Инструмент **stateless**: ключ и пароли нигде не пишутся на диск и не логируются.
|
||
Пароли из CSV во время предпросмотра проходят через браузер (внутренний тул) —
|
||
запускайте только в доверенной сети / за туннелем, не публикуйте наружу.
|
||
- Публикация порта в примере — только на `127.0.0.1`.
|
||
- Слабые пароли mailcow отклонит по политике; такие строки видны в предпросмотре
|
||
как предупреждение и в журнале как `danger: password_complexity`.
|
||
|
||
## Доступы новых ящиков
|
||
|
||
При создании инструмент явно включает IMAP/POP3/SMTP/Sieve/SOGo-доступ, а не
|
||
полагается на серверные дефолты (`$MAILBOX_DEFAULT_ATTRIBUTES`). Иначе на
|
||
инстансе с переопределёнными дефолтами ящик создаётся «успешно», но вход не
|
||
проходит — выглядит как «пароль не подходит», хотя пароль верный. Флаг
|
||
`force_pw_update` управляется галкой «Требовать смену пароля при входе» (по
|
||
умолчанию выключена — иначе клиенты IMAP/SMTP получат отказ до смены пароля
|
||
через веб-интерфейс).
|