Однажды в 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 зашифрован. Основная задача решена: секретов в открытом виде больше нет. А значит, цель, с которой я затевал разбор, достигнута.


