Nginx自带的索引功能过于羸弱。好在江湖上还有FancyIndex来替代,这个模块已经获得了官方的认可,可以放心使用。

下面演示在Debian Buster上如何安装FancyIndex。

对于RPM系Linux,模块作者已经提供了打包好的RPM包,可以直接通过yum安装。

通过apt-get获取nginx源码。

目前的FancyIndex支持编译成动态模块,也就意味着,我们可以直接使用apt安装的nginx,只需要将编译出FancyIndex动态模块载入即可。为了保证编译时使用的源码版本和deb包的nginx版本一致,我们通过apt-get获取源码,也可以自行查看版本,去nginx官方下载对应版本。

apt source nginx

下载的源码保存在/usr/local/src目录下,并且为我们自动解压了。我们进入到对应目录下,下载FancyIndex的源码。

cd /usr/local/src
wget https://github.com/aperezdc/ngx-fancyindex/archive/refs/tags/v0.5.2.tar.gz
tar xzvf v0.5.2.tar.gz

Nginx为了保证模块的兼容性,所以会对模块和Nginx的签名进行检查,这个签名取决于编译时的参数,启用了哪些Nginx的功能。

最简单的办法是使用和原本的Nginx完全一样的编译参数进行编译。可以通过nginx -V查看编译参数。


root@xui:~# nginx -V
nginx version: nginx/1.14.2
built with OpenSSL 1.1.1n  15 Mar 2022
TLS SNI support enabled
configure arguments: --with-cc-opt='-g -O2 -fdebug-prefix-map=/build/nginx-FlcIR2/nginx-1.14.2=. -fstack-protector-strong -Wformat -Werror=format-security -fPIC -Wdate-time -D_FORTIFY_SOURCE=2' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -fPIC' --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-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-stream_ssl_preread_module --with-mail=dynamic --with-mail_ssl_module --add-dynamic-module=/build/nginx-FlcIR2/nginx-1.14.2/debian/modules/http-auth-pam --add-dynamic-module=/build/nginx-FlcIR2/nginx-1.14.2/debian/modules/http-dav-ext --add-dynamic-module=/build/nginx-FlcIR2/nginx-1.14.2/debian/modules/http-echo --add-dynamic-module=/build/nginx-FlcIR2/nginx-1.14.2/debian/modules/http-upstream-fair --add-dynamic-module=/build/nginx-FlcIR2/nginx-1.14.2/debian/modules/http-subs-filter

可以用完全一样的,但是这样的话,需要很多的依赖,实际上可以适当进行精简。签名的作用是保证Nginx主程序包含动态模块所需要的功能,并且我们只需要最终的模块,既然如此,那么编译参数中的和nginx有关的配置,比如路径,以及其他的动态模块实际上就不是必要的了。

最终我精简得到的结果是下面这样的。注意,不同系统以及版本可能有所不同,可以先尝试一下。关于签名在最后解释是如何进行验证的。

./configure \
    --with-http_ssl_module \
    --with-http_realip_module \
    --with-http_auth_request_module \
    --with-http_dav_module \
        --with-threads \
    --add-dynamic-module=../ngx-fancyindex-0.5.2

configure可能会报一些依赖错误,这里我把需要的依赖全部都找出来了。

apt install libssl-dev libpcre++-dev zlib1g-dev

万事俱备,进行编译。

make modules

安装模块,修改Nginx配置文件,载入模块。

install -D objs/ngx_http_fancyindex_module.so /usr/share/nginx/modules
echo "load_module modules/ngx_http_fancyindex_module.so;" > /usr/share/nginx/modules-available/mod_fancyindex.conf
ln -s /usr/share/nginx/modules-available/mod_fancyindex.conf /etc/nginx/modules-enabled/50-mod_fancyindex.conf
nginx -t
nginx -s reload

最后在需要使用文件索引的地方,将原本的autoindex改成fancyindex即可。

location / {
  fancyindex on;
  fancyindex_exact_size off;
}

更多的使用方法,查看源码说明:https://github.com/aperezdc/ngx-fancyindex

补充:Nginx模块兼容性的问题

如果在编译模块的时候,没有带上特定的功能,那么你在载入模块的时候吧,会遇到二进制不兼容的错误。此报错源于源码src/core/ngx_module.c中,相关片段如下:

   if (ngx_strcmp(module->signature, NGX_MODULE_SIGNATURE) != 0) {
        ngx_conf_log_error(NGX_LOG_EMERG, cf, 0,
                           "module \"%V\" is not binary compatible",
                           file);
        return NGX_ERROR;
    }

