NGINX的定制化 | API Management学习第四篇

前言

本文仅代表作者的个人观点;

本文的内容仅限于技术探讨,不能作为指导生产环境的素材;

本文素材是红帽公司产品技术和手册;

本文分为系列文章,将会有多篇,初步预计将会有10篇。

一、3 Scale中的NGINX

3 SCALE中API gateway,是基于NGINX(OpenResty Web Platform = Nginx + Lua )。

NGINX的特点和作用如下:

  • HTTP和反向代理服务器
  • 邮件和TCP / UDP代理服务器
  • 为世界上最繁忙的网站提供30%的能力
  • 非阻塞,基于事件的反应堆
  • 单线程,主人+工人
  • 模块化架构
  • SSL和TLS SNI支持

二、Openshift中的OpenResty Web Platform = Nginx + Lua

OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。

OpenResty 通过汇聚各种设计精良的 Nginx 模块(主要由 OpenResty 团队自主开发),从而将 Nginx 有效地变成一个强大的通用 Web 应用平台。这样,Web 开发人员和系统工程师可以使用 Lua 脚本语言调动 Nginx 支持的各种 C 以及 Lua 模块,快速构造出足以胜任 10K 乃至 1000K 以上单机并发连接的高性能 Web 应用系统。

OpenResty 的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。

三、APIcast的库和配置

APIcast库是扩展的NGINX模块

  • 缓存DNS解析器
  • 负载均衡器
  • HTTP客户端(带缓存)

内部使用

  • 微服务(API mashup)
  • 路由器/代理服务器
  • 上行IP白名单/黑名单

APIcast策略(模块)

  • 一个入口点模块,可以调用其他模块
  • 有每个阶段的方法(init,init_worker,重写,访问等)
  • 默认模块称为APIcast:更换它重新定义了整个网关;与继承一样,自定义模块应该调用apicast模块

九个APIcast模块阶段

init

Executed in the master process, loaded code shared between workers

init_worker

Executed for each worker, can perform background work

rewrite

First request phase to change the request

access

Allow/deny the request

balancer

Used for load balancing

header_filter

Process response headers

body_filter

Process response body

post_action

Not official NGINX phase, update cache information out of band

log

Can extend logging (for example, to external service)

默认模块(默认的apicast策略)

六个NGINX阶段的政策:

  1. init - 加载配置(引导模式)
  2. init_worker - 启动计时器以刷新配置(启动模式)
  3. rewrite - 为给定主机加载配置(延迟模式)

4. 访问:

如果需要,匹配请求并同步授权

如果需要,解析上游DNS记录

设置NGINX变量以正确代理上游

5.balancer - 根据所选算法选择peer

6.post_action - 如果缓存的呼叫通过,则向3scale后端报告

政策链 - 结合政策来提供服务

自定义模块

要自定义APIcast:

  • 覆盖功能
  • 扩展配置
  • 与HTTP请求/响应相关。示例:跨源资源共享(CORS)
  • 自定义验证
  • 限时访问
  • 限制打开的连接

标准配置

  • 主机名,服务配置等的配置
  • 网关的JSON配置
  • 从文件或API加载
  • https://ACCOUNT-admin.3scale.net/admin/api/nginx/spec.json
  • 在init和init_worker阶段加载
  • THREESCALE_CONFIG_FILE定位配置文件

自定义NGNIX配置

  • 将自定义NGINX配置注入网关
  • 不覆盖现有配置
  • 装载到conf.d或sites.d
  • 挂钩到NGINX阶段

Lua

  • 脚本语言
  • 支持程序,面向对象,功能,数据驱动的编程
  • 基于的C
  • 独立或嵌入式
  • 动态键入的语言
  • 八种基本类型:nil,boolean,number,string,function,userdata,thread,table
  • 错误处理 - 控制返回主机
  • 使用自动内存管理进行垃圾收集
  • 变量 - 全局,本地,表
  • 与JavaScript非常相似,Lua表就像JavaScript对象

四、代码集成方式引入3Scale(侵入式)

  • 社区扩展
  • 允许API流量管理
  • 服务管理API的包装器:
  • API访问控制和安全性
  • 流量报告
  • API货币化

