Домены
Интересные статьи
Домены

Как добавить поддержку ГОСТ 34.10 2012 в CentOS 7/Redhat 7

Именно с такой задачей я столкнулся несколько дней назад на работе. Требовалось организовать поддержку ГОСТовых алгоритмов шифрования в CentOS/Redhat 7, чтобы работать с сайтом госуслуг. Сразу оговорюсь, поддержка была добавлена в OpenSSL, curl и PHP 7.1 путем сборки из исходников.

Поддержка ГОСТ алгоритмов осуществляется с помощью модуля GOST Engine, добавляемого в OpenSSL.

Инструкция является сборной солянкой из найденного в интернете и адаптирована под CentOS 7, права на оригинальные инструкции, части которых были использованы принадлежат их авторам.

Исходные данные

Начнем с того, что у меня было:

  • 1 сервер на CentOS 7 (площадка разработки)
  • 3 сервера на Redhat 7 (боевые площадки)
  • OpenSSL 1.0.2k — последняя доступная версия в репозиториях Centos/Redhat 7
  • PHP 7.1.33 (FPM) из репозитория Remi (в режиме MultiVersion)
  • Подключены репозитории EPEL/Remi
  • На Redhat серверах ограничен доступ внешним ресурсам, поэтому часть того, что на CentOS 7 была скачана wget`ом, тут была загружена с помощью копирования скаченного архива на сервер.
  • В PHP была добавлена поддержка БД MySQL и PostgreSQL (MariaDB 10 и PostgreSQL 10 соответственно).

Общий план работ

  • Создание резервной копии директории /etc
  • Обновить ОС на сервере
  • Сборка OpenSSL 1.1.х
  • Сборка GOST Engine
  • Сборка Curl
  • Сборка PHP
  • Фиксация версий

Сборка OpenSSL

Для начала установим необходимые компоненты, а именно: подключим репозиторий EPEL (если не подключен), cmake3

yum install cmake3 wget curl unzip yum-utils gcc

Теперь удалим установленный OpenSSL, он мешает сборке, позже мы его вернем, для обратной совместимости. Предварительно сделайте резервные копии конфигов из /etc т.к. удалятся все зависимые пакеты (у меня удалился как минимум certbot).

yum remove openssl

Проверим, что OpenSSL удалился, попытавшись вызвать его:

openssl version

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

Скачиваем OpenSSL 1.1.0g в любую удобную директорию , только с ним у меня корректно все заработало и собралось. Если доступ к внешним ресурсам ограничен, то загрузите архив на сервер любым доступным вам способом, имя архива должно быть openssl-1.1.0g.tar.gz

wget "https://www.openssl.org/source/openssl-1.1.0g.tar.gz" -O "openssl-1.1.0g.tar.gz"

Распаковываем загруженный архив:

tar -zxvf "openssl-1.1.0g.tar.gz" 

Переходив в директорию openssl-1.1.0g

cd "openssl-1.1.0g"

Запускаем конфигурацию OpenSSL, по окончании всей сборки (последующие этапы) он будет установлен в /usr/local/ssl/

./config no-async shared --prefix=/usr/local/ssl --openssldir=/usr/local/ssl -Wl,-rpath,/usr/local/ssl/lib

Теперь запускаем сборку и после ее успеха — установку:

make && make install

Делаем симлинк на новый OpenSSL:

ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl

Теперь проверим доступность OpenSSL, он должен быть доступен в /usr/bin/openssl и вызываться обычной командой openssl:

openssl version

В результате вы должны получить OpenSSL 1.1.0g

На этом сборка OpenSSL окончена, теперь добавим в него поддержку GOST шифрования.

Сборка GOST Engine

Перейдем в директорию, где будем осуществлять сборку:

cd /usr/local/src

Скачиваем архив с GOST Engine, к сожалению у меня не заработала последняя версия, поэтому загружаем архив, с которым все успешно собиралось. Если доступ к внешним ресурсам ограничен, то загрузите архив любым доступным вам способом и именем gost-engine.zip в эту же директорию.

wget "https://github.com/gost-engine/engine/archive/3bd506dcbb835c644bd15a58f0073ae41f76cb06.zip" -O gost-engine.zip

Распаковываем архив

unzip gost-engine.zip -d ./

Переходим в директорию распакованного архива

cd "engine-3bd506dcbb835c644bd15a58f0073ae41f76cb06/"

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

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

Создадим директорию build, где будем осуществлять сборку и сразу перейдем в нее:

mkdir build && cd build

Сконфигурируем систему сборки:

cmake3 -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_FLAGS='-I/usr/local/ssl/include -L/usr/local/ssl/lib' -DOPENSSL_ROOT_DIR=/usr/local/ssl  -DOPENSSL_INCLUDE_DIR=/usr/local/ssl/include -DOPENSSL_LIBRARIES=/usr/local/ssl/lib ..

И теперь соберем наш GOST Engine:

cmake3 --build . --config Release

Выйдем в директорию bin выше уровнем

cd ../bin

Скопируем утилиту gost12sum в директорию /usr/local/bin/

cp gostsum gost12sum /usr/local/bin

Выйдем еще выше уровнем

cd ..

Скопируем собранный движок gost.so в директорию engines-1.1 openssl

cp bin/gost.so /usr/local/ssl/lib/engines-1.1

Добавим конфигурацию для GOST Engine в файл конфигурации OpenSSL:

sed -i '6i openssl_conf=openssl_def' /usr/local/ssl/openssl.cnf
echo "" >> /usr/local/ssl/openssl.cnf
echo "# OpenSSL default section" >> /usr/local/ssl/openssl.cnf
echo "[openssl_def]" >> /usr/local/ssl/openssl.cnf
echo "engines = engine_section" >> /usr/local/ssl/openssl.cnf
echo "" >> /usr/local/ssl/openssl.cnf
echo "# Engine scetion" >> /usr/local/ssl/openssl.cnf
echo "[engine_section]" >> /usr/local/ssl/openssl.cnf
echo "gost = gost_section" >> /usr/local/ssl/openssl.cnf
echo "" >> /usr/local/ssl/openssl.cnf
echo "# Engine gost section" >> /usr/local/ssl/openssl.cnf
echo "[gost_section]" >> /usr/local/ssl/openssl.cnf
echo "engine_id = gost" >> /usr/local/ssl/openssl.cnf
echo "dynamic_path = /usr/local/ssl/lib/engines-1.1/gost.so" >> /usr/local/ssl/openssl.cnf
echo "default_algorithms = ALL" >> /usr/local/ssl/openssl.cnf
echo "CRYPT_PARAMS = id-Gost28147-89-CryptoPro-A-ParamSet" >> /usr/local/ssl/openssl.cnf

Теперь проверим, что движок GOST подключился:

openssl engine

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

Проверим список доступных шифров и найдем среди них интересующий нас GOST:

openssl ciphers|tr ':' '\n'|grep GOST

В результате вы должны получить следующий список:

Как видите, теперь OpenSSL умеет работать с ГОСТ шифрами. При генерации ключа нужный гост вызывается через -newkey gost2012_256

Сборка Curl

Перейдем в директорию, где будем выполнять сборку:

cd /usr/local/src

Теперь загрузим нужную версию Curl. У меня заработало только с 7.59.0. Если доступ к внешним ресурсам ограничен, то загрузите архив любым доступным вам способом с именем curl-7.59.0.tar.gz

wget "https://curl.haxx.se/download/curl-7.59.0.tar.gz" -O "curl-7.59.0.tar.gz"

Распакуем архив

tar -zxvf "curl-7.59.0.tar.gz" 

Перейдем в распакованную директорию

cd "curl-7.59.0"

Сконфигурируем Curl для работы с нужной версией OpenSSL

CPPFLAGS="-I/usr/local/ssl/include" LDFLAGS="-L/usr/local/ssl/lib -Wl,-rpath,/usr/local/ssl/lib" LD_LIBRARY_PATH=/usr/local/ssl/lib ./configure --prefix=/usr/local/curl --with-ssl=/usr/local/ssl --with-libssl-prefix=/usr/local/ssl

Теперь соберем и установим Curl:

make && make install

Переименуем оригинальный (системный) curl в curl_real:

mv /usr/bin/curl{,_real}

Сделаем симлинк на свежесобранный curl в /usr/bin/curl

ln -s /usr/local/curl/bin/curl /usr/bin/curl

Ну и проверим версию собранного curl:

curl --version

В результате вы должны получить нечто подобное:

На этом и сборку Curl мы закончили.

Теперь соберем PHP

Сборка PHP

При сборке PHP в RedHat 7 столкнулся с тем, что не все devel компоненты есть в стандартных репозиториях (в том числе EPEL). Поэтому пришлось скачать их с помощью другой CentOS 7 системы и закинуть в Redhat 7 нужные rpm файлы.

Только для RHEL:

В любом доступной вам CentOS 7 системе установите yum-utils:

yum install yum-utils

После чего скачайте rpm файлы нужных библиотек:

yumdownloader bison-devel libwebp-devel libedit-devel

В результате вы получите набор rpm файлов, берите те, что оканчиваются на .x86_64.rpm и копируйте в RHEL систему любым доступным вам способом.

После чего установите эти rpm файлы:

yum localinstall *.x86_64.rpm

Общая инструкция:

Удалим devel компоненты zlib, они могут мешать сборке:

yum remove zlib-devel

Перейдем в директорию, где будем собирать PHP

cd /usr/src 

В зависимости от того, какая БД вам нужна (мне пришлось собирать на Dev сервере с поддержкой MySQL и PostgreSQL, а на Prod серверах только с PostgreSQL) — установите нужные пакеты:

MySQL:

yum install libxml2 libxml2-devel bzip2-devel libmcrypt-devel libwebp-devel libjpeg-devel libpng-devel libicu-devel libpqxx-devel readline-devel bison-devel bison systemd-devel pkgconfig autoconf bison re2c libicu-devel e2fsprogs-devel mariadb-devel readline libedit-devel

PostgreSQL:

yum install libxml2 libxml2-devel bzip2-devel libmcrypt-devel libwebp-devel libjpeg-devel libpng-devel libicu-devel libpqxx-devel readline-devel bison-devel bison systemd-devel pkgconfig autoconf bison re2c libicu-devel e2fsprogs-devel readline libedit-devel

Удалим devel компоненты openssl, если они установлены:

yum remove openssl-devel

Создадим директорию, где позже расположим конфигурационные файлы модулей PHP:

mkdir -p /usr/local/php/etc/conf.d

Загрузим PHP 7.1.33, именно эта версия у меня была установлена из Remi. Если доступ к внешним ресурсам ограничен, то загрузите архив любым доступным для вас способом в директорию /usr/src и именем php-7.1.33.tar.gz

wget "https://github.com/php/php-src/archive/php-7.1.33.tar.gz" -O php-7.1.33.tar.gz

Распакуем полученный архив

tar -zxvf php-7.1.33.tar.gz

И перейдем в распакованную директорию:

cd php-src-php-7.1.33/

Сделаем первичную конфигурацию:

./buildconf --force

Теперь в зависимости от поддержки нужных БД, сконфигурируем PHP. Если нужна поддержка обеих версий — добавьте разница одного варианта в другой, и не забудьте установить пакеты для обеих БД

MySQL:

./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-config-file-scan-dir=/usr/local/php/etc/conf.d --enable-fpm --with-fpm-systemd  --with-openssl --with-openssl-dir=/usr/local/ssl --with-pcre-regex --with-pcre-jit --with-zlib --enable-bcmath --with-bz2 --enable-calendar --with-curl=/usr/local/curl --enable-exif --with-gd --enable-intl --enable-mbstring --enable-pcntl --with-pdo-pgsql --enable-soap --enable-sockets --with-xmlrpc --enable-zip --with-webp-dir --with-jpeg-dir --with-png-dir --enable-cli -enable-ftp --with-libedit --with-iconv  --enable-json  --with-mcrypt --enable-opcache --enable-pdo --with-pgsql --enable-xml --enable-xmlreader --enable-xmlwriter  --with-pdo-pgsql=/usr/pgsql-10/bin --with-pgsql=/usr/pgsql-10/bin --with-mysqli --with-pdo-mysql

PostgreSQL:

./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-config-file-scan-dir=/usr/local/php/etc/conf.d --enable-fpm --with-fpm-systemd  --with-openssl --with-openssl-dir=/usr/local/ssl --with-pcre-regex --with-pcre-jit --with-zlib --enable-bcmath --with-bz2 --enable-calendar --with-curl=/usr/local/curl --enable-exif --with-gd --enable-intl --enable-mbstring --enable-pcntl --with-pdo-pgsql --enable-soap --enable-sockets --with-xmlrpc --enable-zip --with-webp-dir --with-jpeg-dir --with-png-dir --enable-cli -enable-ftp --with-libedit --with-iconv  --enable-json  --with-mcrypt --enable-opcache --enable-pdo --with-pgsql --enable-xml --enable-xmlreader --enable-xmlwriter  --with-pdo-pgsql=/usr/pgsql-10/bin --with-pgsql=/usr/pgsql-10/bin

При сборке из исходников 7.4 версии, у меня ругалось на отсутствие openssl, хотя он явно был указан, но это не работало. Пришлось объявить переменную, где путь до вашего openssl и директории lib/pkgconfig внутри

export PKG_CONFIG_PATH="/usr/local/ssl/lib/pkgconfig:/usr/local/curl/lib/pkgconfig"

И команду ./configure привести к виду, который ниже (вариант для MySQL)

./configure --prefix=/usr/local/php --with-config-file-path=/usr/local/php/etc --with-config-file-scan-dir=/usr/local/php/etc/conf.d --enable-fpm --with-fpm-systemd  --with-openssl=/usr/local/ssl --with-openssl-dir=/usr/local/ssl --with-pcre-regex --with-pcre-jit --with-zlib --enable-bcmath --with-bz2 --enable-calendar --with-curl=/usr/local/curl --enable-exif --enable-gd --enable-intl --enable-mbstring --enable-pcntl --enable-soap --enable-sockets --with-xmlrpc --with-zip --with-webp --with-jpeg --enable-cli -enable-ftp --with-libedit --with-iconv  --enable-json  --with-mcrypt --enable-opcache --enable-pdo  --enable-xml --enable-xmlreader --enable-xmlwriter  --with-mysqli --with-pdo-mysql

Очищаем директорию сборки

make clean

Далее есть один нюанс, если доступ к внешним ресурсам у вас ограничен, то последующие шаги у вас не завершатся успехом т.к. PHP пытается скачать PEAR из внешних источников.

Чтобы эту проблему решить — скачаем и положим нужный файл, в нужное место.

Загрузите файл с именем install-pear-nozlib.phar

https://pear.php.net/go-pear.phar

После чего загрузите его в директорию /usr/src/php-src-php-7.1.33/pear/ любым доступным вам способом.

Приступим непосредственно к сборке и установке:

make && make install

Процесс довольно длительный, после окончания процесса, создадим симлинки на новую версию PHP

Приведу список команд без комментариев, но общий принцип таков, что если файл есть — переименовываем его с суффиксом _old

ln -s /usr/local/php/bin/pear /usr/bin/pear
ln -s /usr/local/php/bin/peardev /usr/bin/peardev
ln -s /usr/local/php/bin/pecl /usr/bin/pecl
mv /usr/bin/phar{,_old}
ln -s /usr/local/php/bin/phar /usr/bin/phar
mv /usr/bin/php{,_old}
ln -s /usr/local/php/bin/php /usr/bin/php
mv /usr/bin/php-cgi{,_old}
ln -s /usr/local/php/bin/php-cgi /usr/bin/php-cgi
mv /usr/bin/php-config{,_old}
ln -s /usr/local/php/bin/php-config /usr/bin/php-config
ln -s /usr/local/php/bin/phpdbg /usr/bin/phpdbg
mv /usr/bin/phpize{,_old}
ln -s /usr/local/php/bin/phpize /usr/bin/phpize
ln -s /usr/local/php/sbin/php-fpm /usr/sbin/php-fpm

Файлы конфигурации (php.ini и php-fpm.conf) я брал из Remi

cp /etc/opt/remi/php71/php-fpm.conf /usr/local/php/etc/
cp /etc/opt/remi/php71/php.ini /usr/local/php/etc/

И подключим OpCache модуль:

echo "zend_extension=opcache.so" >> /usr/local/php/etc/conf.d/modules.ini

Сами конфиги пулов у меня лежат по иному пути, а т.к. я скопировал старый php-fpm.conf — то и новый php-fpm запустит их оттуда же.

Теперь создадим Systemd сервис для нашего php-fpm, для этого создайте любым удобным для вас редактором файл /usr/lib/systemd/system/php-fpm.service

vi /usr/lib/systemd/system/php-fpm.service

И заполните его следующим:

[Unit]
Description=The PHP FastCGI Process Manager
After=syslog.target network.target
 
[Service]
Type=simple
PIDFile=/run/php-fpm/php-fpm.pid
ExecStart=/usr/sbin/php-fpm --nodaemonize --fpm-config /usr/local/php/etc/php-fpm.conf
ExecReload=/bin/kill -USR2 $MAINPID
 
[Install]
WantedBy=multi-user.target

Перезагрузите список демонов:

systemctl daemon-reload

Теперь можно останавливать старый FPM и запускать новый. Старый у меня был доступен под именем php71-php-fpm, а новый просто php-fpm:

systemctl stop php71-php-fpm && systemctl start php-fpm

И добавим/исключим в автозагрузку сервисы:

systemctl enable php-fpm && systemctl disable php71-php-fpm

На этом основная часть закончена. Теперь не менее важный этап — установка Openssl (да, того самого, из репозитория) и блокировка обновлений.

Установим снова openssl, для обратной совместимости:

yum install openssl

При установке openssl подменит наш симлинк, поэтому вернем всё к нашей версии:

mv /usr/bin/openssl{,_real}

И восстановим симлинк:

ln -s /usr/local/ssl/bin/openssl /usr/bin/openssl

Теперь зафиксируем версии openssl и curl, чтобы они не начали обновляться. От PHP из Remi мы уже не зависим, поэтому не блокируем.

Установите модуль yum — versionlock

yum install yum-plugin-versionlock

Заблокируем curl

yum versionlock curl-*

Заблокируем OpenSSL

yum versionlock openssl

Важное замечание по OpenSSL

После пары дней работы заметил, что начал при работе с сервисами (использующие SSL/TLS) ошибку вроде:

OpenSSL Error messages: error:1416F086:SSL routines:tls_process_server_certificate:certificate verify failed

Думал где-то в коде проблема, а оказалось в другом. OpenSSL хранит некоторое количество сертификатов (корневые) и при сборке из исходников они не подтягиваются. Т.к. у нас установлен openssl в любом случае (системный), то актуальный список сертификатов у нас есть, а значит его мы можем симлинками подкинуть в нашу версию openssl. Приведу несколько команд, без комментариев. Сутя такая, что удаляем пустые папки и на их место кидаем симлинки.

rm -rf /usr/local/ssl/{certs,private}
ln -s /etc/pki/tls/certs /usr/local/ssl/certs
ln -s /etc/pki/tls/private /usr/local/ssl/private
ln -s /etc/pki/tls/cert.pem /usr/local/ssl/cert.pem

Заключение

Т.к. мне требовались конкретные версии PHP и собралось оно только на определенных версиях всех перечисленных выше компонентов — то привел инструкцию как оно точно собирается, не исключено, что и на других версиях оно соберется. Другие версии сам не пробовал т.к. требовалось запустить сервис клиента как можно скорее (как обычно у клиента всё в последний день).

Знаю, что это всё можно было оптимизировать, как-то систематизировать, но нужно было решить задачу в кратчайшие сроки, а найденные в интернете инструкции были слишком разрознены, либо сводились к использованию только в docker, либо только в Deb системах.

Инструкция в общем плане подходит и для более свежих версий PHP (с теми же версиями openssl/gost/curl), но придется повозиться с зависимостями и конфигурацией.

Итого имеем: Openssl 1.1.0g, Gost Engine, Curl 7.59.0, PHP 7.1.33

Надеюсь статья кому-то поможет 🙂

Обо мне cyber01

Прочитайте также

Как установить Zend Framework на Ubuntu 18.04

Zend Framework 3 это последняя версия фреймворка. Новая версия отличается большей производительностью. Zend это фреймворк …

Как установить и управлять NodeJS с помощью NVM

NVM, также известный как Node Version Manager, это аналог RVM (Ruby Version manager) для языка …

Как использовать прокси для cURL в Linux

cURL — консольная утилита для получения и отправки данных с помощью синтаксиса URL Может быть …

4 комментария

  1. Богдан

    Сергей, watermark по среди изображения смотрится довольно комично

  2. Алексей

    Сделали всё по вашей инструкции,
    получили
    139815904147264:error:8006607F:lib(128):FILL_GOST_EC_PARAMS:unsupported parameter set:/usr/local/src/engine-3bd506dcbb835c644bd15a58f0073ae41f76cb06/gost_ec_sign.c:82:
    139815904147264:error:0606F091:digital envelope routines:EVP_PKCS82PKEY:private key decode error:crypto/evp/evp_pkey.c:44:

    При попытке расшифровать pfx. Подскажите, что может быть не так?

    • Решили проблему? Тут зависит от того, с какими параметрами вызывали… Кстати,если PFX это экспорт из винды — он не прокатит, винда делает кривой pfx, несовместимый с Openssl. С помощью утилиты P12FromGostCSP можно правильный pfx вытащить

  3. Владимир

    Большое спасибо! Собрал openssl по вашей инструкции, буду пробовать!

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

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