Перевод Spring Boot приложения с HTTP на HTTPS без ругани браузера
Всем привет. Данная статья расчитана на тех, кто хочет сделать так, чтобы их Spring Boot приложение могло работать с HTTPS без предупреждений со стороны браузера о небезопасном подключении. В этой статье работаем именно со Spring Boot и вшитым в него Apache Tomcat.
Внимание! Статья игнорирует всевозможные правила безопасноти Linux и призвана показать как достичь элементарной работы с HTTPS в Spring Boot, не сильно углубляясь в смежные темы.
Рисунок 1 — ругань браузера при переходе на HTTPS с помощью самописных сертификатов
От вас требуется:
Spring приложение на Linux сервере (например Ubuntu 20+), на котором у вас есть доступ к root правам
Действующие ендпоинты, например,
@GetMapping
Базовое представление о принципах работы HTTPS
Знакомство с системой сборки Maven
Вообще, можно использовать HTTPS в Spring приложении и с помощью self-signed сертефиката, который генерируется с помощью утилиты keytool, вшитой прямо в JDK, но вызовы ваших ендпоинтов с таким сертификатом будут вызывать предупреждение в браузере, как на рисунке 1.
Чтобы наше приложение могло работать с HTTPS без ругани со стороны браузера нам нужен «сертификат», подписанный CA (Certification Authority).
Ни вы, дорогой читатель, ни я не являемся Certification Authority. Ваш браузер или браузер любого другого пользователя знать не знает, что за кустарные (палёные) сертефикаты мы тут генерируем с помощью keytool
! Отсюда и предупреждение о небезопасном соединении.
Чтобы сгенерировать сертификат, который будет валидым и безопасным в глазах всех браузеров нам не обойтись без помощи вышеупомянутого CA. Кто-то может купить сертификат у провайдеров облачных серверов, но у меня нет лишних денег поэтому в этой статье мы воспользуемся сертефикатом от Let’s Encrypt, который будет нам выдан через Certbot’a.
Важно понимать, что Let’s Encrypt — это Certification Authority (nonprofit организация), сертефикаты который являются валидными в глазах браузеров, а выдавать их сертефикат нам будет утилита под названием Certbot.
Взаимосвязанны ли они? Да. Являются ли они одним и тем же? Нет.
Для примера можно привести HTTPS сертефикат Stack Overflow, они тоже используют Let’s Encrypt.
Рисунок 2 — услугами Let’s Encrypt пользуются Stack Overflow
Собственно сам туториал
Еще раз напомню, что у вас уже должен быть живой Spring Boot проект, который работает через HTTP и имеет какое-то количество ендпоинтов. Чтобы начать работать с HTTPS нам надо выполнить 3 задачи:
Настроить firewall
Правильно сгенерировать сертификат
Настроить Spring Boot
1. Настройка firewall
Firewall мы настроим через ufw
, но это не панацея и вы можете использовать свои инструменты. Установим ufw
sudo apt install ufw
Откроем интересующие нас порты:
sudo ufw allow 80;
sudo ufw allow 443;
sudo ufw allow 8080;
sudo ufw allow ssh;
Внимание! следующая команда заблокирует любой входящий трафик. Это значит, что все порты будут закрыты, пока вы их не откроете, например, через
sudo ufw allow
. Если вы на удаленном сервере обязательно выполнитеsudo ufw allow ssh
, иначе рискуете заблокировать себе доступ к серверу.
sudo ufw enable
Чтобы проверить способность вашего сервера принимать запросы после ufw
конфигурации, запустите приложение и попробуйте отправить любой запрос на: http://<ваш_ipv4_адрес>:8080
Наглядный пример URL запроса с портом: http://90.156.210.6:8080
Имея @RestController
класс с таким методом:
@RestController
public class DuelController {
@GetMapping
public String test(){
return "Transition to https";
}
}
Мы можем достучаться до нашего сервера по порту 8080, как на рисунке 3.
Рисунок 3 — проверяем доступность портов
Если ваш ендпоинт не отзывается, проверьте открыт ли порт, по которому вы обращаетесь к серверу с помощью sudo ufw status
Мы хотим увидеть следующий вывод:
habr@personal_computer:~/demo$ sudo ufw status
Status: active
To Action From
22/tcp ALLOW Anywhere
443 ALLOW Anywhere
80 ALLOW Anywhere
8080 ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
8080 (v6) ALLOW Anywhere (v6)
2. Правильная генерация сертификата
Подключитесь к своему Linux серверу и в терминале начните с проверки наличия на сервере snapd
:
sudo systemctl status snapd
Если вы видите вывод как на рисунке 4 (active (running)...
), то все в порядке и можно двигаться дальше. Если нет, то вам надо установить snap или перезапустить его daemon.
Рисунок 4 — вывод, который говорит, что daemon snapd в данный момент работает
Установим саму утилиту Certbot:
sudo snap install --classic certbot
Теперь создадим symbolic link, так нас просит сделать инструкция Certbot’a:
sudo ln -s /snap/bin/certbot /usr/bin/certbot
Теперь убедитесь, что ваше Spring приложение остановлено. Следующая команда интерактивная и требует заранее подготовленной информации, а конкретно:
Email. Какой хотите, но в инструкции сказано, что использоваться он будет для оповещений по вопросам безопастности и срочного продлевания сертификата
Enter email address (used for urgent renewal and security notices)
Валидное доменное имя, привязанное к вашему IPv4 адрессу. Вам надо купить (или каким-нибудь способом достать) доменное имя, например,
vashdomen.ru
и привязать его к вашему серверу.
Без привязанного к вашему серверу домена вы не сгенерируете сертификат. Certbot выдаст вам ошибку.Вы можете купить самый простецкий домен за «сотку» у рег.ру (не рефералка) и по их инструкции привязать IP адресс вашего сервера к их DNS серверам. Если вы укажите другие DNS сервера, то и настройку надо будет делать в другом месте. Возможно на сайте владельцев этих DNS серверов.
Процесс привязки домена может занять некоторое время.
Чтобы протестировать привязался ли домен к адресу, попробуйте выполнить следующую команду:ping vashdomen.ru
Возможноping
отзовется не так быстро, как обычно, стоит чутка подождать.
С Email и рабочим доменным именем начинаем генерацию сертификата. Вас попросят согласится с парой пунктов, указать почту и доменное имя, которое вы привязали к серверу.
sudo certbot certonly --standalone
Если все хорошо, то вас встретит сообщение об успешной генерации файлов:
...
Successfully received certificate.
Certificate is saved at: /etc/letsencrypt/live/<доменное_имя>/fullchain.pem
Key is saved at: /etc/letsencrypt/live/<доменное_имя>/privkey.pem
This certificate expires on 2024-07-30.
These files will be updated when the certificate renews.
Certbot has set up a scheduled task to automatically renew this certificate in the background.
...
Самая важное тут — это пути к вашим новым файлам:
/etc/letsencrypt/live/<доменное_имя>/
Прошу обратить внимание, что хоть нам и выводится путь лишь к двум файлам по факту их сгенерировалось пять (5). Чтобы посмотреть содержимое этой директории нам нужны root права:
sudo su
Заглянем в директорию:
ls /etc/letsencrypt/live/<доменное_имя>
Вам должен выпасть вот такой список:
cert.pem chain.pem fullchain.pem privkey.pem README
Вы сгенерировали сертификат, подписанный CA Let’s Encrypt. Этот сертификат принимается браузерами за валидный и на него не падают предупреждения! Вы можете применить его и за пределами Spring приложения.
3. Настройка Spring Boot
Теперь надо познакомить ваше приложение с сертификатом. Для этого нам нужна команда openssl. Если ее нет, то установите:
sudo apt install openssl
Перед тем как двигаться дальше вам надо перейти в директорию вашего Spring приложения. Так чтобы при выводе ls
вы видели ваши src, target, pom.xml...
habr@personal_computer:~/demo$ ls
HELP.md mvnw mvnw.cmd pom.xml src target
Теперь не торопитесь и внимательно прочитайте следующую команду. Укажите валидные пути к сгенерированным файлам. Эта команда тоже интерактивная и вы будете создавать пароль к хранилищу. Запомните его! В рамках этого туториала можете использовать пароль «test_tomcat».
sudo openssl pkcs12 -export -chain -inkey /etc/letsencrypt/live/<доменное_имя>/privkey.pem -CAfile /etc/letsencrypt/live/<доменное_имя>/fullchain.pem -in /etc/letsencrypt/live/<доменное_имя>/cert.pem -out keystore.p12 -name tomcat
Теперь, когда у вас на руках есть пароль и хранилище приватных ключей, которое лежит в корневой директории, рядом с pom.xml, в самом Spring надо отредактировать application.properties
Вставьте туда следующие строчки:
server.port = 443
server.ssl.key-store = keystore.p12
server.ssl.key-store-password = test_tomcat
server.ssl.keyStoreType = PKCS12
server.ssl.keyAlias = tomcat
Объясняю:
server.port
— порт, на котором будет работать ваше приложение. HTTPS работает через порт 443server.ssl.key-store
— название хранилища, которое мы сгенерировали (keystore.p12
)server.ssl.key-store-password
— пароль, который вы вводили при генерации хранилища и который надо было запонмитьserver.ssl.keyAlias
— это флаг (-name tomcat
), который мы указали в команде при генерации хранилища
Финальное тестирование
Проверим нужные порты:
sudo ufw status
Видим вывод:
habr@personal_computer:~/demo$ sudo ufw status
Status: active
To Action From
22/tcp ALLOW Anywhere
443 ALLOW Anywhere
80 ALLOW Anywhere
8080 ALLOW Anywhere
22/tcp (v6) ALLOW Anywhere (v6)
443 (v6) ALLOW Anywhere (v6)
80 (v6) ALLOW Anywhere (v6)
8080 (v6) ALLOW Anywhere (v6)
Порты подготовлены.
Порт 22 — это ssh соединение, а (v6)
является обозначением для IPv6.
Итак, мы:
Порты настроили
Сертификат сгенерировали
Spring Boot приложение с сертификатом познакомили
Пора запускать.
Перед тем как запускать приложение надо понимать, что первые 1024 порта зарезервированы и для запуска приложений, которые общаются с этими портами нам нужны root права. Наше приложение будет работать на порте 443, а значит запуск Spring Boot требует повышенных прав.
Укажите полный путь до бинарного файла Maven, чтобы терминал распознал команду через sudo
:
Или можете отредактировать PATH для root пользователя.
sudo /home/habr/apache-maven-3.9.6/bin/mvn spring-boot:run
В информационных логах Spring нас интересует 3 лога.
Первый лог говорит нам о том, что Tomcat запустился на порте 443
INFO 19474 --- [demo] [main] o.s.b.w.embedded.tomcat.TomcatWebServer:
Tomcat initialized with port 443 (https)
Второй лог выводит нам информацию о хранилище приватных ключей
INFO 19474 --- [demo] [main] o.a.t.util.net.NioEndpoint.certificate:
Connector [https-jsse-nio-443], TLS virtual host [default], certificate type [UNDEFINED] configured from keystore [/root/.keystore] using alias [tomcat] with trust store [null]
Третий лог дополняет первый и показывает нам базовый context path. По дефолту »/»
INFO 19474 --- [demo] [main] o.s.b.w.embedded.tomcat.TomcatWebServer:
Tomcat started on port 443 (https) with context path ''
Теперь можем смело обращаться к нашему ендпоинту. Прошу заметить, что при вводе доменного имени в адресной строке не нужно указывать порт, как мы это делали при проверке работы с firewall’ом.
Рисунок 5 — наш сертефикат verified by: Let’s Encrypt
Если все прошло успешно, вы можете обращаться к ендпоинтам вашего Spring Boot приложения через протокол HTTPS без ругани со стороны бразуера.