这里的NGX_MODULE_SIGNATURE宏定义在src/core/ngx_module.h中,我们可以看到相关的定义。

#define NGX_MODULE_SIGNATURE_0                                                \
    ngx_value(NGX_PTR_SIZE) ","                                               \
    ngx_value(NGX_SIG_ATOMIC_T_SIZE) ","                                      \
    ngx_value(NGX_TIME_T_SIZE) ","

#if (NGX_HAVE_KQUEUE)
#define NGX_MODULE_SIGNATURE_1   "1"
#else
#define NGX_MODULE_SIGNATURE_1   "0"
#endif

#if (NGX_HAVE_IOCP)
#define NGX_MODULE_SIGNATURE_2   "1"
#else
#define NGX_MODULE_SIGNATURE_2   "0"
#endif

#if (NGX_HAVE_FILE_AIO || NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_3   "1"
#else
#define NGX_MODULE_SIGNATURE_3   "0"
#endif

#if (NGX_HAVE_AIO_SENDFILE || NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_4   "1"
#else
#define NGX_MODULE_SIGNATURE_4   "0"
#endif

#if (NGX_HAVE_EVENTFD)
#define NGX_MODULE_SIGNATURE_5   "1"
#else
#define NGX_MODULE_SIGNATURE_5   "0"
#endif

#if (NGX_HAVE_EPOLL)
#define NGX_MODULE_SIGNATURE_6   "1"
#else
#define NGX_MODULE_SIGNATURE_6   "0"
#endif

#if (NGX_HAVE_KEEPALIVE_TUNABLE)
#define NGX_MODULE_SIGNATURE_7   "1"
#else
#define NGX_MODULE_SIGNATURE_7   "0"
#endif

#if (NGX_HAVE_INET6)
#define NGX_MODULE_SIGNATURE_8   "1"
#else
#define NGX_MODULE_SIGNATURE_8   "0"
#endif

#define NGX_MODULE_SIGNATURE_9   "1"
#define NGX_MODULE_SIGNATURE_10  "1"

#if (NGX_HAVE_DEFERRED_ACCEPT && defined SO_ACCEPTFILTER)
#define NGX_MODULE_SIGNATURE_11  "1"
#else
#define NGX_MODULE_SIGNATURE_11  "0"
#endif

#define NGX_MODULE_SIGNATURE_12  "1"

#if (NGX_HAVE_SETFIB)
#define NGX_MODULE_SIGNATURE_13  "1"
#else
#define NGX_MODULE_SIGNATURE_13  "0"
#endif

#if (NGX_HAVE_TCP_FASTOPEN)
#define NGX_MODULE_SIGNATURE_14  "1"
#else
#define NGX_MODULE_SIGNATURE_14  "0"
#endif

#if (NGX_HAVE_UNIX_DOMAIN)
#define NGX_MODULE_SIGNATURE_15  "1"
#else
#define NGX_MODULE_SIGNATURE_15  "0"
#endif

#if (NGX_HAVE_VARIADIC_MACROS)
#define NGX_MODULE_SIGNATURE_16  "1"
#else
#define NGX_MODULE_SIGNATURE_16  "0"
#endif

#define NGX_MODULE_SIGNATURE_17  "0"
#define NGX_MODULE_SIGNATURE_18  "0"

#if (NGX_HAVE_OPENAT)
#define NGX_MODULE_SIGNATURE_19  "1"
#else
#define NGX_MODULE_SIGNATURE_19  "0"
#endif

#if (NGX_HAVE_ATOMIC_OPS)
#define NGX_MODULE_SIGNATURE_20  "1"
#else
#define NGX_MODULE_SIGNATURE_20  "0"
#endif

#if (NGX_HAVE_POSIX_SEM)
#define NGX_MODULE_SIGNATURE_21  "1"
#else
#define NGX_MODULE_SIGNATURE_21  "0"
#endif

#if (NGX_THREADS || NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_22  "1"
#else
#define NGX_MODULE_SIGNATURE_22  "0"
#endif

#if (NGX_PCRE)
#define NGX_MODULE_SIGNATURE_23  "1"
#else
#define NGX_MODULE_SIGNATURE_23  "0"
#endif

