专栏名称: 狗厂
目录
相关文章推荐
Sir电影  ·  我们都追过的9.1,慢慢消亡 ·  昨天  
谷饶生活信息站  ·  潮汕电影《夏雨来》有新动向了… ·  昨天  
51好读  ›  专栏  ›  狗厂

用 Nginx 的 auth_request 模块集成 LDAP 认证

狗厂  · 掘金  ·  · 2018-06-14 02:29

正文

前言

很多时候,我们需要给一些本没有身份认证功能的业务增加一个认证模块。

  • 比如免费版的 ELK,Kibana 上是没有身份认证的;
  • 比如 0.1 版的 Open-Falcon,Dashboard 上也是没有认证的;
  • 又或者一些本来对外公开的网站,突然在某些特殊的日子,在某些特殊的时间里,不希望对外公开了。。。

直接修改业务的侵入式方案通常不太容易,非侵入式的方案一般也能实现类似的效果,比如给他增加一个代理然后做 http basic 认证。

这是一个好办法,但是 http basic 认证毕竟太简单了,也不方便集成外部的认证源,比如 LDAP

所以一个更灵活的方案是通过 Nginx 的 auth_request 模块

Nginx 的 auth_request 模块

auth_request 大抵就是在你访问 Nginx 中受 auth_reuqest 保护的路径时,去请求一个特定的服务。根据这个服务返回的状态码,auth_request 模块再进行下一步的动作,允许访问或者重定向跳走什么的。因此我们可以在上面去定制我们所有个性化的需求。

假定我们的环境是 centos ,yum 安装 nginx 就略了。由于通过 yum 等安装的 nginx 默认没有编译 auth_request 模块。我们需要重新编译一下。

先运行 nginx -V 来获取当前 nginx 的编译参数

# nginx -V
nginx version: nginx/1.14.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-16) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie'

先安装一些依赖

yum -y install gcc gcc-c++ autoconf automake make
yum -y install zlib zlib-devel openssl 
yum -y install openssl-devel pcre pcre-devel
yum -y install libxslt-devel
yum -y install redhat-rpm-config
yum -y install gd-devel
yum -y install perl-devel perl-ExtUtils-Embed
yum -y install geoip-devel
yum -y install gperftools-devel

然后下载 nginx 源代码 ,用刚才得到的编译参数,增加 --with-http_auth_request_module 参数重新编译

# ./configure --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --with-http_auth_request_module

# make

# make install

nginx -V 看一下,已经带上 http_auth_request_module

# nginx -V
nginx version: nginx/1.14.0
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-28) (GCC) 
built with OpenSSL 1.0.2k-fips  26 Jan 2017
TLS SNI support enabled
configure arguments: --prefix=/etc/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --http-client-body-temp-path=/var/cache/nginx/client_temp --http-proxy-temp-path=/var/cache/nginx/proxy_temp --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp --http-scgi-temp-path=/var/cache/nginx/scgi_temp --user=nginx --group=nginx --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module --with-http_secure_link_module --with-http_slice_module --with-http_ssl_module --with-http_stub_status_module --with-http_sub_module --with-http_v2_module --with-mail --with-mail_ssl_module --with-stream --with-stream_realip_module --with-stream_ssl_module --with-stream_ssl_preread_module --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -m64 -mtune=generic -fPIC' --with-ld-opt='-Wl,-z,relro -Wl,-z,now -pie' --with-http_auth_request_module

一个简单 demo

nginx.inc 给了一个非常简单的 demo,即 nginx-auth-ldap 这个 repo。

整个逻辑就如下图所示

image.png

详细的流程图大抵如下所示:


