Антон Рябов bio photo

Антон Рябов

Не люблю бриться и у меня умный взгляд.

Email Twitter Telegram Github PGP RSS

Статья будет понятна и полезна только тем, кто уже знаком с процессом выписывания сертификатов через Let’s Encrypt, если это не про вас сначала ознакомьтесь с подробной статьей HTTPS в Nginx с сертификатом от Let’s Encrypt

Обзор

Предисловие

Самый распространенный способ подтверждения владения доменом при выписывании сертификата в Let’s Encrypt является http01, при котором резолвится ваше доменное имя и на полученный адрес отправляется http запрос файла с хешем и ваш веб-сервер должен ответить правильным файлом, иначе валидация не пройдет. Но что делать если у вас несколько веб-серверов и ответить может любой из них, при этом только на одном будет нужный файл с хешем? За ответами предлагаю нырнуть в основной текст статьи.

Почему dns01 метод?

Чтобы ответить на этот вопрос, в начале, нужно понять какие вообще варианты существуют. Официальные ответы на форуме Let’s Encrypt предлагают следующие:

  • Проксировать запросы в папку /.well-known всегда на один и тот же сервер и на нем же выполнять обновление сертификатов.
  • Сделать папку /.well-known общей для всех веб-серверов и тогда неважно на каком сервере запускать обновление сертификатов, все веб-серверы смогут ответить правильным файлом.

Мы в OneTwoTrip столкнулись с описанной выше проблемой и почитав предложенные варианты, решили что нам они не подходят и пошли искать дальше. Нашли малопопулярный вариант с подтверждением через DNS. Предположительно, люди им не пользуются потому что операции с DNS записями очень медленные, некоторые регистраторы и сервисы обновляют записи сутки (как они вообще существуют?). Однако в наше время есть достаточно прогрессивные, которые даже предоставляют API для внесения изменений. Мы пользуемся одним из таких это AWS Route53, он позволяет создавать, обновлять, редактировать и удалять любые DNS записи через aws-sdk и времени на это уходит не больше пары минут.

Amazon Route53 я также использую на данном сайте, о чем писал в статье Под капотом у блога

Свой велосипед

“Я буду долго гнать велосипед…”

Помимо основного инструмента для выписывания сертификатов от Let’s Encrypt certbot существуют уже много реализаций, на разных языках программирования. Но автоматических аналогов работающих с подтверждением через DNS мы не нашли (если плохо искал, жду ваших комментариев). Поэтому было принято решение запилить свой велосипед, удовлетворяющий нашим потребностям и вписывающийся в текущую инфраструктуру. Так как разработка велась быстро и код там пах не очень приятно, приводить его здесь нет смысла, скажу лишь, что первая реализация была самописным демоном, который засыпал на 12 часов с помощью простого sleep, в дальнейшем демон был переписан на rufus-scheduler.

Процесс выписывания точно такой же как и при методе http01, отличается только валидацией, в случае c dns01, вам возвращается хеш, который нужно записать в TXT запись _acme-challenge.exmaple.com., где exmaple.com доменное имя для которого запрошена валидация, сервера Let’s Encrypt проверяют наличие данной записи и если она присутствует и содержит правильный хеш, можно продолжить процесс получения сертификата.

В рамках рефакторинга было решено переписать приложение и заодно добавить возможность использовать любой DNS сервис, который предоставляет программное API.

Присвоил блокам разные цвета, чтобы было легче понимать что есть что на диаграммах.

Всего в процессе выписывания участвуют следующие компоненты:

  • Cron - внутренняя разработка OneTwoTrip, которой скармливается урл и расписание по которому его нужно дергать
  • Legdns App - приложение на Sinatra, в котором, на данный момент, всего один endpoint
  • Sidekiq - фоновый обработчик задач, использующий redis как очередь
  • File System - Файловая система, на которую записываются выписанные сертификаты

Логику сервиса можно описать приведенной выше диаграммой. В Sinatra приложение прилетает POST запрос у которого в теле JSON с массивом доменов, для которых нужно выписать сертификаты:

{
"domains":[
    [
        "example.com",
        "a.example.com",
        "b.example.com"
    ],
    [
        "example.net"
    ]
]
}

Этот json прокидывается в Sidekiq Worker, который проводит проверку, есть ли необходимость обновления сертификата. Делает он это по двум параметрам:

  • Время до истечения сертификата (меньше 30 дней)
  • Совпадают ли запрошенные домены и поддомены с теми что в выписанном сертификате

Далее воркер, используя предоставленный провайдер добавляет необходимую DNS запись, после чего получает сертификат и записывает его в файлы. В конце запускается очистка DNS записей. Провайдер должен соответствовать некоторым требованиям - имя класса должно быть LegdnsProvider и он должен предоставлять два метода upsert и cleanup.

Чтобы сервис заработал нужно установить все зависимости через

bundle install

а также запустить Redis сервер. После чего запустить Sidekiq, с помощью команды:

sidekiq -r app.rb

и собственно Sinatra приложение:

bundle exec rackup -p 3000

Запустить выписывание можно например так:

curl -X POST -is 'http://127.0.0.1:3000/cert' -d '{"domains":[["example.com", "a.example.com", "b.example.com"], ["example.net"]]}'

Если запустить приложение с установленной переменной окружения RACK_ENV=production все не https запросы будут отброшены. Приложение не предусматривает никакой авторизации, самым простым способом ограничить доступ будет установка впереди Nginx, также через него можно будет отдавать файлы с сертификатами.

А теперь рисуем сову

Наша доблестная команда DevOps любит все автоматизировать, а для управления конфигурацией использует Chef, поэтому мало просто сохранить сертификат в файл. Нужно еще загрузить его на Сhef сервер, откуда в свою очередь сертифкаты разливаются во все места где необходимы, а также нужно отправлять уведомления в слак, чтобы знать если выписывание сертификатов сломается.

Включая загрузку в Chef, диаграмма приложения будет выглядеть следующим образом:

Таким образом, если полностью заменить чтение и запись сертификатов на работу с Сhef сервером, приложение можно легко запустить в Docker контейнере, но это уже тема другой статьи.

Open Source версия приложения доступна на GitHub

Мы хантим! Список открытых вакансий и информацию о компании можно посмотреть на нашем специальном сайте

#Letsencrypt