OpenAPIから宣言型APIを使用してAPIゲートウェイとしてのNGINXへ

NGINX | 2024年6月17日

NGINXは、高性能なWebサーバーやロードバランサーを支える有力な選択肢の1つとして、長年にわたり高く評価されてきました。さらに、マイクロサービスアーキテクチャの普及と効率的なAPI管理の必要性が高まる中、APIゲートウェイの構築にも広く採用されています。

このブログでは、宣言型APIアプローチを使用して、OpenAPIスキーマ定義を、Web Application FirewallによるセキュリティとDeveloper Portalを備え、API Gatewayとして動作する完全に機能するNGINX構成へ変換する方法を解説します。

NGINX Plusを活用してAPI管理プロセスを効率化し、アプリケーションのパフォーマンスを最適化する方法について、手順を追って説明するとともに、役立つ知見をご紹介します。

API Gatewayのご紹介

APIゲートウェイは、クライアントとバックエンドサービス間の通信を管理し、セキュリティを確保するための中核的なハブとして機能します。クライアントとバックエンドサーバーの間に配置されるリバースプロキシとして、受信リクエストを適切なサービスにルーティングして振り分けます。これにより、クライアントとサービス間の通信が効率化されるほか、APIゲートウェイで次のような処理が可能になります。認証、認可、レート制限、キャッシュ。

さらに、APIゲートウェイはセキュリティレイヤーとして機能し、潜在的な脅威や攻撃からバックエンドサービスを保護できます。暗号化、トークンベース認証、アクセス制御などのセキュリティ対策を適用し、許可されたユーザーのみがサービスにアクセスできるようにします。これらのセキュリティ機能を一元化して管理することで、APIゲートウェイはシステム全体のセキュリティアーキテクチャを簡素化し、複数のサービスにセキュリティ対策を実装する際の複雑さを軽減します。

NGINX Declarative API Project

コミュニティによってサポートされている「NGINX Declarative API」プロジェクトは、NGINX Instance Manager向けの宣言型REST API一式を提供します。

JSONサービス定義を使用して、NGINX Plus構成のライフサイクルを管理し、NGINX Plus構成を作成できます。NGINX Instance Managerと併用するとGitOps統合がサポートされます。信頼できる唯一の情報源で参照先オブジェクトの更新が確認され、NGINX構成が自動的に同期されます。

OpenAPIスキーマを使用して、NGINXをAPIゲートウェイとして自動的に構成できます。開発者ポータルは、Redoclyを通じて作成できます。

前提条件

