Обычно внутри корпоративной сети нынче полно всяких приложений, и хотелось бы чтобы они работали по SSL. Можно, конечно, поднять свой УЦ, раздать сертификаты, прописать пользователям свой корневой сертификат — и это будет работать. А можно просто воспользоваться сервисом Let’s Encrypt, раздав его сертификаты своим внутренним серверам, которые не видны из Интернета, причем сделать это просто и с минимумом трудозатрат на поддержку.
Необходимые условия
-
Наличие зарегистрированного домена, допустим, mycompany.ru и адресация внутренних серверов в нем.
-
Использование split-DNS, т.е. разрешение доменных имен mycompany.ru в разные IP адреса для внутренней сети и для всего Интернета. Делается либо через механизм View сервера BIND либо просто путем использования разных DNS серверов.
-
Внутренние сервера имеют доступ в Интернет по HTTPS — через маскирующий/NAT шлюз, файрвол, прокси, как угодно. Впрочем, это можно преодолеть, просто эту статью придется дочитать до конца, а поработать чуть больше.
Как это работает
Логика работы сервиса Let’s Encrypt:
-
Сервер-претендент обращается к API Let’s Encrypt по HTTPS, сообщает ему домены, для которых нужен сертификат. Например, server.mycompany.ru.
-
В ответ сервис формирует некий код, который должен быть размещен по запрошенному доменному адресу чтобы подтвердить принадлежность домена. Есть варианты: размещение файла с кодом на веб-сервере или добавление в DNS домена определенной записи. Мы будем использовать файл на веб-сервере.
-
«Проверялка» Let’s Encrypt лезет простым GET запросом на адрес http://server.mycompany.ru/.well-known/acme-challenge/{а здесь сам код}, и если получает ожидаемый ответ — то считает, что домен проверен.
-
Let’s Encrypt выпускает сертификат и предоставляет его серверу.
На самом деле все немного сложнее, но нам детали и не потребуются.
Что мы делаем и зачем
Нам понадобится один прокси-сервер (nginx) и правильная настройка DNS. Больше, удивительным образом, ничего. Прокси-сервер должен быть доступен из Интернет и иметь доступ во внутреннюю сеть (dual-homed).
Полагаем, что все наши сервера во внутреннем DNS прописаны по именам. Во внешнем DNS, в зоне mycompany.ru задаем имя для внешнего адреса прокси-сервера, пусть будет proxy. И там же для каждого сервера из внутренней сети делаем запись CNAME, указывающую на proxy.
Конфигурируем nginx, общая конфигурация самая обычная, нужные нам блоки server в контексте http выглядит так:
server { listen 80; server_name .mycompany.ru; # Принимаем любые имена в домене location /.well-known { if ($request_method != GET) { # Разрешаем только метод GET return 444; # иначе - сбрасываем TCP соединение } resolver 10.0.0.2 10.0.1.2; # Обязательно ипользуем внутренние DNS proxy_pass http://$host; # проксируется на сервер внутрь сети с тем же именем } location / { # Все остальные запросы - return 444; # просто сбрасываем TCP соединение log_not_found off; # и ничего не пишем в лог access_log off; } } server { # На все запросы по IP адресу без домена listen 80 default_server; # отвечаем сбросом TCP соединения server_name _; return 444; log_not_found off; access_log off; }
Кстати, если у вас уже есть dual-homed сервер с nginx для каких-то других задач, то эту конфигурацию можно просто добавить к нему, она не будет мешать обслуживанию других серверов даже с именами в том же домене.
Работает это очень просто:
-
Наш внутренний сервер посылает запрос, получает код авторизации и размещает его у себя как положено.
-
«Проверялка» Let’s Encrypt разрешает запрошенное имя в адрес proxy и посылает туда запрос GET, естественно доменное имя указано в запросе.
-
Получив запрос, прокси еще раз разрешает доменное имя, но уже внутренним DNS и выполняет запрос на реальный сервер, получает ответ и отдает его «проверялке».
-
Проверка пройдена, наш внутренний сервер получает сертификат.
Вуаля! Все работает! Ставим на внутреннем сервере Certbot или активируем встроенную поддержку сертификатов Let’s Encrypt у нужного нам софта — и погнали. Не забываем повесить задачу на автообновление сертификатов!
За, против и немного про безопасность
Преимущества:
-
Простота реализации. По плечу очень среднему админу.
-
Совместимость. Работают любые ACME-клиенты с проверкой по HTTP на всех платформах, в том числе родной Certbot, встроенные ACME клиенты (проверено на Proxmox), да и вообще нет ограничений по использованию ACME кроме верификации по HTTP. И да, работать должно не только с Let’s Encrypt.
-
Минимальные усилия на изменения: чтобы Let’s Encrypt стал доступен для любого сервера внутри — достаточно просто добавить CNAME для него во внешний DNS. Ну и сделать соответствующие ACME-настройки на самом сервере, конечно.
Недостатки:
-
Мы вроде как показываем имена внутренних серверов в Интернете. На самом деле — нет, если только внешний DNS настроен правильно и не позволяет кому попало забирать зону целиком, что является хорошей практикой независимо от целей.
UPDATE: Как правильно заметил в комментарии @HxShard , используя публичные сертификаты внутри сети мы неизбежно делаем доступными доменные имена узлов, для которых мы их получили, как минимум таким вот образом https://crt.sh/?q=habr.com. Тут уж придется каждому решить — насколько большой риск тем самым создается. Лично я оцениваю его как весьма небольшую цену за удобство, во всяком случае вряд ли именно это станет самым низким участком моего виртуального забора. -
Теоретически, прокси-сервер, как любой dual-homed, создает угрозу. Но и меры защиты — самые обычные. Если они непонятны и вы не можете правильно настроить защиту для Linux + Nginx — то вам вообще не стоит иметь дела с серверами, подключенными к Интернету. Позовите взрослых пожалуйста!
-
Опять же, теоретически, существует возможность DoS на внутренний сервер при условии что его имя получено — путем заваливания его запросами в каталог /.well-known. Крайне маловероятный сценарий, но его можно легко купировать, ограничив скорость запросов на прокси. Это же nginx!
-
Внутренние серверы должны иметь доступ в Интернет. Но если это для вас проблема — то ниже покажу как ее решить.
К сожалению, Let’s Encrypt не поддерживает и не публикует список собственных IP-адресов, поэтому ограничить обращение внутренних серверов наружу и запросы извне по IP — не получится.
И все же, изолированные сервера, сделаем?
Ценою усложнения схемы — сделаем и это. Дело в том, что ACME-клиент посылает запрос по HTTPs. С одной стороны — его тоже можно проксировать, но при этом «ломается» сертификат, и скорее всего ACME-клиент выдаст ошибку. SSL соединение нормально проксируется только на уровне TCP, но к счастью nginx и это умеет.
Нам понадобится:
-
По два внутренних IP адреса на каждый ACME сервис (для основного и staging).
-
«Перехватить» домен сервиса — либо путем записей в hosts либо на своем внутреннем DNS.
-
Настроить nginx для проксирования на уровне TCP.
Кстати: адреса API разных ACME-сервисов можно взять из скрипта acme.sh
Ок, делаем, на примере Let’s Encrypt. Будем считать, что для внутренних интерфейсов прокси выделены адреса 10.0.1.34 и 10.0.1.35.
Перехват DNS
Перехват через hosts проще, но его надо не забыть сделать на каждом внутреннем сервере, добавив в файл строки:
10.0.1.34 acme-v02.api.letsencrypt.org 10.0.1.35 acme-staging-v02.api.letsencrypt.org
Перехват внутренним DNS сервером сложнее, но зато сделать его надо один раз и работать он будет для всех серверов. Для этого на внутреннем DNS создаем зону api.letsencrypt.org, и заводим в ней нужные хосты, пример файла зоны для BIND:
;Перехват api.letsencrypt.org ; SOA api.letsencrypt.org. 3600 IN SOA localhost. root.localhost. ( 2022122900 28800 7200 604800 3600 ) ;основной сервер acme-v02 3600 IN A 10.0.1.34 ;staging сервер acme-staging-v02 3600 IN A 10.0.1.35
Вне зависимости от способа, проверяем результат пингуя с внутреннего сервера по именам acme-v02.api.letsencrypt.org и acme-staging-v02.api.letsencrypt.org, пинги должны проходить на .34 и .35 адреса соответственно. Значит, перехват DNS удался.
Настройка Nginx
Обратите внимание, что эти директивы должны находиться в контексте main, в то время как все привычные файлы конфигурации виртуальных хостов в каталоге «из коробки» уже находятся в контексте http. Поэтому надо либо добавлять в основной файл конфигурации /etc/nginx/nginx.conf, либо в каталог либо в отдельный файл и в правильном месте ставить include.
stream { resolver 8.8.8.8; # А вот здесь нам нужен DNS, способный разрешать имена в Интернете server { # Проксируем 443 порт на .34 адресе и отправляем на основной сервер listen 10.0.1.34:443; proxy_pass acme-v02.api.letsencrypt.org:443; } server { # Проксируем 443 порт на .35 адресе и отправляем на stageing сервер listen 10.0.1.35:443; proxy_pass acme-staging-v02.api.letsencrypt.org:443; } }
Ну вот и все! Теперь и серверы внутренние доступа наружу не имеют и Let’s Encrypt на них работает.
P.S. Дед мой, добрая ему память, частенько говорил: «Кабы не клин да мох, да и плотник бы сдох!». Так и хочется перелицевать на «Кабы не nginx, да ???, так и сисадмин бы ???». Но вот что подставить? Предлагайте! )))
ссылка на оригинал статьи https://habr.com/ru/post/708510/
Добавить комментарий