Допустим, наше Java
приложение размещено на платформе "Heroku", и ему требуется подключиться к HTTP серверу, требующему предоставление Клиентского Сертификата.
В этой статье мы рассмотрим вопросы безопасного размещения клиентских хранилищ ключей в облаке
, используя переменные окружения
.
- Хранение паролей и других секретных данных в
переменных окружения
является распространённой практикой для приложений, размещённых воблаке
.
Обычно приватные ключи
содержались в защищённых паролем файлах — контейнерах ключей, таких как .p12
или jks
на файловой системе.
Но проблема становится очевидна, как только становится необходимо разместить подобное приложение в облаке
:
Даже защищённые паролем
приватные ключи
не допустимо размещать в репозитории вместе с исходным кодом
Тоже самое касается образов Docker и любых других подобных артефактов, доступных нескольким лицам
К счастью, это легко исправить в приложениях на
Java
!
Рассмотрим предполагаемый процесс с точки зрения теории безопасности:
- Офицер безопасности (о.б.) экспортирует
приватный ключ
как строку в кодировкеBase64
- О.б. заходит в
консоль управления облака
(в нашем случае в Heroku Dashboard) - О.б. импортирует строку в кодировке
Base64
впеременную окружения
- Как часть автоматического процесса размещения приложения, эта же
переменная окружения
конвертируется в двоичные данные и записывается в физический файл - Путь к этому файлу передаётся приложению на этапе запуска
- Теперь, только круг лиц, имеющих доступ к
консоли управления облака
, получают доступ кприватному ключу
.
Применим этот подход на практике:
- Язык:
Java
- Платформа:
Heroku
- Система сборки:
Gradle
- Формат хранилища ключей:
PKCS12
Экспорт файла .p12
Воспользуйтесь инструкцией, использующей средства, встроенные в Вашу операционную систему:
Экспорт файла в Base64
используя коммандную строку (Windows, Linux)
Никогда не загружайте ваши
приватные ключи
на сторонние онлайн ресурсы
Импорт в Heroku
- Зайдите в
Heroku
- Перейдите в секцию
Settings
Вашего приложения - Нажмите "Reveal the config vars"
- Добавьте следующие переменные окружения:
keyStoreFileName
— любое незанятое имя файла, например "private_key.p12"keyStoreBase64
— вставьте сюда значение строки закодированной в форматеBase64
, экспортированной в предыдущем шаге "Экспорт файла .p12"keyStorePassword
— пароль, ранее использовавшийся с файлом .p12keyStoreType
— в нашем случаеpkcs12
trustStoreType
—jks
Запись файла .p12 используя Gradle
Добавьте нижеприведённое задание в Ваш файл guild.gradle
и назначьте его исполнение перед задачей stage
:
task initKeyStore() {
doLast {
println("Creating keystore file from environment variables.")
String keyStoreFileName = System.getenv("keyStoreFileName")
if (keyStoreFileName != null) {
String keyStoreBase64 = System.getenv("keyStoreBase64")
new File(keyStoreFileName).withOutputStream {
it.write(Base64.decoder.decode(keyStoreBase64))
}
}
}
}
stage.dependsOn(initKeyStore)
Передача реквизитов через "procfile" Heroku
В "procfile" в Heroku
не поддерживаются многострочные команды.
Поэтому, мы рекомендуем создать отдельный shell скрипт
— runApp.sh
.
runApp.sh:
java -Dserver.port=$PORT ...
-Djavax.net.ssl.keyStoreType=$keyStoreType -Djavax.net.ssl.trustStoreType=$trustStoreType -Djavax.net.ssl.keyStore=$keyStoreFileName -Djavax.net.ssl.keyStorePassword=$keyStorePassword $JAVA_OPTS ...
Не забудьте дать права на выполнение
используя git
и протолкнуть (push) подтверждение (commit):
git update-index --chmod=+x runApp.sh
git commit -m 'Добавление прав на выполнение runApp.sh'
git push origin master
Ограничения
В общей сложности Heroku
позволяет хранить максимально 32 килобайта информации в переменных окружения
.
Это накладывает некоторые ограничения, и вопрос их преодоления в настоящий момент прорабатывается в нашей организации.
Заключение
- Теперь каждый раз, когда вы размещаете своё приложение в
Heroku
,Gradle
автоматически создаёт хранилище ключей в виде файлаP12
на основепеременной окружения
. - Затем, используя
procfile
, платформаHeroku
передаёт детали этого файла Вашему приложению. Приватный ключ
становится доступным приложению, и может быть использован дляКлиентской аутентификации
как обычный файл.p12
вJava
- Никаких изменений исторической кодовой базы приложения не требуется для поддержки этого подхода.
Спасибо за внимание!
ladutsko
А что с Trust Store? Или что-то лишнее или чего-то не хватает…
Проясните, пожалуйста.
antonpryamostanov Автор
Концептуально конечно лишнее. На практике надо протестировать, будет ли работать как описано, если убрать javax.net.ssl.trustStoreType.
Чуть позже протестирую и если будет ок, уберу упоминания Trust Store.