Однажды в Hadoop-кластере, с которым я работал, я открыл hive-site.xml, а там лежал пароль от метасторы в открытом виде. Файл был доступен на чтение всей группе hadoop, нескольким сервисам и десяткам инженеров, у которых был доступ к тому кластеру. Формально это нарушение элементарных норм безопасности, но на практике такое встречается сплошь и рядом.
Убрать пароль оказалось несложно благодаря штатному механизму Hadoop, но после возник интересный вопрос: если пароль исчез из конфига, куда он делся, и стало ли от этого действительно безопаснее?
Меня зовут Егор Ившин, я аналитик по безопасности в Arenadata. Я отвечаю за безопасность продукта ADH: моя ключевая задача – выявлять уязвимости и оперативно передавать информацию о них команде разработки, при этом я не только фиксирую проблему, но и помогаю с её устранением. В своей работе я применяю различные виды анализа: SCA, SAST, ручное тестирование.
В этой статье разберём, как работает Hadoop Credential Provider API, где и как хранить JCEKS и какие подводные камни во всём этом есть. А в конце посмотрим, как этот механизм автоматизируется в продуктах Arenadata – чтобы не делать всё вручную.

Откуда секреты в конфигах Hadoop
В экосистеме Hadoop безопасность кластера начинается с защиты конфигурационных файлов. По умолчанию такие компоненты, как Hive, Spark, HBase и др., хранят чувствительные данные (пароли к базам данных, ключи шифрования, токены) в открытом виде в файлах вроде hive-site.xml или core-site.xml. Но Hadoop уже много лет предоставляет штатный механизм, позволяющий этого не делать.
Возьмите любой кластер Hadoop. Откройте hive-site.xml – почти наверняка найдёте там javax.jdo.option.ConnectionPassword с паролем от PostgreSQL или MySQL, где хранится метастора. Загляните в core-site.xml, там могут лежать ключи доступа к S3 или ADLS. В конфигах Spark та же история.
Вот типичный набор секретов, которые мигрируют в XML:
Hive Metastore – пароль к базе данных, где хранится схема Hive.
HBase – пароль для ZooKeeper или REST API.
Spark – ключи доступа к облачным хранилищам (AWS S3, Azure ADLS).
core-site.xml– токены для S3A-файловой системы.Airflow – учётные данные для подключения к базам и выполнения джоб.
Почему так делают?
Первая причина – историческая. Ранние туториалы по Hadoop показывали именно такой способ: вставил пароль в XML и работает. Эти примеры кочуют из статьи в статью, из года в год.
Вторая – скорость. Когда нужно быстро поднять кластер или протестировать интеграцию, никто не хочет заморачиваться с отдельным хранилищем секретов. Вставил строку в конфиг и поехало.
Третья – инерция. Администраторы часто даже не задумываются о рисках, потому что «так все делают». И это нормально: мы все так начинали.
Проблема в том, что привычка превращается в бомбу замедленного действия. Пароль в XML создаёт слишком много поверхностей для утечки: он размножается, копируется, передаётся, хранится в разных местах. Контролировать все эти копии невозможно.
Ошибка может случиться даже не у вас, а у разработчика, администратора бэкапов, инженера из соседней команды. Кто-то случайно сольёт конфиг в публичный репозиторий, кто-то выставит права 644 на весь каталог, кто-то скопирует файл на ноутбук для диагностики, и пароль станет достоянием гораздо более широкого круга, чем предполагалось.
Как работает Credential Provider API
Итак, пароли в XML – это плохо. Но как их оттуда убрать, не ломая приложения?
В Hadoop существует штатный механизм – Credential Provider API. Он появился ещё в Hadoop 2.6 (2014 год) и с тех пор поддерживается многими компонентами экосистемы. Его идея проста:
Вместо того чтобы хранить секрет в XML, приложение запрашивает его у провайдера.
Как это выглядит на практике:
Допустим, HiveServer2 стартует и читает файл hive-site.xml. В нём он видит свойство hadoop.security.credential.provider.path:
<property> <name>hadoop.security.credential.provider.path</name> <value>jceks://hdfs/apps/hive/security/hive.jceks</value></property>
Дальше происходит следующее:
Hive Metastore использует класс ObjectStore для подключения к базе данных. В методе getDataSourceProps() вызывается conf.getPassword("javax.jdo.option.ConnectionPassword").
Метод conf.getPassword() устроен так:
Проверяет, есть ли настроенный Credential Provider (
hadoop.security.credential.provider.path).Если есть – идёт в JCEKS, ищет alias с именем
javax.jdo.option.ConnectionPassword.Находит – расшифровывает и возвращает
char[]с паролем.Если не находит и
hadoop.security.credential.clear-text-fallback = true – fallbackна значение из XML. Еслиhadoop.security.credential.clear-text-fallback = false, то при отсутствии alias в JCEKS приложение не будет искать пароль в XML и завершится ошибкой. Это более безопасный режим: он гарантирует, что секрет не «просочится» обратно в конфиг, но требует, чтобы все alias были созданы заранее.
Именно поэтому после включения шифрования пароль пропадает из XML, но Hive продолжает работать – он читает его из JCEKS.
Не все компоненты поддерживают автоматически.
Документация Apache Hadoop содержит таблицу компонентов, которые используют getPassword() и, следовательно, поддерживают Credential Provider API (https://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-common/CredentialProviderAPI.html).
Если компонент использует conf.get() вместо conf.getPassword(), он не увидит пароль из JCEKS. Для него потребуется либо доработка, либо хранение пароля в XML.
Что такое JCEKS и как он устроен
JCEKS (Java Cryptography Extension KeyStore) – это формат защищённого хранилища, который появился ещё в Java 1.4. Фактически это файл, внутри которого лежат записи (aliases) с секретами. Каждая запись – это пара «имя → значение», зашифрованная с использованием мастер-пароля.
Один файл может содержать десятки секретов для разных сервисов. Главное – чтобы имена свойств в XML совпадали с именами alias в JCEKS.
Hadoop поддерживает три варианта URI для указания пути к хранилищу:
URI |
Описание |
Особенности |
|---|---|---|
jceks://file/path/to/file.jceks |
Локальный файл |
Читается напрямую с диска |
localjceks://file/path/to/file.jceks |
Локальный файл (без кеширования) |
Не попадает в DistributedCache |
jceks://hdfs/path/to/file.jceks |
Файл в HDFS |
Читается через распределённую ФС |
Для большинства сервисов (Hive, Spark, HBase, Oozie) хранение в HDFS является оптимальным выбором. Это избавляет от головной боли с синхронизацией и делает ротацию секретов простой операцией.
По умолчанию JCEKS использует алгоритм PBEWithMD5AndTripleDES. Это устаревший алгоритм – 3DES официально признан устаревшим NIST ещё в 2018 году, а эффективная длина ключа (112 бит) слабее современного AES-128.
Можно ли переключить JCEKS на более стойкий алгоритм, например, PBEWithHmacSHA256AndAES_128? Нет. JCEKS как формат не поддерживает смену алгоритма – Triple DES зашит в его реализацию для совместимости.
Это означает, что, если вам нужен AES для защиты keystore, придётся смотреть в сторону других форматов (например, PKCS12) или использовать внешние менеджеры секретов (Vault, KMS). Но для задач Credential Provider API JCEKS остаётся штатным решением, и его ограничения стоит просто принять.
К слову, в последних версиях ADH появился менеджер секретов OpenBao. О том, как его использовать и какие это преимущества даёт, расскажет Андрей Шитов, Директор департамента разработки облачных решений и сервисов Arenadata на технической онлайн-конференции ArenaTech 29.10.2026.
Основная угроза 3DES – атака Sweet32, которую в 2016 году показали исследователи из INRIA. Она эксплуатирует короткий размер блока – 64 бита. Из-за birthday-парадокса после шифрования примерно 32 ГБ данных одним ключом начинают появляться коллизии блоков, и атакующий может восстановить фрагменты открытого текста. Атака требует сотен гигабайт трафика и десятков часов, но она практична.
3DES не «сломан» в смысле полного взлома. Его эффективная стойкость 112 бит всё ещё требует огромных вычислительных ресурсов для brute-force.
Для JCEKS риск минимален – файл весит килобайты, а не гигабайты, и никогда не приблизится к порогу в 32 ГБ. NIST признал 3DES устаревшим: с 2024 года его запрещено использовать для шифрования новых данных. Но для хранения нескольких секретов это ограничение не критично.
Важный момент: безопасность JCEKS определяется не столько алгоритмом шифрования, сколько сохранностью мастер-пароля. Если мастер-пароль скомпрометирован, то все секреты в файле становятся доступными.
JCEKS – это как сейф с кодовым замком, если вы оставите код от сейфа на стикере под монитором, сейф не поможет. Защищённый контейнер не решает проблему безопасной передачи ключа к этому контейнеру.
Итак, у нас есть JCEKS-файл. Чтобы его расшифровать, процессу нужен мастер-пароль. Это самая болезненная часть всей схемы. Hadoop предлагает несколько вариантов. Выбор зависит от ваших требований к безопасности и инфраструктурных ограничений.
1.Переменная окружения (env)
Как это выглядит:
export HADOOP_CREDSTORE_PASSWORD=MyMasterPassword123
hive --service hiveserver2
Плюсы:
Простота реализации.
Работает «из коробки» в большинстве дистрибутивов.
Не требует дополнительных настроек ОС.
Минусы:
Переменные окружения видны через
/proc/<PID>/environлюбому пользователю с правами на чтение процессов владельца.Видны в дампах
core dump.Можно случайно вывести в лог скрипта запуска (если сработает
set -x в bash).
2. Файл с паролем (password-file)
Более безопасный подход. Вместо переменной окружения мастер-пароль записывается в отдельный файл. Hadoop читает его оттуда при старте.
Как это выглядит:
echo "MyMasterPassword123" > /etc/hive/conf/credstore.pwdchmod 400 /etc/hive/conf/credstore.pwdchown hive:hive /etc/hive/conf/credstore.pwd
В конфигурации сервиса указывается путь к этому файлу через параметр (например, hadoop.security.credential.password-file или аналогичный, зависит от реализации).
Плюсы:
Не виден через /proc – нет переменной окружения.
Можно выставить строгие права доступа.
Можно разместить на tmpfs (в оперативной памяти) – пароль никогда не касается диска.
Возможность использовать разные файлы для разных сервисов.
Минусы:
Всё равно нужен файл на диске (или в tmpfs), который может быть скомпрометирован.
Нужно синхронизировать этот файл на всех нодах (если используется локальное хранение).
При ротации мастер-пароля нужно обновлять файл на всех узлах.
3. Без пароля (no password)
Это специфический сценарий. Используется, когда сам JCEKS-файл уже хранится в зашифрованной файловой системе (например, HDFS с включённой Transparent Data Encryption – TDE).
В этом случае дополнительное шифрование внутри JCEKS считается избыточным. Hadoop полагается на шифрование на уровне блоков HDFS.
Плюсы:
Нет проблем с передачей мастер-пароля.
Минимум движущихся частей.
Минусы:
Требует настройки TDE в HDFS (сложно, требует KMS).
Не везде применимо.
Рано или поздно пароль нужно менять. Причины могут быть разными:
Истек срок действия пароля по политике безопасности.
Уволился сотрудник, который знал пароль.
Проводится плановая ротация учётных данных.
Случилась утечка или подозрение на компрометацию.
И вот тут возникает вопрос: как это сделать?
Если вы управляете JCEKS вручную, ротация выглядит так:
1.Создать новый JCEKS-файл с обновлённым паролем:
hadoop credential create javax.jdo.option.ConnectionPassword -value NewPassword456 -provider jceks://hdfs/apps/hive/security/hive_new.jceks
2.Если файл хранится локально – скопировать его на ВСЕ ноды кластера:
for host in $(cat hosts.txt); do scp hive.jceks $host:/etc/hive/conf/done
Если файл в HDFS – заменить старый на новый:
hdfs dfs -mv /apps/hive/security/hive.jceks /apps/hive/security/hive_backup.jcekshdfs dfs -mv /apps/hive/security/hive_new.jceks /apps/hive/security/hive.jceks
3.Перезапустить все сервисы, которые используют этот JCEKS.
Как мы автоматизировали эту схему в ADH через ADCM
С механикой Credential Provider API мы разобрались, а сейчас разберём реальный production-пример применимости этих знаний. Понимание механики пригодится нам в работе с ADCM, open-source платформой Arenadata для управления жизненным циклом кластеров и приложений, в которой функция шифрования уже встроена.
Разберём функцию кластера ADH «Управление шифрованием учётных данных» в ADCM на примере Hive, выполним действие Manage credential encryption и посмотрим, что изменится в конфигурационных файлах кластера.
Дожидаемся, когда ADCM выполнит операцию. Теперь необходимо удостовериться, что всё настроено как нужно. Давайте проверим:
1.Права доступа к локальному JCEKS-файлу:
ls -l /apps/hive/security/hive.jceks
-rw-r----- 1 hive hadoop 3941 июл 29 00:43 /etc/hive/conf/hive.jceks
ADCM выставил права 640 и владельца hive:hadoop. Файл читают владелец и все, кто входит в группу hadoop. Это значит, что любой пользователь из группы hadoop может прочитать JCEKS и, зная мастер-пароль, расшифровать секреты.
2.А вот как выглядит JCEKS Spark в HDFS:
hdfs dfs -ls /apps/spark/security/spark3.jceks
-rwxr-xr-x 3 spark hadoop 4900 2026-07-29 00:47 /apps/spark/security/spark3.jceks
Права 755. Это означает, что любой пользователь HDFS может прочитать файл. С точки зрения безопасности – это дыра. Но с точки зрения работоспособности – это, скорее всего, вынужденная мера.
Apache в документации CredentialProviderAPI не даёт рекомендаций по правам на JCEKS. Они описывают, как создать хранилище и как его использовать, но не указывают, какие права выставлять. Это означает, что права полностью на усмотрение администратора.
Я проверил, что говорят вендоры, и картина оказалась не такой однозначной:
IBM в своей документации прямо рекомендует выставлять права 755 (
-rwxr--r--) для JCEKS в HDFS, чтобы все пользователи могли читать файл.Huawei Cloud описывает реальную проблему: spark-beeline падает при создании таблицы, потому что на
ssl.jceksв HDFS стоят права 600. Решение – изменить права доступа к файлу на 644.
Моя позиция
Права 600 – это идеал с точки зрения безопасности, но на практике он ломает сценарии, где JCEKS нужен нескольким сервисам или клиентам. И администраторы вынужденно ставят 640 или 755, чтобы всё просто работало.
Главное – это шифрование и секретность мастер-пароля. Даже если файл JCEKS доступен для чтения, без мастер-пароля злоумышленник получит только зашифрованную «белиберду».
Как выбирать права:
Один сервис – 600. Только владелец читает файл.
Несколько сервисов в одной группе – 640. Доступ для группы.
Клиенты от разных пользователей – 755.
3.Список alias в JCEKS:
hadoop credential list -provider jceks://hdfs/apps/hive/security/hive.jceks
Вывод:
Listing aliases for CredentialProvider: jceks://hdfs/apps/hive/security/hive.jceksjavax.jdo.option.ConnectionPassword
Alias совпадает с именем свойства в XML – это обязательное условие. Hadoop credential list не расшифровывает значение, только показывает имена.
4.Какой процесс забирает credential
Hive Metastore использует класс ObjectStore. В методе getDataSourceProps() вызывается conf.getPassword("javax.jdo.option.ConnectionPassword"). Именно этот метод опрашивает Credential Provider, а не обычный conf.get().
В логах HiveServer2 при старте я нашёл строку:
INFO [main] Configuration: found resource resource-types.xml at jar:file:/usr/lib/hive/lib/hive-common.jar!/resource-types.xml
Упоминания CredentialProvider в логах не было – Hive не логирует использование провайдера явно. Но поведение подтвердилось: после включения шифрования пароль исчез из XML, а подключение к метасторе продолжило работать.
5.Что произошло после смены пароля
Протестируем ротацию паролей ADCM и посмотрим, что произойдёт.
ADCM пересоздал JCEKS в HDFS. HiveServer2 подхватил новый пароль без перезапуска – при следующем подключении к базе использовался уже обновлённый секрет.
Но не все сервисы работают так же. HBase, например, требовал перезапуска. Это связано с тем, что не все компоненты вызывают getPassword() при каждом подключении – некоторые кешируют значение.
Рассмотрим ключевые настройки Manage credential encryption ADCM:

Credstore password (мастер-пароль)
Это мастер-пароль, который используется для шифрования записей внутри самого файла .jceks.
Если оставить это поле пустым (<not set>), ADCM при инициализации сгенерирует криптографически стойкий случайный мастер-ключ. Чтобы иметь возможность управлять этим хранилищем (например, обновлять его при ротации паролей сервисов), ADCM сохраняет сгенерированный ключ в своей внутренней базе данных (PostgreSQL).
При этом ключ хранится в зашифрованном виде с использованием механизмов внутренней криптографии ADCM. Согласно открытой документации, ADCM использует Ansible Vault для шифрования данных в своей базе данных, а сам Ansible Vault применяет AES-256 – единственный поддерживаемый им алгоритм шифрования. Кроме того, ADCM поддерживает интеграцию с HashiCorp Vault и совместимыми решениями (например, OpenBao) для централизованного хранения секретов во внешнем хранилище. Это гарантирует, что даже в гипотетическом случае компрометации дампа базы данных ADCM, злоумышленник не получит мастер-пароли в открытом виде.
Если политики безопасности вашей компании или требования регуляторов (например, ФСТЭК) предписывают, чтобы мастер-ключи генерировались и хранились исключительно во внешнем HSM или корпоративном Vault, вы можете задать этот пароль вручную. ADCM будет использовать именно ваш ключ, а не генерировать свой.
Credstore options (способ передачи мастер-пароля процессу)
Определяет, как Java-процесс получит мастер-пароль для расшифровки JCEKS.
Вариант |
Суть |
Риски |
Когда использовать |
|---|---|---|---|
password in the environment |
Пароль в переменной окружения (HADOOP_CREDSTORE_PASSWORD) |
Виден через /proc/<PID>/environ |
Тестовые среды, нет строгих требований ИБ |
password-file |
Пароль в файле |
Нужно синхронизировать при локальном хранении |
Production (рекомендуется) |
no password |
Пароль не нужен (JCEKS в TDE-зоне HDFS) |
Требует настройки HDFS TDE |
Если уже есть KMS/TDE |
Update jceks for service
Разрешает ADCM автоматически обновлять JCEKS при ротации паролей.
ADCM берёт на себя создание, обновление и ротацию JCEKS-файлов. Вам же остаётся выбор способа передачи мастер-пароля и контроль прав доступа.
Что мы выяснили в итоге
Наше путешествие в мир безопасности Hadoop началось с того, что я нашёл пароль от метасторы в открытом виде в hive-site.xml. Потом мы разобрались, как работает Hadoop Credential Provider API и JCEKS. Затем включили шифрование в ADCM и убедились, что механизм работает корректно.
Главные выводы:
JCEKS решает конкретную задачу – убирает секреты из текстовых конфигурационных файлов. Это закрывает самый массовый и легко используемый вектор утечки.
JCEKS – не панацея. Безопасность держится на трёх китах: алгоритм шифрования, сохранность мастер-пароля и правильные права доступа к файлу.
ADCM автоматизирует рутину, но не освобождает от понимания механизма. Инструмент берёт на себя создание, обновление и ротацию JCEKS.
За пределами автоматизации остаются: выбор способа передачи мастер-пароля, контроль прав доступа к файлу, аудит того, что все компоненты действительно используют getPassword(), и мониторинг логов после ротации. Инструмент снимает рутину, но не отменяет понимания механизма.
Мои рекомендации:
Включите шифрование учётных данных для всех сервисов.
Переключите Credstore options на password-file.
Периодически проверяйте логи сервисов на ошибки аутентификации.
«Хватит хранить пароли в открытую» – с этого я начал. Теперь пароль в XML – пустой тег, а JCEKS зашифрован. Основная задача решена: секретов в открытом виде больше нет. А значит, цель, с которой я затевал разбор, достигнута.
stackshortlist
Хороший разбор, особенно вопрос о том, куда на самом деле делся пароль после переноса в JCEKS. Ведь если пароль от самого хранилища остаётся дефолтным или лежит в переменной окружения рядом, то выигрыш скорее в том, что секрет не светится в выводе конфигов и в git. Интересно, как вы решаете ротацию: пересоздаёте JCEKS и раскатываете на все узлы, или сервисы умеют подхватывать новый файл без рестарта?