Docker-образы с поддержкой ГОСТ-сертификатов в openssl, curl, php, nginx +17

В этой статье я расскажу о том, как я решал задачу об интеграции в тестовом режиме с сервисами, которые работают с использованием алгоритмов, определенных ГОСТ Р 34.10-2001 (устарел) и ГОСТ Р 34.10-2012. Приведу примеры некоторых проблем, с которыми столкнулся при решении задачи, дам ссылки на готовое решение и покажу несколько примеров их использования.


Причины


Для начала стоит сказать, что для продакшн-окружения существуют сертифицированные средства от компаний «Криптоком», «Крипто-Про», «Сигнал-КОМ» и других. Мне же нужно было реализовать взаимодействие в тестовом (девелоперском) окружении на Linux, покупать для этого лицензии не очень-то удобно, но что хуже, в открытом доступе отсутствуют документации по этому вопросу. По запросу «https гост» не так много решений. Среди них упоминается использование OpenSSL + Крипто CSP, я не помню деталей, но эта связка у меня не завелась с поддержкой ГОСТ Р 34.10-2012. Другим результатом, который часто встречался, было предложение использовать OpenSSL 1.0, в котором GOST-engine встроен внутрь, но такое решение тоже не содержало в себе поддержку GOST2012-GOST8912-GOST8912.


Рабочим решением оказалась сборка OpenSSL 1.1.0g + вынесенный в отдельно подключаемый динамический движок GOST-engine. В интернете часто встречается упоминание, что некая российская компания приложила усилия по его разработке, но в самом репозитории нет сведений об авторах продукта. Пользуясь случаем, выражаю авторам благодарность за движок в open source. На этой странице сказано, что до OpenSSL 1.1.0 использовался встроенный движок GOST, сейчас для GOST используется сторонний от OpenSSL продукт. Судя по конфигурации, скорее всего, это одна и та же библиотека.


Сборка OpenSSL, GOST-engine, cURL


Сборка стороннего продукта для тех, кто делает это редко, может быть нетривиальной задачей. Для сборки OpenSSL, GOST-engine и cURL пришлось разобраться с кучей опций и перепробовать несколько комбинаций версий. Если в Dockerfile вы заметите странное, то, скорее всего, это осталось от таких экспериментов.


Я зафиксировал все версии проектов для сборки, чтобы исключить ситуацию, что из-за обновления что-то перестанет работать. Например, я собирал OpenSSL 1.1.0h + GOST-engine и команда openssl ciphers не содержала GOST-алгоритмов, хотя openssl engine показывала GOST-engine в списке. Указав предыдущую версию OpenSSL 1.1.0g, все работало, как ожидалось. Это было очень странно, я повторил это еще раз, потом пытался выяснить причины, но в итоге решил остаться на 1.1.0g.


Собирая сам GOST-engine, я не сразу обратил внимание на наличие файла INSTALL.md в master-ветке, потому что собирал из ветки openssl_1_1_0 по неизвестно откуда взятой документации. Та версия собиралась с кастомной сборкой OpenSSL командой


cmake -DCMAKE_C_FLAGS='-I/usr/local/ssl/include -L/usr/local/ssl/lib' ..

Но master-ветка так собираться перестала, появились ошибки об отсутствии DOPENSSL_ROOT_DIR и подобное. В итоге решение было найдено и зафиксировано.


Для сборки cURL с кастомной сборкой OpenSSL гораздо больше документации, тем не менее, документация успевала устаревать, и с опциями пришлось много экспериментировать.
Результат работы выложен здесь.
Образ запушен в Docker Hub.


Примеры использования OpenSSL + GOST-engine


Не буду сильно вдаваться в подробности работы с докер-образами, лишь приведу одну команду для начала работы внутри образа:


docker run --rm -i -t rnix/openssl-gost bash

Для начала можно убедиться, что алгоритмы GOST2012-GOST8912-GOST8912 и GOST2001-GOST89-GOST89 есть в списке поддерживаемых:


openssl ciphers

Если вам известен хост, который работает по HTTPS на основе ГОСТ-алгоритмов, то посмотреть его сертификат и попробовать подключиться можно командой:


