Ошибка cURL 60 при авторизации через ЕСИА

Делаю авторизацию на сайте через госуслуги с помощью пакета https://github.com/ekapusta/oauth2-esia. Так же использую docker с контейнерами:

  • php-fpm - на нем лежит проект на Laravel;
  • nginx;
  • php-fpm с поддержкой алгоритмов шифрования ГОСТ 2012 - на нем крутится пакет ekapusta/oauth2-esia, к которому происходит обращение из проекта Laravel через Guzzle. Dockerfile'ы контейнеров следующие:
  • php-fpm
FROM php:7.4-fpm

# Установка софта
RUN apt-get update && apt-get -y install mc vim zlib1g zlib1g-dev libpng-dev libjpeg62-turbo-dev mariadb-client \
    libfreetype6-dev netcat supervisor libcurl4-openssl-dev libzip-dev \
    && docker-php-ext-install bcmath && docker-php-ext-enable bcmath \
    && docker-php-ext-install mysqli && docker-php-ext-enable mysqli \
    && docker-php-ext-install pdo && docker-php-ext-enable pdo \
    && docker-php-ext-install pdo_mysql && docker-php-ext-enable pdo_mysql \
    && pecl install xdebug && docker-php-ext-enable xdebug \
    && pecl install redis && docker-php-ext-enable redis \
    && docker-php-ext-configure gd --with-freetype --with-jpeg \
    && docker-php-ext-install -j$(nproc) gd \
    && docker-php-ext-install sockets \
    && docker-php-ext-install zip

# Configure non-root user.
ARG PUID=1000
ENV PUID ${PUID}
ARG PGID=1000
ENV PGID ${PGID}

RUN groupmod -o -g ${PGID} www-data && \
    usermod -o -u ${PUID} -g www-data www-data

# Конфиги
RUN mv "$PHP_INI_DIR/php.ini-development" "$PHP_INI_DIR/php.ini"
COPY ./conf.d/custom.ini $PHP_INI_DIR/conf.d/
COPY ./conf.d/xdebug.ini $PHP_INI_DIR/conf.d/
COPY ./conf.d/litn-backend.conf /usr/local/etc/php-fpm.d/www.conf
COPY ./supervisor.config/supervisord.conf /etc/supervisor/conf.d/supervisord.conf
COPY ./supervisor.config/default-worker.conf /etc/supervisor/conf.d/default-worker.conf
COPY ./supervisor.config/email-worker.conf /etc/supervisor/conf.d/email-worker.conf
COPY ./supervisor.config/orders-worker.conf /etc/supervisor/conf.d/orders-worker.conf

COPY ./wait-for-it.sh /wait-for-it.sh
RUN chmod a+x /wait-for-it.sh

COPY ./cryptopro/linux-amd64_deb/ /home/cryptopro/
RUN /home/cryptopro/install.sh

COPY --from=composer /usr/bin/composer /usr/bin/composer
  • php-fpm с ГОСТ 2012
FROM php:7.4-fpm
WORKDIR /var/www

RUN apt-get update && apt-get install -y nginx libzip-dev cmake git libssl-dev \
    && docker-php-ext-install zip

COPY ./nginx/secure.conf /etc/nginx/secure.conf
COPY ./nginx/default /etc/nginx/sites-available/default

ADD https://curl.se/ca/cacert.pem /etc/ca-certificates/cacert.crt
RUN chmod 644 /etc/ca-certificates/cacert.crt
RUN touch ${PHP_INI_DIR}/conf.d/curl.ini
RUN echo curl.cainfo="/etc/ca-certificates/cacert.crt" >> ${PHP_INI_DIR}/conf.d/curl.ini
RUN echo openssl.cafile="/etc/ca-certificates/cacert.crt" >> ${PHP_INI_DIR}/conf.d/curl.ini
RUN update-ca-certificates