支持的语言:

插件API调用后端:

authrep - 访问API和报告流量的单一呼叫

授权 - 访问API请求的调用

报告 - 报告API的流量

插件验证:

  • App ID
  • 用户密钥
  • OAuth的

接下来的实验,一共有三个,分别是:

  • 在NGINX网关中创建自定义模块以进行日志记录
  • 在NGINX网关中创建自定义配置以回显请求标头
  • 为CORS定制NGINX网关(跨源资源共享)

四、实验展现1:为NGINX增加日志模块

NGINX模块插入网关的生命周期阶段。 有各种APIcast阶段,例如init,init_worker,rewrite,access,content,log,post_action,balancer,header_filter,body_filter等。模块处理每个请求的处理。 只能执行一个模块。

执行的模块的名称由环境变量APICAST_MODULE定义,默认为apicast。

要编写自定义模块,我们需要使用继承来使用自定义代码覆盖默认的APICAST_MODULE代码。 自定义模块提供了编写自定义代码的推荐框架。

在本节中,我们将创建一个自定义模块,以在APIcast中提供更详细的日志记录信息。 自定义Lua文件可在此处获得。

检查日志功能的代码:

日志中添加了两个新字段:upstream_response_time和upstream_connect_time。

在成功的API调用中,新日志信息将打印在日志中。

拷贝代码:

[root@master david]# cat verbose.lua

local apicast = require('apicast').new()

local _M = { _VERSION = '0.0' }

local mt = { __index = setmetatable(_M, { __index = apicast }) }

function _M.new()

return setmetatable({}, mt)

end

function _M.log()

ngx.log(ngx.WARN,

'upstream response time: ', ngx.var.upstream_response_time, ' ',

'upstream connect time: ', ngx.var.upstream_connect_time)

return apicast:log()

end

return _M

在OpenShift项目中创建config map,确保提供verbose.lua文件的正确路径:

为容器创建卷,并将其挂载到适当的路径:

oc set volume dc/apicast-staging --add --name=apicast-verbose --mount-path /root/david/verbose.lua --source='{"configMap":{"name":"apicast-verbose","items":[{"key":"verbose.lua","path":"verbose.lua"}]}}'

oc volume命令不支持添加子路径,因此我们需要应用补丁:

oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/0/subPath", "value":"verbose.lua"}]'

设置环境变量APICAST_MODULE:

oc env dc/apicast-staging APICAST_MODULE=verbose

测试效果

使用curl命令将测试请求发送到任何业务服务的暂存路由。

日志中出现:

30#30: *114 [lua] verbose.lua:11: log(): upstream response time: 0.085 upstream connect time: 0.000 while logging request, c

四、实验展现2:NGINX客户化配置

  • 自定义NGNIX配置注入网关 - 例如,添加另一个服务器块以处理某些路由。此配置不会覆盖现有配置。
  • 与自定义模块类似,可以使用自定义配置继承标准配置并对其进行扩展。
  • 在本节中,我们将创建一个自定义配置,通过回显响应中的所有请求标头以及API响应,为客户端提供更详细的响应。

拷贝echo.conf配置

curl -o echo.conf https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/custom-config/echo.conf

我们查看这个配置的内容:

接下来,进行如下操作(完整步骤):

[root@master src]# oc project $OCP_PROJECT_PREFIX-3scale-amp

Already on project "david-3scale-amp" on server "https://master.example.com:8443".

[root@master src]#

[root@master src]# oc create configmap echo-conf --from-file=./echo.conf

configmap "echo-conf" created

[root@master src]# oc set volume dc/apicast-staging --add --name=echo-conf --mount-path /opt/app-root/app/sites.d/echo.conf --source='{"configMap":{"name":"echo-conf","items":[{"key":"echo.conf","path":"echo.conf"}]}}'

\deploymentconfig "apicast-staging" updated

[root@master src]# oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/1/subPath", "value":"echo.conf"}]'

deploymentconfig "apicast-staging" patched

[root@master src]#

接下来,apicast-staging将会重新部署:

测试效果

