Docker-images with support for GOST certificates in openssl, curl, php, nginx

In this article I will talk about how I solved the problem of integration in a test mode with services that work using algorithms defined by GOST R ???-2001 (obsolete) and GOST R ???-2012 . I will give examples of some of the problems that I encountered in solving the problem, I will give links to the ready solution and I will show some examples of their use.
GOST-engine . On the Internet, it is often mentioned that a Russian company has made efforts to develop it, but there is no information about the authors of the product in the repository itself. Taking this opportunity, I express gratitude to the authors for the engine in open source. At see this page said that prior to OpenSSL 1.1.? the built-in GOST engine was used, now for GOST, a third-party product from OpenSSL is used. Judging by the configuration, most likely, this is the same library.
 
Build OpenSSL, GOST-engine, cURL
 
Building a third-party product for those who do this rarely, can be a non-trivial task. To build OpenSSL, GOST-engine and cURL had to deal with a bunch of options and try several combinations of versions. If you notice a strange in the Dockerfile, then, most likely, it remains from such experiments.
 
I fixed all the versions of the projects for assembly to exclude the situation, that because of the update something will stop working. For example, I compiled OpenSSL ???h + GOST-engine and the command openssl ciphers did not contain GOST algorithms, although openssl engine showed the GOST-engine in the list. Specifying the previous version of OpenSSL ???g, everything worked as expected. It was very strange, I repeated it again, then I tried to find out causes , but eventually decided to stay on ???g.
 
Collecting the GOST-engine itself, I did not immediately notice the presence of the file INSTALL.md in the master-branch, because he collected from the branch openssl_1_1_0 by unknown origin of the documentation. That version was assembled with the custom assembly of OpenSSL using the command.
 
    cmake -DCMAKE_C_FLAGS = '- I /usr /local /ssl /include -L /usr /local /ssl /lib'

 

But the master-branch so to cease to collect, there were errors about absence DOPENSSL_ROOT_DIR and the like. As a result, the decision was found and fixed.
 
To build cURL with the custom assembly of OpenSSL, there is much more documentation, however, the documentation has become obsolete, and with the options it was necessary to experiment a lot.
 
The result of the work is found here: https://github.com/rnixik/docker-openssl-gost
 
The image is launched in the Docker Hub: https://hub.docker.com/r/rnix/openssl-gost/


 

Examples of using OpenSSL + GOST-engine


 

I will not go into the details of working with docker-images much, just give one command to start work inside the image:


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

 

To begin with, you can verify that the algorithms GOST2012-GOST8912-GOST8912 and GOST2001-GOST89-GOST89 is in the list of supported:


 
    openssl ciphers    

 

If you know a host that works on HTTPS based on GOST algorithms, then you can see its certificate and try to connect using the command:


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

 

Create private key and certificate according to GOST R ???-2012:


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

 

Sign the file with the previously created certificates:


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

 

Extract the contents of the signed file, a certificate that was signed by itself:


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

 

With the signatures of the certificates themselves, everything is slightly more complicated. Until I got a service from which the certificate was issued by a trusted certification authority. Usually, it is required to additionally specify the certificate of the certifying center when working with certificates issued by it. This can be done globally for the system (in the image), or explicitly specified in each command.
 
The program cURL in use has not changed, it simply supports hosts with GOST certificates.


 

Using the image in working with programming languages ​​


 

If the programming language allows executing programs installed in the system, then the task of using GOST algorithms is most simply solved by copying the binaries of openssl and curl collected at the end of the Dockerfile programming language using multi-stage build . For example:


 
    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

 

You do not even need to copy it to /usr /bin , you can do this in any directory, and then call from your program, passing the full path and all the arguments.


 

Support for GOST algorithms in PHP


 

PHP, of course, allows you to make calls to system commands, using, for example, exec . But, looking at how PHP-FPM assembles in Dockerfile, it seemed to me that it's easy to build PHP with custom assemblies of OpenSSL and cURL. As it turned out, I was mistaken that it was easy. All the same collected.
 
For some reason, I started with PHP-FPM 7.1. The idea was to use multi-stage build. To do this, you must replace the instruction in their Dockerfile. FROM on my FROM rnix /openssl-gost AS openssl-gost , then prescribe copying of the assembled openssl and curl before starting the assembly of the php itself, and finally, specify the path to these libraries in the build option. --with-openssl-dir = /usr /local /ssl and --with-curl = /usr /local /curl replacing the original.
 
