Сертификаты Let’s Encrypt и ACME вообще во внутренней сети

Обычно внутри корпоративной сети нынче полно всяких приложений, и хотелось бы чтобы они работали по 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/

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *