NGINX 一直被视为打造高性能 Web 服务器和负载均衡器的首选之一。不仅如此,随着微服务架构的兴起以及对高效 API 管理的需求日益增长,NGINX 还成为了构建 API 网关的热门选择。
本文将介绍如何使用声明式 API 方法,将 OpenAPI 模式定义转换为全功能 NGINX 配置,从而创建具有 Web 应用防火墙安全防护功能和开发人员门户的 API 网关运行),并将分步说明如何利用 NGINX Plus 简化 API 管理流程,确保最佳应用性能。
API 网关简介
API 网关是管理和保护客户端与后端服务之间通信的中心枢纽,充当客户端与后端服务器之间的反向代理,可对传入请求进行路由,并将其分发到相应的服务。这不仅能够提高客户端与服务之间的通信效率,而且还支持 API 网关处理身份验证、授权、速率限制及缓存等任务。
能够执行加密、基于令牌的身份验证及访问控制等安全防护措施,确保只有授权用户才可访问服务。API 网关支持一站式整合和管理这些安全功能,有助于简化系统的整体安全架构,并降低跨多个服务实施安全防护措施的复杂性。
NGINX 声明式 API 项目
社区支持的 NGINX 声明式 API 项目为 NGINX Instance Manager 提供了一组声明式 REST API。
它可用于管理 NGINX Plus 配置生命周期,并使用 JSON 服务定义创建 NGINX Plus 配置。当与 NGINX Instance Manager 结合使用时,该项目支持 GitOps 集成:检查信源以获得引用对象更新并保持 NGINX 配置自动同步。
OpenAPI 模式可用于将 NGINX 自动配置为 API 网关。通过 Redocly 支持开发人员门户创建。
准备工作
要运行本文所述内容,您需要:
- 运行中的 NGINX Instance Manager 实例
- 订阅 NGINX Plus 和 NGINX App Protect WAF(或获取 30 天免费试用版)。必须安装 NGINX Agent,以便连接到 NGINX Instance Manager,并加入
declarativeAPITest
实例组 - 一台装有 docker 和 docker-compose 的 Linux 主机(裸机或虚拟机),用于运行 NGINX 声明式 API 项目
- Postman,用于提交声明式 API 请求
- 一台客户端主机,用于运行 Postman
实验概述
在安装并运行所有必备组件后,NGINX Instance Manager 显示 NGINX Plus 实例与 NGINX App Protect WAF 联机。
NGINX Plus 实例是 declarativeAPITest
实例组的一部分。
部署声明式 API
NGINX 声明式 API 项目依赖于 NGINX Instance Manager 提供的 REST API,并提供基于 JSON 的声明式抽象。按照以下说明运行声明式 API 项目:
1.运行 docker ps,验证 Docker 是否正在运行:
f5@ubuntu:~$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
2.在 Linux 主机上,复制 Github 仓库:
f5@ubuntu:~$ git clone https://github.com/f5devcentral/NGINX-Declarative-API/
Cloning into 'NGINX-Declarative-API'...
remote: Enumerating objects: 4072, done.
remote: Counting objects: 100% (1982/1982), done.
remote: Compressing objects: 100% (1332/1332), done.
remote: Total 4072 (delta 668), reused 876 (delta 609), pack-reused 2090
Receiving objects: 100% (4072/4072), 19.05 MiB | 4.88 MiB/s, done.
Resolving deltas: 100% (1154/1154), done.
f5@ubuntu:~$
3.切换到 docker-compose 目录:
f5@ubuntu:~$ cd NGINX-Declarative-API/contrib/docker-compose/
4.使用 nginx-dapi.sh
脚本,通过 docker-compose 启动所有容器。在初始启动过程中,所有 docker 镜像都会自动构建:
f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ ./nginx-dapi.sh -c start
-> Updating docker images
[+] Pulling 11/11
[...]
-> Deploying NGINX Declarative API
[+] Running 4/4
✔ Network nginx-dapi_dapi-network Created 0.1s
✔ Container redis Started 1.5s
✔ Container devportal Started 1.5s
✔ Container nginx-dapi Started
5.检查正在运行的 docker 容器:
f5@ubuntu:~/NGINX-Declarative-API/contrib/docker-compose$ docker ps
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
e29a2f783da2 nginx-declarative-api "/deployment/env/bin…" 5 minutes ago Up 5 minutes 0.0.0.0:5000->5000/tcp, :::5000->5000/tcp nginx-dapi
97142840eaf7 redis "docker-entrypoint.s…" 5 minutes ago Up 5 minutes 0.0.0.0:6379->6379/tcp, :::6379->6379/tcp redis
6b50c0426643 nginx-declarative-api-devportal "/deployment/src/sta…" 5 minutes ago Up 5 minutes 0.0.0.0:5001->5000/tcp, :::5001->5000/tcp devportal
6.在客户端主机上,运行 Postman 并导入 NGINX 声明式 API 集合(网址为:https://raw.githubusercontent.com/f5devcentral/NGINX-Declarative-API/main/contrib/postman/NGINX%20Declarative%20API.postman_collection.json)
7.编辑 Postman 集合变量,根据环境进行调整:
8.设置以下变量:
ncg_host
– 运行声明式 API docker-compose 的 Linux 主机的主机名或 IP 地址ncg_port
– NGINX 声明式 API 的 TCP 端口:默认为 5000nim_host
– NGINX Instance Manager 基本 URL(即 https://nms.k8s.ie.ff.lan)nim_username
– NGINX Instance Manager 身份验证用户名nim_password
– NGINX Instance Manager 身份验证密码
9.保存 Postman 上的所有变更
10.在 Postman 集合中,浏览到 Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF
,并创建请求
JSON 声明如下:
{
"output": {
"type": "nms",
"nms": {
"url": "{{nim_host}}",
"username": "{{nim_username}}",
"password": "{{nim_password}}",
"instancegroup": "{{nim_instancegroup}}",
"synctime": 0,
"modules": [
"ngx_http_app_protect_module"
],
"certificates": [
{
"type": "certificate",
"name": "test_cert",
"contents": {
"content": "{{github_gitops_root}}/v4.2/testcert.crt"
}
},
{
"type": "key",
"name": "test_key",
"contents": {
"content": "{{github_gitops_root}}/v4.2/testcert.key"
}
}
],
"policies": [
{
"type": "app_protect",
"name": "production-policy",
"active_tag": "xss-blocked",
"versions": [
{
"tag": "xss-blocked",
"displayName": "Production Policy - XSS blocked",
"description": "This is a production-ready policy - XSS blocked",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json"
}
},
{
"tag": "xss-allowed",
"displayName": "Production Policy - XSS allowed",
"description": "This is a production-ready policy - XSS allowed",
"contents": {
"content": "{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json"
}
}
]
}
]
}
},
"declaration": {
"http": {
"servers": [
{
"name": "Petstore API",
"names": [
"apigw.nginx.lab"
],
"resolver": "8.8.8.8",
"listen": {
"address": "0.0.0.0:443",
"http2": true,
"tls": {
"certificate": "test_cert",
"key": "test_key",
"ciphers": "DEFAULT",
"protocols": [
"TLSv1.2",
"TLSv1.3"
]
}
},
"log": {
"access": "/var/log/nginx/apigw.nginx.lab-access_log",
"error": "/var/log/nginx/apigw.nginx.lab-error_log"
},
"locations": [
{
"uri": "/petstore",
"urimatch": "prefix",
"apigateway": {
"openapi_schema": {
"content": "http://petstore.swagger.io/v2/swagger.json"
},
"api_gateway": {
"enabled": true,
"strip_uri": true,
"server_url": "https://petstore.swagger.io/v2"
},
"developer_portal": {
"enabled": true,
"uri": "/petstore-devportal.html"
},
"authentication": {
"client": [
{
"profile": "Petstore JWT Authentication"
}
],
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
},
"authorization": [
{
"profile": "JWT role based authorization",
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
],
"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"burst": 0,
"delay": 0,
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
]
},
"log": {
"access": "/var/log/nginx/petstore-access_log",
"error": "/var/log/nginx/petstore-error_log"
},
"app_protect": {
"enabled": true,
"policy": "production-policy",
"log": {
"profile_name": "secops_dashboard",
"enabled": true,
"destination": "127.0.0.1:514"
}
}
}
]
}
],
"rate_limit": [
{
"name": "petstore_ratelimit",
"key": "$binary_remote_addr",
"size": "10m",
"rate": "2r/s"
}
],
"authentication": {
"client": [
{
"name": "Petstore JWT Authentication",
"type": "jwt",
"jwt": {
"realm": "Petstore Authentication",
"key": "{\"keys\": [{\"k\":\"ZmFudGFzdGljand0\",\"kty\":\"oct\",\"kid\":\"0001\"}]}",
"cachetime": 5
}
}
]
},
"authorization": [
{
"name": "JWT role based authorization",
"type": "jwt",
"jwt": {
"claims": [
{
"name": "roles",
"value": [
"~(devops)"
],
"errorcode": 403
}
]
}
}
]
}
}
}
“output”
部分定义:
- 声明式 API 将 NGINX 配置发布到的目标 NGINX Instance Manager 服务器
- TLS 证书和密钥 —— 可通过存储这些证书和密钥的信源 URL 进行引用
- NGINX App Protect WAF 安全策略 —— 可通过存储这些策略的信源 URL 进行引用
“declaration”
部分描述:
- 要创建的 NGINX 服务器
- 是否执行 TLS 卸载
- 记录访问和错误条目的位置
“/petstore”
基本 URI,此处将部署 API 网关配置并可供客户端访问- 要发布的 API 网关配置和开发人员门户
- 如何启用 NGINX App Protect WAF、使用何种安全策略以及在何处记录安全违规情况
- 如何对客户端请求进行身份验证和授权
API 网关声明部分描述了声明式 API 将如何交付其结果。
OpenAPI 模式通过其完整 URL 进行引用:
"apigateway": {
"openapi_schema": {
"content": "http://petstore.swagger.io/v2/swagger.json"
},
请求创建 NGINX API 网关配置,并定义上游服务器。当 NGINX 向上游反向代理请求时,将删除 “/petstore”
基本 URI:
"api_gateway": {
"enabled": true,
"strip_uri": true,
"server_url": "https://petstore.swagger.io/v2"
},
请求在特定 URI 下创建和部署开发人员门户:
"developer_portal": {
"enabled": true,
"uri": "/petstore-devportal.html"
},
在 “/user/login
” 和 “/user/logout
” 执行基于指定客户端身份验证配置文件的客户端身份验证:
"authentication": {
"client": [
{
"profile": "Petstore JWT Authentication"
}
],
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
},
在 “/user/login
” 和 “/user/logout
” 执行基于指定客户端授权配置文件的客户端授权:
"authorization": [
{
"profile": "JWT role based authorization",
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
],
根据指定的配置文件,对 “/user/login
” 和 “/user/logout
” 实施速率限制:
"rate_limit": [
{
"profile": "petstore_ratelimit",
"httpcode": 429,
"burst": 0,
"delay": 0,
"enforceOnPaths": true,
"paths": [
"/user/login",
"/user/logout"
]
}
]
},
12.使用 Postman Send
按钮将请求发布到声明式 API。响应如下:
{
"code": 200,
"content": {
"createTime": "2024-04-26T17:09:10.419574328Z",
"details": {
"failure": [],
"pending": [],
"success": [
{
"name": "vm-test"
}
]
},
"id": "1060ec49-120e-45ca-820b-5203c8b3538d",
"message": "Instance Group config successfully published to declarativeAPITest",
"status": "successful",
"updateTime": "2024-04-26T17:09:10.881509913Z"
},
"configUid": "eecf1da6-9d8f-4e44-89cc-a470af79379d"
}
13.在此阶段,NGINX 实例被配置为 API 网关,实施 WAF 安全防护,并发布开发人员门户。
测试 API 网关
注:假定 FQDN “apigw.nginx.lab”
解析到运行 NGINX 实例的虚拟机的 IP 地址
1.切换到 “jwt
” 目录:
f5@ubuntu:~$ cd ~/NGINX-Declarative-API/contrib/gitops-examples/jwt
2.访问未经身份验证的 REST API 端点:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/store/inventory
HTTP/2 200
date: Fri, 26 Apr 2024 17:13:54 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
{"totvs":5,"aut":1,"FORsold":1,[...]
3.速率限制:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login;curl -w '\n' -ki
https://apigw.nginx.lab/petstore/user/login
HTTP/2 401
date: Fri, 26 Apr 2024 17:14:51 GMT
content-type: text/html
content-length: 179
www-authenticate: Bearer realm="Petstore Authentication"
<html>
<head><title>401 Authorization Required</title></head>
<body>
<center><h1>401 Authorization Required</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
HTTP/2 429
date: Fri, 26 Apr 2024 17:14:51 GMT
content-type: text/html
content-length: 169
<html>
<head><title>429 Too Many Requests</title></head>
<body>
<center><h1>429 Too Many Requests</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
4.身份验证和有效授权:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.devops`"
HTTP/2 200
date: Fri, 26 Apr 2024 17:15:41 GMT
content-type: application/json
access-control-allow-origin: *
access-control-allow-methods: GET, POST, DELETE, PUT
access-control-allow-headers: Content-Type, api_key, Authorization
x-expires-after: Fri Apr 26 18:15:41 UTC 2024
x-rate-limit: 5000
{"code":200,"type":"unknown","message":"logged in user session:1714151741883"}
5.身份验证和无效授权:
$ curl -w '\n' -ki https://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer `cat jwt.guest`"
HTTP/2 403
date: Fri, 26 Apr 2024 17:16:07 GMT
content-type: text/html
content-length: 153
<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.25.3</center>
</body>
</html>
6.NGINX App Protect WAF 和跨站脚本安全违规:
$ curl -w '\n' -ki "https://apigw.nginx.lab/petstore/store/inventory?
"
HTTP/2 200
content-type: text/html; charset=utf-8
cache-control: no-cache
pragma: no-cache
content-length: 246
<html><head><title>Request Rejected</title></head><body>The requested URL was rejected. Please consult with your administrator.<br><br>Your support ID is: 7283327928460093545<br><br><a href='javascript:history.back();'>[Go Back]</a></body></html>
7.浏览以下网址即可访问开发人员门户
https://apigw.nginx.lab/petstore/petstore-devportal.html
立即试用
如欲试用本文中介绍的 NGINX 解决方案,请立即下载 30 天免费试用版,或者联系我们讨论您的用例:
您还可以下载免费的开源软件 NGINX Agent。
NGINX 企阅版全解析
助力企业用户规避开源治理风险,应对开源使用挑战