Surprises waited from everywhere. One of the significant things was that when you build php 7.? you use pkg-config, and the explicit libcurl4-openssl-dev installation, libssl-dev, prescribes the packages from pkg-config. As a result, it was not what was needed. If you remove their installation, then /configure php fell, referring to the lack of openssl in pkg-config. It was necessary to additionally copy from openssl and curl assemblies their lib /pkgconfig /* . After dozens of such surprises, the assembly began to pass. It later became clear that the dependencies were installed by openssl, thereby overwriting the previously copied binaries of my custom build. I had to copy it at the very end. But that's not all.
 
In the assembled php there appeared algorithms of hashes openssl_get_md_methods () : GOST R ???-2012 with 256 bit hash , GOST R ???-2012 with 512 bit hash , GOST R ???-94 . This meant that php connected the GOST-engine. But the use of curl extension in php for some reason said that such algorithms do not know, connecting with the host on GOST-HTTPS. I spent several hours trying to find the reason for this. I looked at the source code, how the curl extension works in php, how curl itself works, how they communicate with openssl. I was looking for a place where supported algorithms can be defined or engines can be connected. I searched for master-branches, a lot of google, but anything that would solve the problem immediately did not find. Then I remembered that I was not collecting the latest version of PHP.
 
I tried to build PHP-FPM 7.2. I had to make some changes to my script adjusting the original Dockerfile PHP-FPM, but the assembly began to take place without a lot of surprises. The main news was that now the extension curl inside php skillfully communicate according to GOST algorithms with hosts, but there was one trouble. Each call to php was written in stdout GOST engine is already loaded . Very unpleasant. I did not immediately understand who is doing this, but this line is found in the sources of the GOST-engine. I still do not know which part of the system the error occurred: php, php-curl, curl, openssl. But, apparently, in php 7.1 php-curl did not connect the engine at all, now in php 7.2 it started to connect it twice. Since everything worked correctly, only had a conclusion, I decided to remove it by editing the source code with the Dockerfile:


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

 

There is already a line below goto end; , so I did nothing serious. But, after rebuilding the entire zoo, everything began to work, as desired.
 
The image with PHP-FPM + OpenSSL + GOST + cURL is launched in the Docker Hub: https://hub.docker.com/r/rnix/php-fpm-gost/


 

Support for GOST certificates in nginx


 

The ability to work from programming languages ​​is already a lot, but I wanted two more possibilities:


 
  1.  
  2. It is easy to raise your web server with a GOST certificate by HTTPS (TLS).  
  3. It is easy to proxy all requests for a host with GOST certificate  

 

Easy - it means a docker image. It needs to be created. To do this, you need to compile nginx in the Dockerfile with the custom OpenSSL and GOST-engine. Opening the assembly documentation nginx, I saw one option about ssl - --with-http_ssl_module , which is simply boolean. But nginx is a popular product, assembly instructions with openssl are many, so I found another option --with-openssl =[DIR] . As practice shows, nginx wants to have the sources of openssl here, and the nginx scripts themselves will build themselves. This is not exactly what I would like (I wanted to use multi-stage build). I saw the help-output of the nginx compiler, but I did not find anything that would help me there.
 
I had to duplicate the instructions for downloading the sources of OpenSSL, unpacking, building GOST-engine, including GOST-engine in configs. All this began to be collected, but there was still no support for GOST algorithms in nginx. I checked this with the indication in the config ssl_ciphers GOST2001-GOST89-GOST89: HIGH: MEDIUM; . Running nginx -t said that he does not know this algorithm.
 
As it turned out, openssl, compiled by nginx, did not support dynamic engines, i.e. ./Configure output no-dynamic-engine[forced] . Here we had to carefully read the assembly documentation OpenSSL to find out what put. forced . The reason was found in arguments of the assembly openssl, which called nginx, namely no-shared . If this is specified, then there are no flags to enable support for loading engines. I had to edit the assembly instruction:


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

 

All this was assembled, but nginx began to swear that he could not find gost.so on the way /usr /lib /x86_64-linux-gnu / , this is rather strange, because the same collected openssl searches for and finds engines in a completely different place, namely, where was going. ./lib/engines-???r3r3337. . Added a copy instruction for the assembled engines in /usr /lib /x86_64-linux-gnu / to please nginx. Has earned.
 
Next to the main Dockerfile in the repository, I put demo Dockerfile , which, when assembled, creates GOST-certificates for itself and uses them, processing connections on https://gost.example.com . I'll have to work with the DNS or docker network to try to connect to this demonstration container from one container, but I described all this in documentation .
 
The demo host uses the keys to gost2001 , other options are gost2012_256 , gost2012_512 . And instead of GOST2001-GOST89-GOST89 - GOST2012-GOST8912-GOST8912 .
 
The image with nginx + GOST is launched in the Docker Hub: https://hub.docker.com/r/rnix/nginx-gost/


 

Conclusion


 

I have studied the problem of working with GOST algorithms in Linux systems, provided a solution in the form of docker-images, all this is accompanied by documentation and examples. The solution is in the form of a repository on GitHub.
 
It is worth mentioning the safety of using such a solution. The main thing is not to trust the images on the Docker Hub, even if it says Automated Build . I can still compile an image with any edits of all used libraries and systems and run it into my Docker Hub under any tag. Therefore, I recommend that you forge the repository on the githab, shoot it yourself and collect it yourself, checking the instructions in the Dockerfile for the fact that only official resources are used without any suspicious modification during the build process.
 
Collecting the image yourself, you can make sure that malicious changes are not included in the code, because the assembly is only from open source code, which is available for viewing by everyone. However, this does not guarantee that there are no errors and vulnerabilities in it. Using proprietary certified tools also does not guarantee the absence of errors and vulnerabilities, but also their code is closed from you.
 
References
 
 
Repository with all solutions on GitHub
 
Image on Docker Hub with OpenSSL + GOST + cURL
 
GOST R ???-2012 on Wikipedia with a list of certified solutions
 
GOST-engine repository
 
On the possibilities and limitations of GOST-engine
 
An example of how to solve the problem in the production environment
 

+ 0 -

Add comment