Всем привет. Данная статья расчитана на тех, кто хочет сделать так, чтобы их Spring Boot приложение могло работать с HTTPS без предупреждений со стороны браузера о небезопасном подключении. В этой статье работаем именно со Spring Boot и вшитым в него Apache Tomcat.
Внимание! Статья игнорирует всевозможные правила безопасноти Linux и призвана показать как достичь элементарной работы с HTTPS в Spring Boot, не сильно углубляясь в смежные темы.

От вас требуется:
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.

Собственно сам туториал
Еще раз напомню, что у вас уже должен быть живой 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.

Если ваш ендпоинт не отзывается, проверьте открыт ли порт, по которому вы обращаетесь к серверу с помощью 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.

Установим саму утилиту 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'ом.

Если все прошло успешно, вы можете обращаться к ендпоинтам вашего Spring Boot приложения через протокол HTTPS без ругани со стороны бразуера.