问题背景
? 前段时间, 负责 ELK 那哥们儿想把 Kibana 调整成为 LDAP 内部用户认证, 运维这边了解到这个需求, 着手调研.
解决方案及思路
? 首先 Kibana 现在只是用了一个叫 Search guard 的插件来控制访问, 但是这个插件需要和 JIRA 用户统一认证, 需要手动开通账户, 维护成本高, 并且实施起来的效率也很低, 所以才打算搭建一个 LDAP 服务器, 然后同步 JIRA 用户信息, 实现 Kibana 统一认证.
? 经过讨论方案, 大致的初步方案就是通过结合 Nginx 的 auth_request 模块, 当用户请求一个受特定保护的资源时候, auth_request 会将请求转发到 LDAP 验证服务上, 然后根据验证服务返回的状态码决定重定向或者允许访问等进一步的操作. 其中在 Nginx 配置文件中的 LDAP 认证服务中需要完善 LDAP 服务器的配置信息. 大概的思路就是这样, 想到这里的时候, 又发现一个问题, 如何保证 LDAP 和 JIRA 用户能够及时同步呢, 并且各种权限的信息也需要相应的对接.
? 这时候负责 JIRA 的哥们儿, 说其实我们那边有个接口, 可以通过 POST 用户名和密码信息, 验证是否能够登录. 并且验证通过会返回一个有关用户信息的 JSON, 如果想要进行权限控制的话, 也可以通过这个 JSON. 所以现在事情就变得简单了, 重新简单梳理一下解决方案.
? 如下:
1, 用户请求受保护的 Nginx 反向代理资源
2,Nginx 的 auth_request 模块将请求转发给自己写的 django 的验证服务中. 判断 cookie 解密后是否正确. 如果都符合条件则返回 200 状态码, nginx 不会拦截请求, 而是构建一个 subprocess 请求受保护的本地资源.
3, 如果 django 验证服务没有通过, 则会返回一个 401 请求, nginx 对 401 请求进行拦截并且重定向到登录页面, 所以就返回到 django 的 login 服务中, 展示登录界面.
4, 在登录界面中, 输入用户名和密码提交, 传到后端时, 后端使用内部接口, 发送用户名和密码, 通过返回的状态码验证是否成功, 成功之后将用户名加密之后放入 cookie 并且重定向到受保护的资源中; 验证失败则返回提示用户名或者密码失败.
详细设计
前期准备工作
版本选择:
- Nginx 1.14.1
- Django 1.9.13
- Kibana 6.5.1
- Python 2.7
需要注意事项:
1,Nginx 需要注意的点就是, 默认 yum 安装的 Nginx 没有编译 auth_request 模块,
所以需要到官网重新下载源码, 增加 --with-http_auth_request_module 进行编译.
2, 代码中需要用到 Python 的 requests,Crypto 模块需要额外安装
Nginx 1.4.1 下载地址:
http://nginx.org/download/nginx-1.14.1.tar.gz
requests 模块安装:
pip install requests
Crypto 模块安装:
pip install Crypto
安装成功后, 可分别通过 nginx -V,pip show requests,pip show Crypto 验证:
代码分析
? 最开始设计后端的验证服务时参考了 Nginx 官网上, Nginx 结合 ldap 的身份验证 demo,demo 地址:
? https://github.com/nginxinc/nginx-ldap-auth
?demo 中大致的思路就是接受请求, 首次访问将会重定向到 login 页面上, loging 登录后, 将接收到的用户名和密码, 去查询 ldap 服务器上的信息, 成功后将把用户名和密码以?;? 号连接后做 base64 写入 cookie, 下一次访问受保护的资源时, 然后写? Location? 里写入? target? 的值, 来实现重定向跳回.
? 但是 demo 中首先的问题就是用户和密码这种敏感的信息不应该放入 cookie 中, 并且在 demo 中用到了简单的 BaseHTTPServer?, 用这个模块就会出现几个问题:
1, 不支持 url 的解析和转发, 需要用户自己解析
2, 回写的响应需要自己维护格式, 容易出错
3, 没有模板支持, 如果需要写 html 页面, 也需要自己维护.
? 所以为了解决以上的缺点, 刚好对于 Django 有一定的了解, 就打算基于 Django 框架实现用户验证的服务, 以及登录时模板页面. 废话不多说来看看代码.
代码详解: 首先是验证服务, 通过检查是否存在 cookie, 不存在的话就返回状态码 401; 如果存在的话, 通过将 cookie 解密, 获取其中关键的字段, 判断是否登录, 如果解密成功的话, 返回状态码 200, 解密失败的话, 返回状态码 401.
- class prcrypt(object):
- def __init__(self,key):
- if len(key) < 16:
- key = key+(16-len(key))*'\0'
- self.key = key
- self.mode = AES.MODE_CBC
- def encrypt(self,text):
- cryptor = AES.new(self.key,self.mode,IV=self.key)
- length = 16
- count = len(text)
- add = count % length
- if add :
- text = text + ('\0' * (length-add))
- self.ciphertext = cryptor.encrypt(text)
- return base64.b64encode(self.ciphertext)
- def decrypt(self,text):
- cryptor = AES.new(self.key,self.mode,IV=self.key)
- plain_text = cryptor.decrypt(base64.b64decode(text))
- return plain_text.rstrip('\0')
代码详解: 这段加密操作通过 aes 模块进行加密, 大概原理首先传入一个 key 值, 作为实例化 AES 对象的密钥. 然后将需要加密的文本补足成 16 的倍数进行加密操作, 最后将加密后的文本 base64 进行转换. 解密的话 逆操作即可.
代码详解: 首先前面一部分代码是接收前端传来的用户名和密码, 并且通过 requests 模块 post 到指定的接口地址, 然后解析返回的结果, 根据返回结果的状态码判断是否能登陆成功.
第二部分通过判断登录成功后, 将用户名和登录状态加密, 设置 cookie, 指定超时时间一小时, 只能由 http 协议传输. 然后重定向到受保护的资源, 受保护的资源就会再次请求验证服务, 验证服务对 cookie 进行判断正确后就会允许访问受保护资源; 如果登录失败则会返回无效用户名和密码到登录界面.
前端登录界面:
配置分析
- http {
- include /etc/nginx/mime.types;
- upstream backend {
- server 192.168.128.5:5601;
- }
- # 需要受保护的 Kibana 程序
- server {
- listen 8081;
- # nginx 服务开放 8081 端口
- location / {
- auth_request /auth-proxy;
- error_page 401 =200 /login;
- proxy_pass http://backend/;
- }
- #这个路径下受 auth-request 保护, 所有 401 的请求都会重定向到 login 上
- location /login {
- proxy_pass http://127.0.0.1:8888/login;
- proxy_set_header X-Target $request_uri;
- }
- # 这是我们认证的页面, 指向我们 django 项目中的 login 路径
- location /auth-proxy {
- internal;
- proxy_pass http://127.0.0.1:8888;
- }
- # 这里用作 auth-request 请求的路径
- location /static{
- alias /data/htdocs/www/elk/static/;
- }
- }
- }
上面就是主要的 Nginx 配置文件
- urlpatterns = [
- url(r'^$',login),
- url(r'^login',login),
- url(r'^auth-proxy',nginx_auth),
- url(r'^admin/', include(admin.site.urls)),
- ]
对应 django 项目的 urls 文件
总结
? 其实这个用户认证的解决方案不难理解, 比较麻烦就是最初需求了解确定以及和其他项目负责人沟通的问题. 在技术上单纯的没什么难点.
? 通过在网上查资料, 还有 Nginx 官网上的一些 demo 有助于你迅速了解到模块的用处, 以及对一些问题的解决方案, 不少网友也遇到了类似的问题, 在他们身上多总结经验, 有助于自己解决问题, 所以一句话 "多动手, 多沟通, 知行合一".
? 以下是我参考的一些网站和博客, 大家有兴趣可以看看:
?Nginx 官网上利用 auth_request 结合 LDAP 的认证方案:
?
? 用 Nginx 的 auth_request 模块集成 LDAP 认证:
? https://www.jianshu.com/p/9f2da3cf5579
?python 的 aes 加密和解密:
? https://my.oschina.net/u/1458120/blog/648350
来源: http://www.bubuko.com/infodetail-2888602.html