image.png
  1. 客户端发送 HTTP 请求,以获取 Nginx 上反向代理的受保护资源。

  2. Nginx 的 auth_request 模块 将请求转发给 ldap-auth 这个服务(对应 nginx-ldap-auth-daemon.py),首次肯定会给个 401 .

  3. Nginx 将请求转发给 http:// backend / login ,后者对应于这里的后端服务。它将原始请求的 uri 写入 X-Target ,以便于后面跳转。

  4. 后端服务向客户端发送登录表单(表单在 demo 代码中定义)。根据 error_page 的配置,Nginx 将登录表单的 http 状态码返回 200。

  5. 用户填写表单上的用户名和密码字段并单击登录按钮,从向 / login 发起 POST 请求,Nginx 将其转发到后端的服务上。

  6. 后端服务把用户名密码以 base64 方式写入 cookie。

  7. 客户端重新发送其原始请求(来自步骤1),现在有 cookie 了 。Nginx 将请求转发给 ldap-auth 服务(如步骤2所示)。

  8. ldap-auth 服务解码 cookie,然后做 LDAP 认证。

  9. 下一个操作取决于 LDAP 认证是否成功:

    • 如果认证成功,则 ldap-auth 服务给 Nginx 返回状态码 200。Nginx 从后端服务中请求资源。在 demo 里,后端服务返回以下文本:
         Hello, world! Requested URL: URL
      
    • 如果认证失败, ldap-auth 服务会返回 401 。Nginx 再次将请求转发给后端服务的 Login (如步骤3),并重复该过程。

Demo 测试

先安装下依赖

yum install python-ldap

然后把 repo clone 下来

#git clone https://github.com/nginxinc/nginx-ldap-auth.git
# ls
backend-sample-app.py  Dockerfile  nginx-ldap-auth.conf              nginx-ldap-auth-daemon-ctl.sh  nginx-ldap-auth.default    nginx-ldap-auth.service  rpm
debian                 LICENSE     nginx-ldap-auth-daemon-ctl-rh.sh  nginx-ldap-auth-daemon.py      nginx-ldap-auth.logrotate  README.md

这其中 nginx-ldap-auth.conf 是 Nginx 的配置范例,直接 copy 过去即可

# cp nginx-ldap-auth.conf /etc/nginx/nginx.conf

Nginx 的配置文件如下,做了些精简,加了中文注释。

error_log logs/error.log debug;
# 这里把日志放在 nginx 目录下,所以要么改掉要么在 nginx 目录下建个 log 目录
events { }

http {
    # cache 路径和大小
    proxy_cache_path cache/  keys_zone=auth_cache:10m;

    # 将要被 nginx auth_request 保护的 backend 
    # 在这个 demo 里是 backend-sample-app.py.
    upstream backend {
        server 127.0.0.1:9000;
    }

    # nginx 服务起在 8081 上
    server {
        listen 8081;

        # 这个路径被 auth_request 保护了,  401 重定向到 login 上
        location / {
            auth_request /auth-proxy;

            # redirect 401 to login form
            error_page 401 =200 /login;

            proxy_pass http://backend/;
        }
        # 这里是我们认证的页面
        location /login {
            proxy_pass http://backend/login;
            # 这个 X-Target 是给认证完以后重定向的
            proxy_set_header X-Target $request_uri;
        }
        # 这是用做 auth_request 请求的路径
        location = /auth-proxy {
            internal;
            # 提供 ldap 认证服务的 auth-proxy backend
            # 这个 demo 里是 nginx-ldap-auth-daemon.py.
            proxy_pass http://127.0.0.1:8888;

            proxy_pass_request_body off;
            proxy_set_header Content-Length "";
            proxy_cache auth_cache;
            proxy_cache_valid 200 10m;

            # cookie 会加在这里
            proxy_cache_key "$http_authorization$cookie_nginxauth";

            # ldap 的地址
            proxy_set_header X-Ldap-URL      "ldap://ldap.example.org";

            # 是否开启 starttls
            # 注意 starttls 不能和 tls,也就是 ldaps 同时开启
            #proxy_set_header X-Ldap-Starttls "true";

            # ldap 的  BaseDN
            proxy_set_header X-Ldap-BaseDN   "dc=example,dc=org";

            # ldap 的 binddn,也就是有查询权限的账号
            proxy_set_header X-Ldap-BindDN   "cn=manager,dc=example,dc=org";

            # binddn 的密码
            proxy_set_header X-Ldap-BindPass "password";

            # cookie 的名字和值
            proxy_set_header X-CookieName "nginxauth";
            proxy_set_header Cookie nginxauth=$cookie_nginxauth;

            # ldap 的 searchFilter,就是拿哪个字段作为认证的用户名
            proxy_set_header X-Ldap-Template "(uid=%(username)s)";
        }
    }
}

然后分别执行 ./nginx-ldap-auth-daemon.py ./backend-sample-app.py 即可。







请到「今天看啥」查看全文