openssl s_client -connect gost.example.com:443 -showcerts

Создать закрытый ключ и сертификат по ГОСТ Р 34.10-2012:


openssl req -x509 -newkey gost2012_256 -pkeyopt paramset:A -nodes -keyout key.pem -out cert.pem

Подписать файл ранее созданными сертификатами:


openssl cms -sign -signer cert.pem -inkey key.pem -binary -in file.txt -nodetach -outform DER -nocerts -noattr -out signed.sgn

Извлечь содержимое подписанного файла, сертификатом, который был подписан самим собой:


openssl cms -verify -in signed.sgn -certfile cert.pem -CAfile cert.pem -inform der -out data.txt

С подписями самих сертификатов все немного сложнее. Пока мне не попался сервис, у которого сертификат выдан доверенным центром сертификации. Обычно, требуется дополнительно указывать сертификат удостоверяющего центра при работе с выданными им сертификатами. Это можно сделать глобально для системы (в образе), либо явно указывать в каждой команде.


Программа cURL в использовании не изменилась, просто поддерживает хосты с ГОСТ-сертификатами.


Использование образа в работе с языками программирования


Если язык программирования позволяет исполнять установленные в системе программы, то задача использования ГОСТ-алгоритмов проще всего решается копированием бинарников собранных openssl и curl в конце Dockerfile языка программирования с использованием multi-stage build. Например:


FROM rnix/openssl-gost AS openssl-gost

# Replace with any other image based on Debian x86_64
FROM debian:stretch-slim

COPY --from=openssl-gost /usr/local/ssl /usr/local/ssl
COPY --from=openssl-gost /usr/local/ssl/bin/openssl /usr/bin/openssl
COPY --from=openssl-gost /usr/local/curl /usr/local/curl
COPY --from=openssl-gost /usr/local/curl/bin/curl /usr/bin/curl
COPY --from=openssl-gost /usr/local/bin/gostsum /usr/local/bin/gostsum
COPY --from=openssl-gost /usr/local/bin/gost12sum /usr/local/bin/gost12sum

Даже необязательно копировать в /usr/bin, это можно сделать в любой каталог, а затем вызывать из вашей программы, передав полный путь и все аргументы.


Поддержка ГОСТ-алгоритмов в PHP


PHP, конечно, позволяет делать вызовы системных команд, используя, например, exec. Но, глядя на то, как собирается PHP-FPM в Dockerfile, мне показалось, что можно легко собрать PHP с кастомными сборками OpenSSL и cURL. Как оказалось дальше, я ошибся, что это легко. Всё равно собрал.


По какой-то причине, я начал с PHP-FPM 7.1. Идея была в том, чтобы использовать multi-stage build. Для этого надо заменить в их Dockerfile инструкцию FROM на мою FROM rnix/openssl-gost AS openssl-gost, затем прописать копирования собранных openssl и curl до начала сборки самого php и, наконец, указать в опции сборки путь до этих библиотек --with-openssl-dir=/usr/local/ssl и --with-curl=/usr/local/curl заменив оригинальные.


