Управление сертификатами с помощью протокола ACME
Возникла передо мной такая задача: автоматический выпуск сертификатов для Web приложения. И требования:
- CA должны доверять все браузеры т.е. самоподписанные сертификаты не подходят;
- желательно бесплатно;
- Выпуск надо делать программно с помощью Java Embedded compact1 profile. Это всё по следам Java и без 16Gb памяти?.
Наверное многие уже слышали про бесплатные сертификаты от LetsEncrypt и certbot. А можно ли certbot заменить Java?
ACME
Многие, конечно, любят LetsEncrypt за бесплатные сертификаты, которые, фактически, позволят перевести весь вэб на https. Но не многие знают, что для этого они придумали специальный протокол - ACME. И для меня он по значимости чуть ли не выше самих бесплатных сертификатов.
Основные особенности протокола:
- Описывает взаимодействие клиента и REST сервера;
- Есть поддержка как платных сертификатов, так и бесплатных;
- Несколько способов авторизации владения доменом;
- Внесен на принятие в IETF. Сейчас находится в состоянии draft;
- Все сообщения передаются в формате JSON Web Token.
Для тех, кому интересны детали и все возможные варианты взаимодействия, можно почитать официальную документацию. Она действительно простая и легко читается в отличие от наших гостов. Здесь же я приведу диаграмму последовательности при выдаче сертификата, которую нужно представлять, если Вы решили интегрироваться.
Использование
На официальном сайте LetsEncrypt есть множество клиентов работающих по протоколу ACME. Я взял acme4j. Эта библиотека достаточно компактная и работает в compact1 profile!
У неё есть вполне рабочий пример использования, с помощью которого я смог выпустить себе сертификат. Для того чтобы интегрировать библиотеку, достаточно с минимальными изменениями скопировать этот код.
Для продления сертификата необходимо авторизоваться, взять уже готовый CSR и отправить его в CA. После чего скачать новый сертификат.
Единственная проблема, которая у меня возникла - это подкладывание сертификата в nginx. Поясню на примере:
- приложение стартует в первый раз;
- nginx стартует. Поскольку приложение стартует в первый раз, то сертификата ещё нет, и nginx слушает на 80 порту;
- пользователь заходит в приложение, соглашается с правилами использования сертификатов LetsEncrypt и нажимает кнопку “выдать сертификат”;
- сертификат скачивается.
И вот тут проблема: для того чтобы включить 443 порт с новым сертификатом, nginx должен перезачитать конфигурацию. Но чтобы это сделать нужен root. Запускать приложение из под root - плохая идея. Запускать nginx из-под пользователя тоже - нельзя будет слушать 80 и 443 порты.
Я добавил правило для пользователя в sudoers, чтобы можно было делать sudo nginx -s reload
. Но это выглядит как костыль. Может кто-нибудь знает как это сделать красивее?
Итого
Получение сертификата в автоматическом режиме оказалось достаточно простой процедурой. А если использовать ACME сервер boulder, то можно даже развернуть такую схему у себя в большой организации! Если у вас есть собственный CA для внутренних сервисов, то ACME должен сильно упростить работу с сертификатами.