Dovecot 2.4 может принимать OAuth2 access token вместо пароля при IMAP-аутентификации. Клиент передаёт токен через SASL-механизм OAUTHBEARER или XOAUTH2, а Dovecot проверяет его у Keycloak и определяет имя почтового пользователя по атрибуту email.
В этой инструкции используется онлайн-проверка через endpoint introspection. Такой вариант проще локальной валидации JWT: Dovecot не требуется самостоятельно хранить открытые ключи и учитывать их ротацию. Кроме того, introspection позволяет получить актуальное состояние токена, включая признак active. Цена этого решения — сетевой запрос к Keycloak при каждой аутентификации.

В Dovecot 2.4 OAuth-токены обрабатываются непосредственно механизмами OAUTHBEARER и XOAUTH2: отдельная OAuth2 passdb для такого входа больше не нужна. При этом существующая userdb остаётся необходимой, поскольку после проверки токена сервер должен найти домашний каталог, UID, GID и расположение почтового ящика пользователя. Подробнее — в документации Dovecot.
Что потребуется
- Dovecot ветки 2.4 с доступом к изменению системной конфигурации;
- работающий IMAP-доступ по TLS;
- realm в Keycloak и права на создание клиентов;
- пользователь Keycloak с заполненным email;
- существующий почтовый пользователь, которого userdb Dovecot находит по этому email;
- сетевой доступ от сервера Dovecot к HTTPS-интерфейсу Keycloak;
- тестовый IMAP-клиент или утилита
openssl.
Настройка требует административного доступа к Dovecot. На обычном виртуальном хостинге пользователь, как правило, не может включать SASL-механизмы или изменять системные службы. Для такой интеграции нужен VPS-сервер, выделенный сервер, контейнер с собственной конфигурацией Dovecot либо поддержка OAuth2 со стороны панели или провайдера.
Перед изменениями проверьте фактическую версию и сохраните резервную копию конфигурации:
dovecot --version
cp -a /etc/dovecot /etc/dovecot.backup-before-keycloak
Путь /etc/dovecot типичен, но не универсален. Если Dovecot установлен из нестандартного пакета, запущен в контейнере или собран вручную, используйте реальный каталог конфигурации. Команда копирования также предполагает достаточные права.
До настройки OAuth2 убедитесь, что TLS для IMAP уже работает корректно. Проверку сертификата, SNI и защищённых IMAPS/POP3S-подключений разбирает материал о TLS в Dovecot.
Определяем адреса Keycloak
В примерах будут использоваться следующие условные значения:
- адрес Keycloak —
https://sso.example.com; - realm —
mail; - идентификатор клиента —
dovecot-imap; - почтовый пользователь —
user@example.com; - IMAP-сервер —
imap.example.com.
Заменяйте их своими значениями. Имя realm чувствительно к регистру и должно совпадать во всех URL.
Keycloak публикует параметры OIDC realm через discovery-документ:
https://sso.example.com/realms/mail/.well-known/openid-configuration
В нём можно проверить issuer, token_endpoint, introspection_endpoint и jwks_uri. Для рассматриваемой схемы особенно важны точный issuer и endpoint introspection. Стандартный путь последнего имеет вид:
https://sso.example.com/realms/mail/protocol/openid-connect/token/introspect
Endpoint introspection сообщает, активен ли переданный токен, и доступен только конфиденциальным клиентам. Поэтому клиент, чьи реквизиты использует Dovecot, должен иметь включённую клиентскую аутентификацию. Это соответствует описанию OIDC endpoints в документации Keycloak.
Откройте discovery URL в браузере или проверьте его с сервера Dovecot:
curl --fail --silent --show-error https://sso.example.com/realms/mail/.well-known/openid-configuration
Ошибка сертификата, DNS или соединения должна быть устранена до настройки Dovecot. Не используйте параметр -k и не отключайте проверку сертификата в рабочей среде: сервер будет отправлять Keycloak действующие токены и реквизиты конфиденциального клиента.
Создаём OIDC-клиент в Keycloak
Откройте административную консоль Keycloak, выберите нужный realm и перейдите в раздел клиентов. Названия отдельных элементов интерфейса могут немного различаться между выпусками Keycloak, но требуемые параметры остаются теми же.
- Нажмите создание нового клиента.
- Выберите протокол или тип клиента
OpenID Connect. - Укажите Client ID
dovecot-imap. - Включите Client authentication. Это делает клиента конфиденциальным и позволяет ему обращаться к introspection endpoint.
- Сохраните клиента.
- Откройте вкладку с реквизитами клиента и скопируйте Client secret.
Для самой проверки токена не требуются redirect URI, web origins или service account. Standard Flow понадобится только в том случае, если этот же клиент участвует в интерактивном Authorization Code Flow. Direct Access Grants нужен лишь для приведённого ниже CLI-теста с логином и паролем; в постоянной конфигурации его лучше не оставлять включённым без необходимости.
В рабочей архитектуре получение токена и его проверка могут обслуживаться разными клиентами. Например, настольному приложению назначают публичный OIDC-клиент без секрета, а Dovecot использует отдельного конфиденциального клиента для introspection. Секрет Dovecot нельзя передавать пользовательскому почтовому приложению.
Добавляем email в access token
Dovecot должен получить из результата проверки значение, однозначно соответствующее почтовому ящику. В этой инструкции таким значением служит claim email.
Сначала откройте пользователя Keycloak и убедитесь, что в его профиле заполнен email:
user@example.com
Затем проверьте настройки клиента и назначенные ему client scopes. Стандартный scope email должен быть доступен клиенту, а при запросе токена нужно запросить как минимум:
openid email
Если после этого claim отсутствует в access token или результате introspection, создайте для клиента OIDC protocol mapper со следующей логикой:
- источник значения — пользовательское свойство
email; - имя token claim —
email; - тип claim — String;
- добавление в access token — включено;
- добавление в introspection response — включено, если такая отдельная настройка присутствует в используемой версии Keycloak.
Не следует подставлять в email отображаемое имя, логин без домена или произвольный псевдоним. Значение должно совпадать с ключом, по которому Dovecot находит реальный почтовый ящик.
Проверяем соответствие email и пользователя Dovecot
OAuth2 отвечает только за подтверждение личности. После успешной проверки Dovecot передаёт имя пользователя своей userdb. Поэтому до включения OAuth убедитесь, что текущая пользовательская база уже знает адрес:
doveadm user user@example.com
Успешный ответ обычно содержит UID, GID, домашний каталог и другие поля, зависящие от вашей схемы хранения. Ошибка unknown user означает, что проблема находится не в Keycloak: сначала нужно настроить существующую userdb — SQL, LDAP, passwd-file, системных или статических пользователей.
В статье предполагается прямое соответствие:
email в Keycloak = имя для входа в IMAP = ключ пользователя в userdb
user@example.com = user@example.com = user@example.com
Если ящики в Dovecot называются иначе, например только user, безопаснее привести обе системы к единому каноническому имени или выпустить отдельный claim с точным идентификатором ящика. Простого указания username_attribute = email недостаточно для произвольного преобразования адреса в другую внутреннюю учётную запись.
Особое внимание уделите регистру. Хотя доменная часть email регистронезависима, пользовательская база и файловая система могут воспринимать User@example.com и user@example.com как разные строки. Практичный вариант — хранить почтовые идентификаторы в нижнем регистре и выдавать их из Keycloak в том же виде.
Получаем тестовый access token
Для первичной проверки можно временно включить Direct Access Grants у созданного клиента. Это позволяет запросить токен командой curl, не настраивая браузерное перенаправление. Такой способ предназначен именно для контролируемого теста: он требует передачи пароля Keycloak token endpoint и может быть несовместим с обязательной многофакторной аутентификацией.
Не записывайте настоящий пароль и Client secret в историю команд на многопользовательском сервере. Для лабораторного окружения запрос выглядит так:
read -r -p 'Keycloak username: ' KC_USER
read -r -s -p 'Keycloak password: ' KC_PASSWORD
printf '\012'
read -r -s -p 'Client secret: ' KC_CLIENT_SECRET
printf '\012'
TOKEN_RESPONSE=$(curl --fail --silent --show-error -X POST -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode 'grant_type=password' --data-urlencode 'client_id=dovecot-imap' --data-urlencode "client_secret=${KC_CLIENT_SECRET}" --data-urlencode "username=${KC_USER}" --data-urlencode "password=${KC_PASSWORD}" --data-urlencode 'scope=openid email' 'https://sso.example.com/realms/mail/protocol/openid-connect/token')
ACCESS_TOKEN=$(printf '%s' "$TOKEN_RESPONSE" | jq -r '.access_token')
unset KC_PASSWORD
printf 'Token length: %s\012' "${#ACCESS_TOKEN}"
Команды предполагают наличие jq. Не выводите access token в общий журнал и не отправляйте его в сторонние JWT-декодеры: до истечения срока действия это полноценный секрет доступа.
Если Keycloak возвращает unauthorized_client, проверьте, включён ли Direct Access Grants. Ошибка invalid_client обычно указывает на неверный Client ID, секрет или режим клиентской аутентификации. invalid_grant может означать неверный пароль, обязательное действие в профиле пользователя, временную блокировку либо поток аутентификации, который нельзя выполнить через password grant.
Проверяем токен через introspection
До редактирования Dovecot убедитесь, что конфиденциальный клиент действительно может проверить токен:
curl --fail --silent --show-error -u "dovecot-imap:${KC_CLIENT_SECRET}" -H 'Content-Type: application/x-www-form-urlencoded' --data-urlencode "token=${ACCESS_TOKEN}" 'https://sso.example.com/realms/mail/protocol/openid-connect/token/introspect' | jq
В ответе должны присутствовать как минимум:
{
"active": true,
"email": "user@example.com"
}
Реальный ответ будет содержать и другие поля. Проверьте также iss и scope, если собираетесь ограничивать по ним Dovecot. Значение issuer нужно копировать точно, без добавления или удаления завершающего слеша.
Если active равно false, возможны истечение срока действия, отзыв токена, передача ID token вместо access token или выпуск токена в другом realm. Если токен активен, но email отсутствует, вернитесь к client scopes и protocol mapper.
Подключаем OAuth2 к Dovecot 2.4
Создайте отдельный конфигурационный файл в каталоге, который подключается основной конфигурацией Dovecot. Например, в пакетной установке это может быть файл /etc/dovecot/conf.d/90-keycloak-oauth2.conf. Не копируйте путь вслепую: сначала проверьте структуру своей установки.
Минимальная конфигурация для проверки токена через Keycloak:
auth_mechanisms {
oauthbearer = yes
xoauth2 = yes
}
oauth2 {
introspection_mode = post
introspection_url = https://dovecot-imap:CLIENT_SECRET_URLENCODED@sso.example.com/realms/mail/protocol/openid-connect/token/introspect
force_introspection = yes
active_attribute = active
active_value = true
username_attribute = email
issuers {
https://sso.example.com/realms/mail = yes
}
token_expire_grace = 30s
openid_configuration_url = https://sso.example.com/realms/mail/.well-known/openid-configuration
}
Назначение параметров:
auth_mechanismsвключает оба SASL-механизма. OAUTHBEARER стандартизован для OAuth2, а XOAUTH2 нужен для совместимости с клиентами, которые поддерживают только этот формат;introspection_mode = postпередаёт токен endpoint в теле POST-запроса;introspection_urlзадаёт endpoint и реквизиты конфиденциального клиента;force_introspection = yesзаставляет выполнять introspection, что необходимо для надёжной проверки поля активности;active_attributeиactive_valueтребуют, чтобы Keycloak вернулactive: true;username_attribute = emailвыбирает email как имя почтового пользователя;issuersразрешает токены только указанного realm;token_expire_graceоставляет небольшую погрешность для часов и сетевой задержки;openid_configuration_urlпозволяет Dovecot сообщать адрес OIDC-конфигурации в диагностическом ответе OAUTHBEARER.
Dovecot поддерживает для OAuth2 проверку через tokeninfo или introspection, а для JWT — локальную валидацию. Также доступны ограничения по issuer и scope и выбор атрибута имени пользователя.

