博客 | NGINX

使用声明式 API 从 OpenAPI 到 NGINX 作为 API 网关

Fabrizio Fiorucci 缩略图
法布里奇奥·菲奥鲁奇
2024 年 6 月 17 日发布

NGINX 一直被公认为支持高性能 Web 服务器和负载均衡器的首选之一。 然而,随着微服务架构的兴起和高效API管理的需求,NGINX也成为构建API网关的热门选择。

在此博客中,我们将探讨如何使用声明性 API 方法将 OpenAPI 模式定义转换为功能齐全的 NGINX 配置,该配置作为具有Webapplication防火墙安全性的 API 网关和开发人员门户运行。

我们将提供分步说明和见解,指导如何利用 NGINX Plus 简化您的 API 管理流程并确保您的应用获得最佳性能。

介绍 API 网关

API 网关作为管理和保护客户端和后端服务之间通信的中央枢纽。 它充当客户端和后端服务器之间的反向代理,路由传入的请求并将其分发到适当的服务。 这使得客户端和服务之间的通信更加有效,并且使得 API 网关能够处理诸如身份验证授权速率限制和缓存等任务。

此外,API 网关可以充当安全层,保护后端服务免受潜在威胁和攻击。 它可以实施加密、基于令牌的身份验证和访问控制等安全措施,确保只有授权用户才能访问服务。 通过在一个地方整合和管理这些安全功能,API 网关有助于简化系统的整体安全架构,并降低跨多个服务实施安全措施的复杂性。

NGINX 声明式 API 项目

社区支持的NGINX Declarative API项目为NGINX Instance Manager提供了一组声明式 REST API。

它可用于管理 NGINX Plus 配置生命周期并使用 JSON 服务定义创建 NGINX Plus 配置。 与 NGINX 实例管理器一起使用时支持 GitOps 集成:检查引用对象更新的真实来源,并自动保持 NGINX 配置同步。

OpenAPI 模式可用于自动将 NGINX 配置为 API 网关。 通过Redocly支持开发人员门户创建。

先决条件

要运行本博客的内容,您需要:

实验室概述

安装并运行所有先决条件后,NGINX 实例管理器会显示 NGINX Plus 实例与 NGINX App Protect WAF 在线。

NGINX Plus 实例是declarativeAPI 测试实例组的一部分

部署声明式 API

NGINX Declarative 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 Declarative 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 Declarative API 的 TCP 端口: 5000 是默认值
  • nim_host - NGINX 实例管理器基本 URL(即 https://nms.k8s.ie.ff.lan)
  • nim_username - NGINX 实例管理器身份验证用户名
  • nim_password - NGINX 实例管理器身份验证密码

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
                            }
                        ]
                    }
                }
            ]
        }
    }
}


输出部分定义:

  • Declarative API 将把 NGINX 配置发布到 NGINX 实例管理器服务器
  • TLS 证书和密钥 - 这些可以通过存储它们的可信来源的 URL 来引用
  • NGINX App 保护 WAF 安全策略 - 这些可以通过存储事实来源的 URL 来引用

声明部分描述:

  • 要创建的 NGINX 服务器
  • 是否执行 TLS 卸载
  • 在哪里记录访问和错误条目
  • /petstore基本 URI,API 网关配置将部署在此 URI 中,并可供客户端访问
  • 待发布的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的“发送”按钮将请求发布到声明性 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 – 它是免费且开源的。


“这篇博文可能引用了不再可用和/或不再支持的产品。 有关 F5 NGINX 产品和解决方案的最新信息,请探索我们的NGINX 产品系列。 NGINX 现在是 F5 的一部分。 所有之前的 NGINX.com 链接都将重定向至 F5.com 上的类似 NGINX 内容。”