Building Dynamic Modules for Nginx

image







Recently, we built a dynamic module for Nginx, and when everything was ready, it turned out that our module was not compatible with Nginx, which was already installed on the server. We could not find a ready-made solution to the problem and we began to fight it on our own. We spent a lot of time, but got a new experience and, most importantly, a working solution. Which I would like to share.







Let's start with a description of the dynamic module assembly process using the example https://github.com/vozlt/nginx-module-vts . And then we will show what error occurs and what to do with it.







Inputs:

Debian 9 OS

Nginx v1.10.3 from the standard Debian repository. nginx -V



output:







 nginx version: nginx/1.10.3 built with OpenSSL 1.1.0k 28 May 2019 (running with OpenSSL 1.1.1c 28 May 2019) TLS SNI support enabled configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/ nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path =/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module - -with-http_stub_status_module --with-http_realip_mod ule --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module= dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-auth-pam --add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-dav-ext-module --add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-echo --add-dynamic-module=/build/nginx-DhOtPd/nginx-1.10.3/debian/modules/nginx-upstream-fair --add-dynamic-module=/build/nginx-DhOtPd/nginx -1.10.3/debian/modules/ngx_http_substitutions_filter_module
      
      





The output of this command contains information that is necessary to build dynamic modules, namely, everything that is written after configure arguments:



and before the first - --add-dynamic-module



, that is:







 --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/ nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path =/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_mod ule --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module= dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module
      
      





So, we proceed to the assembly of the module:









 docker run --rm -it -v /tmp/nginx_module:/nginx_module debian:9 bash
      
      







 apt update apt install git make gcc autoconf wget libpcre3-dev libpcre++-dev zlib1g-dev libxml2-dev libxslt-dev libgd-dev libgeoip-dev
      
      





The libssl-dev package is missing from this list, because the nginx installed on the server is built with the version 1.1.0k of OpenSSL, and at the time of writing when installing the libssl-dev package from the repository, the version of OpenSSL in it was already 1.1.0l. Therefore, the OpenSSL source code for the correct version must be downloaded separately.









 cd /usr/local/src/ wget http://nginx.org/download/nginx-1.10.3.tar.gz wget https://www.openssl.org/source/old/1.1.0/openssl-1.1.0k.tar.gz git clone git://github.com/vozlt/nginx-module-vts.git tar xvfz nginx-1.10.3.tar.gz tar xzvf openssl-1.1.0k.tar.gz cd nginx-1.10.3
      
      







 ./configure --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-DhOtPd/nginx-1.10.3=. -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now' --prefix=/usr/share/nginx --conf-path=/etc/nginx/nginx.conf --http-log-path=/var/log/nginx/access.log --error-log-path=/var/log/nginx/error.log --lock-path=/var/lock/nginx.lock --pid-path=/run/nginx.pid --modules-path=/usr/lib/nginx/modules --http-client-body-temp-path=/var/lib/nginx/body --http-fastcgi-temp-path=/var/lib/nginx/fastcgi --http-proxy-temp-path=/var/lib/nginx/proxy --http-scgi-temp-path=/var/lib/nginx/scgi --http-uwsgi-temp-path=/var/lib/nginx/uwsgi --with-debug --with-pcre-jit --with-ipv6 --with-http_ssl_module --with-http_stub_status_module --with-http_realip_module --with-http_auth_request_module --with-http_v2_module --with-http_dav_module --with-http_slice_module --with-threads --with-http_addition_module --with-http_geoip_module=dynamic --with-http_gunzip_module --with-http_gzip_static_module --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_xslt_module=dynamic --with-stream=dynamic --with-stream_ssl_module --with-mail=dynamic --with-mail_ssl_module --with-openssl=../openssl-1.1.0k/ --add-dynamic-module=../nginx-module-vts/
      
      







 make modules
      
      





Note that this command is only available with nginx



version 1.9.13.









 cp objs/ngx_http_vhost_traffic_status_module.so /nginx_module/
      
      







 load_module modules/ngx_http_vhost_traffic_status_module.so;
      
      





And create a symbolic link to this file in the /etc/nginx/modules-enabled



