Поддержка https совсем без настроек

    Написал программу для автоматического выписывания ssl-сертификатов налету (при первом запросе к домену). Для начала работы программы её достаточно просто запустить, настраивать не надо совсем (даже домены для сертификатов указывать не надо).

    Подробности внутри.

    Предыстория
    Я работаю с let's encrypt примерно с апреля этого года, выписывая сертификаты в больших количествах (много тысяч, постоянно добавляются новые).
    Изначально это выглядело примерно так: одна программа составляет список доменов для которых нужен сертификат, вторая программа идёт и эти сертификаты выписывает, третья проверяет выписанные сертификаты и составляет их список для haproxy/nginx (потому что с неправильными сертификатами они не перезапустятся при обновлении списка сертификатов и все https-сайты лягут). Работает это вполне надёжно, но получается много компонентов.

    Кроме того запуск этого чуда на windows + iis оказался нетривиальным и там коллега придумывал дополнительные костыли.

    Требования:

    1. Со стороны клиента поддержка SNI (основными браузерами давно поддерживается). — уже может и без SNI один домен обрабатывать (указывается параметром)
    2. Со стороны сервера: чтобы туда можно было из golang бинарник скомпилировать (windows,linux,freebsd,mac). Проверяется на windows и linux.
    3. Домен соответствует правилам Lets encrypt, на данный момент это: длина домена 64 символа или короче, без punycode (т.е. что-то.рф сертификата не получит) — уже можно punycode.
    4. Сервер с сайтом доступен из публичной сети.

    Принцип работы:

    Программа работает как реверс-прокси, выписывая правильные сертификаты на ходу по мере необходимости.

    При получении запроса через расширение SNI программа узнаёт домен для которого нужен сертификат. Если сертификат уже есть — дальше идёт обработка с уже существующим сертификатом.

    Если сертификата нет — отправляется запрос в lets encrypt на получение сертификата из SNI-заголовка и потом обработка идёт с только что полученным сертификатом (сертификат сохраняется в кеш).

    В lets encrypt домен проверяется методом tls-sni-01 — путём выдачи сертификата на специально сформированный https-запрос.

    Время на все проверки и получение сертификата — около 3 секунд. Это задержка для первого https-запроса к домену.

    Дальше запрос уходит на тот же IP, на котором был принят https-запрос, но уже на порт 80 и без шифрования — т.е. обычный http.

    » github.com/rekby/lets-proxy/releases/latest

    Несмотря на простоту варианта по умолчанию у программы много ключей запуска для определения нюансов работы. Для полного перечня можно воспользоваться ключем --help.
    Share post

    Comments 32

      0
      Я так понимаю, эта штука один сертификат на сразу несколько доменов сделать не позволит?
        0
        Да, в сертификате будет указан только один домен.

        Однако самих сертификатов может быть много, например на сервере где сейчас программа в пробном режиме работает за сутки выписалось около 500 сертификатов.

        т.е. если у вас к одному серверу привязано несколько доменов для каждого из них будет выписан свой сертификат и все они могут работать одновременно.
          0
          добавка — теперь в один сертификат можно записать домен и его поддомены. По умолчанию сертификат выписывается сразу для поддомена www, т.е. для domain.com и www.domain.com в одном сертификате.
          +1
          Интересная реализация. А что на счёт производительности? Интересно было бы увидеть замеры: с этим прокси против стандартной реализации ssl в nginx, на различном количестве соединений (в большом диапазоне), с различным размером тела запроса (от маленького до большого), как при этом ведёт себя сервис, сколько потребляет памяти. Так же было бы здорово, если бы сразу был представлен результат проверки такого сайта популярным ssl-чекером. Поскольку это приложение претендует на точку входа — оно может стать неожиданной причиной падения сервиса, поэтому такие подробности были бы очень кстати.
            0
            отчет чекера можно посмотреть тут https://www.ssllabs.com/ssltest/analyze.html?d=www.f1f2.ru

            тесты производительности занимают много времени, пока провести их не готов. Но могу для теста выделить пару виртуальных машин на несколько дней человеку, который знает как это делается и готов провести и опубликовать результаты сравнений.
            0
            Думаю, надежнее и производительнее было бы сделать так: вначале стоит nginx, у которого сконфигурированы все домены, для которых уже есть сертификаты, а сайт по умолчанию — тот, что с прокси. Прокси, получив запрос, создает сертификат и генерирует конфигурацию для этого домена, добавляет ее nginx'у и делает ему reload. После чего проксирует его на обновленный nginx. При такой конфигурации запросы к уже существующим доменам до прокси не дойдут (точнее их будет проксировать nginx, который протестирован гораздо лучше), а прокся будет заниматься только одной своей задачей.
              0
              Думаю такое возможно, если заставить для сайта по умолчанию nginx проксировать запросы тоже по https.
              Однако тут потребуются дополнительные инструменты:
              1. для формирования/обновления конфига nginx, причем при обновлении надо заново проверять все сертификаты которые туда включаются (вдруг файл повредился и nginx из-за битого сертификата перестанет работать).
              2. Для перезапуска nginx.
              3. Мне кажется что в случаях
              4. Потребуется отдельная простукивался, которая будет бегать по выписанным сертификатам, проверять срок их действия и заранее что-то делать (обращаться к выписывалке, переделывать временно конфиг nginx и т.п.) чтобы своевременно обновлять сертификат.

              В такой схеме думаю проще будет выписывать сертификаты без такой прокси, просто по списку. Хотя мысль интересная, попробую может ли это в принципе работать.
                0
                Посмотрел, nginx вроде нельзя настроить таким образом чтобы при несовпадении заголовков запрос начал бы проксироваться в tcp-режиме на отдельный сервер с сохранением начального запроса, содержащего запрашиваемый домен.

                Т.е. сам домен передать не проблема, заголовок передать тоже относительно не проблема — можно просто сделать новый https-запрос к бэкэнду. (вопрос как быть с фейковыми запросами чтобы как проксёй для внешних сервисов не служить — отдельный). Проблема потом передать обратно ответ сервера в неизменном виде, как при обычном tcp-проксировании.

                Если есть желание проксировать именно через nginx — можно или отдельно сертификаты выписывать, по спискам или воспользоваться https://github.com/GUI/lua-resty-auto-ssl как Erelecano рекомендовал
                0
                А зачем нам велосипед с квадратными колесами, если уже есть готовое решение для OpenResty?
                https://github.com/GUI/lua-resty-auto-ssl
                И даже пакеты для trusty openresty и бэкпорт openssl'а нужного для его работы вон в ppa https://launchpad.net/~ernillew/+archive/ubuntu/operesty-for-le есть(не обновлялся правда, ибо перестало быть нужным, но желающие могут взять все в свои руки и форкнуться)
                  0
                  1. К сожалению я не нашел его, когда искал.
                  2. Система нужна универсальная для windows и linux, lua-resty-auto-ssl на работу в windows явно не рассчитан и собрать его если и можно то думаю что нетривиально.

                  за наводку спасибо, если бы нашел раньше — попробовал бы под винду собрть.

                  Ну и в любом случае для настройки мой вариант явно проще, так что это скорее альтернатива чем велосипед.
                    +2
                    Как-то странно искали, ибо ссылка на него тут https://letsencrypt.org/docs/client-options/
                    Я его оттуда и нашел, в свое время.
                    Посмотрите, может вам чем-то будет полезен, в любом случае.

                    Я его у себя ставил, что бы при запросе на *.domain.tld всегда был ответ с валидным сертификатом, потом нужда отпала(потому OpenResty в ppa и не обновляю)
                      0
                      Да, согласен, странно искал — самый простой вариант (эту страницу) почему-то не посмотрел.
                        0
                        А я вот рассматривал там внимательно что есть, на основных серверах выбрал для себя https://github.com/hlandau/acme, а под кейс описанный выше вот нашел вариант для OpenResty.
                        Там, вообще, много интересного.
                        Раз пишите свой вариант, то вам могут быть интересны ссылки оттуда, что бы посмотреть кто как реализововал это дело.
                          0
                          https://github.com/hlandau/acme — я как раз внутри использую, как api к lets encrypt.

                          Другие варианты тоже еще посмотрю — что у себя мог упустить.
                            0
                            https://github.com/hlandau/acme чертовски удобен и как основной клиент. Настроил его один раз левой пяткой и можно забыть о всех проблемах, он обновляет себе сертификаты и есть не просит.
                  0
                  А как с проверкой/защитой от флуда?
                  Допустим начнут приходить запросы на ssl подключения с рандомными сабдоменами.
                  Вы для всех сертификат будете генерировать?
                    +2
                    нет, перед выпиской сертификата производится проверка через локальный dns, dns yandex, dnsgoogle для A и AAAA записей. Если найдётся хоть одна, которая не соответствует одному из IP-адресов сервера — сертификат выписываться не будет.

                    собственно они и без проверок бы не выписались, но можно было бы упереться в лимиты на висящие авторизации.
                      0

                      тот же вопрос. у меня на каждом шаред хостинг сервере не одна тысяча доменов с поддоменами, и кое где домены протухают, например, но боты ещё месяц и больше могут ломиться...

                        0
                        Собственно ответ чуть выше — перед попыткой выписать сертификат делаются проверки о том что домен действительно работает на этом сервере. Рассчет изнакально как раз для использования на хостинге.
                      0

                      А в качестве модуля к nginx это оформить как-то можно?

                        0
                        Выше есть ссылка на https://github.com/GUI/lua-resty-auto-ssl работающий с openresty/nginx. К сожалению в чистом nginx так и нет ssl_certificate_by_lua, который он использует, так что только в openresty/nginx, а не в чистом, но если вам оно нужно посмотрите в сторону openresty.
                        0
                        Так же вроде бы CloudFlare работает как прокси. Всё что надо для перехода — указать DNS cloudflare + настроить редирект в .htaccess.
                          +2
                          да, cloudflare умеет раздавать сайт по https, но это решение другого плана — это внешний сервис, а внешний сервис это сразу ряд рисков:
                          1. Домен нужно переделегировать и дальше его работа целиком зависит от cloudflare — как в плане работоспособности, так и в плане ценовой/тарифной политики. Кроме того это может быть сложно (как технически так и административно).
                          2. http/https трафик будет проходить через чужие серверы, при этом если у вас на конечном сервере https не насртроен то от cloudflare до вас трафик пойдет в открытом виде (будет ли собираться и использоваться зависит уже от везения).
                          3. Дополнительная точка отказа.
                          Сама инфраструктура по опыту вполне надёжна, но бывают недоразумения — например с блокировкой IP-адреса из-за нехорошего соседа. CF — иностранная компания и на предупреждения роскомнадзора может внимания не обратить даже если его получит и не выключить провинившегося.
                          Лично сталкивался с тем что у некоторых клиентов сайт просто не работал при пропускании трафика через CF (еще когда блокировок даже небыло). Разбираться с этим в теории можно через поддержку, но это время (часто — потеря денег) + нужен английский.
                          4. Это может стать проблемой когда доменов много и/или когда они чужите и свободного управления доменами нет (хостинг, веб-студии).
                            0
                            > на предупреждения роскомнадзора может внимания не обратить

                            Не совсем верно. Не может не обратить внимания, а не обращает и это — официальная позиция. Они выпускали заявление, что им писульки Росцензуры до лампочки и они не выполняют и не будут выполнять приказы незаконного цензурного органа из РФ.
                              0
                              Мы так пару лет попали, у части клиентов выдавалось сообщение о том, что сайт в реестре. Выяснилось, что они добавили один IP CloudFlare в реестр.
                                0
                                Какие-то из IPшников CF и сейчас в реестре. Но для сайтов с ЦА в РФ это решается одной заявкой в саппорт, просто пишешь, что у тебя сайт направлен на ЦА в РФ, а IPшник на который тебя повесили там заблокирован, тебе отвечают, что «Ок, сейчас мы вам адрес сменим» и меняют. Работает на бесплатных тарифах совершенно спокойно, на платных(подозреваю) тем более.
                            0
                            Что-то я сомневаюсь, что CF сделает wildcard(не пробовал прописывать домены которые у меня работают через них по *). А тут предлагается решение фактически заменяющее wildcard, просто строчащее сертификаты по мере обращения.
                            +1
                            Похожий проект https://github.com/artyom/leproxy — используется в production уже около года, отлично себя зарекомендовал. Простой, компактный, мало зависимостей. Написал мой коллега, за что ему огромное спасибо. Экономит кучу времени и нервов.
                              +1
                              Посмотрел, отличное решение, особенно в плане краткости. Мне понравилось. https://github.com/artyom/leproxy реализован заметно проще, чем у меня как за счет более активного использования сторонних компонентов, так и за счет меньшего количества проверок.

                              Увидел реальное использование golang.org/x/crypto/acme/autocert — как раз интересно было насколько реально работает этот инструмент или он еще совсем в разработке. Смотрел его, когда свой проект писать начинал.
                              Собственно и сейчас на него посматриваю, чтобы выкинуть значительную часть своего кода и проект упростить. Пока удерживают некоторые нюансы в реализации и сложность работы с гуглом как с автором библиотеки, т.е. несмотря на то что это BSD-лицензия внести туда изменения может быть проблемой. Например они pull-request'ы не принимают и надо отдельно изучать как у них это устроено. Чуть позже буду выделять часть управления сертификатами в отдельный пакет — чтобы его можно было переиспользовать отдельно от прокси, как раз буду ориентироваться на интерфейс autocert.

                              Сам менеджер еще не очень готов, например у них только запланирована, но еще не сделана поддержка таймаутов при выписке сертификата + еще какие-то мелочи нашел, которые меня в менеджере сертификатов не устроили. Т.е. я бы и поправил с удовольствием, но потом непонятно как свои изменения обратно влить.

                              По итогам сравнения:
                              https://github.com/artyom/leproxy
                              Очень простой для понимания и дополнения кода. Собственно самого кода минимум, все основные компоненты переиспользуются из сторонних библиотек (проксирование, выписка и хранение сертификатов).
                              Для проксирования используется обработчик, встроенный в язык. Он точно лучше протестирован, чем у меня в lets-proxy.

                              Работает с заранее заданным списком доменов.
                              Может проксировать запросы к разным доменам на разные бэкенды, причем не только по tcp, но и через сокеты.

                              https://github.com/rekby/lets-proxy
                              Работает с произвольным, заранее неизвестным, изменяющимся во времени набором доменов. Например на сервере хостинга (для этого и делается) — постоянно появляются новые домены, удаляются старые.

                              В простейшем случае совсем не требует настройки, в т.ч. задания списка серверов.

                              По команде умеет устанавливаться/работать как служба в windows и демон в linux.

                              Поддержка не только http, но и tcp-проксирования.
                              Собственная реализация прокси-сервера более топорная и экономичная по ресурсам, чем встроенный вариант (намного меньше манипуляций, выделений памяти и т.п.). Кроме того встроенный вариант режет поддержку websocket (вырезает заголовок upgrade из запроса).
                              +1
                              Вот спасибо, прям только подумал об этом под win сервер, а оно уже тут!

                              Only users with full accounts can post comments. Log in, please.