Как безопасно записать Client secret
В URL используется HTTP Basic Authentication. Если секрет содержит @, :, /, %, # или другие зарезервированные символы, его необходимо URL-кодировать. Например, это можно сделать локально с помощью Python:
python3 -c 'import getpass, urllib.parse; print(urllib.parse.quote(getpass.getpass("Client secret: "), safe=""))'
Результат вставляется вместо CLIENT_SECRET_URLENCODED. Это не шифрование, а только корректное представление значения внутри URL.
Ограничьте чтение конфигурационного файла пользователями системы:
chown root:root /etc/dovecot/conf.d/90-keycloak-oauth2.conf
chmod 600 /etc/dovecot/conf.d/90-keycloak-oauth2.conf
В контейнере или при запуске Dovecot не от root владелец и способ передачи секрета будут другими. Главное требование — секрет не должен быть доступен непривилегированным пользователям и не должен попадать в публичные резервные копии.
Ограничение по scope
После успешного базового теста можно потребовать специальный scope, например imap. Сначала создайте или назначьте соответствующий client scope в Keycloak, получите новый токен и убедитесь, что introspection возвращает imap в поле scope. Только после этого добавляйте в блок oauth2:
scope {
imap = yes
}
Не включайте ограничение до проверки токена: иначе корректные токены без этого scope будут отклоняться, а диагностика смешается с проверкой email и соединения с Keycloak.
Проверяем конфигурацию и применяем её
Сначала выполните синтаксическую проверку:
doveconf -n
Команда не должна сообщать о неизвестных параметрах, незакрытых блоках или ошибках разбора URL. Учтите, что полный вывод эффективной конфигурации может содержать секрет клиента. Не публикуйте его в тикетах и открытых журналах.
Если синтаксис корректен, перезагрузите Dovecot способом, принятым в вашей системе. Для systemd обычно используется:
systemctl reload dovecot
Если служба или пакет не поддерживает reload, потребуется контролируемый restart:
systemctl restart dovecot
systemctl status dovecot --no-pager
Не закрывайте административную сессию, пока не убедитесь, что IMAP снова слушает нужные адреса IPv4 и IPv6 и обычные пользователи могут подключаться. При ошибке восстановите резервную копию конфигурации и повторно запустите службу.
Проверьте, что сервер объявляет новые механизмы. Подключитесь к IMAPS:
openssl s_client -quiet -crlf -connect imap.example.com:993 -servername imap.example.com
В открывшейся сессии выполните:
a1 CAPABILITY
Ответ должен содержать AUTH=OAUTHBEARER и AUTH=XOAUTH2. Если их нет, проверьте, действительно ли изменённый файл включён основной конфигурацией и не переопределяется ли auth_mechanisms ниже по порядку.
Тестируем вход через XOAUTH2
XOAUTH2 передаёт имя пользователя и bearer token в строке с разделителями ASCII Control-A. Обычные символы \x01 вводить нельзя: нужны настоящие управляющие байты. Сформируйте Base64-строку в оболочке:
IMAP_USER='user@example.com'
XOAUTH2_B64=$(printf 'user=%s\001auth=Bearer %s\001\001' "$IMAP_USER" "$ACCESS_TOKEN" | base64 | tr -d '\012')
printf '%s\012' "$XOAUTH2_B64"
Скопируйте результат, снова подключитесь через openssl s_client и отправьте:
a1 AUTHENTICATE XOAUTH2
После ответа сервера + вставьте Base64-строку отдельной строкой. При успешной проверке ожидается ответ вида:
a1 OK Logged in
Точный текст после OK зависит от сборки и конфигурации. Проверьте доступ к ящикам:
a2 LIST "" "*"
a3 LOGOUT
При ошибке сервер может вернуть дополнительную Base64-строку с диагностикой. Чтобы корректно завершить неудавшийся SASL-обмен, отправьте пустую строку, после чего изучите ответ и журнал Dovecot.
Тестируем вход через OAUTHBEARER
OAUTHBEARER использует другой формат initial response. В него можно включить авторизационное имя, имя IMAP-хоста и порт:
IMAP_USER='user@example.com'
IMAP_HOST='imap.example.com'
OAUTHBEARER_B64=$(printf 'n,a=%s,\001host=%s\001port=993\001auth=Bearer %s\001\001' "$IMAP_USER" "$IMAP_HOST" "$ACCESS_TOKEN" | base64 | tr -d '\012')
printf '%s\012' "$OAUTHBEARER_B64"
В TLS-сессии выполните:
a1 AUTHENTICATE OAUTHBEARER
После приглашения + вставьте сформированное значение. Успешный результат снова начинается с помеченного ответа a1 OK.
Если сервер объявляет возможность SASL-IR, initial response можно передать в той же IMAP-команде:
a1 AUTHENTICATE OAUTHBEARER BASE64_VALUE
Двухшаговый вариант с ожиданием + удобнее для ручной диагностики и работает независимо от поддержки SASL-IR клиентом.
Что происходит при входе
- Почтовый клиент получает access token у Keycloak подходящим OIDC-потоком.
- Клиент открывает защищённое TLS-соединение с IMAP.
- Токен передаётся Dovecot через OAUTHBEARER или XOAUTH2.
- Dovecot отправляет токен в Keycloak introspection endpoint, а клиентские реквизиты — через HTTP Basic Authentication.
- Keycloak возвращает состояние токена и доступные claims.
- Dovecot требует
active: true, проверяет issuer и извлекаетemail. - Полученный email сопоставляется с именем IMAP-входа и передаётся userdb.
- Userdb возвращает параметры существующего почтового ящика, после чего создаётся IMAP-сессия.
Dovecot не получает пароль пользователя Keycloak и не обновляет access token. Получение и обновление токенов остаётся задачей почтового клиента или отдельного приложения. Настройка SMTP-аутентификации также выполняется отдельно и в эту схему не входит.
Типичные ошибки
Keycloak возвращает active: false
Убедитесь, что в introspection передаётся access token из того же realm. Не используйте ID token или refresh token вместо ожидаемого access token. Получите новый токен и проверьте системное время на обоих серверах.
В ответе нет email
Проверьте профиль пользователя, назначение scope email и protocol mapper. После изменения настроек нужно получить новый токен: уже выпущенный токен не приобретёт новые claims.
Проверка curl проходит, но Dovecot не соединяется с Keycloak
Запустите тестовый curl именно с почтового сервера. Проверьте DNS, маршрутизацию, исходящий TCP-порт 443, доверие к цепочке сертификатов и наличие промежуточных сертификатов. Если Keycloak доступен по IPv4 и IPv6, убедитесь, что оба опубликованных адреса действительно работают либо DNS не выдаёт недоступный адрес.
Keycloak отвечает invalid_client
Проверьте Client ID, секрет, включённую клиентскую аутентификацию и URL-кодирование секрета в конфигурации Dovecot. После ротации секрета старое значение сразу перестанет подходить.
Dovecot сообщает о несовпадении пользователя
Сравните три значения: имя, переданное в SASL, claim email и результат doveadm user. Они должны совпадать в ожидаемом формате. Частые причины — другой домен, регистр символов, алиас вместо основного адреса или отсутствие полного адреса в userdb.
Аутентификация успешна, но ящик не открывается
Это обычно проблема userdb, прав файловой системы, UID/GID или расположения почты. OAuth2 к этому моменту уже выполнил свою задачу. Проверьте вывод doveadm user user@example.com и доступ процесса Dovecot к почтовому хранилищу.
В CAPABILITY нет OAuth-механизмов
Проверьте эффективную конфигурацию через doveconf -n, порядок подключения файлов и версию Dovecot. Не переносите конфигурацию ветки 2.3 без проверки: в 2.4 изменена модель OAuth-аутентификации.
Токен работает через curl, но отклоняется после ограничения scope
Посмотрите фактическое поле scope в introspection response. Назначение client scope в административной консоли ещё не гарантирует, что нужное значение присутствует в конкретном токене: это зависит от типа scope и параметров запроса токена.
Журналирование и безопасная диагностика
На системе с systemd журнал службы можно смотреть так:
journalctl -u dovecot -f
В других окружениях используйте назначенный в конфигурации Dovecot файл журнала или механизм контейнерной платформы. На время диагностики можно увеличить детализацию аутентификации, но не включайте вывод паролей и секретов. Access token также не должен попадать в журнал: любой имеющий доступ к нему сможет использовать оставшееся время действия токена.
После завершения проверки:
- отключите Direct Access Grants, если он включался только ради теста;
- очистите переменные оболочки;
- удалите временные файлы с токенами;
- проверьте права на конфигурацию Dovecot;
- при необходимости ротируйте тестовый Client secret;
- оставьте доступ к introspection только по HTTPS;
- ограничьте сетевой доступ к Keycloak средствами уже используемого файрвола, не заменяя его без анализа текущих правил.
unset ACCESS_TOKEN TOKEN_RESPONSE KC_CLIENT_SECRET XOAUTH2_B64 OAUTHBEARER_B64
Когда выбирать локальную проверку JWT
Dovecot 2.4 умеет локально проверять подписанные JWT по открытым ключам. Это сокращает количество запросов к Keycloak и позволяет продолжать новые IMAP-входы при кратковременной недоступности introspection endpoint. Однако администратору нужно правильно разместить ключи в словаре Dovecot, учитывать kid, алгоритм, azp и ротацию ключей. Загруженные ключи кэшируются, поэтому их обновление требует отдельной эксплуатационной процедуры.
Для первой интеграции с Keycloak онлайн-introspection обычно проще диагностировать: можно отдельно проверить тот же endpoint через curl и увидеть поля, на основании которых Dovecot принимает решение. Переходить на локальную валидацию имеет смысл после того, как выпуск токенов, email, issuer и пользовательская база уже согласованы.
Итоговая проверка
Рабочую конфигурацию можно считать завершённой, если выполняются все условия:
- discovery URL Keycloak доступен с сервера Dovecot по доверенному HTTPS;
- OIDC-клиент является конфиденциальным и имеет действующий секрет;
- introspection возвращает
active: trueи корректныйemail; doveadm userнаходит почтовый ящик по этому email;doveconf -nпринимает конфигурацию без ошибок;- IMAP CAPABILITY объявляет OAUTHBEARER и XOAUTH2;
- вход с действующим токеном проходит обоими механизмами;
- истёкший, отозванный или выпущенный другим realm токен отклоняется;
- Direct Access Grants отключён, если он не нужен рабочему приложению;
- Client secret и access token не доступны непривилегированным пользователям.
В результате Keycloak становится источником OAuth2-идентичности, а Dovecot продолжает отвечать только за IMAP и существующее почтовое хранилище. Связующим идентификатором служит email: Keycloak включает его в данные токена, Dovecot извлекает после introspection, а userdb сопоставляет с конкретным почтовым ящиком.