登录到apicast-staging pod

发送请求到localhost的端口8080:

五、实验展现3:为NGINX增加模块:CORS

跨源资源共享(CORS)是一种机制,它使用其他HTTP标头让用户代理获得从当前正在使用的站点的不同源(域)上的服务器访问所选资源的权限。 用户代理在请求来自与当前文档所源自的域,协议或端口不同的域,协议或端口的资源时,会发出跨源HTTP请求。

两个文件-cors.lua和cors.conf-为NGINX配置CORS。

将CORS自定义模块和配置部署到OpenShift

两个配置文件的内容:

[root@master src]# cors.conf

more_set_headers 'Access-Control-Allow-Origin: $http_origin';

more_set_headers 'Access-Control-Allow-Credentials: true';

more_set_headers 'Access-Control-Allow-Headers: $http_access_control_request_headers';

more_set_headers 'Access-Control-Allow-Methods: $http_access_control_request_method';

[root@master src]# vi cors.lua

local apicast = require('apicast').new()

local _M = { _VERSION = apicast._VERSION, _NAME = 'APIcast with CORS' }

local mt = { __index = setmetatable(_M, { __index = apicast }) }

function _M.new()

return setmetatable({}, mt)

end

local function set_cors_headers()

local origin = ngx.var.http_origin

if not origin then return end

ngx.header['Access-Control-Allow-Headers'] = ngx.var.http_access_control_request_headers

ngx.header['Access-Control-Allow-Methods'] = ngx.var.http_access_control_request_method

ngx.header['Access-Control-Allow-Origin'] = origin

ngx.header['Access-Control-Allow-Credentials'] = 'true'

end

local function cors_preflight_response()

set_cors_headers()

ngx.status = 204

ngx.exit(ngx.status)

end

local function cors_preflight()

return (

ngx.req.get_method() == 'OPTIONS' and

ngx.var.http_origin and

ngx.var.http_access_control_request_method

)

end

-- header_filter is used to manipulate response headers

function _M.header_filter()

set_cors_headers()

return apicast:header_filter()

end

-- rewrite is the first phase executed and can hijack the whole request handling

function _M.rewrite()

-- for CORS preflight sent by the browser, return a 204 status code

if cors_preflight() then

[root@master src]# cat cors.lua

local apicast = require('apicast').new()

local _M = { _VERSION = apicast._VERSION, _NAME = 'APIcast with CORS' }

local mt = { __index = setmetatable(_M, { __index = apicast }) }

function _M.new()

return setmetatable({}, mt)

end

local function set_cors_headers()

local origin = ngx.var.http_origin

if not origin then return end

ngx.header['Access-Control-Allow-Headers'] = ngx.var.http_access_control_request_headers

ngx.header['Access-Control-Allow-Methods'] = ngx.var.http_access_control_request_method

ngx.header['Access-Control-Allow-Origin'] = origin

ngx.header['Access-Control-Allow-Credentials'] = 'true'

end

local function cors_preflight_response()

set_cors_headers()

ngx.status = 204

ngx.exit(ngx.status)

end

local function cors_preflight()

return (

ngx.req.get_method() == 'OPTIONS' and

ngx.var.http_origin and

ngx.var.http_access_control_request_method

)

end

-- header_filter is used to manipulate response headers

function _M.header_filter()

set_cors_headers()

return apicast:header_filter()

end

-- rewrite is the first phase executed and can hijack the whole request handling

function _M.rewrite()

-- for CORS preflight sent by the browser, return a 204 status code

if cors_preflight() then

return cors_preflight_response()

else

-- if the request is not CORS preflight jut continue with APIcast flow

return apicast:rewrite()

end

end

return _M

操作步骤:

curl -o cors.lua https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/cors/cors.lua

curl -o cors.conf https://raw.githubusercontent.com/3scale/apicast/3.1-stable/examples/cors/cors.conf

oc project $OCP_PROJECT_PREFIX-3scale-amp

oc create configmap apicast-cors --from-file=./cors.lua

oc create configmap cors-conf --from-file=./cors.conf

