Construyendo Módulos Dinámicos para Nginx

imagen


Recientemente, creamos un módulo dinámico para Nginx, y cuando todo estaba listo, resultó que nuestro módulo no era compatible con Nginx, que ya estaba instalado en el servidor. No pudimos encontrar una solución preparada para el problema y comenzamos a luchar por nuestra cuenta. Pasamos mucho tiempo, pero obtuvimos una nueva experiencia y, lo más importante, una solución de trabajo. Que me gustaría compartir.


Comencemos con una descripción del proceso de ensamblaje del módulo dinámico usando el ejemplo https://github.com/vozlt/nginx-module-vts . Y luego mostraremos qué error ocurre y qué hacer con él.


Datos de entrada:
Sistema operativo Debian 9
Nginx v1.10.3 del repositorio estándar de Debian. Salida nginx -V :


 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 

El resultado de este comando contiene información que es necesaria para el ensamblaje de módulos dinámicos, es decir, todo lo que se escribe después de configure arguments: y antes del primero - --add-dynamic-module , es decir:


 --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 

Entonces, procedemos al ensamblaje del módulo:


  • Para el ensamblaje, es conveniente usar un contenedor acoplable para no saturar el sistema principal, tome la imagen debian: 9 como base, para la conveniencia de copiar el módulo terminado del contenedor, puede especificar el volumen. El comando para iniciar el contenedor en este caso se verá así:

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

  • Después de iniciar el contenedor, instale los paquetes necesarios. Tenga en cuenta que los paquetes se seleccionan de acuerdo con la salida del comando nginx -V, por lo que es posible que no necesite algunas de las bibliotecas. En cualquier caso, el script de configuración le advertirá sobre la ausencia de dependencias:

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

Falta el paquete libssl-dev en esta lista, porque el nginx instalado en el servidor está construido con la versión 1.1.0k de OpenSSL, y al momento de la escritura, al instalar el paquete libssl-dev desde el repositorio, la versión de OpenSSL ya era 1.1.0l. Por lo tanto, el código fuente de OpenSSL para la versión correcta debe descargarse por separado.



 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 

  • Ahora está listo para ejecutar el script de configuración. Especificamos todos los parámetros de la salida del comando nginx -V como los parámetros del script, sobre los que escribí al principio del artículo. También agregamos el parámetro --add-dynamic-module para construir el módulo nginx-module-vts y especificamos la ruta al directorio con los archivos fuente de OpenSSL a través del parámetro --with-openssl. El comando final se verá así:

 ./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/ 

  • Una vez que el script de configure termina de funcionar, comenzamos el ensamblaje de módulos; no necesitamos recopilar todos los nginx :

 make modules 

Tenga en cuenta que este comando solo está disponible con nginx versión 1.9.13.


  • Una vez completado el proceso de ensamblaje, copie el archivo resultante en el volume , que se montó cuando se inició el contenedor:

 cp objs/ngx_http_vhost_traffic_status_module.so /nginx_module/ 

  • A continuación, colocamos el archivo en el servidor de destino, en este caso, el directorio con los módulos se encuentra en la ruta /usr/share/nginx/modules . Para habilitar el módulo en nginx creamos el archivo /etc/nginx/modules-enabled/mod-http-vhost-traffic-status.conf con los siguientes contenidos:

 load_module modules/ngx_http_vhost_traffic_status_module.so; 

Y cree un enlace simbólico a este archivo en el directorio /etc/nginx/modules-enabled .


Después del trabajo realizado, llamamos al comando nginx -t y ... obtenemos el 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 

Y aquí suele surgir cierta confusión, porque la versión de origen y la versión nginx instalada en el servidor son las mismas, las versiones de OpenSSL también, los parámetros para el script de configure también se copian en su totalidad. Entonces, ¿cuál es el trato?


Para comprender este problema, tenía que entender cómo nginx comprueba si el módulo dinámico es adecuado para él. El código que se encuentra en las fuentes nginx en el src/core/ngx_module.h ( https://github.com/nginx/nginx/blob/master/src/core/ngx_module.h ) es responsable de esta verificación. Hay una serie de comprobaciones en este archivo, en este caso 34, durante las cuales nginx establece las variables de la forma NGX_MODULE_SIGNATURE_0 (1,2,3, etc.)
valores 1 o 0. Luego está la variable NGX_MODULE_SIGNATURE
que recopila los resultados de todos los controles en una línea. En consecuencia, el problema es que la cadena de firma del nuevo módulo no coincide con la cadena de firma del nginx existente. Los siguientes comandos se pueden usar para verificar los valores de las cadenas de firma:


  • La línea de firma del módulo que se creó:

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

  • Línea de firma del archivo binario nginx instalado en el servidor:

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

Al comparar estas líneas a simple vista, está claro que en la línea de firma del módulo, la cuarta variable desde el final resultó ser 0, mientras que nginx tiene 1. Para comprender qué es exactamente lo que falta en el módulo, debe src/core/ngx_module.h y encuentra la cuarta variable desde el final, se ve más o menos así:


 #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 

Estamos interesados ​​en la variable NGX_HTTP_HEADERS , al construir nginx era 1 y al construir el módulo 0. Para que el módulo se NGX_HTTP_HEADERS con NGX_HTTP_HEADERS debe ajustar el archivo de configuración en el directorio con el código fuente del módulo agregando la línea have=NGX_HTTP_HEADERS . auto/have have=NGX_HTTP_HEADERS . auto/have al comienzo del archivo de config. El siguiente es el comienzo del archivo de configuración después de realizar los cambios:


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

A continuación, make clean y luego reinicie configure y make modules . Después de ensamblar el módulo, verificamos su cadena de firma y vemos que ahora coincide con la cadena nginx :


 $ 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 

Después de eso, el módulo se conecta sin ningún problema y puede recopilar estadísticas avanzadas con nginx .


En su caso, tal vez la línea de firma se verá diferente, así como las variables que son responsables de cada posición en esta línea. Sin embargo, ahora puede comprender qué es exactamente lo que está mal con el ensamblaje del módulo y solucionar el problema.


Espero que este artículo le ahorre a alguien unas pocas horas de tiempo, ya que personalmente varias veces enfrenté el problema de que el módulo no se ajustaba a nginx , como resultado, por lo general, lo resolví a través del ensamblaje completo de nginx junto con el módulo correcto.


Lea también otros artículos en nuestro blog:


Source: https://habr.com/ru/post/473578/


All Articles