Сюрпризы ждали отовсюду. Одним из значительных было то, что при сборки php 7.1 используется pkg-config, а явная установка libcurl4-openssl-dev, libssl-dev прописывали в pkg-config версии из пакетов. В результате собиралось не то, что нужно. Если убрать их установку, то /configure php падал, ссылаясь на отсутствие openssl в pkg-config. Пришлось дополнительно копировать из кастомных сборок openssl и curl их lib/pkgconfig /*. После десятков таких сюрпризов сборка начала проходить. Дальше выяснилось, что зависимости устанавливали openssl, тем самым перезатирая ранее скопированный бинарник моей кастомной сборки. Пришлось дополнительно копировать его в самом конце. Но это не всё.


В собранном php появились алгоритмы хешей openssl_get_md_methods(): GOST R 34.11-2012 with 256 bit hash, GOST R 34.11-2012 with 512 bit hash, GOST R 34.11-94. Это значило, что php подключил GOST-engine. А вот использование расширения curl в php почему-то говорило, что таких алгоритмов не знает, соединяясь с хостом по GOST-HTTPS. Я потратил несколько часов, пытаясь найти причину этого. Я смотрел исходники, как устроено расширение curl в php, как устроен сам curl, как они связываются с openssl. Я искал место, где могут определяться поддерживаемые алгоритмы или подключатся движки. Я искал по master-веткам, много гуглил, но ничего, что решило бы сразу проблему, не нашел. Тогда я вспомнил, что я собираю не последнюю версию PHP.


Я попробовал собрать PHP-FPM 7.2. Пришлось внести некоторые правки в мой скрипт корректировки оригинального Dockerfile PHP-FPM, но сборка начала проходить без большого количества сюрпризов. Главной новостью стало то, что теперь расширение curl внутри php умело общаться по ГОСТ-алгоритмам с хостами, но была одна неприятность. Каждый вызов php писал в stdout GOST engine already loaded. Очень неприятно. Я не сразу понял, кто это делает, но такую строчку нашел в исходниках GOST-engine. Я до сих пор не знаю, в какой части системы случилась ошибка: php, php-curl, curl, openssl. Но, видимо, в php 7.1 php-curl не подключал движок совсем, теперь в php 7.2 начал подключать его дважды. Так как все работало корректно, только был вывод, я решил его убрать правкой исходника инструкцией в Dockerfile:


sed -i 's|printf("GOST engine already loaded\\n");|goto end;|' gost_eng.c

Там строчкой ниже уже есть goto end;, поэтому ничего серьезного я не сделал. Зато, пересобрав весь зоопарк, все начало работать, как и хотелось.
Образ с PHP-FPM + OpenSSL + GOST + cURL запушен в Docker Hub.


Поддержка ГОСТ-сертификатов в nginx


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


  1. Легко поднимать свой веб-сервер с ГОСТ-сертификатом по HTTPS (TLS).
  2. Легко проксировать все запросы на хост с ГОСТ-сертификатом

Легко – это означает докер-образ. Его нужно создать. Для этого нужно в Dockerfile собрать nginx с кастомным OpenSSL и GOST-engine. Открыв документацию сборки nginx, я увидел одну опцию о ssl — --with-http_ssl_module, которая просто булева. Но nginx популярный продукт, инструкций по сборке с openssl много, поэтому я нашел еще опцию --with-openssl=[DIR]. Как показала практика, nginx хочет, чтобы здесь были исходники openssl, а сборкой скрипты nginx займутся сами. Это не совсем то, что я хотел бы (я хотел использовать multi-stage build). Я ознакомился с help-выводом сборщика nginx, но ничего, что мне там помогло бы, я не нашел.


Пришлось дублировать инструкции выкачивания исходников OpenSSL, распаковки, сборки GOST-engine, включения GOST-engine в конфиги. Всё это начало собираться, но поддержки ГОСТ-алгоритмов в nginx все ещё не было. Это я проверял указанием в конфиге ssl_ciphers GOST2001-GOST89-GOST89:HIGH:MEDIUM;. Выполнение nginx -t говорило, что не знает этого алгоритма.


Как оказалось, openssl, собранный nginx, не поддерживал динамические движки, т.е. ./Configure выводил no-dynamic-engine [forced]. Здесь пришлось внимательно почитать документацию сборки OpenSSL, чтобы выяснить, что проставило forced. Причина нашлась в аргументах сборки openssl, которые вызывал nginx, а именно no-shared. Если это указано, то нет никаких флагов, чтобы включить поддержку загрузки движков. Пришлось править инструкцию сборки:


sed -i 's|--prefix=$ngx_prefix no-shared|--prefix=$ngx_prefix|' auto/lib/openssl/make

Всё это собралось, но nginx начал ругаться, что не может найти gost.so по пути /usr/lib/x86_64-linux-gnu/, это довольно странно, потому что тот же собранный openssl ищет и находит движки совсем в другом месте, а именно там, где и собирался ./lib/engines-1.1. Добавил инструкцию копирования собранных движков в /usr/lib/x86_64-linux-gnu/, чтобы угодить nginx. Заработало.


Рядом с основным Dockerfile в репозитории я положил демонстрационный Dockerfile, который при сборке создает себе ГОСТ-сертификаты и использует их, обрабатывая соединения на https://gost.example.com. Придется поработать с DNS или docker network, чтобы из одного контейнера попробовать подключится к этому демонстрационному, но все это я описал в документации.
Демонстрационный хост использует ключи по gost2001, другие варианты это gost2012_256, gost2012_512. А вместо GOST2001-GOST89-GOST89GOST2012-GOST8912-GOST8912.
Образ с nginx + GOST запушен в Docker Hub: https://hub.docker.com/r/rnix/nginx-gost/


Заключение


Мной изучена проблема работы с ГОСТ-алгоритмами в системах Linux, предоставлено решение в виде docker-образов, все это сопровождено документацией и примерами. Решение оформлено в виде репозитория на GitHub.


Стоит сказать о безопасности использования такого решения. Главное, не стоит доверять образам на Docker Hub, даже если там написано Automated Build. Я все равно могу собрать образ с любыми правками всех используемых библиотек и систем и запушить его в свой Docker Hub под любым тегом. Поэтому рекомендую форкать репозиторий на гитхабе, пулить его себе и уже самостоятельно собирать, проверив инструкции в Dockerfile на наличие того, что используются только официальные ресурсы без подозрительных модификацией по ходу сборки.


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


Ссылки


  1. Репозиторий со всеми решениями на GitHub
  2. Образ на Docker Hub с OpenSSL + GOST + cURL
  3. ГОСТ Р 34.10-2012 на Википедии со списком сертифицированных решений
  4. Репозиторий GOST-engine
  5. О возможностях и ограничениях GOST-engine
  6. Пример того, как решают вопрос в продакшн-окружении

Вы можете помочь и перевести немного средств на развитие сайта



Комментарии (24):

  1. VolCh
    /#10755866 / +2

    Кто-нибудь оценивал шансы подобных сборок получить «одобрямс» для использовании в продакшене там, где требуется криптография законом или подзаконными актами?

    • stork_teadfort
      /#10755960 / +1

      Нулевые. Там если не «сертифицированное СКЗИ», то до свиданья. Это надо же такую крамолу предложить, бесплатно гостовую крипту раздавать! Кто её покупать за сотни денег тогда будет? :)

      • thatsme
        /#10756224

        Зато роскомпозор с ФСБ не обидятся, т.к. ключи у них уже есть.

  2. andreykp
    /#10755948

    Для ДСТУ 4145-2002 что-то подобное никто не встречал?

    • gecube
      /#10756100

      Очень жаль, что не осилили сборку deb-пакетов с openssl и всеми компонентами. Мы таким образом делали сборку пакетов с нашим кодом, а на втором этапе сборки докер-образа разворачивали их поверх штатного образа (как правило — debian или ubuntu).

  3. shifttstas
    /#10756238

    не стоит помогать использовать ГОСТ шифрование, оно не безопасно, особенно в качестве одного из шифров для https, вот когда оно войдет в официальную библиотеку — тогда пожалуйста. (никогда)

    • barsuksergey
      /#10756272

      Если вам не сложно, приведите пример небезопасного ГОСТ-шифрования.

      • foi
        /#10756350

        Наверное отсылка к товарищу майору

  4. kirmorozov
    /#10756318

    Спасибо, что вложил усилия в прикручивание ГОСТов, может серьезно помочь с автоматизациями.

  5. elobachev
    /#10756902

    Упражнение интересное, но подход странный.
    Для начала отмечу, что в опенсорсной библиотеке ГОСТовый код скорее всего от вендора криптоком, линейка продуктов МагПро. Возможно ли, что какие то из опробованных вами подходов помогут в работе с сертифицированными СКЗИ? Нет. Почему? Я опишу ниже.
    Зачем тогда вообще работать с ГОСТом?
    Тестовое окружение? Не факт, что тестовый стенд окажется совместимым с сертифицированными скзи. Если нужен тестовый стенд для ГОСТа (ну вот не уберег Вас Господь) — гораздо разумнее получить триальные версии сертифицированных СКЗИ, все вендоры их дают, а некоторые даже позволяют скачать с сайта.
    Есть нюанс — даже сертифицированные СКЗИ между собою как правило или совсем несовместимы, или совместимы с бубном. А вы что то там на опенсорсе пробуете…

    Нет документации? Да полноте, есть она на все. Что нет на сайтах прямо сразу — вендоры дают по первой просьбе. У криптопры даже форум есть, там даже тем, кто первый раз в жизни make запускает рассказывают как его так запустить, что бы ngix с криптопрой собрался.

    А теперь вишенка на торт… или кучу, тут уж как посмотреть. С сертифицированным средством связываются, когда нужен сертификат. На сертификате написано ( на любом, посмотрите) «безопасность информации обеспечивается при условии выполнения требований формуляра №Формуляра». А в формуляре написано, что инсталлировать СКЗИ можно только с официально полученного дистрибутива. Т.е. не с гитхаба, а с CD диска с номером экземпляра. А еще сказано(ну грубо говоря, сами почитайте )что при встраивании СКЗИ в апач или нжинкс нужно проходить проверку корректности встраивания (ну или оценку влияния прикладного ПО на функции защиты, тоже самое) по тех. заданию, согласованному с 8 центром, что примерно то же самое, что и сертификация нового СКЗИ.
    Кактотак.

    • getId
      /#10756924

      Где требуются сертифицированные средства, там, пожалуйста, заполняем формуляр №, проходим проверки корректности, и, возможно, сертификации. Где не требуется, то решение рабочее. Некоторые вендоры не дают просто так экспортировать ключ в PEM, но решения есть: habrahabr.ru/post/317406

      Видел я эти форумы. Помогите сообществу, дайте ссылку, где запускают nginx на ГОСТ-2012. Я пробовал эту, не получилось. Может кто-то сделал это в докере?

      • elobachev
        /#10756994

        (из-под стола)Ээээ… Куда-куда вы ключи запихиваете?

        Вопрос я ваш не совсем понял.
        Что конкретно вам нужно получить?
        nginx, поддерживающий серверные сертификаты, подписанные на ГОСТ Р 34.10-2012?
        Или какой-то( какой тогда? ) chiper suite?
        На опенсорсе? Или коммерческие решения тоже устраивают?

        • getId
          /#10757060

          У криптопры даже форум есть, там даже тем, кто первый раз в жизни make запускает рассказывают как его так запустить, что бы ngix с криптопрой собрался.

          Я спрашивал ссылку на то самое обсуждение на форуме, где помогают собрать и запустить nginx с поддержкой сертификатов по ГОСТ Р 34.10-2012. Интересует хотя бы возможность проксирования запросов на хосты с такими сертификатами, но можно и про сам веб-сервер, поддерживающий сертификаты по ГОСТ Р 34.10-2012 своего хоста.

          Как бы вы могли по-другому понять «запускают nginx на ГОСТ-2012» в контексте этой статьи?

          • getId
            /#10757098

            www.cryptopro.ru/forum2/default.aspx?g=posts&m=82530#post82530 вот здесь начинают говорить о ГОСТ-2012 для КриптоПРО и nginx и о такой штуке, как «gostengy». Мои поисковые системы по «gostengy» кроме этой ветки на форуме пока ничего дельного не предлагают. Оттуда же есть ссылка на github.com/deemru/nginx, документация по которому трехлетней давности.

            Является ли такое решение из opensource openssl + opensource nginx + proprietary КриптоПро сертифицированным или же, как вы высказались, это еще одна куча?

            • elobachev
              /#10757128 / +1

              Сертифицированным из этого является КриптоПро, а остальное — некое прикладное ПО, правила встраивания в которое описано в формуляре. Работать — работает, именно так, через gostengy. Подробности сборки не подскажу, т.к. мне нужно было решение, удовлетворяющее законодательству РФ, а возиться с корректностью встраивания мутно и дорого. А без корректности встраивания это нарушение требований эксплуатационной документации==использование несертифицированного СКЗИ.

              Ну, тоесть ответ — нет, не является.

              На основе nginx + openssl + cryptopro есть сертифицированные аппаратные решения. И на основе nginx + openssl + VipNet тоже. По сути — аппаратный реверсивный прокси с веб интерфейсом управления. Про первый могу с уверенностью сказать, что работает.
              Это — да, является. Но стоит подороже и занимает минимум 1 юнит в стойке.

              Если нужно дешевле и сертифицированное, и без нарушений — то нужно накатить криптопро на виндовс, а IIS настроить как реверсивный прокси на что угодно.
              Про IIS в формуляре в явном виде написано, что для него корректность встраивания подтверждать ненужно.
              Но нужно таки прочитать формуляр и выполнить остальные требования из серии наличия сертифицированного антивируса и т.д.

              Есть еще вариант — в коробке с криптопрой лежит stunnel какой-то древней версии, собранный с поддержкой госта. На него тоже не нужно корректности встраивания, но остальные требования тоже нужно соблюдать. В некоторых случаях это помогает.

            • HomeDimoN
              /#10757142

              Обновленная инструкция…
              Настройка nginx для работы с сертификатами ГОСТ 2012 года
              Библиотека OpenSSL качается с update.cryptopro.ru/support/nginx-gost
              Есть скрипт авто установки, но КриптоПро для linux надо достать скачать самому…

              Как прокси работает…
              Решение пока никак не дружит с nGinx Plus…
              Если область действия сертификата содержит пробелы — надо экранировать строку параметр в конфиге nGinx правильно (ssl_certificate_key 'engine:gostengy: Петя Петров';)…

              github.com/deemru/nginx — сборки обновляются, но только под Windows :-(

    • SchmeL
      /#10757252

      Если нужен тестовый стенд для ГОСТа (ну вот не уберег Вас Господь) — гораздо разумнее получить триальные версии сертифицированных СКЗИ, все вендоры их дают, а некоторые даже позволяют скачать с сайта.

      И каждые 3 месяца менять ключи. А у КриптоПРО есть еще подлянка в серверных лицензиях, пользуешь linux — в большинстве случаев покупай серверную. Разница только в цене. Функционал тот же.

      А вот в каких случаях требуется именно сертифицированное средство? Допустим если получить доступ к сайту, который по госту работает…

      • elobachev
        /#10757932

        В случае, если вы являетесь оператором информационной системы, обрабатывающей информацию, подлежащую защите в соответствии с законодательством РФ, и таковая информация передается вне контролируемой зоны.

        Пример конкретный приведите, общими словами можно долго перекидываться.

        • SchmeL
          /#10760154

          ну вот например есть сайт НБКИ, который работает по ГОСТ. Для доступа к нему необходимо сертифицированное средство, или пойдет обычный openssl + gost?

          • elobachev
            /#10760596

            НБКИ — коммерческая организация, являющаяся оператором ИСПДн, и в соответствии со ст. 7 N 218-ФЗ обеспечивает защиту информации при ее обработке, хранении и передаче сертифицированными средствами защиты в соответствии с законодательством Российской Федерации.

            Таким образом, если вас связывают с НБКИ договорные отношения, в этом договоре должно быть предусмотрено обязательства для вас по защите информации.
            Если НБКИ вам это не прописал, то вы никому ничего не должны и можете пользоваться обычным openssl + gost, вы ничего не нарушаете, а нарушение законодательства допустил НБКИ.

            Если такие требования в договоре есть, то НБКИ закон выполнил, а вы нарушаете договор.

            Нарушаете ли вы закон в этом случае ( кроме нарушения договора) зависит от того, чьи данные вы оттуда вытягиваете. Если вы физ. лицо и вытягиваете свои данные, то закон не нарушаете, только договор. Если вы физ. лицо и вытягиваете данные другого физ. лица, либо вы юр.лицо и вытягиваете данные физ. лиц, то вы нарушаете требования 152-фз и всей линейки 1119-пп, 21 приказа фстек и 378 приказа фсб.
            Если вы вытягиваете данные юр.лиц, то это нарушение в области банковской, налоговоой или коммерческой тайны ( в зависимости от состава данных и обстоятельств), что бы сослаться на подзаконные акты нужно разбираться конкретнее.

  6. past
    /#10756906

    Вангую, что ГОСТ сделал нам Pastafarianist

  7. HomeDimoN
    /#10757292

    Может кому сгодиться…
    Docker контейнер с CryptoPro 4 и nginx.
    КриптоПро для linux надо достать скачать самому…