RUN php -r "copy('http://getcomposer.org/installer', 'composer-setup.php');" && \
    php -r "if (hash_file('sha384', 'composer-setup.php') === '756890a4488ce9024fc62c56153228907f1545c228516cbf63f885e036d37e9a59d27d63f46af1d4d07ee0f76181c7d3') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;" && \
    php composer-setup.php && \
    php -r "unlink('composer-setup.php');"

# создаем файл логов
RUN touch esia.log

#### СТАВИМ Gost Engine для OpenSSL
RUN git clone --branch=openssl_1_1_0 https://github.com/gost-engine/engine.git gost-engine/engine \
    && cd gost-engine/engine && cmake . && make && cp bin/gost.so /usr/lib/x86_64-linux-gnu/engines-1.1
COPY ./openssl.cnf /etc/ssl/openssl.cnf
###############

COPY entrypoint.sh /etc/entrypoint.sh
RUN chmod o+x /etc/entrypoint.sh

EXPOSE 80

ENTRYPOINT ["/etc/entrypoint.sh"]

Алгоритм запроса следующий:

  • пользователь переходит по ссылке на регистрацию через ЕСИА, за это отвечает метод в контроллере который использует сервис, который генерирует ссылку для авторизации:
public function buildAuthLink()
    {
        $client = new Client();
        $response = $client->get($this->getLinkUrl, [
            'query' => [
                'client_id' => $this->appId,
                'redirect_uri' => $this->redirectUri,
                'remote_url' => $this->remoteUrl,
            ]
        ]);
        $body = json_decode($response->getBody(), true);
        $link = $body['link'];
        $this->state = $body['state'];
        dd($link);
        return $link;
    }

здесь происходит get запрос на контейнер с php-fpm с ГОСТ 2012, на файл start.php:

$certPath = getenv('CERT_PATH');
$privateKeyPath = getenv('KEY_PATH');

$clientId = $_GET['client_id'];
$redirectUri = $_GET['redirect_uri'];
$remoteUrl = $_GET['remote_url'];

$provider = new EsiaProvider(
    [
        'clientId'      => $clientId,
        'redirectUri'   => $redirectUri,
        'defaultScopes' => ['birthdate', 'gender', 'email', 'mobile'],
        'remoteUrl' => $remoteUrl,
        'remotePublicKey' => EsiaProvider::RESOURCES.'esia.test.public.key',
        'remoteCertificatePath' => EsiaProvider::RESOURCES.'esia.gost.prod.public.key',
    ],
    [
        'signer' => new OpensslPkcs7($certPath, $privateKeyPath, getenv('KEY_PASS')),
        'remoteSigner' => new OpenSslCliJwtSigner('/usr/bin/openssl'),
    ]
);

try {
    $link = $provider->getAuthorizationUrl();
    fwrite($file, $link."\n");
    $_SESSION['oauth2.esia.state'] = $provider->getState();
    fwrite($file, $_SESSION['oauth2.esia.state']."\n");

    $result = [
        'link' => $link,
        'state' => $provider->getState(),
    ];
    $result = json_encode($result);
} catch(\Exception $e) {
    fwrite($file, $e->getMessage()."\n");
    fwrite($file, $e->getTraceAsString()."\n");
    $result = [
        'error' => $e->getMessage(),
        'openssl_error' => openssl_error_string(),
    ];
    $result = json_encode($result);
} finally {
    fwrite($file, "Код отработал!\n");
    fclose($file);
    echo $result;
}
return;

Данный скрипт успешно возвращает ссылку для авторизации на госуслугах.

  • далее происходит редирект на ссылку и открывается тестовая страница госуслуг с формой ввода логина и пароля от учетной записи госуслуг.
  • пользователь вводит логин и пароль и его редиректит на метод в контроллере для получения данных пользователя:
public function esiaResponse(Request $request)
    {
        $code = $request->get('code');
        $state = $request->get('state');

        $this->authentication->setState($state);
        $accessToken = $this->authentication->getAccessToken($code);
        $esiaUser = $this->authentication->getEsiaUser($accessToken);
    }