oc set volume dc/apicast-staging --add --name=apicast-cors --mount-path /opt/app-root/src/src/cors.lua --source='{"configMap":{"name":"apicast-cors","items":[{"key":"cors.lua","path":"cors.lua"}]}}'

oc set volume dc/apicast-staging --add --name=cors-conf --mount-path /opt/app-root/src/apicast.d/cors.conf --source='{"configMap":{"name":"cors-conf","items":[{"key":"cors.conf","path":"cors.conf"}]}}'

oc patch dc/apicast-staging --type=json -p '[{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/2/subPath", "value":"cors.lua"},{"op": "add", "path": "/spec/template/spec/containers/0/volumeMounts/3/subPath", "value":"cors.conf"}]'

oc env dc/apicast-staging APICAST_MODULE=cors

接下来pod会重新部署:

测试效果:

对API enpoint发起curl请求:

我们观察到,输出结果中有如下信息,说明CORS 起作用了。

魏新宇

  • 红帽资深解决方案架构师
  • 专注开源云计算、容器及自动化运维在金融行业的推广
  • 拥有MBA、ITIL V3、Cobit5、C-STAR、TOGAF9.1(鉴定级)等管理认证。
  • 拥有红帽RHCE/RHCA、VMware VCP-DCV、VCP-DT、VCP-Network、VCP-Cloud、AIX、HPUX等技术认证。

原文发布于微信公众号 - 大魏分享(david-share)

原文发表时间:2018-07-23

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏小白安全

web渗透思路及总结

(一)针对网站程序,不考虑服务器。 一、查找注入,注意数据库用户权限和站库是否同服。 二、查找XSS,最近盲打很流行,不管怎样我们的目的是进入后台。...

7457
来自专栏信安之路

SeLoadDriverPrivilege 在提权中的应用

我本想翻译学习一下,结果发现安全客已经有人翻译了,我也就没必要翻译了,就复现学习一下吧,顺便算是稍微补充下原文的内容,验证下提权风险。希望能对大家有所帮助。

1660
来自专栏沃趣科技

ASM 翻译系列第五弹:高级知识 ASM 元数据概述

原作者:Bane Radulovic 译者: 赵恩东 审核: 魏兴华 DBGeeK社群联合出品 ASM 元数据概述 ASM的元数据由ASM实例进行维...

3913
来自专栏walterlv - 吕毅的博客

Windows 10 四月更新,文件夹名称也能区分大小写了

发布于 2018-06-14 00:02 更新于 2018-09...

3093
来自专栏为数不多的Android技巧

让Alfred支持拼音搜索

Alfred是个好东西,不过检索程序的时候不支持拼音搜索;我在论坛看到有人给作者反馈过,无奈作者说支持中文,他不知道拼音是什么,于是就不了了之了。举个例子:我想...

3071
来自专栏北京马哥教育

Windows10+Ubuntu双系统安装

最近因为毕设重新回归Ubuntu,手头有一台装了Win10的ThinkPad X240s,最终成功完成了Windows 10 教育版和Ubuntu Kylin ...

7836
来自专栏我是极客人

程序算法|PHP、安卓、C++程序代码交流

使用android studio的安卓开发者可能发现它和eclipse+ADT大同小异,个人用完android studio之后发现android studio...

1641
来自专栏FreeBuf

Firefox/Chrome渗透测试插件推荐

注意:360安全浏览器有些比较不错的功能值得体验下(firefox/chrome有些功能我没能正常使用)不喜勿喷。firefox一直是各位渗透测试必备的利器,这...

8097
来自专栏北京马哥教育

Python框架:Django写图书管理系统(LMS)

今天我会带大家真正写一个Django项目,对于入门来说是有点难度的,因为逻辑比较复杂,但是真正的知识就是函数与面向对象,这也是培养用Django思维写项目的开始

2211
来自专栏地方网络工作室的专栏

Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(十三)集成 UEditor 百度富文本编辑器

Vue2+VueRouter2+Webpack+Axios 构建项目实战2017重制版(十三)集成 UEditor 百度富文本编辑器 前情回顾 通过前面系统的学...

3338

扫码关注云+社区

领取腾讯云代金券