このブログの内容を実行するには、以下が必要です。

  • 実行中のNGINX Instance Manager
  • A subscription (or 30日間の無料トライアルまたはNGINX PlusおよびNGINX App Protect WAFのサブスクリプションが必要です。NGINX AgentをインストールしてNGINX Instance Managerに接続し、インスタンスグループ「declarativeAPITest」に所属させる必要があります
  • NGINX Declarative APIプロジェクトを実行するための、dockerおよびdocker-composeを備えたLinuxホスト(ベアメタルまたはVM)
  • 宣言型APIリクエストの送信に使用するPostman
  • Postmanを実行するクライアントホスト

ラボの概要

すべての前提条件をインストールして実行すると、NGINX Instance Managerでは、NGINX App Protect WAFを備えたNGINX Plusインスタンスがオンラインとして表示されます。

NGINX Instance Managerの概要

NGINX Plusインスタンスは、declarativeAPITestインスタンスグループに属しています

NGINX Instance Managerのインスタンスグループ

Declarative APIのデプロイ

NGINX Declarative APIプロジェクトは、NGINX Instance Managerが提供するREST APIを利用し、JSONベースの宣言型抽象化を提供します。Declarative 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コレクション変数を編集し、ご利用の環境に合わせて調整します:

NGINX Declarative API

8. 次の変数を設定します:

  • ncg_host - Declarative APIのdocker-composeが実行されているLinuxホストのホスト名またはIPアドレス
  • ncg_port - NGINX Declarative APIのTCPポート。デフォルトは5000
  • nim_host - NGINX Instance ManagerのベースURL(例:https://nms.k8s.ie.ff.lan)
  • nim_username - NGINX Instance Managerの認証ユーザー名
  • nim_password - NGINX Instance Managerの認証パスワード
NGINX Declarative APIの変数

9. Postmanですべての変更を保存します

10. Postmanコレクションで「Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF」に移動し、リクエストを開きます

NGINX Declarative APIコレクション

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

The outputセクションでは、以下を定義します。

  • Declarative APIがNGINX設定を公開するNGINX Instance Managerサーバー
  • TLS証明書と鍵 - 保存先となる信頼できる情報源のURLを使用して参照できます
  • NGINX App Protect WAFセキュリティポリシー - 保存先となる信頼できる情報源のURLを使用して参照できます

The declarationセクションでは、以下について説明します。

  • 作成するNGINXサーバー
  • TLSオフロードを実行するかどうか
  • アクセスログとエラーログの記録先
  • API Gateway構成をデプロイし、クライアントからアクセスできるようにするベースURI「/petstore」
  • 公開するAPI Gateway構成と開発者ポータル
  • NGINX App Protect WAFを有効にする方法、使用するセキュリティポリシー、セキュリティ違反のログの記録先
  • クライアントリクエストを認証および認可する方法

API Gateway宣言セクションでは、宣言型APIがどのように結果を提供するかを説明します。

OpenAPIスキーマは、完全なURLを使用して参照します:


"apigateway": {
    "openapi_schema": {
        "content": "http://petstore.swagger.io/v2/swagger.json"
    },

NGINX API Gateway構成の作成をリクエストし、アップストリームサーバーを定義します。NGINXがリクエストをアップストリームにリバースプロキシする際、ベースURIの「/petstore」は削除されます。


"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 Gatewayとして構成され、WAFセキュリティが適用され、開発者ポータルが公開されています。

API Gatewayのテスト

注: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

7. デベロッパーポータルにアクセスするには、次のURLを開きます。


https://apigw.nginx.lab/petstore/petstore-devportal.html

始める

この記事でご紹介したNGINXソリューションをお試しいただくには、今すぐ30日間の無料トライアルを開始するか、お問い合わせのうえユースケースについてご相談ください。

無料のオープンソースであるNGINX Agentをダウンロードしてください。

共有
タグ: F5 NGINX, 2024

著者について

Fabrizio Fiorucci
Fabrizio FiorucciNGINXソリューションアーキテクト | F5 (EMEA)

Fabrizio Fiorucciのその他のブログ記事

関連するブログ記事

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する
NGINX | 2024/01/18

チュートリアル:NGINXを使用してアプリケーション向けにOpenTelemetryを構成する

OpenTelemetryを使用してNGINXサーバーの状態と健全性をより明確に把握することで、問題のトラブルシューティングを行い、Webアプリケーションのパフォーマンスを向上できます。

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正
NGINX | 2023/05/31

AkitaのNGINX Plus認定モジュールでAPIエンドポイントの問題を特定・修正

Akita SoftwareのAPIオブザーバビリティ向け新しいNGINX Plus Certified Moduleを活用して、APIエンドポイントの問題をより迅速に特定し、修正できます。

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法
NGINX | 2023/02/09

JavaプロジェクトをスキャフォールディングしてKubernetesネイティブアプリを構築する方法

Red HatのシニアプリンシパルデベロッパーアドボケイトであるDaniel Oh氏が、Javaプロジェクトをゼロから構築するためのひな形を作成し、Kubernetesネイティブアプリケーションとしてビルドする方法を解説します。

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング
NGINX | 2022/05/24

F5 NGINXaaS for Azureのご紹介:Microsoft Azureでネイティブに利用できるSaaS型ロードバランシング

NGINXaaS for Azureは、NGINX PlusをAzureネイティブのSaaSとして利用できるサービスです。NGINXの設定を容易にリフト&シフトできるため、クラウド導入とモダンアプリの展開を加速できます。

SELinuxでNGINXおよびNGINX Plusを使用する
NGINX | 2018/08/17

SELinuxでNGINXおよびNGINX Plusを使用する

Red Hat Enterprise Linux(RHEL)および関連ディストリビューションでSecurity-Enhanced Linux(SELinux)を有効にすると、デフォルト設定によりNGINXとNGINX Plusの一部の操作が制限されます。この記事では、すべての機能を利用できるようにSELinuxの設定を変更する方法について説明します。

NGINX PlusによるAWS Auto Scalingグループの負荷分散
NGINX | 2017/03/06

NGINX PlusによるAWS Auto Scalingグループの負荷分散

新しいnginx-asg-sync統合ソフトウェアにより、NGINX PlusはAWS Auto Scalingグループの負荷を直接分散できるため、クラウドでの負荷分散がさらに柔軟になります