метод getEsiaUser:

public function getEsiaUser(string $accessToken)
    {
        $client = new Client();
        $response = $client->get($this->getUserUrl, [
            'query' => [
                'client_id' => $this->appId,
                'redirect_uri' => $this->redirectUri,
                'remote_url' => $this->remoteUrl,
                'code' => $this->code,
                'state' => $this->state,
            ]
        ]);
        $body = json_decode($response->getBody(), true);
        dd($body);
        return $body['user'];
    }

Здесь происходит обращение на контейнер с ГОСТ на файл finish.php:

$certPath = getenv('CERT_PATH');
$privateKeyPath = getenv('KEY_PATH');

$clientId = $_GET['client_id'];
$redirectUri = $_GET['redirect_uri'];
$remoteUrl = $_GET['remote_url'];
$code = $_GET['code'];
$state = $_GET['state'];


$provider = new EsiaProvider(
    [
        'clientId'      => $clientId,
        'redirectUri'   => $redirectUri,
        'defaultScopes' => ['birthdate', 'gender', 'email', 'mobile'],
        'remoteUrl' => $remoteUrl,
        'remoteCertificatePath' => EsiaProvider::RESOURCES.'esia.gost.prod.public.key',
    ],
    [
        'signer' => new OpensslPkcs7($certPath, $privateKeyPath, getenv('KEY_PASS')),
        'remoteSigner' => new OpenSslCliJwtSigner('/usr/bin/openssl'),
    ]
);

try {
    fwrite($file, $provider->getBaseAccessTokenUrl([])."\n");
    $accessToken = $provider->getAccessToken('authorization_code', ['code' => $code]);
    fwrite($file, $provider->getState()."\n");
    fwrite($file, var_export($accessToken)."\n");
    $esiaPersonData = $provider->getResourceOwner($accessToken);

    $result = [
        'user' => $esiaPersonData->toArray(),
        'access_token' => $accessToken->__toString(),
    ];
    $result = json_encode($result);
} catch(\Exception $e) {
    fwrite($file, $e->getMessage()."\n");
    fwrite($file, $e->getTraceAsString()."\n");
    fwrite($file, json_encode(openssl_get_cert_locations())."\n");
    $result = [
        'error' => $e->getMessage(),
        'openssl_error' => openssl_error_string(),
        'openssl_cert_location' => openssl_get_cert_locations(),
    ];
    $result = json_encode($result);
} finally {
    fwrite($file, "Код отработал!\n");
    fclose($file);
    echo $result;
}
return;

Данный скрипт отрабатывает до строки $accessToken = $provider->getAccessToken('authorization_code', ['code' => $code]); и выбрасывает ошибку:

array:3 [▼
  "error" => "cURL error 60: SSL certificate problem: unable to get local issuer certificate (see https://curl.haxx.se/libcurl/c/libcurl-errors.html)"
  "openssl_error" => "error:140E0197:SSL routines:SSL_shutdown:shutdown while in init"
  "openssl_cert_location" => array:8 [▼
    "default_cert_file" => "/usr/lib/ssl/cert.pem"
    "default_cert_file_env" => "SSL_CERT_FILE"
    "default_cert_dir" => "/usr/lib/ssl/certs"
    "default_cert_dir_env" => "SSL_CERT_DIR"
    "default_private_dir" => "/usr/lib/ssl/private"
    "default_default_cert_area" => "/usr/lib/ssl"
    "ini_cafile" => "/etc/ca-certificates/cacert.crt"
    "ini_capath" => ""
  ]
]

Все решения подобные https://stackoverflow.com/questions/24611640/curl-60-ssl-certificate-problem-unable-to-get-local-issuer-certificate, отключение верификации в cURL и Guzzle не решают мою проблему. Плюсом ко всему, был сделан HTTPS на локальном сервере - не помогло, размещение сборки на сервере с HTTPS так же не помогло.


Ответы (0 шт):