Допустим, наше 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— в нашем случаеpkcs12trustStoreType—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.