directory.







After the work done, we call the nginx -t



command and ... we get the error:







 nginx: [emerg] module "/usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so" is not binary compatible in /etc/nginx/modules-enabled/50-mod-http-vhost-traffic-status.conf:1
      
      





And here, usually, there is some bewilderment, because the source versions and the nginx



version installed on the server are the same, the OpenSSL versions too, the parameters for the configure



script are also copied entirely. So what's the deal?







To understand this problem, I had to understand how nginx checks if the dynamic module is suitable for it. The code that is located in the nginx



sources in the src/core/ngx_module.h



( https://github.com/nginx/nginx/blob/master/src/core/ngx_module.h ) is responsible for this check. There are a number of checks in this file, in this case 34, during which nginx sets for variables of the form NGX_MODULE_SIGNATURE_0



(1,2,3, etc.)

values ​​1 or 0. Next is the variable NGX_MODULE_SIGNATURE





which collects the results of all checks in one line. Accordingly, the problem is that the signature string of the new module does not match the signature string of the existing nginx. To check the values ​​of signature strings, you can use the following commands:









 strings /usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so| fgrep '8,4,8' 8,4,8,000011111101011111111111110110111
      
      







 strings /usr/sbin/nginx| fgrep '8,4,8' 8,4,8,000011111101011111111111110111111
      
      





When comparing these lines with the naked eye, it can be seen that in the module signature line, the fourth variable from the end turned out to be 0, while nginx



has 1. It is necessary to refer to the src/core/ngx_module.h



file to understand what exactly is missing in the module src/core/ngx_module.h



and find the fourth variable from the end, it looks something like this:







 #if (NGX_HTTP_REALIP) #define NGX_MODULE_SIGNATURE_29 "1" #else #define NGX_MODULE_SIGNATURE_29 "0" #endif #if (NGX_HTTP_HEADERS) #define NGX_MODULE_SIGNATURE_30 "1" #else #define NGX_MODULE_SIGNATURE_30 "0" #endif #if (NGX_HTTP_DAV) #define NGX_MODULE_SIGNATURE_31 "1" #else #define NGX_MODULE_SIGNATURE_31 "0" #endif #if (NGX_HTTP_CACHE) #define NGX_MODULE_SIGNATURE_32 "1" #else #define NGX_MODULE_SIGNATURE_32 "0" #endif #if (NGX_HTTP_UPSTREAM_ZONE) #define NGX_MODULE_SIGNATURE_33 "1" #else #define NGX_MODULE_SIGNATURE_33 "0" #endif #define NGX_MODULE_SIGNATURE
      
      





We are interested in the variable NGX_HTTP_HEADERS



, when building nginx



it was 1, and when building the module 0. In order for the module to NGX_HTTP_HEADERS



with NGX_HTTP_HEADERS



you need to adjust the config file in the directory with the source code of the module by adding the line have=NGX_HTTP_HEADERS . auto/have



have=NGX_HTTP_HEADERS . auto/have



at the beginning of the config.



file config.



The following is the beginning of the config file after making the changes:







 ngx_addon_name=ngx_http_vhost_traffic_status_module have=NGX_STAT_STUB . auto/have have=NGX_HTTP_HEADERS . auto/have ...
      
      





Next, do make clean



, and then re-run configure



and make modules



. After assembling the module, we check its signature string and see that now it corresponds to the nginx



string:







 $ strings /usr/sbin/nginx| fgrep '8,4,8' 8,4,8,000011111101011111111111110111111 $ strings /usr/share/nginx/modules/ngx_http_vhost_traffic_status_module.so | fgrep '8,4,8' 8,4,8,000011111101011111111111110111111
      
      





After that, the module connects without any problems and you can collect advanced statistics with nginx



.







In your case, perhaps the signature line will look different, as well as the variables that are responsible for each position in this line. However, now you can understand what exactly is wrong with the assembly of the module and fix the problem.







I hope this article saves someone a few hours of time, since I personally several times faced the problem that the module did not fit nginx



, as a result, as a rule, I solved this through the full assembly of nginx



along with the desired module.







Also read other articles on our blog:






All Articles