#if (NGX_HTTP_SSL || NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_24  "1"
#else
#define NGX_MODULE_SIGNATURE_24  "0"
#endif

#define NGX_MODULE_SIGNATURE_25  "1"

#if (NGX_HTTP_GZIP)
#define NGX_MODULE_SIGNATURE_26  "1"
#else
#define NGX_MODULE_SIGNATURE_26  "0"
#endif

#define NGX_MODULE_SIGNATURE_27  "1"

#if (NGX_HTTP_X_FORWARDED_FOR)
#define NGX_MODULE_SIGNATURE_28  "1"
#else
#define NGX_MODULE_SIGNATURE_28  "0"
#endif

#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

#if (NGX_COMPAT)
#define NGX_MODULE_SIGNATURE_34  "1"
#else
#define NGX_MODULE_SIGNATURE_34  "0"
#endif

#define NGX_MODULE_SIGNATURE                                                  \
    NGX_MODULE_SIGNATURE_0 NGX_MODULE_SIGNATURE_1 NGX_MODULE_SIGNATURE_2      \
    NGX_MODULE_SIGNATURE_3 NGX_MODULE_SIGNATURE_4 NGX_MODULE_SIGNATURE_5      \
    NGX_MODULE_SIGNATURE_6 NGX_MODULE_SIGNATURE_7 NGX_MODULE_SIGNATURE_8      \
    NGX_MODULE_SIGNATURE_9 NGX_MODULE_SIGNATURE_10 NGX_MODULE_SIGNATURE_11    \
    NGX_MODULE_SIGNATURE_12 NGX_MODULE_SIGNATURE_13 NGX_MODULE_SIGNATURE_14   \
    NGX_MODULE_SIGNATURE_15 NGX_MODULE_SIGNATURE_16 NGX_MODULE_SIGNATURE_17   \
    NGX_MODULE_SIGNATURE_18 NGX_MODULE_SIGNATURE_19 NGX_MODULE_SIGNATURE_20   \
    NGX_MODULE_SIGNATURE_21 NGX_MODULE_SIGNATURE_22 NGX_MODULE_SIGNATURE_23   \
    NGX_MODULE_SIGNATURE_24 NGX_MODULE_SIGNATURE_25 NGX_MODULE_SIGNATURE_26   \
    NGX_MODULE_SIGNATURE_27 NGX_MODULE_SIGNATURE_28 NGX_MODULE_SIGNATURE_29   \
    NGX_MODULE_SIGNATURE_30 NGX_MODULE_SIGNATURE_31 NGX_MODULE_SIGNATURE_32   \
    NGX_MODULE_SIGNATURE_33 NGX_MODULE_SIGNATURE_34

最终生成的签名长这样:8,4,8,0011111111010111001111111111111111。
根据这些定义和签名,我们其实可以判断出需要添加哪些编译参数。所以如何获取到这个签名呢?

我通过objdump这个工具,对nginx进行反编译,得到汇编代码,字符串常量存放于.rodata片段,通过一些模糊的查找,不难得出完整的签名。

root@xui:~# objdump -j .rodata -s /usr/sbin/nginx |grep -n 8,4,8

300: d0270 382c342c 382c3030 30303131 31313131  8,4,8,0000111111
1696: d59b0 696f6e5f 69645f63 6f6e7465 78742829  ion_id_context()

root@xui:~# objdump -j .rodata -s /usr/sbin/nginx |sed -n "300,+2p"

 d0270 382c342c 382c3030 30303131 31313131  8,4,8,0000111111
 d0280 30313031 31313030 31313131 31313131  0101110011111111
 d0290 31313131 31313130 00000000 7926f5ff  11111110....y&..

参考一些文章:

https://www.cnblogs.com/weifeng1463/p/15536573.html
https://blog.csdn.net/zhangge3663/article/details/108753118
https://developer.aliyun.com/article/780347

写在最后

其实到最后我才发现我做了一个无用功,Github源码中只提及了rpm包,我便以为debian没有提供,回过头来想,debian在国外比redhat系要流行的多,也开放很多,人家的官方仓库已经收录了这个模块,可以直接通过包管理工具进行安装。

apt install nginx-extras

在这个过程中学到了很多,也不算浪费时间。

最后修改:2023 年 08 月 02 日
如果觉得我的文章对你有用,请随意赞赏