diff --git a/.gitignore b/.gitignore index e54f2a6..ce89575 100644 --- a/.gitignore +++ b/.gitignore @@ -20,8 +20,7 @@ Thumbs.db ======= /.idea/ /src/__pycache__/ -/src/v4_1/__pycache__/ /src/v4_2/__pycache__/ -/src/v4_3/__pycache__/ +/src/v5_0/__pycache__/ /contrib/devportal/redocly/src/__pycache__/ /venv/ diff --git a/FEATURES.md b/FEATURES.md index c27130d..e63cdaa 100644 --- a/FEATURES.md +++ b/FEATURES.md @@ -2,25 +2,25 @@ ### NGINX `http` and `stream` servers -| Feature | API v4.1 | API v4.2 | Notes | -|----------------------------|-----------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| Upstreams | CRUD | CRUD |
  • Snippets supported: static and from source of truth
  • | -| HTTP servers | CRUD | CRUD |
  • Snippets supported (`http`, `servers`, `locations`): static and from source of truth
  • | -| TCP/UDP servers | CRUD | CRUD |
  • Snippets supported (`streams`, `servers`): static and from source of truth
  • | -| TLS | CRUD | CRUD |
  • Certificates and keys can be dynamically fetched from source of truth
  • | -| Client authentication | X | X | See [client authentication](#Client-authentication) | -| Upstream authentication | X | X | See [upstream and Source of truth authentication](#Upstream-and-Source-of-truth-authentication) | -| Rate limiting | X | X | | -| Active healthchecks | X | X | | -| Cookie-based stickiness | X | X | | -| HTTP headers manipulation | | X |
  • To server: set, delete
  • To client: add, delete, replace
  • | -| Maps | X | X | | -| NGINX Plus REST API access | X | X | | -| NGINX App Protect WAF | X | X |
  • Per-policy CRUD at `server` and `location` level
  • Support for dataplane-based bundle compilation
  • Security policies can be fetched from source of truth
  • | +| Feature | API v4.2 | API v5.0 | Notes | +|----------------------------|----------|----------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| +| Upstreams | CRUD | CRUD |
  • Snippets supported: static and from source of truth
  • | +| HTTP servers | CRUD | CRUD |
  • Snippets supported (`http`, `servers`, `locations`): static and from source of truth
  • | +| TCP/UDP servers | CRUD | CRUD |
  • Snippets supported (`streams`, `servers`): static and from source of truth
  • | +| TLS | CRUD | CRUD |
  • Certificates and keys can be dynamically fetched from source of truth
  • | +| Client authentication | X | X | See [client authentication](#Client-authentication) | +| Upstream authentication | X | X | See [upstream and Source of truth authentication](#Upstream-and-Source-of-truth-authentication) | +| Rate limiting | X | X | | +| Active healthchecks | X | X | | +| Cookie-based stickiness | X | X | | +| HTTP headers manipulation | X | X |
  • To server: set, delete
  • To client: add, delete, replace
  • | +| Maps | X | X | | +| NGINX Plus REST API access | X | X | | +| NGINX App Protect WAF | X | X |
  • Per-policy CRUD at `server` and `location` level
  • Support for dataplane-based bundle compilation
  • Security policies can be fetched from source of truth
  • | ### API Gateway -| Feature | API v4.1 | API v4.2 | Notes | +| Feature | API v4.2 | API v5.0 | Notes | |----------------------------------------------|----------|----------|-------------------------------------------------------------------------------| | Configuration generation from OpenAPI schema | X | X | | | HTTP methods enforcement | X | X | | @@ -30,13 +30,13 @@ ### API Gateway - Developer Portal -| Feature | API v4.1 | API v4.2 | Notes | +| Feature | API v4.2 | API v5.0 | Notes | |-------------------------------------------------|----------|----------|---------------------------| | Developer Portal generation from OpenAPI schema | X | X |
  • Based on Redocly
  • | ### Client authentication -| Type | Description | API v4.1 | API v4.2 | Notes | +| Type | Description | API v4.2 | API v5.0 | Notes | |------|----------------------|----------|----------|-------------------------------------| | jwt | Java Web Token (JWT) | X | X | | | mtls | Mutual TLS | X | X |
  • Supported for HTTP servers
  • | @@ -85,9 +85,9 @@ Client-side authentication profiles to be defined under `.declaration.http.authe ### Client authorization -| Type | Description | API v4.1 | API v4.2 | Notes | +| Type | Description | API v4.2 | API v5.0 | Notes | |------|----------------------|----------|----------|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| -| jwt | Java Web Token (JWT) | | X | Based on JWT claims. Supported under
  • .declaration.http.server[]
  • .declaration.http.server[].location[]
  • .declaration.http.server[].location[].apigateway
  • | +| jwt | Java Web Token (JWT) | X | X | Based on JWT claims. Supported under
  • .declaration.http.server[]
  • .declaration.http.server[].location[]
  • .declaration.http.server[].location[].apigateway
  • | #### Examples @@ -115,10 +115,10 @@ Client-side authorization profiles to be defined under `.declaration.http.author ### Upstream and Source of truth authentication -| Type | Description | API v4.1 | API v4.2 | Notes | +| Type | Description | API v4.2 | API v5.0 | Notes | |--------------|----------------------------------------------|----------|----------|----------------------------------------------------------------------------------------| | Bearer token | Authentication token as Authorization Bearer | X | X | `Bearer` Authorization header is injected in requests to upstreams and source of truth | -| Basic Auth | Authentication token as Authorization Basic | | X | `Basic` Authorization header is injected in requests to upstreams and source of truth | +| Basic Auth | Authentication token as Authorization Basic | X | X | `Basic` Authorization header is injected in requests to upstreams and source of truth | | HTTP header | Authentication token in custom HTTP header | X | X | HTTP header is injected in requests to upstreams and source of truth | | mTLS | Mutual TLS | X | X | Client certificate is sent to upstream / source of truth | @@ -183,10 +183,10 @@ Server-side authentication profiles to be defined under `.declaration.http.authe ### HTTP Headers manipulation -| Type | API v4.1 | API v4.2 | Notes | -|-----------------------------|----------|----------|------------------------------------------------------------------------------------------------------------------------------| -| Request (client to server) | | X |
  • `set` - new header injection
  • `delete` - client header removal
  • | -| Response (server to client) | | X |
  • `add` - new header injection
  • `delete` - server header removal
  • `replace` - server header replacement
  • | +| Type | API v4.2 | API v5.0 | Notes | +|-----------------------------|----------|---------|------------------------------------------------------------------------------------------------------------------------------| +| Request (client to server) | X | X |
  • `set` - new header injection
  • `delete` - client header removal
  • | +| Response (server to client) | X | X |
  • `add` - new header injection
  • `delete` - server header removal
  • `replace` - server header replacement
  • | #### Examples @@ -232,14 +232,14 @@ To be defined under `.declaration.http.servers[].headers` and/or `.declaration.h ### NGINX Javascript -| Hook type | API v4.1 | API v4.2 | Notes | +| Hook type | API v4.2 | API v5.0 | Notes | |-------------------|----------|----------|------------------------------------------------------------------------------------------------------------------------------| -| js_body_filter | | X | Available in
  • `declaration.http.server[].location[]`
  • | -| js_content | | X | Available in
  • `declaration.http.server[].location[]`
  • | -| js_header_filter | | X | Available in
  • `declaration.http.server[].location[]`
  • | -| js_periodic | | X | Available in
  • `declaration.http.server[].location[]`
  • | -| js_preload_object | | X | Available in
  • `.declaration.http`
  • `declaration.http.server[]`
  • `declaration.http.server[].location[]`
  • | -| js_set | | X | Available in
  • `.declaration.http`
  • `declaration.http.server[]`
  • `declaration.http.server[].location[]`
  • | +| js_body_filter | X | X | Available in
  • `declaration.http.server[].location[]`
  • | +| js_content | X | X | Available in
  • `declaration.http.server[].location[]`
  • | +| js_header_filter | X | X | Available in
  • `declaration.http.server[].location[]`
  • | +| js_periodic | X | X | Available in
  • `declaration.http.server[].location[]`
  • | +| js_preload_object | X | X | Available in
  • `.declaration.http`
  • `declaration.http.server[]`
  • `declaration.http.server[].location[]`
  • | +| js_set | X | X | Available in
  • `.declaration.http`
  • `declaration.http.server[]`
  • `declaration.http.server[].location[]`
  • | Note: `njs` profiles can be included in base64-encoded format under `.declaration.http.njs[]` of fetched from an external source of truth For detailed examples see the [Postman collection](/contrib/postman) diff --git a/README.md b/README.md index a4e7d49..9298259 100644 --- a/README.md +++ b/README.md @@ -144,8 +144,8 @@ See the [features list](/FEATURES.md) Usage details and JSON schema are available here: -- [API v4.2](/USAGE-v4.2.md) - latest -- [API v4.1](/USAGE-v4.1.md) +- [API v5.0](/USAGE-v5.0.md) - latest +- [API v4.2](/USAGE-v4.2.md) A sample Postman collection and usage instructions can be found [here](/contrib/postman) diff --git a/USAGE-v4.1.md b/USAGE-v4.1.md deleted file mode 100644 index 4b5e1be..0000000 --- a/USAGE-v4.1.md +++ /dev/null @@ -1,257 +0,0 @@ -# Usage for API v4.1 - -Version 4.1 API requires: - -- NGINX Instance Manager 2.14+ -- NGINX Plus R30+ - -If NGINX App Protect declarations are used: -- NGINX App Protect Policy Compiler 4.2.0+ -- NGINX Plus instances running App Protect WAF 4.2.0+ using `precompiled_publication: true` in `/etc/nginx-agent/nginx-agent.conf` - -The JSON schema is self explanatory. See also the [sample Postman collection](/contrib/postman) - -- `.output.type` defines how NGINX configuration will be returned: - - *plaintext* - plaintext format - - *json* - JSON-wrapped, base64-encoded - - *configmap* - Kubernetes Configmap in YAML format. - - `.output.configmap.name` must be set to the ConfigMap name - - `.output.configmap.filename` must be set to the NGINX configuration filename - - `.output.configmap.namespace` the optional namespace for the ConfigMap - - *http* - NGINX configuration is POSTed to custom url - - `.output.http.url` the URL to POST the configuration to - - *nms* - NGINX configuration is published as a Staged Config to NGINX Instance Manager - - `.output.nms.url` the NGINX Instance Manager URL - - `.output.nms.username` the NGINX Instance Manager authentication username - - `.output.nms.password` the NGINX Instance Manager authentication password - - `.output.nms.instancegroup` the NGINX Instance Manager instance group to publish the configuration to - - `.output.nms.synctime` **optional**, used for GitOps autosync. When specified and the declaration includes HTTP(S) references to NGINX App Protect policies, TLS certificates/keys/chains, the HTTP(S) endpoints will be checked every `synctime` seconds and if external contents have changed, the updated configuration will automatically be published to NGINX Instance Manager - - `.output.nms.modules` an optional array of NGINX module names (ie. 'ngx_http_app_protect_module', 'ngx_http_js_module','ngx_stream_js_module') - - `.output.nms.certificates` an optional array of TLS certificates/keys/chains to be published - - `.output.nms.certificates[].type` the item type ('certificate', 'key', 'chain') - - `.output.nms.certificates[].name` the certificate/key/chain name with no path/extension (ie. 'test-application') - - `.output.nms.certificates[].contents` the content: this can be either base64-encoded or be a HTTP(S) URL that will be fetched dynamically from a source of truth - - `.output.nms.policies[]` an optional array of NGINX App Protect security policies - - `.output.nms.policies[].type` the policy type ('app_protect') - - `.output.nms.policies[].name` the policy name (ie. 'prod-policy') - - `.output.nms.policies[].active_tag` the policy tag to enable among all available versions (ie. 'v1') - - `.output.nms.policies[].versions[]` array with all available policy versions - - `.output.nms.policies[].versions[].tag` the policy version's tag name - - `.output.nms.policies[].versions[].displayName` the policy version's display name - - `.output.nms.policies[].versions[].description` the policy version's description - - `.output.nms.policies[].versions[].contents` this can be either base64-encoded or be a HTTP(S) URL that will be fetched dynamically from a source of truth -- `.declaration` describes the NGINX configuration to be created. - -### Locations ### - -Locations `.declaration.http.servers[].locations[].uri` match modifiers in `.declaration.http.servers[].locations[].urimatch` can be: - -- *prefix* - prefix URI matching -- *exact* - exact URI matching -- *regex* - case sensitive regex matching -- *iregex* - case insensitive regex matching -- *best* - case sensitive regex matching that halts any other location matching once a match is made - -### API Gateway ### - -Swagger files and OpenAPI schemas can be used to automatically configure NGINX as an API Gateway. Developer portal creation is supported through [Redocly](https://redocly.com/) - -Declaration path `.declaration.http.servers[].locations[].apigateway` defines the API Gateway configuration: - -- `openapi_schema` - the base64-encoded schema, or the schema URL. YAML and JSON are supported -- `api_gateway.enabled` - enable/disable API Gateway provisioning -- `api_gateway.strip_uri` - removes the `.declaration.http.servers[].locations[].uri` part of the URI before forwarding requests to the upstream -- `api_gateway.server_url` - the base URL of the upstream server -- `developer_portal.enabled` - enable/disable Developer portal provisioning -- `developer_portal.uri` - the trailing part of the Developer portal URI, this is appended to `.declaration.http.servers[].locations[].uri`. If omitted it defaults to `devportal.html` -- `authentication` - optional, used to enforce JWT authentication at the API Gateway level -- `authentication.client` - JWT authentication profile name -- `authentication.enforceOnPaths` - if set to `true` JWT authentication is enforced on all API endpoints listed under `authentication.paths`. if set to `false` JWT authentication is enforced on all API endpoints but those listed under `authentication.paths` -- `rate_limit` - optional, used to enforce rate limiting at the API Gateway level -- `rate_limit.enforceOnPaths` - if set to `true` rate limiting is enforced on all API endpoints listed under `rate_limit.paths`. if set to `false` rate limiting is enforced on all API endpoints but those listed under `rate_limit.paths` - -A sample API Gateway declaration to publish the `https://petstore.swagger.io` REST API and enforce: - -- REST API endpoint URIs -- HTTP Methods -- Rate limiting on `/user/login` and `/user/logout` -- JWT authentication on `/user/login` and `/usr/logout` - -is: - -```commandline -{ - "output": { - "type": "nms", - "nms": { - "url": "{{nim_host}}", - "username": "{{nim_username}}", - "password": "{{nim_password}}", - "instancegroup": "{{nim_instancegroup}}", - "synctime": 0, - "modules": [ - "ngx_http_js_module", - "ngx_stream_js_module" - ] - } - }, - "declaration": { - "http": { - "servers": [ - { - "name": "Petstore API", - "names": [ - "apigw.nginx.lab" - ], - "resolver": "8.8.8.8", - "listen": { - "address": "80" - }, - "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", - "authentication": [ - { - "profile": "Source of truth authentication profile using HTTP header token authentication" - } - ] - }, - "api_gateway": { - "enabled": true, - "strip_uri": true, - "server_url": "https://petstore.swagger.io/v2" - }, - "developer_portal": { - "enabled": false, - "uri": "/petstore-devportal.html" - }, - "authentication": { - "client": [ - { - "profile": "Petstore JWT Authentication" - } - ], - "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" - } - } - ] - } - ], - "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 - } - } - ], - "server": [ - { - "name": "Source of truth authentication profile using HTTP header token authentication", - "type": "token", - "token": { - "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU", - "type": "header", - "location": "X-AUTH-TOKEN" - } - } - ] - } - } - } -} -``` - -It can be tested using: - -``` -curl -iH "Host: apigw.nginx.lab" http:///petstore/store/inventory -``` - -Authentication failed: - -``` -curl -i http://apigw.nginx.lab/petstore/user/login -``` - -Authentication Succeeded: - -``` -curl -i http://apigw.nginx.lab/petstore/user/login -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU" -``` - -The API Developer portal can be accessed at: - - http:///petstore/petstore-devportal.html - -### Maps ### - -Map entries `.declaration.maps[].entries.keymatch` can be: - -- *exact* - exact variable matching -- *regex* - case sensitive regex matching -- *iregex* - case insensitive regex matching - -### Snippets ### - -Snippets for http, upstream, server and location can be specified as: -- base64-encoded content -- HTTP(S) URL of a source of truth to fetch snippet content from. Content on the source of truth must be plaintext, it will be automatically base64-encoded - -### Methods ### - -- `POST /v4.1/config/` - Publish a new declaration -- `PATCH /v4.1/config/{config_uid}` - Update an existing declaration - - Per-HTTP server CRUD - - Per-HTTP upstream CRUD - - Per-Stream server CRUD - - Per-Stream upstream CRUD - - Per-NGINX App Protect WAF policy CRUD -- `GET /v4.1/config/{config_uid}` - Retrieve an existing declaration -- `DELETE /v4.1/config/{config_uid}` - Delete an existing declaration - -### Usage Examples ### - -A sample Postman collection is available [here](/contrib/postman) \ No newline at end of file diff --git a/USAGE-v5.0.md b/USAGE-v5.0.md new file mode 100644 index 0000000..f399187 --- /dev/null +++ b/USAGE-v5.0.md @@ -0,0 +1,197 @@ +# Usage for NGINX Declarative API v5.0 + +Version 5.0 supports: + +- [NGINX Instance Manager](https://docs.nginx.com/nginx-management-suite/nim/) 2.14+ +- [NGINX Plus](https://docs.nginx.com/nginx/) R30+ +- [NGINX App Protect WAF](https://docs.nginx.com/nginx-app-protect-waf/) 4.2.0+ with compiled [policy bundles](https://docs.nginx.com/nginx-app-protect-waf/v5/admin-guide/compiler/) + +The JSON schema is self explanatory. See also the [sample Postman collection](/contrib/postman) + +- `.output.type` defines how NGINX configuration will be returned: + - *plaintext* - plaintext format + - *json* - JSON-wrapped, base64-encoded + - *configmap* - Kubernetes Configmap in YAML format. + - `.output.configmap.name` must be set to the ConfigMap name + - `.output.configmap.filename` must be set to the NGINX configuration filename + - `.output.configmap.namespace` the optional namespace for the ConfigMap + - *http* - NGINX configuration is POSTed to custom url + - `.output.http.url` the URL to POST the configuration to + - *nms* - NGINX configuration is published as a Staged Config to NGINX Instance Manager + - `.output.nms.url` the NGINX Instance Manager URL + - `.output.nms.username` the NGINX Instance Manager authentication username + - `.output.nms.password` the NGINX Instance Manager authentication password + - `.output.nms.instancegroup` the NGINX Instance Manager instance group to publish the configuration to + - `.output.nms.synctime` **optional**, used for GitOps autosync. When specified and the declaration includes HTTP(S) references to NGINX App Protect policies, TLS certificates/keys/chains, the HTTP(S) endpoints will be checked every `synctime` seconds and if external contents have changed, the updated configuration will automatically be published to NGINX Instance Manager + - `.output.nms.modules` an optional array of NGINX module names (ie. 'ngx_http_app_protect_module', 'ngx_http_js_module','ngx_stream_js_module') + - `.output.nms.certificates` an optional array of TLS certificates/keys/chains to be published + - `.output.nms.certificates[].type` the item type ('certificate', 'key', 'chain') + - `.output.nms.certificates[].name` the certificate/key/chain name with no path/extension (ie. 'test-application') + - `.output.nms.certificates[].contents` the content: this can be either base64-encoded or be a HTTP(S) URL that will be fetched dynamically from a source of truth + - `.output.nms.policies[]` an optional array of NGINX App Protect security policies + - `.output.nms.policies[].type` the policy type ('app_protect') + - `.output.nms.policies[].name` the policy name (ie. 'prod-policy') + - `.output.nms.policies[].active_tag` the policy tag to enable among all available versions (ie. 'v1') + - `.output.nms.policies[].versions[]` array with all available policy versions + - `.output.nms.policies[].versions[].tag` the policy version's tag name + - `.output.nms.policies[].versions[].displayName` the policy version's display name + - `.output.nms.policies[].versions[].description` the policy version's description + - `.output.nms.policies[].versions[].contents` this can be either base64-encoded or be a HTTP(S) URL that will be fetched dynamically from a source of truth +- `.declaration` describes the NGINX configuration to be created. + +### Locations ### + +Locations `.declaration.http.servers[].locations[].uri` match modifiers in `.declaration.http.servers[].locations[].urimatch` can be: + +- *prefix* - prefix URI matching +- *exact* - exact URI matching +- *regex* - case sensitive regex matching +- *iregex* - case insensitive regex matching +- *best* - case sensitive regex matching that halts any other location matching once a match is made + +### Javascript profiles ### + +NGINX Javascript profiles are defined in `.declaration.http.njs[]`: + +- `name` - the NJS profile name +- `file.content` - the base64-encoded njs source code or the `http(s)://` URL of the file +- `file.authentication.server[0].profile` - authentication profile name if `file.content` is a URL and the request must be authenticated + +### Javascript hooks ### + +NGINX Javascript hooks can be used in: + +- `.declaration.http.njs` + - Supported hooks: + - `js_preload_object' + - 'js_set` +- `.declaration.http.server[].njs` + - Supported hooks: + - `js_preload_object' + - 'js_set` +- `.declaration.http.server[].location[].njs` + - Supported hooks: + - `js_body_filter' + - 'js_content' + - 'js_header_filter' + - 'js_periodic' + - 'js_preload_object' + - 'js_set` + +Hooks invocation is: + +``` +"njs": [ + { + "hook": { + "name": "", + "parameters": [ + { + "name": "", + "value": "" + } + ] + }, + "profile": "", + "function": "" + } +] +``` + +For detailed examples see the [Postman collection](/contrib/postman) + +### API Gateway ### + +Swagger files and OpenAPI schemas can be used to automatically configure NGINX as an API Gateway. Developer portal creation is supported through [Redocly](https://redocly.com/) + +Declaration path `.declaration.http.servers[].locations[].apigateway` defines the API Gateway configuration: + +- `openapi_schema` - the base64-encoded schema, or the schema URL. YAML and JSON are supported +- `api_gateway.enabled` - enable/disable API Gateway provisioning +- `api_gateway.strip_uri` - removes the `.declaration.http.servers[].locations[].uri` part of the URI before forwarding requests to the upstream +- `api_gateway.server_url` - the base URL of the upstream server +- `developer_portal.enabled` - enable/disable Developer portal provisioning +- `developer_portal.uri` - the trailing part of the Developer portal URI, this is appended to `.declaration.http.servers[].locations[].uri`. If omitted it defaults to `devportal.html` +- `authentication` - optional, used to enforce authentication at the API Gateway level +- `authentication.client[]` - authentication profile names +- `authentication.enforceOnPaths` - if set to `true` authentication is enforced on all API endpoints listed under `authentication.paths`. if set to `false` authentication is enforced on all API endpoints but those listed under `authentication.paths` +- `authentication.paths` - paths to enforce authentication +- `authorization[]` - optional, used to enforce authorization +- `authorization[].profile` - authorization profile name +- `authorization[].enforceOnPaths` - if set to `true` authorization is enforced on all API endpoints listed under `authorization.paths`. if set to `false` authorization is enforced on all API endpoints but those listed under `authorization[].paths` +- `authorization[].paths` - paths to enforce authorization +- `rate_limit` - optional, used to enforce rate limiting at the API Gateway level +- `rate_limit.enforceOnPaths` - if set to `true` rate limiting is enforced on all API endpoints listed under `rate_limit.paths`. if set to `false` rate limiting is enforced on all API endpoints but those listed under `rate_limit.paths` + +A sample API Gateway declaration to publish the `https://petstore.swagger.io` REST API and enforce: + +- REST API endpoint URIs +- HTTP Methods +- Rate limiting on `/user/login`, `/usr/logout` and `/pet/{petId}/uploadImage` +- JWT authentication on `/user/login`, `/usr/logout` and `/pet/{petId}/uploadImage` +- JWT claim-based authorization on `/user/login`, `/usr/logout` and `/pet/{petId}/uploadImage` + +can be found in the [Postman collection](/contrib/) + +### Maps ### + +Map entries `.declaration.maps[].entries.keymatch` can be: + +- *exact* - exact variable matching +- *regex* - case sensitive regex matching +- *iregex* - case insensitive regex matching + +### Snippets ### + +Snippets for `http`, `upstream`, `server` and `location` can be specified as: +- base64-encoded content +- HTTP(S) URL of a source of truth to fetch snippet content from. Content on the source of truth must be plaintext +- source of truth authentication is supported through authentication profiles + +### Authentication profiles ### + +Client and Server authentication profiles can be defined in the declarative json at `.declaration.http.authentication` + +```commandline + "authentication": { + "client": [ + { + "name": "", + "type": "", + "": { + "": "", + [...] + } + }, + [...] + ], + "server": [ + { + "name": "", + "type": "", + "": { + "": "", + [...] + } + }, + [...] + ] +``` + +For a list of all supported authentication profile types see the [feature matrix](/FEATURES.md) + +### API endpoints ### + +- `POST /v5.0/config/` - Publish a new declaration +- `PATCH /v5.0/config/{config_uid}` - Update an existing declaration + - Per-HTTP server CRUD + - Per-HTTP upstream CRUD + - Per-Stream server CRUD + - Per-Stream upstream CRUD + - Per-NGINX App Protect WAF policy CRUD +- `GET /v5.0/config/{config_uid}` - Retrieve an existing declaration +- `DELETE /v5.0/config/{config_uid}` - Delete an existing declaration + +### Usage Examples ### + +A sample Postman collection is available [here](/contrib/postman) \ No newline at end of file diff --git a/contrib/postman/NGINX Declarative API.postman_collection.json b/contrib/postman/NGINX Declarative API.postman_collection.json index 5484297..9ca3cde 100644 --- a/contrib/postman/NGINX Declarative API.postman_collection.json +++ b/contrib/postman/NGINX Declarative API.postman_collection.json @@ -1,15 +1,15 @@ { "info": { - "_postman_id": "2c626339-a5f6-4283-8a6a-6dc7469cd514", + "_postman_id": "5182067d-a117-4979-850b-80d3310afd9a", "name": "NGINX Declarative API", - "description": "Declarative REST API and GitOps automation layer for NGINX Instance Manager", + "description": "Declarative REST API and GitOps automation layer for NGINX Instance Manager\n\n[https://github.com/f5devcentral/NGINX-Declarative-API/](https://github.com/f5devcentral/NGINX-Declarative-API/)", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", "_exporter_id": "1667416", - "_collection_link": "https://orange-rocket-1353.postman.co/workspace/NGINX-Declarative-API~8ba6e9c1-a04b-4484-8193-bbb142560553/collection/1667416-2c626339-a5f6-4283-8a6a-6dc7469cd514?action=share&source=collection_link&creator=1667416" + "_collection_link": "https://orange-rocket-1353.postman.co/workspace/NGINX-Declarative-API~8ba6e9c1-a04b-4484-8193-bbb142560553/collection/1667416-5182067d-a117-4979-850b-80d3310afd9a?action=share&source=collection_link&creator=1667416" }, "item": [ { - "name": "v4.1", + "name": "v4.2", "item": [ { "name": "Configuration generation", @@ -231,7 +231,8 @@ "exec": [ "" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -240,7 +241,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"json\"\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"json\"\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n }\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -271,7 +272,8 @@ "exec": [ "" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -280,7 +282,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"configmap\",\n \"configmap\": {\n \"name\": \"nginx.test\",\n \"filename\": \"testservice.conf\",\n \"namespace\": \"test-namespace\"\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"configmap\",\n \"configmap\": {\n \"name\": \"nginx.test\",\n \"filename\": \"testservice.conf\",\n \"namespace\": \"test-namespace\"\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n }\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -311,7 +313,8 @@ "exec": [ "" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -320,7 +323,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"http\",\n \"http\": {\n \"url\": \"http://192.168.1.19:8080/path/service\"\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"http\",\n \"http\": {\n \"url\": \"http://192.168.1.19:8080/path/service\"\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"sample_layer4_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\",\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"upstream\": \"l4_upstream\",\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbDQgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"l4_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n }\n ]\n }\n ]\n },\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"server_8080.nginx.lab\",\n \"server_8081.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/test\",\n \"urimatch\": \"exact\",\n \"upstream\": \"http://test_upstream\",\n \"health_check\": {\n \"enabled\": true,\n \"uri\": \"/healthcheck\",\n \"interval\": 5,\n \"fails\": 3,\n \"passes\": 2\n },\n \"rate_limit\": {\n \"profile\": \"test_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 10,\n \"delay\": 3\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_illegal\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgbG9jYXRpb24gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"test_policy\",\n \"log\": {\n \"profile_name\": \"log_blocked\",\n \"enabled\": true,\n \"destination\": \"192.168.1.5:514\"\n }\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgc2VydmVyIHNuaXBwZXQgY29tbWVudAo=\"\n }\n },\n {\n \"name\": \"another HTTP test application\",\n \"names\": [\n \"server_443\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"10.0.0.2:80\",\n \"backup\": true\n }\n ],\n \"sticky\": {\n \"cookie\": \"cookie_name\",\n \"expires\": \"1h\",\n \"domain\": \".testserver\",\n \"path\": \"/\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgdXBzdHJlYW0gc25pcHBldCBjb21tZW50Cg==\"\n }\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"test_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n }\n ],\n \"maps\": [\n {\n \"match\": \"$host$request_uri\",\n \"variable\": \"$backend\",\n \"entries\": [\n {\n \"key\": \"www.test.lab/app1/\",\n \"keymatch\": \"iregex\",\n \"value\": \"upstream_1\"\n },\n {\n \"key\": \"(.*).test.lab/app2/\",\n \"keymatch\": \"regex\",\n \"value\": \"upstream_2\"\n }\n ]\n }\n ],\n \"nginx_plus_api\": {\n \"write\": true,\n \"listen\": \"127.0.0.1:8080\",\n \"allow_acl\": \"0.0.0.0/0\"\n },\n \"snippet\": {\n \"content\": \"IyBUaGlzIGlzIGEgSFRUUCBzbmlwcGV0IGNvbW1lbnQK\"\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -368,7 +371,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -377,7 +381,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -417,7 +421,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -426,7 +431,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"name\": \"Petstore API Gateway\",\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -449,7 +454,7 @@ "response": [] }, { - "name": "Petstore API Gateway RateLimit + JWT Authentication", + "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ", "event": [ { "listen": "test", @@ -461,7 +466,53 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\",\n \"authentication\": [\n {\n \"profile\": \"Source of truth authentication profile using HTTP header token authentication\"\n }\n ]\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ],\n \"server\": [\n {\n \"name\": \"Source of truth authentication profile using bearer token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Source of truth authentication profile using HTTP header token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + }, + { + "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} } } ], @@ -470,7 +521,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\",\n \"authentication\": [\n {\n \"profile\": \"Source of truth authentication profile using HTTP header token authentication\"\n }\n ]\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ],\n \"server\": [\n {\n \"name\": \"Source of truth authentication profile using bearer token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Source of truth authentication profile using HTTP header token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n }\n ]\n }\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ],\n \"errorcode\": 403\n }\n ]\n }\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -507,7 +558,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -516,7 +568,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore and Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"192.168.2.13\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n },\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"ergast_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"ergast_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n },\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n }\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore and Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"192.168.2.13\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n },\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"ergast_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"ergast_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n },\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -556,7 +608,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -565,7 +618,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -600,7 +653,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -609,7 +663,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n },\n {\n \"server\": \"10.0.0.3:80\"\n },\n {\n \"server\": \"10.0.0.4:80\"\n },\n {\n \"server\": \"10.0.0.5:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n },\n {\n \"server\": \"10.0.0.3:80\"\n },\n {\n \"server\": \"10.0.0.4:80\"\n },\n {\n \"server\": \"10.0.0.5:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -645,7 +699,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -654,7 +709,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n },\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.1:80\"\n },\n {\n \"server\": \"192.168.1.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n },\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.1:80\"\n },\n {\n \"server\": \"192.168.1.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -690,7 +745,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -699,7 +755,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server_v2.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_v2_log\",\n \"error\": \"/var/log/nginx/error_v2_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream_added\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.100:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"192.168.1.101:80\"\n },\n {\n \"server\": \"192.168.1.102:80\"\n },\n {\n \"server\": \"192.168.1.103:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server_v2.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_v2_log\",\n \"error\": \"/var/log/nginx/error_v2_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream_added\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.100:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"192.168.1.101:80\"\n },\n {\n \"server\": \"192.168.1.102:80\"\n },\n {\n \"server\": \"192.168.1.103:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -735,7 +791,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -744,7 +801,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\"\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\"\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -780,7 +837,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -789,7 +847,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_10053_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n },\n {\n \"server\": \"10.0.0.3:53\"\n },\n {\n \"server\": \"10.0.0.4:53\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_10053_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n },\n {\n \"server\": \"10.0.0.3:53\"\n },\n {\n \"server\": \"10.0.0.4:53\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -825,7 +883,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -834,7 +893,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\",\n \"listen\": {\n \"address\": \"15432\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_15432_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\",\n \"origin\": [\n {\n \"server\": \"172.16.10.1:5432\"\n },\n {\n \"server\": \"172.16.10.1:5432\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\",\n \"listen\": {\n \"address\": \"15432\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_15432_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\",\n \"origin\": [\n {\n \"server\": \"172.16.10.1:5432\"\n },\n {\n \"server\": \"172.16.10.1:5432\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -870,7 +929,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -879,7 +939,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\"\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\"\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -915,7 +975,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -924,137 +985,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\"\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration status", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}", - "status" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete declaration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\"\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1318,7 +1249,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -1327,7 +1259,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\"\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1482,10 +1414,10 @@ ] }, { - "name": "JWT Client Authentication", + "name": "HTTP Headers Manipulation", "item": [ { - "name": "JWT Client Authentication - local JWT key", + "name": "Create test echo service", "event": [ { "listen": "test", @@ -1506,7 +1438,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online-boutique.lan-access_log\",\n \"error\": \"/var/log/nginx/online-boutique.lan-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://onlineboutique_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"online_boutique_jwt_authentication_local\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"onlineboutique_upstream\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"online_boutique_jwt_authentication_local\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Online Boutique Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n },\n {\n \"name\": \"online_boutique_jwt_authentication_key_from_url\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Online Boutique Authentication GitOps\",\n \"key\": \"http://192.168.2.19/jwks.json\",\n \"cachetime\": 5\n }\n }\n ]\n }\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1529,7 +1461,7 @@ "response": [] }, { - "name": "Change to use JWT key stored on external URL", + "name": "Manipulate headers - server level", "event": [ { "listen": "test", @@ -1550,7 +1482,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online-boutique.lan-access_log\",\n \"error\": \"/var/log/nginx/online-boutique.lan-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://onlineboutique_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"online_boutique_jwt_authentication_key_from_url\"\n }\n ]\n }\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1572,14 +1504,9 @@ } }, "response": [] - } - ] - }, - { - "name": "Load Balancing", - "item": [ + }, { - "name": "Create Coffee App v1", + "name": "Manipulate headers - location level", "event": [ { "listen": "test", @@ -1596,11 +1523,11 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Coffee v1\",\n \"names\": [\n \"coffee-v1.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/coffee-v1-access_log\",\n \"error\": \"/var/log/nginx/coffee-v1-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"coffee-v1.k8s.f5.ff.lan\"\n }\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"Date\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Hidden Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://coffee-v1\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"coffee-v1\",\n \"origin\": [\n {\n \"server\": \"192.168.2.47\"\n },\n {\n \"server\": \"192.168.2.48\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1608,7 +1535,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1616,34 +1543,43 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] - }, + } + ], + "description": "Test using:\n\n`curl -i echo.vm-test.ie.ff.lan`\n\nThe actual FQDN can be modified in the request JSON body" + }, + { + "name": "HTTPS server", + "item": [ { - "name": "Get declaration status", + "name": "TLS Offload", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, "request": { - "method": "GET", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1651,7 +1587,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1659,36 +1595,36 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}", - "status" + "config" ] } }, "response": [] }, { - "name": "Get declaration", + "name": "TLS Offload and NGINX App Protect", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, "request": { - "method": "GET", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - GitOps\",\n \"description\": \"This is a production-ready policy - Managed by GitOps\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1696,7 +1632,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1704,32 +1640,40 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}" + "config" ] } }, "response": [] - }, + } + ] + }, + { + "name": "JWT Client Authentication", + "item": [ { - "name": "Delete declaration", + "name": "JWT Client Authentication - local JWT key and Bearer token", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], "type": "text/javascript" } } ], "request": { - "method": "DELETE", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"JWT Auth with hardwired key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\"\n }\n },\n {\n \"name\": \"JWT Auth with external key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication GitOps\",\n \"key\": \"http://192.168.2.5:20080/jwks.json\",\n \"cachetime\": 5\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in auth_token query string parameter\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$arg_auth_token\"\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$http_x_auth_token\"\n }\n }\n ]\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1737,7 +1681,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1745,20 +1689,14 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}" + "config" ] } }, "response": [] - } - ] - }, - { - "name": "mTLS Client Authentication", - "item": [ + }, { - "name": "HTTPS server with mTLS, OCSP, SSL Stapling", + "name": "JWT secret fetched from URL", "event": [ { "listen": "test", @@ -1775,11 +1713,11 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"server_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdSRENDQkN5Z0F3SUJBZ0lVTTNJQVZIRmxhSTVsY1d0TjZxOUVhcnlka0w4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TVRBd01qTTVXaGNOCk1qUXdOakEzTVRBd01qTTVXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFMRFQ2bnZleVZlNi9VZlk2aUtHVC9oV1A0cktDSGR0ClloZWU3RGVZR29QWGhGVjB6a3grVWExanBEZ21WUE1kVEJBdnoxODg5NzlEcHBqdmNYeFhsRmpnaUhjWDhpWVgKSXovSUVMc3dKRUNITWNsNkxmelA5eDVUY1gxTEdFblFOTWhHRzA2MjlxU2NCQmQyUUNiWlY0UWE1TkxlQnQ4cQpHQ2lXY3JiQnR3YlpiSGo1dk9aenJrdHBtRFBGS1V4bXR5b2dBQnNaTllnL0F3Y1l2RXdBOEQ0QTN0VEgxcGhvCkdYY3ZvZWpJelhRMUdmYys5azR3OFhHYWFQOGd2bTdOMXN2MnU2Yld4SHRGZHpWQk9udzJyaHUvWGYyY0N0dW4KUnIxSENKQXRRSDlkbDhzZks1czBSRlVuTlVYbFBiNTFBTjBjVFVGbEYrZlVUVmVON3dNMTdmeVZVY3IydTltSwo0UGdoWjkvMml0ZUpZV3hjK3k4V2NEQzBUV3hwZ2paVEw5Tk1GK2t6SXV2TjJOWFFybjcvSU5UQTMvNFlmWGRPCloxelpTdTlkclRMcG5DZHRpOWxuRHBKODd3bW41cVZSTlZiTlZRbldEeW5yZnoyTU1DY21jLzcvdkJFN2dDemQKNFJLWHJLdHloenlQSitycmh3NmpxYVA4QytaZGRvKzkvak9QVDFTSnUxZ21VbzFuZ2hBMWh2N0M5RUYrM2xQVApYSk5WV3dtYkdWK0p4cUdKSjJSa2toMlIrZTVIREdRY2hGWjJIcXBGTGVQN0trTHJBR2RkZFZQWEZhQ0RiU0R6ClJQd0I5WFlhakg5Zm5QWEtFT3ZpVEJhQVNjWUZwTXB5cm02UkxHUGRSVnE2RUNYVlB4MDdHdGFCaEVvVWIwK2YKVkZnNExtQkx4MldQQWdNQkFBR2pnZ0VpTUlJQkhqQUpCZ05WSFJNRUFqQUFNQkVHQ1dDR1NBR0crRUlCQVFRRQpBd0lHUURBekJnbGdoa2dCaHZoQ0FRMEVKaFlrVDNCbGJsTlRUQ0JIWlc1bGNtRjBaV1FnVTJWeWRtVnlJRU5sCmNuUnBabWxqWVhSbE1CMEdBMVVkRGdRV0JCVHZFZWJGK1JDV0JhcGVPWUdpQ0YyVHZxbExYekNCaEFZRFZSMGoKQkgwd2U0QVVFdW9Db3kvcmhMQmxzcm5KdXE2QzFJczQxbFNoVGFSTE1Fa3hDekFKQmdOVkJBWVRBa2xVTVEwdwpDd1lEVlFRSURBUkJjM1JwTVJFd0R3WURWUVFLREFoVVpYTjBJRXhoWWpFWU1CWUdBMVVFQXd3UGRtMHRZbXhoCmJtc3VabVl1YkdGdWdoUld4QjhCa3lmK1RkQXc2Q3dPZE1aT0k0NlZ2REFPQmdOVkhROEJBZjhFQkFNQ0JhQXcKRXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFHUDR6ZkdseTI1RwpneTBSeC9SSTNpNzJDVlIrSXY3SW5WTUVGWDZqRHRNV3hSblFtRGZsMWtTOVF1Y3hNb0tnOE9URStMcnlzdGJsClF4WGZiakZQekNoNHB1UGtGTmNBeG1mVmR4b20xR1lodWpoYTBQOUswUURZSDZycGlUaFdSQ2greUovQm1qZ2wKTlJabks4WGRqME85Ui9XKzJrTFRac2VFbS9hZHFVQ3dkYzNBWWlNWGh4QXkvQlh3bFRQeDMyMHZCcXYxZGFyVgp5ZlVoRlM1Rkg3enV2bGtGQ1p6M3lpOGYvYXMwbkRTUkFrY3dPRFQvN1diQlN4QTk3ZzJmRk1EMEI3WlUvbndGCmU4VnRzNDl3YmZ6QWJRMk40RUc2OEVhODE1VlFRM2N6YWthdjBCdkxHL2UwT0habGxYcUVhV1ZlWFJtSWFFOHcKWko5OEhUaDJMbUlFV2Jpdm94Kyt2UXd3bVhKTm1DRFVXNnVmcHdBOVdKQ0VhYmhxeXdGVzh1dFVENzRTVXE3SApEUDhNamtJZ0o3ekl2Tkd1RkFsSzd6c2xpV2pzeUN1OGVNamhvN2pVRFhGR1R0R0ZMUGtVa08vSysrSGVVRFg0Cm1OWDJ2aHI3NGRqRkNBTTEvOTYxWnB5NUFYUzZkd2g3MFlJL2dMdldSL0J1ejBnNEp6YUI2UFo4M1ErYm9QVHYKM1ZIS2xOWjlKQlhRTmtSc3N6U0dYWG5MYmtOTmNwVFg2cnAyZ1pUSS9NNDhGTnBxanAxOXRpQVg3bWN0cTl2SgpNejhvemhEcHZmSTlnMjFsNFZlRGdpbWEwTDVBc1pQbFdIQlZjcy9yL3dMU2YzWFVYZEs0UHpCQUdIRFBidXYrCnpKOVNqS0NFVll2bHRhMHlUUVBCSFJPa2Y2MG1sVmh6Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"server_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBc05QcWU5N0pWN3I5UjlqcUlvWlArRlkvaXNvSWQyMWlGNTdzTjVnYWc5ZUVWWFRPClRINVJyV09rT0NaVTh4MU1FQy9QWHp6M3YwT21tTzl4ZkZlVVdPQ0lkeGZ5SmhjalA4Z1F1ekFrUUljeHlYb3QKL00vM0hsTnhmVXNZU2RBMHlFWWJUcmIycEp3RUYzWkFKdGxYaEJyazB0NEczeW9ZS0paeXRzRzNCdGxzZVBtOAo1bk91UzJtWU04VXBUR2EzS2lBQUd4azFpRDhEQnhpOFRBRHdQZ0RlMU1mV21HZ1pkeStoNk1qTmREVVo5ejcyClRqRHhjWnBvL3lDK2JzM1d5L2E3cHRiRWUwVjNOVUU2ZkRhdUc3OWQvWndLMjZkR3ZVY0lrQzFBZjEyWHl4OHIKbXpSRVZTYzFSZVU5dm5VQTNSeE5RV1VYNTlSTlY0M3ZBelh0L0pWUnl2YTcyWXJnK0NGbjMvYUsxNGxoYkZ6NwpMeFp3TUxSTmJHbUNObE12MDB3WDZUTWk2ODNZMWRDdWZ2OGcxTURmL2hoOWQwNW5YTmxLNzEydE11bWNKMjJMCjJXY09rbnp2Q2FmbXBWRTFWczFWQ2RZUEtldC9QWXd3Snlaei92KzhFVHVBTE4zaEVwZXNxM0tIUEk4bjZ1dUgKRHFPcG8vd0w1bDEyajczK000OVBWSW03V0NaU2pXZUNFRFdHL3NMMFFYN2VVOU5jazFWYkNac1pYNG5Hb1lrbgpaR1NTSFpINTdrY01aQnlFVm5ZZXFrVXQ0L3NxUXVzQVoxMTFVOWNWb0lOdElQTkUvQUgxZGhxTWYxK2M5Y29RCjYrSk1Gb0JKeGdXa3luS3VicEVzWTkxRldyb1FKZFUvSFRzYTFvR0VTaFJ2VDU5VVdEZ3VZRXZIWlk4Q0F3RUEKQVFLQ0FnQVBUR1pQRFRsU004VlIvL3hSdkZrUzNUTm1LSkNPOUpHMkJYUGVZM1IzejUrTlhTdTBCb0craEk1aQpwVDVZUWtLZ2ErSi9GT0ZDVlBJRzdVQmVSNTE0Q3dVRGVMamtmci8zOXJFcjRNQmlMTkFyNUR3eVVUUEtGZUlOCnV2K0E4MWg5czBNTmpsck1ad3NibElsOFV2VjFZblpGb0J2c0Z0SThRTGZ3QTlaMzZ6dXRRNzRLR2h3TVBqaUMKMGgzK2xDeG9vcGdmd0JDWGx3d0dBeWZYVTRWMWQ5SFBpdktRQVFHakJDWDM0OWVTcEQxNDNLT21wQ2xmY01LQQp3QzU1bTZsbndCTUFIamlsaVo4RXBuNE8zUlEzSmxsVlpiaXl4RWdrZkE3TG1uNm9Ca3Jwc2VxdDVObThuRVhKCnBFbXhQcUl5Znc1WUNBMEhhNkM5WUhRN1RPRW9BbHBmWld4azAxSnpoVi9aK3FmVHM1YlMwQWNaTzFOVDRaeDgKWlF2eHQ0TDJINVcrK2R6RjhReTlidzQ2M3lKb1dydWxtNy9uQ3YvL1FpNGl0eHRnYyt0N2lwVXZzaUdTVktVWQpPelhCSXNWTUlnd0F6eUtTSEhPL21rMkEwVkgxaHB3emY2L0RzR2wxSjM4TU9pVGo4dEx1RWt3cFY4WGh5MnZwCkd0cXpsT21DS1hodlVDam9iZWlYSWJwSlIzeEM1NmliRjVadk0vQUdONzI5K0xKRFNwbHJtWVJRVHh1UTJWSE8KQWFXQ01SQWFBdUtCVnBxYTRjd25WRy9POEpkN2ZPSi9tMFlIN3FpRlJHREdvdVNOdHZJUUVtaXVkK3dRWjJ6dwpUcmFNVWk0SENtNEFPa0ZNVXBsRmt1ajA2ZHRqM2RIWUtPQkdMK25vaUp4WmJxb3kwUUtDQVFFQTFiZUl6WHh6CnRFRlp2OGRlOXljOWdCUUtNNUNIbHp6NUNMZXVkTitvemxxeDNCMW1PRStxbFkyaEd3RklIWVBJajFLYS83RlkKbExmNFpiUEJRMFhiNUo5VzQzSGIyTnEydXdRQ3ZiSXhVMW9zaGJVWlhZc2FUaE15azc2VzQ5YjU3UC9HdFE3NwpTbkVZTXNrTzRUQndyS3lBdVhDVHRtTk1Qa2J1NFBxT05PeVFQY3o3Yi92VEU1eERjMENMVS9oUXM3NWFHeCs1Citld2VjeEZNa0JKTVo2c2N5TzcySEdSNHZwTHduRXUvcU5uN2JmUElSaUx1T3BwTTdHNlUwQlBPL2todHJ5ZmQKV3U3MHJYZGJSdGRJUHlsQWxSOG9zczJqWWsrRHNPUnNESm9pbkk5WU1Va3dmdHdCNTRQbytGRGtGOHBzV202RQpSaklpenFBK0piWDlTd0tDQVFFQTA4Ly9oM0NabDg2M2xUZHNrU1JKRUZKc0RtdkZkUStzMWtlNUFwMjdnWTBXCmZJbEFGZFlRR3RORUVlTk9xS3EwdTFtS0lqWHFacWNTdU9DNzZIYTE5Tk9waHVoK1dwV0t2Ni9BTWtQSjE5SUIKQ3RqS0lkc2s0U2M3WG02MnNOV1pnQm5XT1Z3QVdzU0VzTHRac1NvWUJUVTJJS1pBOVJOWHhkSEQreGZ2SWJkNApZYngzTzk4WklNQzNlVFFiOW9jVHZab0RNWGdLaHRtTy9iMnlSeEVDSGpGRmxzYlhhc1RPeG5XOWZSVXJtdGVqCk9pdVlXaEZOM2R6dmpuVEdLY0xieWY0MWpHaUVUeFViUHVpei9ZMmk5NldCNVN6MW9zaGorRU1OaFhtRzZSYXUKQUIvelhwNldtSUJ2bDNpU0lzOGJRNkh3Qm1DTjc1R2VVVG1GUUlyaVRRS0NBUUVBbTkzWVN5MXA0VndNRGI5bApObElMRzM4Q0ZhdGlDRjR5cmpYd2FWSzVkWTVWeTFneHRmMzhSa2hkNkNrZUpGQjVsSFhGajVnVEo1dW84TnVSCnB2T3JOT2swNEhxb3dWWjZFSmtUT3JCY0l4TlFCMUFXS05BTHBrZUFDcHJreDFTQlFHVW0wZVFVUjYyRjNYd2YKZXdMdUdqRlJURzJiZlZpY1FZdFFLd3J4YmczZUFRU2ZtSU9MNVBDQmpPdlU4YS9YZzgvZlBZcjlBeFkrK3VMeAorTjB2bGlnSXZVN3lkYkNkRXpodGZVQU5qeU16cVhRemExdU1iWGNkaFEzOVFHaEIvZGhyRG1TL250Tko1YjEzCjk0bUpLbTkycDR0ckRrVEYxU3h5dWk5TjBqOFQ0U1QyU0RPOXg3ZkROOHRQdk5LYUYvUE01SU5YdXk1VGptajIKQ21EWlV3S0NBUUFOUVJYSFh1ZHRsWFR0ZEhOcHZiQ0l3ZStiRTJsZXd1VlkzMUlYZE5GWDhRRTROOHAzMDFaYwpwMTI2Rk5SR1A3QmhqTi9VOWpTOXliU2xOd0xyTUFxQTBJSHFQRUF6NE9tMnh3T3E0WTBPNFVoSmFubHpsdWYrCjR0cVhOU3hmY201UmtzeFIrSXpaSVRVQWJpalZxa0dvaWNUaVZDVDZjUVJzRDQxSStCMXhxYTV4eHo1YTA4SVoKeDVWemt5d3d5QkVYS3owSjZtNFdOQ1Q3Z2RSWEdCeGUwVXgrZStEZEFJWEQ2M2c1RElzVy9HbHRhVzcySytFSQpnaHZIZVUweExjMWRIWGd5V2hQMWN1ZXFqeHM4UVpHeUYzeENZQWJhOGRrM250S0l5S3NGaVBMSWRUZGdjMklQCkZ2SmtzeG5KN2RYUjdKODlkdXRLMDN6cHJrVEZYaXQ5QW9JQkFDcjhkb2ZCcFlFL1JuTlFwbVNET29DRm1sdTkKQlozN3h5K0puZ2FrQ2RSdHFyR1lDdkZMSnI2QnpGdXE0SHpsM0piTkRCM1BkYSs4Z2VNd2cxU1htTEhrRVFrTQpXV2ptNHpmU3hiTUtKamx3REdoeUlwSU9nQ2FQL1hyT2hxTGl4bnJ6UHFHZmM4R0FZTDE2Rm1PeGVqbVk5aERtCmNibkFqZlNwUjF1WEt2S2d6d1NLQ0VWdzc0VjJSRmRqQXBLVDl3bkpOQTZiWHQ5SXFkaS96d3BYbDQ0OVczdVMKNjRjVVpaK3luYnQ5QUlxbFNjMDdNRHl1TUtueExMbDFLeEJYenNxZlVsYWtlRGVoVmdGS05OOTNXQWJJc09ieAp1d1hTd0hXa1B6RGFHeE9wdzlSMHo2S2t2N25YZnBIYW1RWENBZEdsRjkyc1QwYW80Y3FuejFJSmJ2bz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"cacert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZjekNDQTF1Z0F3SUJBZ0lVVnNRZkFaTW4vazNRTU9nc0RuVEdUaU9PbGJ3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TURrMU5EQTRXaGNOCk1qUXdOakEzTURrMU5EQTRXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFLMlQwWXpkcjB0dWQzaVJRNGNzaGNhRVJTRzVjTDE2CkhRblhoYWw4emlUL1VRQUNIUGdzZDYwcWlEaldvQTJXb0lGWFFpUHkzOG1vZGtWRlR4Qmt5U2VldndOOFJiLzEKOFhaMS8yS1RnVmRDcHkvNm11WE15bXZYODJad05CVkV3QnoxUk5kbklUSk44cVh3a0d4bHozbDBib1loRkFyUQpNdmkxcW1RaHpDa2Zpb041MVkrYlBXOXpTQlFQdXNrcXJYYzRqTTJ0VENNQ2pTcFlvd1hXM1ppRmc5WEJ1Z09aCjFmdWd1Zmw4K1FJYzNZSEFoL1Z1NloraXFEOGxQeGRKODlBeDZaazVtOGdkVG9JdUhBbUNWaHFpUXBGRjkzSTgKbkYrSnRuYnBaNTRJUTZBbWYrYiswakMxdmY4Kzg0WUppaEVzWExyaGMxZTRTZ2dwdzEvcWpDb21QblhGVjEzUwpsUG5kVlhVR0taa1ZKdXdZTjJyZElmd3YrdCs5MGhwUVBmNmFBTjRCamRxOXdkdkQzSXVnS2JYZG5CQ0FUTEY4ClYyRTFTSE9VZGdRY3duK1d1WDVVOGdPa3B2b2VFN0g1REJ6Rks1WTZ2SHZlaTRlNkp3RTRDK3FJL1BmbTgreTEKNEpsOFBSOW5JQmdGQ3hrZWpwa2tRQ0I5U0dvMVZidzZhWmdZd0VQNHh6YXFYYXV3L3F4c0oxNUkrRTBndEs1OApuWUtkM0hqelk5Slh6V0NVNTdXbmc2SzNvTTIzNXpyRzJnNm1FaHQ4SStDckVMUFNuZURjZU8zVlJkc2dlblBCCis4U1JxVU8vWG9LWHNEU3I5amoxdWluVzYwTG5MZ0Zmc3JQeGlQVlZlMFh1TFZESlhCSlNoRDZDeGRyMnBSOGQKS25SRDZrTFpZZEtMQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqVwpWREFmQmdOVkhTTUVHREFXZ0JRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqV1ZEQVBCZ05WSFJNQkFmOEVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJad3B4Z2Z4N2thZFhvRHNyT1hUVXJ6dEFPMkFQRVJNaTAKaTkyNk9DTGFPbVVYZW1uKytXSUU1K2tUSE0wcS8vbUZCTURzSmdZSFVLUlNvRGNsNmh4TnVFNUNzS2trRVFTSgpMTHZrWlB0S2J5NGlxMitLZ1JtdVZxbXJNVTBYQzZMZDl3WmttL2huUjNtT3V6bko4MGZmV1JDQ0xGWDEwY2EzCnc5TGM1d1JLTFBZZXQvcEs5SitOYWN3TFJRYTczVFovMUpQNW9BU3czVjNoYkxlLy9UeWpnOURqUlZGY3FYWnEKWWs2Mm5qSkhZVzh3WmlhZzc0QXU4dHE5OG5KandBV1ROMFV5L2w1Q2VpWnV5bzZlU0RHVDNJNm1BdGU1VXBvWAppNXBkYlZ6VDdOZC9IOEwwZHZNdVZ2N0FmakZlcU91cUZNNkkzTnlvbStLWENxNmJQdGxBWEkzeVFZc0t4ZlRkCkw3SnRaTmx6MGJ6eHJhcHI4RmpYcjhML1ZkeHQza00xMnJwb2kzL3hsckR6Q2Q2b2YrQ1MxelBocUdpOUhvcUoKZEU5VGhYMklTdkd2akVSYzVVNFRsNjJBNHNyeGJQbUt0eWx3dGNGVEJacUJiRGY3ZjBBc2cveWhndXdTcktsQQpBNkRWVXVCRFErdGpwZ0N0b0ZlOEhLVDJ6UFVlaEQ2ZjVNQkhmU2ZUZ1crTlhFSXNvVDNsampjY1hsYXhPcFJWCkNQNWxCczNmekxyYnBxbUlLaWZhdWlTNWM4TzlSUjhjQTVzeWlBOTBmbmJIdDlmdGxpRG9jcFRzNUtrbjk2NkIKZUxMM1dXVldCYUtvanJzY1RkVXJoalNnVVBmam5FTXpnVzR2eEc3d3BVNHR2ME4yaEtHUWc0bVhhcDV0SU5Pcwp4WktnZXRHUldnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"vm-blank.ff.lan\"\n ],\n \"resolver\": \"192.168.1.13\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"server_cert\",\n \"key\": \"server_key\",\n \"trusted_ca_certificates\": \"cacert\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ],\n \"mtls\": {\n \"enabled\": \"on\",\n \"client_certificates\": \"cacert\"\n },\n \"ocsp\": {\n \"enabled\": \"on\",\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n },\n \"stapling\": {\n \"enabled\": true,\n \"verify\": true,\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n }\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/vm-blank.ff.lan_access_log\",\n \"error\": \"/var/log/nginx/vm-blank.ff.lan_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://origin_server\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"origin_server\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with external key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1787,7 +1725,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1795,19 +1733,15 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] - } - ] - }, - { - "name": "NGINX App Protect WAF", - "item": [ + }, { - "name": "Create initial NGINX configuration with NGINX App Protect WAF", + "name": "JWT token in auth_token query string parameter", "event": [ { "listen": "test", @@ -1824,11 +1758,11 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in auth_token query string parameter\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1836,7 +1770,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1844,14 +1778,15 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] }, { - "name": "Change active NGINX App Protect policy", + "name": "JWT token in HTTP X-Auth-Token header", "event": [ { "listen": "test", @@ -1872,7 +1807,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-allowed\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"Production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"Production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1894,9 +1829,14 @@ } }, "response": [] - }, + } + ] + }, + { + "name": "JWT Client Authentication and Authorization", + "item": [ { - "name": "Update TLS certificates", + "name": "JWT Client Authentication and Authorization", "event": [ { "listen": "test", @@ -1913,11 +1853,11 @@ } ], "request": { - "method": "PATCH", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www2.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www2.online-boutique.local.key\"\n }\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"JWT AuthN and AuthZ test server\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/auth-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/auth-test.nginx.lab_error_log\"\n },\n \"headers\": {\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-JWT-Group\",\n \"value\": \"$jwt_claim_roles\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"jwt_authentication_local\"\n }\n ]\n },\n \"authorization\": {\n \"profile\": \"jwt role based authorization\"\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"jwt_authentication_local\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Client Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"jwt_type\": \"signed\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"jwt role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1925,7 +1865,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1933,15 +1873,19 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}" + "config" ] } }, "response": [] - }, + } + ] + }, + { + "name": "mTLS Client Authentication", + "item": [ { - "name": "Disable NGINX App Protect WAF", + "name": "HTTPS server with mTLS, OCSP, SSL Stapling", "event": [ { "listen": "test", @@ -1953,16 +1897,17 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], "request": { - "method": "PATCH", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"server_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdSRENDQkN5Z0F3SUJBZ0lVTTNJQVZIRmxhSTVsY1d0TjZxOUVhcnlka0w4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TVRBd01qTTVXaGNOCk1qUXdOakEzTVRBd01qTTVXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFMRFQ2bnZleVZlNi9VZlk2aUtHVC9oV1A0cktDSGR0ClloZWU3RGVZR29QWGhGVjB6a3grVWExanBEZ21WUE1kVEJBdnoxODg5NzlEcHBqdmNYeFhsRmpnaUhjWDhpWVgKSXovSUVMc3dKRUNITWNsNkxmelA5eDVUY1gxTEdFblFOTWhHRzA2MjlxU2NCQmQyUUNiWlY0UWE1TkxlQnQ4cQpHQ2lXY3JiQnR3YlpiSGo1dk9aenJrdHBtRFBGS1V4bXR5b2dBQnNaTllnL0F3Y1l2RXdBOEQ0QTN0VEgxcGhvCkdYY3ZvZWpJelhRMUdmYys5azR3OFhHYWFQOGd2bTdOMXN2MnU2Yld4SHRGZHpWQk9udzJyaHUvWGYyY0N0dW4KUnIxSENKQXRRSDlkbDhzZks1czBSRlVuTlVYbFBiNTFBTjBjVFVGbEYrZlVUVmVON3dNMTdmeVZVY3IydTltSwo0UGdoWjkvMml0ZUpZV3hjK3k4V2NEQzBUV3hwZ2paVEw5Tk1GK2t6SXV2TjJOWFFybjcvSU5UQTMvNFlmWGRPCloxelpTdTlkclRMcG5DZHRpOWxuRHBKODd3bW41cVZSTlZiTlZRbldEeW5yZnoyTU1DY21jLzcvdkJFN2dDemQKNFJLWHJLdHloenlQSitycmh3NmpxYVA4QytaZGRvKzkvak9QVDFTSnUxZ21VbzFuZ2hBMWh2N0M5RUYrM2xQVApYSk5WV3dtYkdWK0p4cUdKSjJSa2toMlIrZTVIREdRY2hGWjJIcXBGTGVQN0trTHJBR2RkZFZQWEZhQ0RiU0R6ClJQd0I5WFlhakg5Zm5QWEtFT3ZpVEJhQVNjWUZwTXB5cm02UkxHUGRSVnE2RUNYVlB4MDdHdGFCaEVvVWIwK2YKVkZnNExtQkx4MldQQWdNQkFBR2pnZ0VpTUlJQkhqQUpCZ05WSFJNRUFqQUFNQkVHQ1dDR1NBR0crRUlCQVFRRQpBd0lHUURBekJnbGdoa2dCaHZoQ0FRMEVKaFlrVDNCbGJsTlRUQ0JIWlc1bGNtRjBaV1FnVTJWeWRtVnlJRU5sCmNuUnBabWxqWVhSbE1CMEdBMVVkRGdRV0JCVHZFZWJGK1JDV0JhcGVPWUdpQ0YyVHZxbExYekNCaEFZRFZSMGoKQkgwd2U0QVVFdW9Db3kvcmhMQmxzcm5KdXE2QzFJczQxbFNoVGFSTE1Fa3hDekFKQmdOVkJBWVRBa2xVTVEwdwpDd1lEVlFRSURBUkJjM1JwTVJFd0R3WURWUVFLREFoVVpYTjBJRXhoWWpFWU1CWUdBMVVFQXd3UGRtMHRZbXhoCmJtc3VabVl1YkdGdWdoUld4QjhCa3lmK1RkQXc2Q3dPZE1aT0k0NlZ2REFPQmdOVkhROEJBZjhFQkFNQ0JhQXcKRXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFHUDR6ZkdseTI1RwpneTBSeC9SSTNpNzJDVlIrSXY3SW5WTUVGWDZqRHRNV3hSblFtRGZsMWtTOVF1Y3hNb0tnOE9URStMcnlzdGJsClF4WGZiakZQekNoNHB1UGtGTmNBeG1mVmR4b20xR1lodWpoYTBQOUswUURZSDZycGlUaFdSQ2greUovQm1qZ2wKTlJabks4WGRqME85Ui9XKzJrTFRac2VFbS9hZHFVQ3dkYzNBWWlNWGh4QXkvQlh3bFRQeDMyMHZCcXYxZGFyVgp5ZlVoRlM1Rkg3enV2bGtGQ1p6M3lpOGYvYXMwbkRTUkFrY3dPRFQvN1diQlN4QTk3ZzJmRk1EMEI3WlUvbndGCmU4VnRzNDl3YmZ6QWJRMk40RUc2OEVhODE1VlFRM2N6YWthdjBCdkxHL2UwT0habGxYcUVhV1ZlWFJtSWFFOHcKWko5OEhUaDJMbUlFV2Jpdm94Kyt2UXd3bVhKTm1DRFVXNnVmcHdBOVdKQ0VhYmhxeXdGVzh1dFVENzRTVXE3SApEUDhNamtJZ0o3ekl2Tkd1RkFsSzd6c2xpV2pzeUN1OGVNamhvN2pVRFhGR1R0R0ZMUGtVa08vSysrSGVVRFg0Cm1OWDJ2aHI3NGRqRkNBTTEvOTYxWnB5NUFYUzZkd2g3MFlJL2dMdldSL0J1ejBnNEp6YUI2UFo4M1ErYm9QVHYKM1ZIS2xOWjlKQlhRTmtSc3N6U0dYWG5MYmtOTmNwVFg2cnAyZ1pUSS9NNDhGTnBxanAxOXRpQVg3bWN0cTl2SgpNejhvemhEcHZmSTlnMjFsNFZlRGdpbWEwTDVBc1pQbFdIQlZjcy9yL3dMU2YzWFVYZEs0UHpCQUdIRFBidXYrCnpKOVNqS0NFVll2bHRhMHlUUVBCSFJPa2Y2MG1sVmh6Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"server_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBc05QcWU5N0pWN3I5UjlqcUlvWlArRlkvaXNvSWQyMWlGNTdzTjVnYWc5ZUVWWFRPClRINVJyV09rT0NaVTh4MU1FQy9QWHp6M3YwT21tTzl4ZkZlVVdPQ0lkeGZ5SmhjalA4Z1F1ekFrUUljeHlYb3QKL00vM0hsTnhmVXNZU2RBMHlFWWJUcmIycEp3RUYzWkFKdGxYaEJyazB0NEczeW9ZS0paeXRzRzNCdGxzZVBtOAo1bk91UzJtWU04VXBUR2EzS2lBQUd4azFpRDhEQnhpOFRBRHdQZ0RlMU1mV21HZ1pkeStoNk1qTmREVVo5ejcyClRqRHhjWnBvL3lDK2JzM1d5L2E3cHRiRWUwVjNOVUU2ZkRhdUc3OWQvWndLMjZkR3ZVY0lrQzFBZjEyWHl4OHIKbXpSRVZTYzFSZVU5dm5VQTNSeE5RV1VYNTlSTlY0M3ZBelh0L0pWUnl2YTcyWXJnK0NGbjMvYUsxNGxoYkZ6NwpMeFp3TUxSTmJHbUNObE12MDB3WDZUTWk2ODNZMWRDdWZ2OGcxTURmL2hoOWQwNW5YTmxLNzEydE11bWNKMjJMCjJXY09rbnp2Q2FmbXBWRTFWczFWQ2RZUEtldC9QWXd3Snlaei92KzhFVHVBTE4zaEVwZXNxM0tIUEk4bjZ1dUgKRHFPcG8vd0w1bDEyajczK000OVBWSW03V0NaU2pXZUNFRFdHL3NMMFFYN2VVOU5jazFWYkNac1pYNG5Hb1lrbgpaR1NTSFpINTdrY01aQnlFVm5ZZXFrVXQ0L3NxUXVzQVoxMTFVOWNWb0lOdElQTkUvQUgxZGhxTWYxK2M5Y29RCjYrSk1Gb0JKeGdXa3luS3VicEVzWTkxRldyb1FKZFUvSFRzYTFvR0VTaFJ2VDU5VVdEZ3VZRXZIWlk4Q0F3RUEKQVFLQ0FnQVBUR1pQRFRsU004VlIvL3hSdkZrUzNUTm1LSkNPOUpHMkJYUGVZM1IzejUrTlhTdTBCb0craEk1aQpwVDVZUWtLZ2ErSi9GT0ZDVlBJRzdVQmVSNTE0Q3dVRGVMamtmci8zOXJFcjRNQmlMTkFyNUR3eVVUUEtGZUlOCnV2K0E4MWg5czBNTmpsck1ad3NibElsOFV2VjFZblpGb0J2c0Z0SThRTGZ3QTlaMzZ6dXRRNzRLR2h3TVBqaUMKMGgzK2xDeG9vcGdmd0JDWGx3d0dBeWZYVTRWMWQ5SFBpdktRQVFHakJDWDM0OWVTcEQxNDNLT21wQ2xmY01LQQp3QzU1bTZsbndCTUFIamlsaVo4RXBuNE8zUlEzSmxsVlpiaXl4RWdrZkE3TG1uNm9Ca3Jwc2VxdDVObThuRVhKCnBFbXhQcUl5Znc1WUNBMEhhNkM5WUhRN1RPRW9BbHBmWld4azAxSnpoVi9aK3FmVHM1YlMwQWNaTzFOVDRaeDgKWlF2eHQ0TDJINVcrK2R6RjhReTlidzQ2M3lKb1dydWxtNy9uQ3YvL1FpNGl0eHRnYyt0N2lwVXZzaUdTVktVWQpPelhCSXNWTUlnd0F6eUtTSEhPL21rMkEwVkgxaHB3emY2L0RzR2wxSjM4TU9pVGo4dEx1RWt3cFY4WGh5MnZwCkd0cXpsT21DS1hodlVDam9iZWlYSWJwSlIzeEM1NmliRjVadk0vQUdONzI5K0xKRFNwbHJtWVJRVHh1UTJWSE8KQWFXQ01SQWFBdUtCVnBxYTRjd25WRy9POEpkN2ZPSi9tMFlIN3FpRlJHREdvdVNOdHZJUUVtaXVkK3dRWjJ6dwpUcmFNVWk0SENtNEFPa0ZNVXBsRmt1ajA2ZHRqM2RIWUtPQkdMK25vaUp4WmJxb3kwUUtDQVFFQTFiZUl6WHh6CnRFRlp2OGRlOXljOWdCUUtNNUNIbHp6NUNMZXVkTitvemxxeDNCMW1PRStxbFkyaEd3RklIWVBJajFLYS83RlkKbExmNFpiUEJRMFhiNUo5VzQzSGIyTnEydXdRQ3ZiSXhVMW9zaGJVWlhZc2FUaE15azc2VzQ5YjU3UC9HdFE3NwpTbkVZTXNrTzRUQndyS3lBdVhDVHRtTk1Qa2J1NFBxT05PeVFQY3o3Yi92VEU1eERjMENMVS9oUXM3NWFHeCs1Citld2VjeEZNa0JKTVo2c2N5TzcySEdSNHZwTHduRXUvcU5uN2JmUElSaUx1T3BwTTdHNlUwQlBPL2todHJ5ZmQKV3U3MHJYZGJSdGRJUHlsQWxSOG9zczJqWWsrRHNPUnNESm9pbkk5WU1Va3dmdHdCNTRQbytGRGtGOHBzV202RQpSaklpenFBK0piWDlTd0tDQVFFQTA4Ly9oM0NabDg2M2xUZHNrU1JKRUZKc0RtdkZkUStzMWtlNUFwMjdnWTBXCmZJbEFGZFlRR3RORUVlTk9xS3EwdTFtS0lqWHFacWNTdU9DNzZIYTE5Tk9waHVoK1dwV0t2Ni9BTWtQSjE5SUIKQ3RqS0lkc2s0U2M3WG02MnNOV1pnQm5XT1Z3QVdzU0VzTHRac1NvWUJUVTJJS1pBOVJOWHhkSEQreGZ2SWJkNApZYngzTzk4WklNQzNlVFFiOW9jVHZab0RNWGdLaHRtTy9iMnlSeEVDSGpGRmxzYlhhc1RPeG5XOWZSVXJtdGVqCk9pdVlXaEZOM2R6dmpuVEdLY0xieWY0MWpHaUVUeFViUHVpei9ZMmk5NldCNVN6MW9zaGorRU1OaFhtRzZSYXUKQUIvelhwNldtSUJ2bDNpU0lzOGJRNkh3Qm1DTjc1R2VVVG1GUUlyaVRRS0NBUUVBbTkzWVN5MXA0VndNRGI5bApObElMRzM4Q0ZhdGlDRjR5cmpYd2FWSzVkWTVWeTFneHRmMzhSa2hkNkNrZUpGQjVsSFhGajVnVEo1dW84TnVSCnB2T3JOT2swNEhxb3dWWjZFSmtUT3JCY0l4TlFCMUFXS05BTHBrZUFDcHJreDFTQlFHVW0wZVFVUjYyRjNYd2YKZXdMdUdqRlJURzJiZlZpY1FZdFFLd3J4YmczZUFRU2ZtSU9MNVBDQmpPdlU4YS9YZzgvZlBZcjlBeFkrK3VMeAorTjB2bGlnSXZVN3lkYkNkRXpodGZVQU5qeU16cVhRemExdU1iWGNkaFEzOVFHaEIvZGhyRG1TL250Tko1YjEzCjk0bUpLbTkycDR0ckRrVEYxU3h5dWk5TjBqOFQ0U1QyU0RPOXg3ZkROOHRQdk5LYUYvUE01SU5YdXk1VGptajIKQ21EWlV3S0NBUUFOUVJYSFh1ZHRsWFR0ZEhOcHZiQ0l3ZStiRTJsZXd1VlkzMUlYZE5GWDhRRTROOHAzMDFaYwpwMTI2Rk5SR1A3QmhqTi9VOWpTOXliU2xOd0xyTUFxQTBJSHFQRUF6NE9tMnh3T3E0WTBPNFVoSmFubHpsdWYrCjR0cVhOU3hmY201UmtzeFIrSXpaSVRVQWJpalZxa0dvaWNUaVZDVDZjUVJzRDQxSStCMXhxYTV4eHo1YTA4SVoKeDVWemt5d3d5QkVYS3owSjZtNFdOQ1Q3Z2RSWEdCeGUwVXgrZStEZEFJWEQ2M2c1RElzVy9HbHRhVzcySytFSQpnaHZIZVUweExjMWRIWGd5V2hQMWN1ZXFqeHM4UVpHeUYzeENZQWJhOGRrM250S0l5S3NGaVBMSWRUZGdjMklQCkZ2SmtzeG5KN2RYUjdKODlkdXRLMDN6cHJrVEZYaXQ5QW9JQkFDcjhkb2ZCcFlFL1JuTlFwbVNET29DRm1sdTkKQlozN3h5K0puZ2FrQ2RSdHFyR1lDdkZMSnI2QnpGdXE0SHpsM0piTkRCM1BkYSs4Z2VNd2cxU1htTEhrRVFrTQpXV2ptNHpmU3hiTUtKamx3REdoeUlwSU9nQ2FQL1hyT2hxTGl4bnJ6UHFHZmM4R0FZTDE2Rm1PeGVqbVk5aERtCmNibkFqZlNwUjF1WEt2S2d6d1NLQ0VWdzc0VjJSRmRqQXBLVDl3bkpOQTZiWHQ5SXFkaS96d3BYbDQ0OVczdVMKNjRjVVpaK3luYnQ5QUlxbFNjMDdNRHl1TUtueExMbDFLeEJYenNxZlVsYWtlRGVoVmdGS05OOTNXQWJJc09ieAp1d1hTd0hXa1B6RGFHeE9wdzlSMHo2S2t2N25YZnBIYW1RWENBZEdsRjkyc1QwYW80Y3FuejFJSmJ2bz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"cacert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZjekNDQTF1Z0F3SUJBZ0lVVnNRZkFaTW4vazNRTU9nc0RuVEdUaU9PbGJ3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TURrMU5EQTRXaGNOCk1qUXdOakEzTURrMU5EQTRXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFLMlQwWXpkcjB0dWQzaVJRNGNzaGNhRVJTRzVjTDE2CkhRblhoYWw4emlUL1VRQUNIUGdzZDYwcWlEaldvQTJXb0lGWFFpUHkzOG1vZGtWRlR4Qmt5U2VldndOOFJiLzEKOFhaMS8yS1RnVmRDcHkvNm11WE15bXZYODJad05CVkV3QnoxUk5kbklUSk44cVh3a0d4bHozbDBib1loRkFyUQpNdmkxcW1RaHpDa2Zpb041MVkrYlBXOXpTQlFQdXNrcXJYYzRqTTJ0VENNQ2pTcFlvd1hXM1ppRmc5WEJ1Z09aCjFmdWd1Zmw4K1FJYzNZSEFoL1Z1NloraXFEOGxQeGRKODlBeDZaazVtOGdkVG9JdUhBbUNWaHFpUXBGRjkzSTgKbkYrSnRuYnBaNTRJUTZBbWYrYiswakMxdmY4Kzg0WUppaEVzWExyaGMxZTRTZ2dwdzEvcWpDb21QblhGVjEzUwpsUG5kVlhVR0taa1ZKdXdZTjJyZElmd3YrdCs5MGhwUVBmNmFBTjRCamRxOXdkdkQzSXVnS2JYZG5CQ0FUTEY4ClYyRTFTSE9VZGdRY3duK1d1WDVVOGdPa3B2b2VFN0g1REJ6Rks1WTZ2SHZlaTRlNkp3RTRDK3FJL1BmbTgreTEKNEpsOFBSOW5JQmdGQ3hrZWpwa2tRQ0I5U0dvMVZidzZhWmdZd0VQNHh6YXFYYXV3L3F4c0oxNUkrRTBndEs1OApuWUtkM0hqelk5Slh6V0NVNTdXbmc2SzNvTTIzNXpyRzJnNm1FaHQ4SStDckVMUFNuZURjZU8zVlJkc2dlblBCCis4U1JxVU8vWG9LWHNEU3I5amoxdWluVzYwTG5MZ0Zmc3JQeGlQVlZlMFh1TFZESlhCSlNoRDZDeGRyMnBSOGQKS25SRDZrTFpZZEtMQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqVwpWREFmQmdOVkhTTUVHREFXZ0JRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqV1ZEQVBCZ05WSFJNQkFmOEVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJad3B4Z2Z4N2thZFhvRHNyT1hUVXJ6dEFPMkFQRVJNaTAKaTkyNk9DTGFPbVVYZW1uKytXSUU1K2tUSE0wcS8vbUZCTURzSmdZSFVLUlNvRGNsNmh4TnVFNUNzS2trRVFTSgpMTHZrWlB0S2J5NGlxMitLZ1JtdVZxbXJNVTBYQzZMZDl3WmttL2huUjNtT3V6bko4MGZmV1JDQ0xGWDEwY2EzCnc5TGM1d1JLTFBZZXQvcEs5SitOYWN3TFJRYTczVFovMUpQNW9BU3czVjNoYkxlLy9UeWpnOURqUlZGY3FYWnEKWWs2Mm5qSkhZVzh3WmlhZzc0QXU4dHE5OG5KandBV1ROMFV5L2w1Q2VpWnV5bzZlU0RHVDNJNm1BdGU1VXBvWAppNXBkYlZ6VDdOZC9IOEwwZHZNdVZ2N0FmakZlcU91cUZNNkkzTnlvbStLWENxNmJQdGxBWEkzeVFZc0t4ZlRkCkw3SnRaTmx6MGJ6eHJhcHI4RmpYcjhML1ZkeHQza00xMnJwb2kzL3hsckR6Q2Q2b2YrQ1MxelBocUdpOUhvcUoKZEU5VGhYMklTdkd2akVSYzVVNFRsNjJBNHNyeGJQbUt0eWx3dGNGVEJacUJiRGY3ZjBBc2cveWhndXdTcktsQQpBNkRWVXVCRFErdGpwZ0N0b0ZlOEhLVDJ6UFVlaEQ2ZjVNQkhmU2ZUZ1crTlhFSXNvVDNsampjY1hsYXhPcFJWCkNQNWxCczNmekxyYnBxbUlLaWZhdWlTNWM4TzlSUjhjQTVzeWlBOTBmbmJIdDlmdGxpRG9jcFRzNUtrbjk2NkIKZUxMM1dXVldCYUtvanJzY1RkVXJoalNnVVBmam5FTXpnVzR2eEc3d3BVNHR2ME4yaEtHUWc0bVhhcDV0SU5Pcwp4WktnZXRHUldnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"vm-blank.ff.lan\"\n ],\n \"resolver\": \"192.168.1.13\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"server_cert\",\n \"key\": \"server_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ],\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"mTLS-client-profile\"\n }\n ]\n }\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/vm-blank.ff.lan_access_log\",\n \"error\": \"/var/log/nginx/vm-blank.ff.lan_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://origin_server\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"origin_server\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"mTLS-client-profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"enabled\": \"on\",\n \"client_certificates\": \"cacert\",\n \"trusted_ca_certificates\": \"cacert\",\n \"ocsp\": {\n \"enabled\": \"on\",\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n },\n \"stapling\": {\n \"enabled\": true,\n \"verify\": true,\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n }\n }\n }\n ]\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -1970,7 +1915,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -1978,35 +1923,41 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}" + "config" ] } }, "response": [] - }, + } + ] + }, + { + "name": "NGINX App Protect WAF", + "item": [ { - "name": "Get declaration status", + "name": "Create initial NGINX configuration with NGINX App Protect WAF", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, "request": { - "method": "GET", + "method": "POST", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2014,7 +1965,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", "protocol": "http", "host": [ "{{ncg_host}}" @@ -2022,36 +1973,36 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config", - "{{configUid}}", - "status" + "config" ] } }, "response": [] }, { - "name": "Get declaration", + "name": "Change active NGINX App Protect policy", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, "request": { - "method": "GET", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-allowed\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"Production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"Production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2075,24 +2026,29 @@ "response": [] }, { - "name": "Delete declaration", + "name": "Update TLS certificates", "event": [ { "listen": "test", "script": { "exec": [ - "" + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], "request": { - "method": "DELETE", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.key\"\n }\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2114,14 +2070,9 @@ } }, "response": [] - } - ] - }, - { - "name": "Server-side and source of truth authentication", - "item": [ + }, { - "name": "Server-side and source of truth authentication", + "name": "Disable NGINX App Protect WAF", "event": [ { "listen": "test", @@ -2133,16 +2084,17 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"Bearer token-based authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"Bearer token-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Header-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n },\n {\n \"name\": \"Basic authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"type\": \"basic\",\n \"username\": \"authusername\",\n \"password\": \"YXV0aHBhc3N3b3Jk\"\n }\n }\n ]\n }\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2150,7 +2102,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -2158,22 +2110,167 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] } ] - } - ] - } - ] - }, - { - "name": "v4.2", - "item": [ - { + }, + { + "name": "NGINX Javascript", + "item": [ + { + "name": "NGINX Javascript test", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Example HTTP server with Javascript\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"njs-test.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/njs-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/njs-test.nginx.lab_error_log\"\n },\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$serverVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/echo\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n },\n {\n \"uri\": \"/generatecontent\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_content\"\n },\n \"profile\": \"njs_set_content\",\n \"function\": \"njsSetContent\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$httpVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"njs_profiles\": [\n {\n \"name\": \"njs_set_variable\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0VmFyaWFibGUocikgewogICAgcmV0dXJuICJWYXJpYWJsZV9zZXRfYnlfamF2YXNjcmlwdCAtIFVSSSAiK3IudXJpOwp9CgpleHBvcnQgZGVmYXVsdCB7bmpzU2V0VmFyaWFibGV9Cgo=\"\n }\n },\n {\n \"name\": \"njs_set_content\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0Q29udGVudChyKSB7CiAgci5yZXR1cm4oMjAwLCAiSGVsbG8gd29ybGQhXG4iKTsKfQoKZXhwb3J0IGRlZmF1bHQge25qc1NldENvbnRlbnR9Cg==\"\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Server-side and source of truth authentication", + "item": [ + { + "name": "Bearer Token, Basic and HTTP header upstream authentication", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"Bearer token-based authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"Bearer token-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Header-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n },\n {\n \"name\": \"Basic authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"type\": \"basic\",\n \"username\": \"authusername\",\n \"password\": \"YXV0aHBhc3N3b3Jk\"\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + }, + { + "name": "mTLS upstream authentication", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"client_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZBakNDQStxZ0F3SUJBZ0lVYXZ1aFhBOWFLVFJlYmZ2Y1BFU2Z0MjBUQXhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1ZqRUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlZCQWdNQkVOdmNtc3hEVEFMQmdOVkJBY01CRU52Y21zeApFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUll3RkFZRFZRUUREQTEwWlhOMExtRmpiV1F1YkdGdU1CNFhEVEkwCk1EUXhOVEUzTURnMU0xb1hEVEkxTURReE5URTNNRGcxTTFvd1dERUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlYKQkFnTUJFTnZjbXN4RFRBTEJnTlZCQWNNQkVOdmNtc3hFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUmd3RmdZRApWUVFEREE5amJHbGxiblF1WVdOdFpTNXNZV0l3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQ0R3QXdnZ0lLCkFvSUNBUURUQ2kydi8rc08rdTJTcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkQKUWkvL0dIZXhYS3NaelBURDUyN0ZMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOApPa3lxVFdma0xkRnF2akFGclU5czMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2CmpsV1E5NTVWUzlmZlNLU3hpQTRCQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUMKRjYrRGZmQ2FNLzVockljRkswSVFheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNOAo5UlhxQmlaNnFWdGFPQXFEK0pRVGNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4CnZvczA4TSsvOUlhWTdQeGtQNXB6bDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0QKbTlURDJKT0hTU3VmdkRkRnV1YjFwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwegpkZ1RsMDVVWjZtMFpFNUdlK1FRaG5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BCmFLZ0hTbTJROE9MS1lJdlhVN1RpME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzQKc3I0UXJ4Q2xuY2pwbUMrdkRmdHJuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJvNEhGTUlIQwpNQWtHQTFVZEV3UUNNQUF3RVFZSllJWklBWWI0UWdFQkJBUURBZ1dnTURNR0NXQ0dTQUdHK0VJQkRRUW1GaVJQCmNHVnVVMU5NSUVkbGJtVnlZWFJsWkNCRGJHbGxiblFnUTJWeWRHbG1hV05oZEdVd0hRWURWUjBPQkJZRUZGQmIKdmltZDYxekxLdlA0U2RZRFRWbndYdFVwTUI4R0ExVWRJd1FZTUJhQUZLY29GRHpMVnc1QXBiMzB5UTM0c25MZQpHQ1FWTUE0R0ExVWREd0VCL3dRRUF3SUY0REFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQWdZSUt3WUJCUVVICkF3UXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSmg0cm9kZEVGemJwWk5kSW5zUmdubWVnWVIxSWlzUUxRU0IKcmNydEZxVmtLb3Rsc09EUXBFMThIRjBsbVA4WE9IU0hqYytYUVF5YlFNd1ViZ2RNbnhBZE1WaGFaMXppWElnaQpiUHgwOEY0YXR5MElJY1hyOVpFVnZTd1pheURBZHErb2s5RGpoRjQwYU5iQmFBTXB2NTRCL2U3OUNMSWZ6REo4CnNZaWw3K09abWlOUTJtZUNreVBYdXdhd3hTeVRnNVhWS3Q4VEtKQVJ5aTFJeWRHWkZRMTJGWFJHTE1BSjdaYngKRzlPd1ZvazExenpWNkRQZHFuZU5ER3BQRU5WZ2VmVjI0ZU1JVXUrYzhnSThYTU1GMU9VVWR3MmlEQmU4Q01TQQppc2h2aDFWYVZPNC96czlma2dLV2FXejNwQWprN3pkbWdaUnpXRUVHR0JMekJoQk5FQTg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"client_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRRFRDaTJ2LytzTyt1MlMKcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkRRaS8vR0hleFhLc1p6UFRENTI3RgpMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOE9reXFUV2ZrTGRGcXZqQUZyVTlzCjMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2amxXUTk1NVZTOWZmU0tTeGlBNEIKQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUNGNitEZmZDYU0vNWhySWNGSzBJUQpheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNODlSWHFCaVo2cVZ0YU9BcUQrSlFUCmNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4dm9zMDhNKy85SWFZN1B4a1A1cHoKbDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0RtOVREMkpPSFNTdWZ2RGRGdXViMQpwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwemRnVGwwNVVaNm0wWkU1R2UrUVFoCm5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BYUtnSFNtMlE4T0xLWUl2WFU3VGkKME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzRzcjRRcnhDbG5janBtQyt2RGZ0cgpuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJBb0lDQUJzWEZBK2krRmVFbW5rdE9xdHRUL1FsCk5TUlozL05WYjJuRlcyUGRqRHhKcGtUTzV2VS9qYThVd2ZmTVIrdWxCbWhSWkVmOFBweVBMc2J5USszV29iSjAKUk9JQlprVjdYd2ZDWDdEVUJDc0VtS3ZscDlvN3JVQmJqV0w1SmpJNDhYeDI3VFR0NlFMY05uVXhUZDUxanRPagpXYXFJdzNqNU1Oc0tFdDRncWlET2RhQzIvMzJaekZkTHNHNWdnRmRnUElZOUpYUXN3bHk2VnZjdzVpU0RoTnRICkcyYWxYYXdiL2s1Sk9OWTIyY3RDT0kyUnhPUGtPc3lYRkoxYTBRQStGYmxBWExKenVubE5ZNU9iOXEwWTFpa2sKSHpGeXBUcENSdmY2dzY3em00NUYvZVdkS0MzdmpnM29JK29GUS9vMTJUdjhWV3Bhc0ptSmoraUJ5aFRpOGx4YQpMempXaHNrUFZJUGJDYjFDTmIzZHNQSWVWYTVQYmRORGh2bHBQZFV4cXhKRGI4ZUFhemxSQXJVdmtaQnpkTjZtCjVwekljNGtMQnd3Wno5eGg1MzdsQ0tlZ3ZJYmdEMlQvMEM4SVFsWmFIK1M3azlWaVU2Z1RWSGNzU1RsSm52NVYKN3VCMWczVzJZRVloZFJxNlJOUXRZaUFuTktUS2ZYb3d1UUJteWRwVWRYWUljTE5GcER6eHgvRGdQSEhnU0J1dQpQZXVMZzdpemhSSndKMTQ1eW5VTlloQnROVVJaOWlRL1cwWTJOa242UWFBZDNld1pLaVg0S3lZU2k3TTF1MFJWCmFDc1BmZWJrWkRTUnRnQ2Q5N3dGSVYyTVh0UnpXNlpKUUlDWkhHTDVkYkJrNU0xQTI2NnJSYmcvTWZJSE9mdTMKRUZBVjc2aXoyVmNtSDRzblNZUHhBb0lCQVFEc0pNWElhM3ZyRXdRSkh0alRWZE9ONk9rbEExRVN4aWtjSWdSOAo3aFRWQXk3dmtORlpFVUhYczQ1TXlYQzRxTWlvUnFhZHVtOXpQWnFBQ0tQMHRWcXdDMVdOcUFrbDBQc1pZYXcrCmpKNjRDQXA4ZnJ4UDUrOVpWREtiamRWK1ZrK1lrbC9UL0Q1djNFTnZIdkVxN3ZMSE56dE9VSmFWZXNFRERQSW8KaER5SHhjRXQwTHRXTFhoNGNMSDhIQk9Kc0wxYXcxRlhNTk1qdERzazlBMTlJRFlYTHcwaWpIeC81c3hHUGcxSQpmMXRIemhpTlN5NUU5RG9pTmJxVFQvWEEyYVdCdjJ1ZlQybmw2ekticHZHNVo0VGlOUE5ESU5MaUFXOVNNQnJnCkJtaXJFVHU4MnVSa3hnaWZTM01CZ0ZFdXVtaSt2cEhwNzhGaUNOcHpZZUFwTUxjN0FvSUJBUURreVFVT29HVjEKTzZaNXJ4KzhLbVh1S3dmbXNHNmh5cmkrNEw4MzRQakVhbHdkdFhFUmphUG8vSTVhOVFoYjNnLzF5SUdiMzdLSgpPaTFCcGVRZ1owUEVQdFgyRWI0QTNCSTA5SzExWWFybnJGM2d6WS9CVWgveEZoL1YvVEVzTTloZTZ4YlhtRm9WCktvMlY1b2NlNnQzNUJuNHVLL2xkSEJKMHJnUENQdXNKVU9JMEhtUzZkdXNQcHczRFpESkZ5SEU4anptSllwcUsKMnBEMlBBMVZ5aVBBM3MwOU12TWdNSmJXajJLQVBoYk4xQXJnTERaUTFuZ0VwaVFtM3hWdmxIVkMzN0JHM0IzcApWbmtSRE9UZnF1N1BXek40aHZnMDRaNkxBdEZuYzNnMFFxRGcvcXZlS0xuclZwOUJEUlhWb0pZZkNMaVBkajZCCmFiYnFXQ0lYTnMwTkFvSUJBUURFWllrQk9UT2t2UG44UStWOVRzSldJa0hWZ0w2cTZKaEVSNTZIOE5MdW5ta28KNGI3Ylh0anQ5dTRBdXdDKzg5Ris4dE9jRnZTZVdidm5oRWdvTzdTaSthbzcyR2RUUmsyd1BHV3UxL0VoaWI1Kwo4RURhREVJcWZ6WmYzVVNVZ0dCT3VsNXN4anQvZVNlMGdYMStnYUQxUXVCV0wvd3RjaHlZMXVtSC9RTUN3TnY3CnFNQkYyaWQ1cy9Demh2NVE0K2Q0VnoyTlVKUXArN204OENWUHpieHU1N2o2NVBDZXgydFplRDQvNzN3UmFqMU4KTmh2VFNYUVlBNnVhM1VPOUVzYnQ5REFrSFQ0cjlNTHdaWlpnNXRIRCtObmhHS21MUWpvOWxyaWpYWEVyNVhkVAppSkd2cG15Qlg1VFV5TTI4R1ZrSVd5S3I5N1ZVUFp6Qm5jTjdQb01GQW9JQkFDSWRCekFESXAyMFkwSkpwb1c4CkhLN0NvODcyQjhrQUhVUDQ1d3BCOVZYME5nQUlDZkFBR3F5bTIrTWNIajcwZ1pTNGJQcjlBL1lLUXExRE94ekoKeVFUK0NaRkRXLzFzMHhvcVVhTHJDVHk2S3RWV1VWVVdGY1V3ODFaSkJvZjh3d3FFSzBmQ1k4dzhLQmh0NHovcwo3V1F3WDZncXptZmZ3N0M2TWIxSS9HckxNSzlzeU1BMDh4L0dYUHNCZWEyR0VieGg3c1paZVlteXhXS3gyWnN0CkpOK2hXU0VDODlXYzZTRGRDR2J1MngrZHVuRnFwajZ2ZS8zVmVCYUR0UUtLTkdIZ1VMeUFIY1dwS3l3cnJBVGQKeS9ZSE4wbUZkb1VNRDBQVEM3NU5MV005ZkJlUVliZ2lnblpnMkNZdStVNTlQMlVwTzd2SWVkRjZIZGdiaEJuSwpCaEVDZ2dFQkFNZmQ2Zi90RThTQlNFUnd5YW80SThuZkhTZUlMUklvM3pqRE5vWHZ6VHlwbG00a3ovYW9QeEF0CmhPYVNmZU41bFljMXFFUkRxZzRIVGRNTUkyenZPaVFuMHNGTEJFZ0UwT0VIOEZxZVZlZGgycUJucFdXWEhwTW8KREd4TnhpUnVpSDZpOEVMVWFoaU1NQkdQVi9ONjI5MDREbnUyYVVHcHlMTzZxems3SmM5VXlITC90QVNRTFIzagp6NFBhb2xTRE5rNzJSUks0VjIyZUprM2EwYVhjRU5vM1R0OUlTUTliMG5DYVVKbFJsZTA0d2QrOWUyN0FRNUNaCkVWbUtZT1JYcW9VTGNQOEcxRm1VUGhDRkU2aEI3L1hSVWpQdmJFdm5rS05Cdko2UHppT0RMdktRK3I4TzR0WkMKQi9LTDlCdjNmdUkvQlJLeU9WZVU1VnZ5MzUveUFxVT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"mTLS authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"mTLS authentication profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"certificate\": \"client_cert\",\n \"key\": \"client_key\"\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + } + ] + } + ] + } + ], + "description": "[https://github.com/f5devcentral/NGINX-Declarative-API/blob/main/USAGE-v4.2.md](https://github.com/f5devcentral/NGINX-Declarative-API/blob/main/USAGE-v4.2.md)" + }, + { + "name": "v5.0", + "item": [ + { "name": "Configuration generation", "item": [ { @@ -2515,13 +2612,203 @@ "name": "Declarative automation examples", "item": [ { - "name": "API Gateway", + "name": "NGINX Instance Manager", "item": [ { - "name": "Ergast API", + "name": "API Gateway", "item": [ { - "name": "Ergast API Gateway and DevPortal", + "name": "Ergast API", + "item": [ + { + "name": "Ergast API Gateway and DevPortal", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Petstore API", + "item": [ + { + "name": "Petstore API Gateway RateLimit", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"name\": \"Petstore API Gateway\",\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + }, + { + "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\",\n \"authentication\": [\n {\n \"profile\": \"Source of truth authentication profile using HTTP header token authentication\"\n }\n ]\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ],\n \"server\": [\n {\n \"name\": \"Source of truth authentication profile using bearer token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Source of truth authentication profile using HTTP header token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + }, + { + "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ],\n \"errorcode\": 403\n }\n ]\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Petstore & Ergast API Gateway all in one", "event": [ { "listen": "test", @@ -2543,7 +2830,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore and Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"192.168.2.13\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n },\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"ergast_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"ergast_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n },\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n }\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2568,10 +2855,10 @@ ] }, { - "name": "Petstore API", + "name": "CRUD automation", "item": [ { - "name": "Petstore API Gateway RateLimit", + "name": "Create initial NGINX configuration", "event": [ { "listen": "test", @@ -2593,7 +2880,7 @@ "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"name\": \"Petstore API Gateway\",\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2616,7 +2903,7 @@ "response": [] }, { - "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ", + "name": "Update HTTP upstream", "event": [ { "listen": "test", @@ -2634,11 +2921,11 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\",\n \"authentication\": [\n {\n \"profile\": \"Source of truth authentication profile using HTTP header token authentication\"\n }\n ]\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ],\n \"server\": [\n {\n \"name\": \"Source of truth authentication profile using bearer token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Source of truth authentication profile using HTTP header token authentication\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n },\n {\n \"server\": \"10.0.0.3:80\"\n },\n {\n \"server\": \"10.0.0.4:80\"\n },\n {\n \"server\": \"10.0.0.5:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2646,7 +2933,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -2654,14 +2941,15 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] }, { - "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ + WAF", + "name": "Modify and add HTTP upstream", "event": [ { "listen": "test", @@ -2679,11 +2967,11 @@ } ], "request": { - "method": "POST", + "method": "PATCH", "header": [], "body": { "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked-bot-allowed.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"8.8.8.8\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n },\n \"authorization\": [\n {\n \"profile\": \"JWT role based authorization\",\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\",\n \"/pet/{petId}/uploadImage\"\n ]\n }\n ]\n },\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n },\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"JWT role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ],\n \"errorcode\": 403\n }\n ]\n }\n }\n ]\n }\n }\n}", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n },\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.1:80\"\n },\n {\n \"server\": \"192.168.1.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -2691,7 +2979,7 @@ } }, "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", "protocol": "http", "host": [ "{{ncg_host}}" @@ -2699,1734 +2987,1550 @@ "port": "{{ncg_port}}", "path": [ "{{ngc_api_version}}", - "config" + "config", + "{{configUid}}" ] } }, "response": [] - } - ] - }, - { - "name": "Petstore & Ergast API Gateway all in one", - "event": [ + }, { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Petstore and Ergast API\",\n \"names\": [\n \"apigw.nginx.lab\"\n ],\n \"resolver\": \"192.168.2.13\",\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/apigw.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/apigw.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/petstore\",\n \"urimatch\": \"prefix\",\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"http://petstore.swagger.io/v2/swagger.json\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true,\n \"server_url\": \"https://petstore.swagger.io/v2\"\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/petstore-devportal.html\"\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"Petstore JWT Authentication\"\n }\n ],\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n },\n \"rate_limit\": [\n {\n \"profile\": \"petstore_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0,\n \"enforceOnPaths\": true,\n \"paths\": [\n \"/user/login\",\n \"/user/logout\"\n ]\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/petstore-access_log\",\n \"error\": \"/var/log/nginx/petstore-error_log\"\n }\n }\n },\n {\n \"uri\": \"/ergast\",\n \"urimatch\": \"prefix\",\n \"snippet\": {\n \"content\": \"IyBUZXN0IFNOSVBQRVQK\"\n },\n \"apigateway\": {\n \"openapi_schema\": {\n \"content\": \"https://raw.githubusercontent.com/adampax/ergast-f1-openapi-doc/e558eea18e176e4f78a8765ac7eccc804b5157ff/ergast-openapi-doc.yaml\"\n },\n \"api_gateway\": {\n \"enabled\": true,\n \"strip_uri\": true\n },\n \"developer_portal\": {\n \"enabled\": true,\n \"uri\": \"/ergast-devportal.html\"\n },\n \"rate_limit\": [\n {\n \"profile\": \"ergast_ratelimit\",\n \"httpcode\": 429,\n \"burst\": 0,\n \"delay\": 0\n }\n ],\n \"log\": {\n \"access\": \"/var/log/nginx/ergast-access_log\",\n \"error\": \"/var/log/nginx/ergast-error_log\"\n }\n }\n }\n ]\n }\n ],\n \"rate_limit\": [\n {\n \"name\": \"ergast_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"1r/s\"\n },\n {\n \"name\": \"petstore_ratelimit\",\n \"key\": \"$binary_remote_addr\",\n \"size\": \"10m\",\n \"rate\": \"2r/s\"\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"Petstore JWT Authentication\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"Petstore Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"cachetime\": 5\n }\n }\n ]\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Modify and add HTTP server and upstream", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "CRUD automation", - "item": [ - { - "name": "Create initial NGINX configuration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_log\",\n \"error\": \"/var/log/nginx/error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - }, - { - "name": "Update HTTP upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n },\n {\n \"server\": \"10.0.0.3:80\"\n },\n {\n \"server\": \"10.0.0.4:80\"\n },\n {\n \"server\": \"10.0.0.5:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Modify and add HTTP upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:80\"\n },\n {\n \"server\": \"10.0.0.2:80\"\n }\n ]\n },\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.1:80\"\n },\n {\n \"server\": \"192.168.1.2:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Modify and add HTTP server and upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server_v2.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_v2_log\",\n \"error\": \"/var/log/nginx/error_v2_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream_added\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.100:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"192.168.1.101:80\"\n },\n {\n \"server\": \"192.168.1.102:80\"\n },\n {\n \"server\": \"192.168.1.103:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Remove HTTP server and upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\"\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Modify Stream server and upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_10053_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n },\n {\n \"server\": \"10.0.0.3:53\"\n },\n {\n \"server\": \"10.0.0.4:53\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Add stream server and upstream", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\",\n \"listen\": {\n \"address\": \"15432\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_15432_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\",\n \"origin\": [\n {\n \"server\": \"172.16.10.1:5432\"\n },\n {\n \"server\": \"172.16.10.1:5432\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Remove stream server and upstream #1", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\"\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Remove stream server and upstream #2", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\"\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "GitOps autosync", - "item": [ - { - "name": "NGINX Plus and GitOps", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 5,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" - } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - }, - { - "name": "NGINX App Protect WAF and GitOps", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 5,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"gitops\",\n \"versions\": [\n {\n \"tag\": \"gitops\",\n \"displayName\": \"Production Policy - GitOps\",\n \"description\": \"This is a production-ready policy - Managed by GitOps\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/nap-policy-gitops.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\",\n \"names\": [\n \"patched_server_v2.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"127.0.0.1:8080\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/access_v2_log\",\n \"error\": \"/var/log/nginx/error_v2_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream_added\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\",\n \"origin\": [\n {\n \"server\": \"192.168.1.100:80\",\n \"weight\": 5,\n \"max_fails\": 2,\n \"fail_timeout\": \"30s\",\n \"max_conns\": 3,\n \"slow_start\": \"30s\"\n },\n {\n \"server\": \"192.168.1.101:80\"\n },\n {\n \"server\": \"192.168.1.102:80\"\n },\n {\n \"server\": \"192.168.1.103:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration status", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "name": "Remove HTTP server and upstream", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}", - "status" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"HTTP test application\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream_added\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete declaration", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "name": "Modify Stream server and upstream", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "Housekeeping - common endpoints", - "item": [ - { - "name": "Clean NGINX configuration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\"\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\",\n \"listen\": {\n \"address\": \"10053\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_10053_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\",\n \"origin\": [\n {\n \"server\": \"10.0.0.1:53\"\n },\n {\n \"server\": \"10.0.0.2:53\"\n },\n {\n \"server\": \"10.0.0.3:53\"\n },\n {\n \"server\": \"10.0.0.4:53\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration status", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "name": "Add stream server and upstream", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}", - "status" - ] - } - }, - "response": [] - }, - { - "name": "Get declaration", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "protocolProfileBehavior": { - "disableBodyPruning": true - }, - "request": { - "method": "GET", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\",\n \"listen\": {\n \"address\": \"15432\",\n \"protocol\": \"tcp\"\n },\n \"upstream\": \"TCP_15432_upstream\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\",\n \"origin\": [\n {\n \"server\": \"172.16.10.1:5432\"\n },\n {\n \"server\": \"172.16.10.1:5432\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Delete declaration", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "DELETE", - "header": [], - "body": { - "mode": "raw", - "raw": "", - "options": { - "raw": { - "language": "json" + "name": "Remove stream server and upstream #1", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "HTTP Headers Manipulation", - "item": [ - { - "name": "Create test echo service", - "event": [ + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_10053_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_10053_upstream\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Remove stream server and upstream #2", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"layer4\": {\n \"servers\": [\n {\n \"name\": \"TCP_15432_service\"\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"TCP_15432_upstream\"\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Manipulate headers - server level", - "event": [ + "name": "GitOps autosync", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "NGINX Plus and GitOps", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - }, - { - "name": "Manipulate headers - location level", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 5,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.1/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "NGINX App Protect WAF and GitOps", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ], - "description": "Test using:\n\n`curl -i echo.vm-test.ie.ff.lan`\n\nThe actual FQDN can be modified in the request JSON body" - }, - { - "name": "HTTPS server", - "item": [ - { - "name": "TLS Offload", - "event": [ + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 5,\n \"modules\": [\n \"ngx_http_app_protect_module\",\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"gitops\",\n \"versions\": [\n {\n \"tag\": \"gitops\",\n \"displayName\": \"Production Policy - GitOps\",\n \"description\": \"This is a production-ready policy - Managed by GitOps\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/nap-policy-gitops.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Get declaration status", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}", + "status" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Get declaration", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] + }, + { + "name": "Delete declaration", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "TLS Offload and NGINX App Protect", - "event": [ + "name": "Housekeeping - common endpoints", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - GitOps\",\n \"description\": \"This is a production-ready policy - Managed by GitOps\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Clean NGINX configuration", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\"\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Get declaration status", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "JWT Client Authentication", - "item": [ - { - "name": "JWT Client Authentication - local JWT key and Bearer token", - "event": [ + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}/status", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}", + "status" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"JWT Auth with hardwired key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\"\n }\n },\n {\n \"name\": \"JWT Auth with external key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication GitOps\",\n \"key\": \"http://192.168.2.5:20080/jwks.json\",\n \"cachetime\": 5\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in auth_token query string parameter\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$arg_auth_token\"\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$http_x_auth_token\"\n }\n }\n ]\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Get declaration", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } + ], + "protocolProfileBehavior": { + "disableBodyPruning": true + }, + "request": { + "method": "GET", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Delete declaration", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "DELETE", + "header": [], + "body": { + "mode": "raw", + "raw": "", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "JWT secret fetched from URL", - "event": [ + "name": "HTTP Headers Manipulation", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with external key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Create test echo service", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Manipulate headers - server level", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] + }, + { + "name": "Manipulate headers - location level", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test echo app\",\n \"names\": [\n \"echo.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"resolver\": \"8.8.8.8\",\n \"log\": {\n \"access\": \"/var/log/nginx/test-echo-access_log\",\n \"error\": \"/var/log/nginx/test-echo-error_log\"\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-Injected-Echo-Test-Version\",\n \"value\": \"v1\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ],\n \"delete\": [\n \"User-Agent\"\n ]\n },\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-Response-Status\",\n \"value\": \"$status\"\n }\n ],\n \"delete\": [\n \"vary\"\n ],\n \"replace\": [\n {\n \"name\": \"Server\",\n \"value\": \"Echo Test Server\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test-echo-upstream\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test-echo-upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] + ], + "description": "Test using:\n\n`curl -i echo.vm-test.ie.ff.lan`\n\nThe actual FQDN can be modified in the request JSON body" }, { - "name": "JWT token in auth_token query string parameter", - "event": [ + "name": "HTTPS server", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in auth_token query string parameter\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "TLS Offload", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "TLS Offload and NGINX App Protect", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - GitOps\",\n \"description\": \"This is a production-ready policy - Managed by GitOps\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "JWT token in HTTP X-Auth-Token header", - "event": [ + "name": "JWT Client Authentication", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "JWT Client Authentication - local JWT key and Bearer token", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "JWT Client Authentication and Authorization", - "item": [ - { - "name": "JWT Client Authentication and Authorization", - "event": [ - { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"JWT AuthN and AuthZ test server\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/auth-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/auth-test.nginx.lab_error_log\"\n },\n \"headers\": {\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-JWT-Group\",\n \"value\": \"$jwt_claim_roles\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"jwt_authentication_local\"\n }\n ]\n },\n \"authorization\": {\n \"profile\": \"jwt role based authorization\"\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"jwt_authentication_local\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Client Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"jwt_type\": \"signed\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"jwt role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"JWT Auth with hardwired key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\"\n }\n },\n {\n \"name\": \"JWT Auth with external key and Bearer token\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication GitOps\",\n \"key\": \"http://192.168.2.5:20080/jwks.json\",\n \"cachetime\": 5\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in auth_token query string parameter\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$arg_auth_token\"\n }\n },\n {\n \"name\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"token_location\": \"$http_x_auth_token\"\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "mTLS Client Authentication", - "item": [ - { - "name": "HTTPS server with mTLS, OCSP, SSL Stapling", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"server_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdSRENDQkN5Z0F3SUJBZ0lVTTNJQVZIRmxhSTVsY1d0TjZxOUVhcnlka0w4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TVRBd01qTTVXaGNOCk1qUXdOakEzTVRBd01qTTVXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFMRFQ2bnZleVZlNi9VZlk2aUtHVC9oV1A0cktDSGR0ClloZWU3RGVZR29QWGhGVjB6a3grVWExanBEZ21WUE1kVEJBdnoxODg5NzlEcHBqdmNYeFhsRmpnaUhjWDhpWVgKSXovSUVMc3dKRUNITWNsNkxmelA5eDVUY1gxTEdFblFOTWhHRzA2MjlxU2NCQmQyUUNiWlY0UWE1TkxlQnQ4cQpHQ2lXY3JiQnR3YlpiSGo1dk9aenJrdHBtRFBGS1V4bXR5b2dBQnNaTllnL0F3Y1l2RXdBOEQ0QTN0VEgxcGhvCkdYY3ZvZWpJelhRMUdmYys5azR3OFhHYWFQOGd2bTdOMXN2MnU2Yld4SHRGZHpWQk9udzJyaHUvWGYyY0N0dW4KUnIxSENKQXRRSDlkbDhzZks1czBSRlVuTlVYbFBiNTFBTjBjVFVGbEYrZlVUVmVON3dNMTdmeVZVY3IydTltSwo0UGdoWjkvMml0ZUpZV3hjK3k4V2NEQzBUV3hwZ2paVEw5Tk1GK2t6SXV2TjJOWFFybjcvSU5UQTMvNFlmWGRPCloxelpTdTlkclRMcG5DZHRpOWxuRHBKODd3bW41cVZSTlZiTlZRbldEeW5yZnoyTU1DY21jLzcvdkJFN2dDemQKNFJLWHJLdHloenlQSitycmh3NmpxYVA4QytaZGRvKzkvak9QVDFTSnUxZ21VbzFuZ2hBMWh2N0M5RUYrM2xQVApYSk5WV3dtYkdWK0p4cUdKSjJSa2toMlIrZTVIREdRY2hGWjJIcXBGTGVQN0trTHJBR2RkZFZQWEZhQ0RiU0R6ClJQd0I5WFlhakg5Zm5QWEtFT3ZpVEJhQVNjWUZwTXB5cm02UkxHUGRSVnE2RUNYVlB4MDdHdGFCaEVvVWIwK2YKVkZnNExtQkx4MldQQWdNQkFBR2pnZ0VpTUlJQkhqQUpCZ05WSFJNRUFqQUFNQkVHQ1dDR1NBR0crRUlCQVFRRQpBd0lHUURBekJnbGdoa2dCaHZoQ0FRMEVKaFlrVDNCbGJsTlRUQ0JIWlc1bGNtRjBaV1FnVTJWeWRtVnlJRU5sCmNuUnBabWxqWVhSbE1CMEdBMVVkRGdRV0JCVHZFZWJGK1JDV0JhcGVPWUdpQ0YyVHZxbExYekNCaEFZRFZSMGoKQkgwd2U0QVVFdW9Db3kvcmhMQmxzcm5KdXE2QzFJczQxbFNoVGFSTE1Fa3hDekFKQmdOVkJBWVRBa2xVTVEwdwpDd1lEVlFRSURBUkJjM1JwTVJFd0R3WURWUVFLREFoVVpYTjBJRXhoWWpFWU1CWUdBMVVFQXd3UGRtMHRZbXhoCmJtc3VabVl1YkdGdWdoUld4QjhCa3lmK1RkQXc2Q3dPZE1aT0k0NlZ2REFPQmdOVkhROEJBZjhFQkFNQ0JhQXcKRXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFHUDR6ZkdseTI1RwpneTBSeC9SSTNpNzJDVlIrSXY3SW5WTUVGWDZqRHRNV3hSblFtRGZsMWtTOVF1Y3hNb0tnOE9URStMcnlzdGJsClF4WGZiakZQekNoNHB1UGtGTmNBeG1mVmR4b20xR1lodWpoYTBQOUswUURZSDZycGlUaFdSQ2greUovQm1qZ2wKTlJabks4WGRqME85Ui9XKzJrTFRac2VFbS9hZHFVQ3dkYzNBWWlNWGh4QXkvQlh3bFRQeDMyMHZCcXYxZGFyVgp5ZlVoRlM1Rkg3enV2bGtGQ1p6M3lpOGYvYXMwbkRTUkFrY3dPRFQvN1diQlN4QTk3ZzJmRk1EMEI3WlUvbndGCmU4VnRzNDl3YmZ6QWJRMk40RUc2OEVhODE1VlFRM2N6YWthdjBCdkxHL2UwT0habGxYcUVhV1ZlWFJtSWFFOHcKWko5OEhUaDJMbUlFV2Jpdm94Kyt2UXd3bVhKTm1DRFVXNnVmcHdBOVdKQ0VhYmhxeXdGVzh1dFVENzRTVXE3SApEUDhNamtJZ0o3ekl2Tkd1RkFsSzd6c2xpV2pzeUN1OGVNamhvN2pVRFhGR1R0R0ZMUGtVa08vSysrSGVVRFg0Cm1OWDJ2aHI3NGRqRkNBTTEvOTYxWnB5NUFYUzZkd2g3MFlJL2dMdldSL0J1ejBnNEp6YUI2UFo4M1ErYm9QVHYKM1ZIS2xOWjlKQlhRTmtSc3N6U0dYWG5MYmtOTmNwVFg2cnAyZ1pUSS9NNDhGTnBxanAxOXRpQVg3bWN0cTl2SgpNejhvemhEcHZmSTlnMjFsNFZlRGdpbWEwTDVBc1pQbFdIQlZjcy9yL3dMU2YzWFVYZEs0UHpCQUdIRFBidXYrCnpKOVNqS0NFVll2bHRhMHlUUVBCSFJPa2Y2MG1sVmh6Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"server_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBc05QcWU5N0pWN3I5UjlqcUlvWlArRlkvaXNvSWQyMWlGNTdzTjVnYWc5ZUVWWFRPClRINVJyV09rT0NaVTh4MU1FQy9QWHp6M3YwT21tTzl4ZkZlVVdPQ0lkeGZ5SmhjalA4Z1F1ekFrUUljeHlYb3QKL00vM0hsTnhmVXNZU2RBMHlFWWJUcmIycEp3RUYzWkFKdGxYaEJyazB0NEczeW9ZS0paeXRzRzNCdGxzZVBtOAo1bk91UzJtWU04VXBUR2EzS2lBQUd4azFpRDhEQnhpOFRBRHdQZ0RlMU1mV21HZ1pkeStoNk1qTmREVVo5ejcyClRqRHhjWnBvL3lDK2JzM1d5L2E3cHRiRWUwVjNOVUU2ZkRhdUc3OWQvWndLMjZkR3ZVY0lrQzFBZjEyWHl4OHIKbXpSRVZTYzFSZVU5dm5VQTNSeE5RV1VYNTlSTlY0M3ZBelh0L0pWUnl2YTcyWXJnK0NGbjMvYUsxNGxoYkZ6NwpMeFp3TUxSTmJHbUNObE12MDB3WDZUTWk2ODNZMWRDdWZ2OGcxTURmL2hoOWQwNW5YTmxLNzEydE11bWNKMjJMCjJXY09rbnp2Q2FmbXBWRTFWczFWQ2RZUEtldC9QWXd3Snlaei92KzhFVHVBTE4zaEVwZXNxM0tIUEk4bjZ1dUgKRHFPcG8vd0w1bDEyajczK000OVBWSW03V0NaU2pXZUNFRFdHL3NMMFFYN2VVOU5jazFWYkNac1pYNG5Hb1lrbgpaR1NTSFpINTdrY01aQnlFVm5ZZXFrVXQ0L3NxUXVzQVoxMTFVOWNWb0lOdElQTkUvQUgxZGhxTWYxK2M5Y29RCjYrSk1Gb0JKeGdXa3luS3VicEVzWTkxRldyb1FKZFUvSFRzYTFvR0VTaFJ2VDU5VVdEZ3VZRXZIWlk4Q0F3RUEKQVFLQ0FnQVBUR1pQRFRsU004VlIvL3hSdkZrUzNUTm1LSkNPOUpHMkJYUGVZM1IzejUrTlhTdTBCb0craEk1aQpwVDVZUWtLZ2ErSi9GT0ZDVlBJRzdVQmVSNTE0Q3dVRGVMamtmci8zOXJFcjRNQmlMTkFyNUR3eVVUUEtGZUlOCnV2K0E4MWg5czBNTmpsck1ad3NibElsOFV2VjFZblpGb0J2c0Z0SThRTGZ3QTlaMzZ6dXRRNzRLR2h3TVBqaUMKMGgzK2xDeG9vcGdmd0JDWGx3d0dBeWZYVTRWMWQ5SFBpdktRQVFHakJDWDM0OWVTcEQxNDNLT21wQ2xmY01LQQp3QzU1bTZsbndCTUFIamlsaVo4RXBuNE8zUlEzSmxsVlpiaXl4RWdrZkE3TG1uNm9Ca3Jwc2VxdDVObThuRVhKCnBFbXhQcUl5Znc1WUNBMEhhNkM5WUhRN1RPRW9BbHBmWld4azAxSnpoVi9aK3FmVHM1YlMwQWNaTzFOVDRaeDgKWlF2eHQ0TDJINVcrK2R6RjhReTlidzQ2M3lKb1dydWxtNy9uQ3YvL1FpNGl0eHRnYyt0N2lwVXZzaUdTVktVWQpPelhCSXNWTUlnd0F6eUtTSEhPL21rMkEwVkgxaHB3emY2L0RzR2wxSjM4TU9pVGo4dEx1RWt3cFY4WGh5MnZwCkd0cXpsT21DS1hodlVDam9iZWlYSWJwSlIzeEM1NmliRjVadk0vQUdONzI5K0xKRFNwbHJtWVJRVHh1UTJWSE8KQWFXQ01SQWFBdUtCVnBxYTRjd25WRy9POEpkN2ZPSi9tMFlIN3FpRlJHREdvdVNOdHZJUUVtaXVkK3dRWjJ6dwpUcmFNVWk0SENtNEFPa0ZNVXBsRmt1ajA2ZHRqM2RIWUtPQkdMK25vaUp4WmJxb3kwUUtDQVFFQTFiZUl6WHh6CnRFRlp2OGRlOXljOWdCUUtNNUNIbHp6NUNMZXVkTitvemxxeDNCMW1PRStxbFkyaEd3RklIWVBJajFLYS83RlkKbExmNFpiUEJRMFhiNUo5VzQzSGIyTnEydXdRQ3ZiSXhVMW9zaGJVWlhZc2FUaE15azc2VzQ5YjU3UC9HdFE3NwpTbkVZTXNrTzRUQndyS3lBdVhDVHRtTk1Qa2J1NFBxT05PeVFQY3o3Yi92VEU1eERjMENMVS9oUXM3NWFHeCs1Citld2VjeEZNa0JKTVo2c2N5TzcySEdSNHZwTHduRXUvcU5uN2JmUElSaUx1T3BwTTdHNlUwQlBPL2todHJ5ZmQKV3U3MHJYZGJSdGRJUHlsQWxSOG9zczJqWWsrRHNPUnNESm9pbkk5WU1Va3dmdHdCNTRQbytGRGtGOHBzV202RQpSaklpenFBK0piWDlTd0tDQVFFQTA4Ly9oM0NabDg2M2xUZHNrU1JKRUZKc0RtdkZkUStzMWtlNUFwMjdnWTBXCmZJbEFGZFlRR3RORUVlTk9xS3EwdTFtS0lqWHFacWNTdU9DNzZIYTE5Tk9waHVoK1dwV0t2Ni9BTWtQSjE5SUIKQ3RqS0lkc2s0U2M3WG02MnNOV1pnQm5XT1Z3QVdzU0VzTHRac1NvWUJUVTJJS1pBOVJOWHhkSEQreGZ2SWJkNApZYngzTzk4WklNQzNlVFFiOW9jVHZab0RNWGdLaHRtTy9iMnlSeEVDSGpGRmxzYlhhc1RPeG5XOWZSVXJtdGVqCk9pdVlXaEZOM2R6dmpuVEdLY0xieWY0MWpHaUVUeFViUHVpei9ZMmk5NldCNVN6MW9zaGorRU1OaFhtRzZSYXUKQUIvelhwNldtSUJ2bDNpU0lzOGJRNkh3Qm1DTjc1R2VVVG1GUUlyaVRRS0NBUUVBbTkzWVN5MXA0VndNRGI5bApObElMRzM4Q0ZhdGlDRjR5cmpYd2FWSzVkWTVWeTFneHRmMzhSa2hkNkNrZUpGQjVsSFhGajVnVEo1dW84TnVSCnB2T3JOT2swNEhxb3dWWjZFSmtUT3JCY0l4TlFCMUFXS05BTHBrZUFDcHJreDFTQlFHVW0wZVFVUjYyRjNYd2YKZXdMdUdqRlJURzJiZlZpY1FZdFFLd3J4YmczZUFRU2ZtSU9MNVBDQmpPdlU4YS9YZzgvZlBZcjlBeFkrK3VMeAorTjB2bGlnSXZVN3lkYkNkRXpodGZVQU5qeU16cVhRemExdU1iWGNkaFEzOVFHaEIvZGhyRG1TL250Tko1YjEzCjk0bUpLbTkycDR0ckRrVEYxU3h5dWk5TjBqOFQ0U1QyU0RPOXg3ZkROOHRQdk5LYUYvUE01SU5YdXk1VGptajIKQ21EWlV3S0NBUUFOUVJYSFh1ZHRsWFR0ZEhOcHZiQ0l3ZStiRTJsZXd1VlkzMUlYZE5GWDhRRTROOHAzMDFaYwpwMTI2Rk5SR1A3QmhqTi9VOWpTOXliU2xOd0xyTUFxQTBJSHFQRUF6NE9tMnh3T3E0WTBPNFVoSmFubHpsdWYrCjR0cVhOU3hmY201UmtzeFIrSXpaSVRVQWJpalZxa0dvaWNUaVZDVDZjUVJzRDQxSStCMXhxYTV4eHo1YTA4SVoKeDVWemt5d3d5QkVYS3owSjZtNFdOQ1Q3Z2RSWEdCeGUwVXgrZStEZEFJWEQ2M2c1RElzVy9HbHRhVzcySytFSQpnaHZIZVUweExjMWRIWGd5V2hQMWN1ZXFqeHM4UVpHeUYzeENZQWJhOGRrM250S0l5S3NGaVBMSWRUZGdjMklQCkZ2SmtzeG5KN2RYUjdKODlkdXRLMDN6cHJrVEZYaXQ5QW9JQkFDcjhkb2ZCcFlFL1JuTlFwbVNET29DRm1sdTkKQlozN3h5K0puZ2FrQ2RSdHFyR1lDdkZMSnI2QnpGdXE0SHpsM0piTkRCM1BkYSs4Z2VNd2cxU1htTEhrRVFrTQpXV2ptNHpmU3hiTUtKamx3REdoeUlwSU9nQ2FQL1hyT2hxTGl4bnJ6UHFHZmM4R0FZTDE2Rm1PeGVqbVk5aERtCmNibkFqZlNwUjF1WEt2S2d6d1NLQ0VWdzc0VjJSRmRqQXBLVDl3bkpOQTZiWHQ5SXFkaS96d3BYbDQ0OVczdVMKNjRjVVpaK3luYnQ5QUlxbFNjMDdNRHl1TUtueExMbDFLeEJYenNxZlVsYWtlRGVoVmdGS05OOTNXQWJJc09ieAp1d1hTd0hXa1B6RGFHeE9wdzlSMHo2S2t2N25YZnBIYW1RWENBZEdsRjkyc1QwYW80Y3FuejFJSmJ2bz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"cacert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZjekNDQTF1Z0F3SUJBZ0lVVnNRZkFaTW4vazNRTU9nc0RuVEdUaU9PbGJ3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TURrMU5EQTRXaGNOCk1qUXdOakEzTURrMU5EQTRXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFLMlQwWXpkcjB0dWQzaVJRNGNzaGNhRVJTRzVjTDE2CkhRblhoYWw4emlUL1VRQUNIUGdzZDYwcWlEaldvQTJXb0lGWFFpUHkzOG1vZGtWRlR4Qmt5U2VldndOOFJiLzEKOFhaMS8yS1RnVmRDcHkvNm11WE15bXZYODJad05CVkV3QnoxUk5kbklUSk44cVh3a0d4bHozbDBib1loRkFyUQpNdmkxcW1RaHpDa2Zpb041MVkrYlBXOXpTQlFQdXNrcXJYYzRqTTJ0VENNQ2pTcFlvd1hXM1ppRmc5WEJ1Z09aCjFmdWd1Zmw4K1FJYzNZSEFoL1Z1NloraXFEOGxQeGRKODlBeDZaazVtOGdkVG9JdUhBbUNWaHFpUXBGRjkzSTgKbkYrSnRuYnBaNTRJUTZBbWYrYiswakMxdmY4Kzg0WUppaEVzWExyaGMxZTRTZ2dwdzEvcWpDb21QblhGVjEzUwpsUG5kVlhVR0taa1ZKdXdZTjJyZElmd3YrdCs5MGhwUVBmNmFBTjRCamRxOXdkdkQzSXVnS2JYZG5CQ0FUTEY4ClYyRTFTSE9VZGdRY3duK1d1WDVVOGdPa3B2b2VFN0g1REJ6Rks1WTZ2SHZlaTRlNkp3RTRDK3FJL1BmbTgreTEKNEpsOFBSOW5JQmdGQ3hrZWpwa2tRQ0I5U0dvMVZidzZhWmdZd0VQNHh6YXFYYXV3L3F4c0oxNUkrRTBndEs1OApuWUtkM0hqelk5Slh6V0NVNTdXbmc2SzNvTTIzNXpyRzJnNm1FaHQ4SStDckVMUFNuZURjZU8zVlJkc2dlblBCCis4U1JxVU8vWG9LWHNEU3I5amoxdWluVzYwTG5MZ0Zmc3JQeGlQVlZlMFh1TFZESlhCSlNoRDZDeGRyMnBSOGQKS25SRDZrTFpZZEtMQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqVwpWREFmQmdOVkhTTUVHREFXZ0JRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqV1ZEQVBCZ05WSFJNQkFmOEVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJad3B4Z2Z4N2thZFhvRHNyT1hUVXJ6dEFPMkFQRVJNaTAKaTkyNk9DTGFPbVVYZW1uKytXSUU1K2tUSE0wcS8vbUZCTURzSmdZSFVLUlNvRGNsNmh4TnVFNUNzS2trRVFTSgpMTHZrWlB0S2J5NGlxMitLZ1JtdVZxbXJNVTBYQzZMZDl3WmttL2huUjNtT3V6bko4MGZmV1JDQ0xGWDEwY2EzCnc5TGM1d1JLTFBZZXQvcEs5SitOYWN3TFJRYTczVFovMUpQNW9BU3czVjNoYkxlLy9UeWpnOURqUlZGY3FYWnEKWWs2Mm5qSkhZVzh3WmlhZzc0QXU4dHE5OG5KandBV1ROMFV5L2w1Q2VpWnV5bzZlU0RHVDNJNm1BdGU1VXBvWAppNXBkYlZ6VDdOZC9IOEwwZHZNdVZ2N0FmakZlcU91cUZNNkkzTnlvbStLWENxNmJQdGxBWEkzeVFZc0t4ZlRkCkw3SnRaTmx6MGJ6eHJhcHI4RmpYcjhML1ZkeHQza00xMnJwb2kzL3hsckR6Q2Q2b2YrQ1MxelBocUdpOUhvcUoKZEU5VGhYMklTdkd2akVSYzVVNFRsNjJBNHNyeGJQbUt0eWx3dGNGVEJacUJiRGY3ZjBBc2cveWhndXdTcktsQQpBNkRWVXVCRFErdGpwZ0N0b0ZlOEhLVDJ6UFVlaEQ2ZjVNQkhmU2ZUZ1crTlhFSXNvVDNsampjY1hsYXhPcFJWCkNQNWxCczNmekxyYnBxbUlLaWZhdWlTNWM4TzlSUjhjQTVzeWlBOTBmbmJIdDlmdGxpRG9jcFRzNUtrbjk2NkIKZUxMM1dXVldCYUtvanJzY1RkVXJoalNnVVBmam5FTXpnVzR2eEc3d3BVNHR2ME4yaEtHUWc0bVhhcDV0SU5Pcwp4WktnZXRHUldnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"vm-blank.ff.lan\"\n ],\n \"resolver\": \"192.168.1.13\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"server_cert\",\n \"key\": \"server_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ],\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"mTLS-client-profile\"\n }\n ]\n }\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/vm-blank.ff.lan_access_log\",\n \"error\": \"/var/log/nginx/vm-blank.ff.lan_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://origin_server\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"origin_server\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"mTLS-client-profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"enabled\": \"on\",\n \"client_certificates\": \"cacert\",\n \"trusted_ca_certificates\": \"cacert\",\n \"ocsp\": {\n \"enabled\": \"on\",\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n },\n \"stapling\": {\n \"enabled\": true,\n \"verify\": true,\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n }\n }\n }\n ]\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "JWT secret fetched from URL", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with external key and Bearer token\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] } - } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" - ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "NGINX App Protect WAF", - "item": [ - { - "name": "Create initial NGINX configuration with NGINX App Protect WAF", - "event": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "JWT token in auth_token query string parameter", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in auth_token query string parameter\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "JWT token in HTTP X-Auth-Token header", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Test service\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/test.nginx.lab-access_log\",\n \"error\": \"/var/log/nginx/test.nginx.lab-error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"JWT Auth with hardwired key and token in X-Auth-Token HTTP header\"\n }\n ]\n },\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Change active NGINX App Protect policy", - "event": [ + "name": "JWT Client Authentication and Authorization", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-allowed\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"Production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"Production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "JWT Client Authentication and Authorization", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"JWT AuthN and AuthZ test server\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"test.nginx.lab\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/auth-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/auth-test.nginx.lab_error_log\"\n },\n \"headers\": {\n \"to_client\": {\n \"add\": [\n {\n \"name\": \"X-Injected-JWT-Group\",\n \"value\": \"$jwt_claim_roles\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"jwt_authentication_local\"\n }\n ]\n },\n \"authorization\": {\n \"profile\": \"jwt role based authorization\"\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"jwt_authentication_local\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"realm\": \"JWT Client Authentication\",\n \"key\": \"{\\\"keys\\\": [{\\\"k\\\":\\\"ZmFudGFzdGljand0\\\",\\\"kty\\\":\\\"oct\\\",\\\"kid\\\":\\\"0001\\\"}]}\",\n \"jwt_type\": \"signed\"\n }\n }\n ]\n },\n \"authorization\": [\n {\n \"name\": \"jwt role based authorization\",\n \"type\": \"jwt\",\n \"jwt\": {\n \"claims\": [\n {\n \"name\": \"roles\",\n \"value\": [\n \"~(devops)\"\n ]\n }\n ]\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Update TLS certificates", - "event": [ + "name": "mTLS Client Authentication", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.key\"\n }\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "HTTPS server with mTLS, OCSP, SSL Stapling", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"server_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUdSRENDQkN5Z0F3SUJBZ0lVTTNJQVZIRmxhSTVsY1d0TjZxOUVhcnlka0w4d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TVRBd01qTTVXaGNOCk1qUXdOakEzTVRBd01qTTVXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFMRFQ2bnZleVZlNi9VZlk2aUtHVC9oV1A0cktDSGR0ClloZWU3RGVZR29QWGhGVjB6a3grVWExanBEZ21WUE1kVEJBdnoxODg5NzlEcHBqdmNYeFhsRmpnaUhjWDhpWVgKSXovSUVMc3dKRUNITWNsNkxmelA5eDVUY1gxTEdFblFOTWhHRzA2MjlxU2NCQmQyUUNiWlY0UWE1TkxlQnQ4cQpHQ2lXY3JiQnR3YlpiSGo1dk9aenJrdHBtRFBGS1V4bXR5b2dBQnNaTllnL0F3Y1l2RXdBOEQ0QTN0VEgxcGhvCkdYY3ZvZWpJelhRMUdmYys5azR3OFhHYWFQOGd2bTdOMXN2MnU2Yld4SHRGZHpWQk9udzJyaHUvWGYyY0N0dW4KUnIxSENKQXRRSDlkbDhzZks1czBSRlVuTlVYbFBiNTFBTjBjVFVGbEYrZlVUVmVON3dNMTdmeVZVY3IydTltSwo0UGdoWjkvMml0ZUpZV3hjK3k4V2NEQzBUV3hwZ2paVEw5Tk1GK2t6SXV2TjJOWFFybjcvSU5UQTMvNFlmWGRPCloxelpTdTlkclRMcG5DZHRpOWxuRHBKODd3bW41cVZSTlZiTlZRbldEeW5yZnoyTU1DY21jLzcvdkJFN2dDemQKNFJLWHJLdHloenlQSitycmh3NmpxYVA4QytaZGRvKzkvak9QVDFTSnUxZ21VbzFuZ2hBMWh2N0M5RUYrM2xQVApYSk5WV3dtYkdWK0p4cUdKSjJSa2toMlIrZTVIREdRY2hGWjJIcXBGTGVQN0trTHJBR2RkZFZQWEZhQ0RiU0R6ClJQd0I5WFlhakg5Zm5QWEtFT3ZpVEJhQVNjWUZwTXB5cm02UkxHUGRSVnE2RUNYVlB4MDdHdGFCaEVvVWIwK2YKVkZnNExtQkx4MldQQWdNQkFBR2pnZ0VpTUlJQkhqQUpCZ05WSFJNRUFqQUFNQkVHQ1dDR1NBR0crRUlCQVFRRQpBd0lHUURBekJnbGdoa2dCaHZoQ0FRMEVKaFlrVDNCbGJsTlRUQ0JIWlc1bGNtRjBaV1FnVTJWeWRtVnlJRU5sCmNuUnBabWxqWVhSbE1CMEdBMVVkRGdRV0JCVHZFZWJGK1JDV0JhcGVPWUdpQ0YyVHZxbExYekNCaEFZRFZSMGoKQkgwd2U0QVVFdW9Db3kvcmhMQmxzcm5KdXE2QzFJczQxbFNoVGFSTE1Fa3hDekFKQmdOVkJBWVRBa2xVTVEwdwpDd1lEVlFRSURBUkJjM1JwTVJFd0R3WURWUVFLREFoVVpYTjBJRXhoWWpFWU1CWUdBMVVFQXd3UGRtMHRZbXhoCmJtc3VabVl1YkdGdWdoUld4QjhCa3lmK1RkQXc2Q3dPZE1aT0k0NlZ2REFPQmdOVkhROEJBZjhFQkFNQ0JhQXcKRXdZRFZSMGxCQXd3Q2dZSUt3WUJCUVVIQXdFd0RRWUpLb1pJaHZjTkFRRUxCUUFEZ2dJQkFHUDR6ZkdseTI1RwpneTBSeC9SSTNpNzJDVlIrSXY3SW5WTUVGWDZqRHRNV3hSblFtRGZsMWtTOVF1Y3hNb0tnOE9URStMcnlzdGJsClF4WGZiakZQekNoNHB1UGtGTmNBeG1mVmR4b20xR1lodWpoYTBQOUswUURZSDZycGlUaFdSQ2greUovQm1qZ2wKTlJabks4WGRqME85Ui9XKzJrTFRac2VFbS9hZHFVQ3dkYzNBWWlNWGh4QXkvQlh3bFRQeDMyMHZCcXYxZGFyVgp5ZlVoRlM1Rkg3enV2bGtGQ1p6M3lpOGYvYXMwbkRTUkFrY3dPRFQvN1diQlN4QTk3ZzJmRk1EMEI3WlUvbndGCmU4VnRzNDl3YmZ6QWJRMk40RUc2OEVhODE1VlFRM2N6YWthdjBCdkxHL2UwT0habGxYcUVhV1ZlWFJtSWFFOHcKWko5OEhUaDJMbUlFV2Jpdm94Kyt2UXd3bVhKTm1DRFVXNnVmcHdBOVdKQ0VhYmhxeXdGVzh1dFVENzRTVXE3SApEUDhNamtJZ0o3ekl2Tkd1RkFsSzd6c2xpV2pzeUN1OGVNamhvN2pVRFhGR1R0R0ZMUGtVa08vSysrSGVVRFg0Cm1OWDJ2aHI3NGRqRkNBTTEvOTYxWnB5NUFYUzZkd2g3MFlJL2dMdldSL0J1ejBnNEp6YUI2UFo4M1ErYm9QVHYKM1ZIS2xOWjlKQlhRTmtSc3N6U0dYWG5MYmtOTmNwVFg2cnAyZ1pUSS9NNDhGTnBxanAxOXRpQVg3bWN0cTl2SgpNejhvemhEcHZmSTlnMjFsNFZlRGdpbWEwTDVBc1pQbFdIQlZjcy9yL3dMU2YzWFVYZEs0UHpCQUdIRFBidXYrCnpKOVNqS0NFVll2bHRhMHlUUVBCSFJPa2Y2MG1sVmh6Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"server_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBSU0EgUFJJVkFURSBLRVktLS0tLQpNSUlKS0FJQkFBS0NBZ0VBc05QcWU5N0pWN3I5UjlqcUlvWlArRlkvaXNvSWQyMWlGNTdzTjVnYWc5ZUVWWFRPClRINVJyV09rT0NaVTh4MU1FQy9QWHp6M3YwT21tTzl4ZkZlVVdPQ0lkeGZ5SmhjalA4Z1F1ekFrUUljeHlYb3QKL00vM0hsTnhmVXNZU2RBMHlFWWJUcmIycEp3RUYzWkFKdGxYaEJyazB0NEczeW9ZS0paeXRzRzNCdGxzZVBtOAo1bk91UzJtWU04VXBUR2EzS2lBQUd4azFpRDhEQnhpOFRBRHdQZ0RlMU1mV21HZ1pkeStoNk1qTmREVVo5ejcyClRqRHhjWnBvL3lDK2JzM1d5L2E3cHRiRWUwVjNOVUU2ZkRhdUc3OWQvWndLMjZkR3ZVY0lrQzFBZjEyWHl4OHIKbXpSRVZTYzFSZVU5dm5VQTNSeE5RV1VYNTlSTlY0M3ZBelh0L0pWUnl2YTcyWXJnK0NGbjMvYUsxNGxoYkZ6NwpMeFp3TUxSTmJHbUNObE12MDB3WDZUTWk2ODNZMWRDdWZ2OGcxTURmL2hoOWQwNW5YTmxLNzEydE11bWNKMjJMCjJXY09rbnp2Q2FmbXBWRTFWczFWQ2RZUEtldC9QWXd3Snlaei92KzhFVHVBTE4zaEVwZXNxM0tIUEk4bjZ1dUgKRHFPcG8vd0w1bDEyajczK000OVBWSW03V0NaU2pXZUNFRFdHL3NMMFFYN2VVOU5jazFWYkNac1pYNG5Hb1lrbgpaR1NTSFpINTdrY01aQnlFVm5ZZXFrVXQ0L3NxUXVzQVoxMTFVOWNWb0lOdElQTkUvQUgxZGhxTWYxK2M5Y29RCjYrSk1Gb0JKeGdXa3luS3VicEVzWTkxRldyb1FKZFUvSFRzYTFvR0VTaFJ2VDU5VVdEZ3VZRXZIWlk4Q0F3RUEKQVFLQ0FnQVBUR1pQRFRsU004VlIvL3hSdkZrUzNUTm1LSkNPOUpHMkJYUGVZM1IzejUrTlhTdTBCb0craEk1aQpwVDVZUWtLZ2ErSi9GT0ZDVlBJRzdVQmVSNTE0Q3dVRGVMamtmci8zOXJFcjRNQmlMTkFyNUR3eVVUUEtGZUlOCnV2K0E4MWg5czBNTmpsck1ad3NibElsOFV2VjFZblpGb0J2c0Z0SThRTGZ3QTlaMzZ6dXRRNzRLR2h3TVBqaUMKMGgzK2xDeG9vcGdmd0JDWGx3d0dBeWZYVTRWMWQ5SFBpdktRQVFHakJDWDM0OWVTcEQxNDNLT21wQ2xmY01LQQp3QzU1bTZsbndCTUFIamlsaVo4RXBuNE8zUlEzSmxsVlpiaXl4RWdrZkE3TG1uNm9Ca3Jwc2VxdDVObThuRVhKCnBFbXhQcUl5Znc1WUNBMEhhNkM5WUhRN1RPRW9BbHBmWld4azAxSnpoVi9aK3FmVHM1YlMwQWNaTzFOVDRaeDgKWlF2eHQ0TDJINVcrK2R6RjhReTlidzQ2M3lKb1dydWxtNy9uQ3YvL1FpNGl0eHRnYyt0N2lwVXZzaUdTVktVWQpPelhCSXNWTUlnd0F6eUtTSEhPL21rMkEwVkgxaHB3emY2L0RzR2wxSjM4TU9pVGo4dEx1RWt3cFY4WGh5MnZwCkd0cXpsT21DS1hodlVDam9iZWlYSWJwSlIzeEM1NmliRjVadk0vQUdONzI5K0xKRFNwbHJtWVJRVHh1UTJWSE8KQWFXQ01SQWFBdUtCVnBxYTRjd25WRy9POEpkN2ZPSi9tMFlIN3FpRlJHREdvdVNOdHZJUUVtaXVkK3dRWjJ6dwpUcmFNVWk0SENtNEFPa0ZNVXBsRmt1ajA2ZHRqM2RIWUtPQkdMK25vaUp4WmJxb3kwUUtDQVFFQTFiZUl6WHh6CnRFRlp2OGRlOXljOWdCUUtNNUNIbHp6NUNMZXVkTitvemxxeDNCMW1PRStxbFkyaEd3RklIWVBJajFLYS83RlkKbExmNFpiUEJRMFhiNUo5VzQzSGIyTnEydXdRQ3ZiSXhVMW9zaGJVWlhZc2FUaE15azc2VzQ5YjU3UC9HdFE3NwpTbkVZTXNrTzRUQndyS3lBdVhDVHRtTk1Qa2J1NFBxT05PeVFQY3o3Yi92VEU1eERjMENMVS9oUXM3NWFHeCs1Citld2VjeEZNa0JKTVo2c2N5TzcySEdSNHZwTHduRXUvcU5uN2JmUElSaUx1T3BwTTdHNlUwQlBPL2todHJ5ZmQKV3U3MHJYZGJSdGRJUHlsQWxSOG9zczJqWWsrRHNPUnNESm9pbkk5WU1Va3dmdHdCNTRQbytGRGtGOHBzV202RQpSaklpenFBK0piWDlTd0tDQVFFQTA4Ly9oM0NabDg2M2xUZHNrU1JKRUZKc0RtdkZkUStzMWtlNUFwMjdnWTBXCmZJbEFGZFlRR3RORUVlTk9xS3EwdTFtS0lqWHFacWNTdU9DNzZIYTE5Tk9waHVoK1dwV0t2Ni9BTWtQSjE5SUIKQ3RqS0lkc2s0U2M3WG02MnNOV1pnQm5XT1Z3QVdzU0VzTHRac1NvWUJUVTJJS1pBOVJOWHhkSEQreGZ2SWJkNApZYngzTzk4WklNQzNlVFFiOW9jVHZab0RNWGdLaHRtTy9iMnlSeEVDSGpGRmxzYlhhc1RPeG5XOWZSVXJtdGVqCk9pdVlXaEZOM2R6dmpuVEdLY0xieWY0MWpHaUVUeFViUHVpei9ZMmk5NldCNVN6MW9zaGorRU1OaFhtRzZSYXUKQUIvelhwNldtSUJ2bDNpU0lzOGJRNkh3Qm1DTjc1R2VVVG1GUUlyaVRRS0NBUUVBbTkzWVN5MXA0VndNRGI5bApObElMRzM4Q0ZhdGlDRjR5cmpYd2FWSzVkWTVWeTFneHRmMzhSa2hkNkNrZUpGQjVsSFhGajVnVEo1dW84TnVSCnB2T3JOT2swNEhxb3dWWjZFSmtUT3JCY0l4TlFCMUFXS05BTHBrZUFDcHJreDFTQlFHVW0wZVFVUjYyRjNYd2YKZXdMdUdqRlJURzJiZlZpY1FZdFFLd3J4YmczZUFRU2ZtSU9MNVBDQmpPdlU4YS9YZzgvZlBZcjlBeFkrK3VMeAorTjB2bGlnSXZVN3lkYkNkRXpodGZVQU5qeU16cVhRemExdU1iWGNkaFEzOVFHaEIvZGhyRG1TL250Tko1YjEzCjk0bUpLbTkycDR0ckRrVEYxU3h5dWk5TjBqOFQ0U1QyU0RPOXg3ZkROOHRQdk5LYUYvUE01SU5YdXk1VGptajIKQ21EWlV3S0NBUUFOUVJYSFh1ZHRsWFR0ZEhOcHZiQ0l3ZStiRTJsZXd1VlkzMUlYZE5GWDhRRTROOHAzMDFaYwpwMTI2Rk5SR1A3QmhqTi9VOWpTOXliU2xOd0xyTUFxQTBJSHFQRUF6NE9tMnh3T3E0WTBPNFVoSmFubHpsdWYrCjR0cVhOU3hmY201UmtzeFIrSXpaSVRVQWJpalZxa0dvaWNUaVZDVDZjUVJzRDQxSStCMXhxYTV4eHo1YTA4SVoKeDVWemt5d3d5QkVYS3owSjZtNFdOQ1Q3Z2RSWEdCeGUwVXgrZStEZEFJWEQ2M2c1RElzVy9HbHRhVzcySytFSQpnaHZIZVUweExjMWRIWGd5V2hQMWN1ZXFqeHM4UVpHeUYzeENZQWJhOGRrM250S0l5S3NGaVBMSWRUZGdjMklQCkZ2SmtzeG5KN2RYUjdKODlkdXRLMDN6cHJrVEZYaXQ5QW9JQkFDcjhkb2ZCcFlFL1JuTlFwbVNET29DRm1sdTkKQlozN3h5K0puZ2FrQ2RSdHFyR1lDdkZMSnI2QnpGdXE0SHpsM0piTkRCM1BkYSs4Z2VNd2cxU1htTEhrRVFrTQpXV2ptNHpmU3hiTUtKamx3REdoeUlwSU9nQ2FQL1hyT2hxTGl4bnJ6UHFHZmM4R0FZTDE2Rm1PeGVqbVk5aERtCmNibkFqZlNwUjF1WEt2S2d6d1NLQ0VWdzc0VjJSRmRqQXBLVDl3bkpOQTZiWHQ5SXFkaS96d3BYbDQ0OVczdVMKNjRjVVpaK3luYnQ5QUlxbFNjMDdNRHl1TUtueExMbDFLeEJYenNxZlVsYWtlRGVoVmdGS05OOTNXQWJJc09ieAp1d1hTd0hXa1B6RGFHeE9wdzlSMHo2S2t2N25YZnBIYW1RWENBZEdsRjkyc1QwYW80Y3FuejFJSmJ2bz0KLS0tLS1FTkQgUlNBIFBSSVZBVEUgS0VZLS0tLS0K\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"cacert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZjekNDQTF1Z0F3SUJBZ0lVVnNRZkFaTW4vazNRTU9nc0RuVEdUaU9PbGJ3d0RRWUpLb1pJaHZjTkFRRUwKQlFBd1NURUxNQWtHQTFVRUJoTUNTVlF4RFRBTEJnTlZCQWdNQkVGemRHa3hFVEFQQmdOVkJBb01DRlJsYzNRZwpUR0ZpTVJnd0ZnWURWUVFEREE5MmJTMWliR0Z1YXk1bVppNXNZVzR3SGhjTk1qTXdOakE0TURrMU5EQTRXaGNOCk1qUXdOakEzTURrMU5EQTRXakJKTVFzd0NRWURWUVFHRXdKSlZERU5NQXNHQTFVRUNBd0VRWE4wYVRFUk1BOEcKQTFVRUNnd0lWR1Z6ZENCTVlXSXhHREFXQmdOVkJBTU1EM1p0TFdKc1lXNXJMbVptTG14aGJqQ0NBaUl3RFFZSgpLb1pJaHZjTkFRRUJCUUFEZ2dJUEFEQ0NBZ29DZ2dJQkFLMlQwWXpkcjB0dWQzaVJRNGNzaGNhRVJTRzVjTDE2CkhRblhoYWw4emlUL1VRQUNIUGdzZDYwcWlEaldvQTJXb0lGWFFpUHkzOG1vZGtWRlR4Qmt5U2VldndOOFJiLzEKOFhaMS8yS1RnVmRDcHkvNm11WE15bXZYODJad05CVkV3QnoxUk5kbklUSk44cVh3a0d4bHozbDBib1loRkFyUQpNdmkxcW1RaHpDa2Zpb041MVkrYlBXOXpTQlFQdXNrcXJYYzRqTTJ0VENNQ2pTcFlvd1hXM1ppRmc5WEJ1Z09aCjFmdWd1Zmw4K1FJYzNZSEFoL1Z1NloraXFEOGxQeGRKODlBeDZaazVtOGdkVG9JdUhBbUNWaHFpUXBGRjkzSTgKbkYrSnRuYnBaNTRJUTZBbWYrYiswakMxdmY4Kzg0WUppaEVzWExyaGMxZTRTZ2dwdzEvcWpDb21QblhGVjEzUwpsUG5kVlhVR0taa1ZKdXdZTjJyZElmd3YrdCs5MGhwUVBmNmFBTjRCamRxOXdkdkQzSXVnS2JYZG5CQ0FUTEY4ClYyRTFTSE9VZGdRY3duK1d1WDVVOGdPa3B2b2VFN0g1REJ6Rks1WTZ2SHZlaTRlNkp3RTRDK3FJL1BmbTgreTEKNEpsOFBSOW5JQmdGQ3hrZWpwa2tRQ0I5U0dvMVZidzZhWmdZd0VQNHh6YXFYYXV3L3F4c0oxNUkrRTBndEs1OApuWUtkM0hqelk5Slh6V0NVNTdXbmc2SzNvTTIzNXpyRzJnNm1FaHQ4SStDckVMUFNuZURjZU8zVlJkc2dlblBCCis4U1JxVU8vWG9LWHNEU3I5amoxdWluVzYwTG5MZ0Zmc3JQeGlQVlZlMFh1TFZESlhCSlNoRDZDeGRyMnBSOGQKS25SRDZrTFpZZEtMQWdNQkFBR2pVekJSTUIwR0ExVWREZ1FXQkJRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqVwpWREFmQmdOVkhTTUVHREFXZ0JRUzZnS2pMK3VFc0dXeXVjbTZyb0xVaXpqV1ZEQVBCZ05WSFJNQkFmOEVCVEFECkFRSC9NQTBHQ1NxR1NJYjNEUUVCQ3dVQUE0SUNBUUJad3B4Z2Z4N2thZFhvRHNyT1hUVXJ6dEFPMkFQRVJNaTAKaTkyNk9DTGFPbVVYZW1uKytXSUU1K2tUSE0wcS8vbUZCTURzSmdZSFVLUlNvRGNsNmh4TnVFNUNzS2trRVFTSgpMTHZrWlB0S2J5NGlxMitLZ1JtdVZxbXJNVTBYQzZMZDl3WmttL2huUjNtT3V6bko4MGZmV1JDQ0xGWDEwY2EzCnc5TGM1d1JLTFBZZXQvcEs5SitOYWN3TFJRYTczVFovMUpQNW9BU3czVjNoYkxlLy9UeWpnOURqUlZGY3FYWnEKWWs2Mm5qSkhZVzh3WmlhZzc0QXU4dHE5OG5KandBV1ROMFV5L2w1Q2VpWnV5bzZlU0RHVDNJNm1BdGU1VXBvWAppNXBkYlZ6VDdOZC9IOEwwZHZNdVZ2N0FmakZlcU91cUZNNkkzTnlvbStLWENxNmJQdGxBWEkzeVFZc0t4ZlRkCkw3SnRaTmx6MGJ6eHJhcHI4RmpYcjhML1ZkeHQza00xMnJwb2kzL3hsckR6Q2Q2b2YrQ1MxelBocUdpOUhvcUoKZEU5VGhYMklTdkd2akVSYzVVNFRsNjJBNHNyeGJQbUt0eWx3dGNGVEJacUJiRGY3ZjBBc2cveWhndXdTcktsQQpBNkRWVXVCRFErdGpwZ0N0b0ZlOEhLVDJ6UFVlaEQ2ZjVNQkhmU2ZUZ1crTlhFSXNvVDNsampjY1hsYXhPcFJWCkNQNWxCczNmekxyYnBxbUlLaWZhdWlTNWM4TzlSUjhjQTVzeWlBOTBmbmJIdDlmdGxpRG9jcFRzNUtrbjk2NkIKZUxMM1dXVldCYUtvanJzY1RkVXJoalNnVVBmam5FTXpnVzR2eEc3d3BVNHR2ME4yaEtHUWc0bVhhcDV0SU5Pcwp4WktnZXRHUldnPT0KLS0tLS1FTkQgQ0VSVElGSUNBVEUtLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"vm-blank.ff.lan\"\n ],\n \"resolver\": \"192.168.1.13\",\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"server_cert\",\n \"key\": \"server_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ],\n \"authentication\": {\n \"client\": [\n {\n \"profile\": \"mTLS-client-profile\"\n }\n ]\n }\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/vm-blank.ff.lan_access_log\",\n \"error\": \"/var/log/nginx/vm-blank.ff.lan_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://origin_server\"\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"origin_server\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"client\": [\n {\n \"name\": \"mTLS-client-profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"enabled\": \"on\",\n \"client_certificates\": \"cacert\",\n \"trusted_ca_certificates\": \"cacert\",\n \"ocsp\": {\n \"enabled\": \"on\",\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n },\n \"stapling\": {\n \"enabled\": true,\n \"verify\": true,\n \"responder\": \"http://ocsp.k8s.ie.ff.lan\"\n }\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "Disable NGINX App Protect WAF", - "event": [ + "name": "NGINX App Protect WAF", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "PATCH", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Create initial NGINX configuration with NGINX App Protect WAF", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert.key\"\n }\n }\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-blocked\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"This is a production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"This is a production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ],\n \"app_protect\": {\n \"enabled\": true,\n \"policy\": \"production-policy\",\n \"log\": {\n \"profile_name\": \"secops_dashboard\",\n \"enabled\": true,\n \"destination\": \"127.0.0.1:514\"\n }\n }\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.1.200:80\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Change active NGINX App Protect policy", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config", - "{{configUid}}" - ] - } - }, - "response": [] - } - ] - }, - { - "name": "NGINX Javascript", - "item": [ - { - "name": "NGINX Javascript test", - "event": [ + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"policies\": [\n {\n \"type\": \"app_protect\",\n \"name\": \"production-policy\",\n \"active_tag\": \"xss-allowed\",\n \"versions\": [\n {\n \"tag\": \"xss-blocked\",\n \"displayName\": \"Production Policy - XSS blocked\",\n \"description\": \"Production-ready policy - XSS blocked\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-blocked.json\"\n }\n },\n {\n \"tag\": \"xss-allowed\",\n \"displayName\": \"Production Policy - XSS allowed\",\n \"description\": \"Production-ready policy - XSS allowed\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/nap-policy-xss-allowed.json\"\n }\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] + }, { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Example HTTP server with Javascript\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"njs-test.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/njs-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/njs-test.nginx.lab_error_log\"\n },\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$serverVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/echo\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n },\n {\n \"uri\": \"/generatecontent\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_content\"\n },\n \"profile\": \"njs_set_content\",\n \"function\": \"njsSetContent\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$httpVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"njs_profiles\": [\n {\n \"name\": \"njs_set_variable\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0VmFyaWFibGUocikgewogICAgcmV0dXJuICJWYXJpYWJsZV9zZXRfYnlfamF2YXNjcmlwdCAtIFVSSSAiK3IudXJpOwp9CgpleHBvcnQgZGVmYXVsdCB7bmpzU2V0VmFyaWFibGV9Cgo=\"\n }\n },\n {\n \"name\": \"njs_set_content\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0Q29udGVudChyKSB7CiAgci5yZXR1cm4oMjAwLCAiSGVsbG8gd29ybGQhXG4iKTsKfQoKZXhwb3J0IGRlZmF1bHQge25qc1NldENvbnRlbnR9Cg==\"\n }\n }\n ]\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Update TLS certificates", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } + ], + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_app_protect_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.crt\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v4.2/testcert2.key\"\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "Disable NGINX App Protect WAF", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "PATCH", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": []\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\"\n }\n ]\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config/{{configUid}}", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config", + "{{configUid}}" + ] + } + }, + "response": [] } - }, - "response": [] - } - ] - }, - { - "name": "Server-side and source of truth authentication", - "item": [ + ] + }, { - "name": "Bearer Token, Basic and HTTP header upstream authentication", - "event": [ + "name": "NGINX Javascript", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript" - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"Bearer token-based authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"Bearer token-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Header-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n },\n {\n \"name\": \"Basic authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"type\": \"basic\",\n \"username\": \"authusername\",\n \"password\": \"YXV0aHBhc3N3b3Jk\"\n }\n }\n ]\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "NGINX Javascript test", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript" + } } - } - }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Example HTTP server with Javascript\",\n \"resolver\": \"8.8.8.8\",\n \"names\": [\n \"njs-test.vm-test.ie.ff.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:80\"\n },\n \"log\": {\n \"access\": \"/var/log/nginx/njs-test.nginx.lab_access_log\",\n \"error\": \"/var/log/nginx/njs-test.nginx.lab_error_log\"\n },\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$serverVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n },\n \"locations\": [\n {\n \"uri\": \"/echo\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n },\n {\n \"uri\": \"/generatecontent\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://test_upstream\",\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_content\"\n },\n \"profile\": \"njs_set_content\",\n \"function\": \"njsSetContent\"\n }\n ],\n \"headers\": {\n \"to_server\": {\n \"set\": [\n {\n \"name\": \"X-Injected-Client-IP\",\n \"value\": \"$remote_addr\"\n },\n {\n \"name\": \"X-HTTP-Var-Set-By-Njs\",\n \"value\": \"$httpVarSetByNjs\"\n },\n {\n \"name\": \"X-Server-Var-Set-By-Njs\",\n \"value\": \"$serverVarSetByNjs\"\n },\n {\n \"name\": \"Host\",\n \"value\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"test_upstream\",\n \"origin\": [\n {\n \"server\": \"echo.free.beeceptor.com\"\n }\n ]\n }\n ],\n \"njs\": [\n {\n \"hook\": {\n \"type\": \"js_set\",\n \"js_set\": {\n \"variable\": \"$httpVarSetByNjs\"\n }\n },\n \"profile\": \"njs_set_variable\",\n \"function\": \"njsSetVariable\"\n }\n ],\n \"njs_profiles\": [\n {\n \"name\": \"njs_set_variable\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0VmFyaWFibGUocikgewogICAgcmV0dXJuICJWYXJpYWJsZV9zZXRfYnlfamF2YXNjcmlwdCAtIFVSSSAiK3IudXJpOwp9CgpleHBvcnQgZGVmYXVsdCB7bmpzU2V0VmFyaWFibGV9Cgo=\"\n }\n },\n {\n \"name\": \"njs_set_content\",\n \"file\": {\n \"content\": \"ZnVuY3Rpb24gbmpzU2V0Q29udGVudChyKSB7CiAgci5yZXR1cm4oMjAwLCAiSGVsbG8gd29ybGQhXG4iKTsKfQoKZXhwb3J0IGRlZmF1bHQge25qc1NldENvbnRlbnR9Cg==\"\n }\n }\n ]\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] } - }, - "response": [] + ] }, { - "name": "mTLS upstream authentication", - "event": [ + "name": "Server-side and source of truth authentication", + "item": [ { - "listen": "test", - "script": { - "exec": [ - "var respData = JSON.parse(responseBody);", - "", - "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", - "", - "pm.collectionVariables.set('configUid',respData.configUid);" - ], - "type": "text/javascript", - "packages": {} - } - } - ], - "request": { - "method": "POST", - "header": [], - "body": { - "mode": "raw", - "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"client_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZBakNDQStxZ0F3SUJBZ0lVYXZ1aFhBOWFLVFJlYmZ2Y1BFU2Z0MjBUQXhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1ZqRUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlZCQWdNQkVOdmNtc3hEVEFMQmdOVkJBY01CRU52Y21zeApFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUll3RkFZRFZRUUREQTEwWlhOMExtRmpiV1F1YkdGdU1CNFhEVEkwCk1EUXhOVEUzTURnMU0xb1hEVEkxTURReE5URTNNRGcxTTFvd1dERUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlYKQkFnTUJFTnZjbXN4RFRBTEJnTlZCQWNNQkVOdmNtc3hFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUmd3RmdZRApWUVFEREE5amJHbGxiblF1WVdOdFpTNXNZV0l3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQ0R3QXdnZ0lLCkFvSUNBUURUQ2kydi8rc08rdTJTcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkQKUWkvL0dIZXhYS3NaelBURDUyN0ZMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOApPa3lxVFdma0xkRnF2akFGclU5czMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2CmpsV1E5NTVWUzlmZlNLU3hpQTRCQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUMKRjYrRGZmQ2FNLzVockljRkswSVFheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNOAo5UlhxQmlaNnFWdGFPQXFEK0pRVGNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4CnZvczA4TSsvOUlhWTdQeGtQNXB6bDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0QKbTlURDJKT0hTU3VmdkRkRnV1YjFwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwegpkZ1RsMDVVWjZtMFpFNUdlK1FRaG5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BCmFLZ0hTbTJROE9MS1lJdlhVN1RpME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzQKc3I0UXJ4Q2xuY2pwbUMrdkRmdHJuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJvNEhGTUlIQwpNQWtHQTFVZEV3UUNNQUF3RVFZSllJWklBWWI0UWdFQkJBUURBZ1dnTURNR0NXQ0dTQUdHK0VJQkRRUW1GaVJQCmNHVnVVMU5NSUVkbGJtVnlZWFJsWkNCRGJHbGxiblFnUTJWeWRHbG1hV05oZEdVd0hRWURWUjBPQkJZRUZGQmIKdmltZDYxekxLdlA0U2RZRFRWbndYdFVwTUI4R0ExVWRJd1FZTUJhQUZLY29GRHpMVnc1QXBiMzB5UTM0c25MZQpHQ1FWTUE0R0ExVWREd0VCL3dRRUF3SUY0REFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQWdZSUt3WUJCUVVICkF3UXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSmg0cm9kZEVGemJwWk5kSW5zUmdubWVnWVIxSWlzUUxRU0IKcmNydEZxVmtLb3Rsc09EUXBFMThIRjBsbVA4WE9IU0hqYytYUVF5YlFNd1ViZ2RNbnhBZE1WaGFaMXppWElnaQpiUHgwOEY0YXR5MElJY1hyOVpFVnZTd1pheURBZHErb2s5RGpoRjQwYU5iQmFBTXB2NTRCL2U3OUNMSWZ6REo4CnNZaWw3K09abWlOUTJtZUNreVBYdXdhd3hTeVRnNVhWS3Q4VEtKQVJ5aTFJeWRHWkZRMTJGWFJHTE1BSjdaYngKRzlPd1ZvazExenpWNkRQZHFuZU5ER3BQRU5WZ2VmVjI0ZU1JVXUrYzhnSThYTU1GMU9VVWR3MmlEQmU4Q01TQQppc2h2aDFWYVZPNC96czlma2dLV2FXejNwQWprN3pkbWdaUnpXRUVHR0JMekJoQk5FQTg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"client_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRRFRDaTJ2LytzTyt1MlMKcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkRRaS8vR0hleFhLc1p6UFRENTI3RgpMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOE9reXFUV2ZrTGRGcXZqQUZyVTlzCjMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2amxXUTk1NVZTOWZmU0tTeGlBNEIKQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUNGNitEZmZDYU0vNWhySWNGSzBJUQpheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNODlSWHFCaVo2cVZ0YU9BcUQrSlFUCmNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4dm9zMDhNKy85SWFZN1B4a1A1cHoKbDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0RtOVREMkpPSFNTdWZ2RGRGdXViMQpwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwemRnVGwwNVVaNm0wWkU1R2UrUVFoCm5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BYUtnSFNtMlE4T0xLWUl2WFU3VGkKME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzRzcjRRcnhDbG5janBtQyt2RGZ0cgpuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJBb0lDQUJzWEZBK2krRmVFbW5rdE9xdHRUL1FsCk5TUlozL05WYjJuRlcyUGRqRHhKcGtUTzV2VS9qYThVd2ZmTVIrdWxCbWhSWkVmOFBweVBMc2J5USszV29iSjAKUk9JQlprVjdYd2ZDWDdEVUJDc0VtS3ZscDlvN3JVQmJqV0w1SmpJNDhYeDI3VFR0NlFMY05uVXhUZDUxanRPagpXYXFJdzNqNU1Oc0tFdDRncWlET2RhQzIvMzJaekZkTHNHNWdnRmRnUElZOUpYUXN3bHk2VnZjdzVpU0RoTnRICkcyYWxYYXdiL2s1Sk9OWTIyY3RDT0kyUnhPUGtPc3lYRkoxYTBRQStGYmxBWExKenVubE5ZNU9iOXEwWTFpa2sKSHpGeXBUcENSdmY2dzY3em00NUYvZVdkS0MzdmpnM29JK29GUS9vMTJUdjhWV3Bhc0ptSmoraUJ5aFRpOGx4YQpMempXaHNrUFZJUGJDYjFDTmIzZHNQSWVWYTVQYmRORGh2bHBQZFV4cXhKRGI4ZUFhemxSQXJVdmtaQnpkTjZtCjVwekljNGtMQnd3Wno5eGg1MzdsQ0tlZ3ZJYmdEMlQvMEM4SVFsWmFIK1M3azlWaVU2Z1RWSGNzU1RsSm52NVYKN3VCMWczVzJZRVloZFJxNlJOUXRZaUFuTktUS2ZYb3d1UUJteWRwVWRYWUljTE5GcER6eHgvRGdQSEhnU0J1dQpQZXVMZzdpemhSSndKMTQ1eW5VTlloQnROVVJaOWlRL1cwWTJOa242UWFBZDNld1pLaVg0S3lZU2k3TTF1MFJWCmFDc1BmZWJrWkRTUnRnQ2Q5N3dGSVYyTVh0UnpXNlpKUUlDWkhHTDVkYkJrNU0xQTI2NnJSYmcvTWZJSE9mdTMKRUZBVjc2aXoyVmNtSDRzblNZUHhBb0lCQVFEc0pNWElhM3ZyRXdRSkh0alRWZE9ONk9rbEExRVN4aWtjSWdSOAo3aFRWQXk3dmtORlpFVUhYczQ1TXlYQzRxTWlvUnFhZHVtOXpQWnFBQ0tQMHRWcXdDMVdOcUFrbDBQc1pZYXcrCmpKNjRDQXA4ZnJ4UDUrOVpWREtiamRWK1ZrK1lrbC9UL0Q1djNFTnZIdkVxN3ZMSE56dE9VSmFWZXNFRERQSW8KaER5SHhjRXQwTHRXTFhoNGNMSDhIQk9Kc0wxYXcxRlhNTk1qdERzazlBMTlJRFlYTHcwaWpIeC81c3hHUGcxSQpmMXRIemhpTlN5NUU5RG9pTmJxVFQvWEEyYVdCdjJ1ZlQybmw2ekticHZHNVo0VGlOUE5ESU5MaUFXOVNNQnJnCkJtaXJFVHU4MnVSa3hnaWZTM01CZ0ZFdXVtaSt2cEhwNzhGaUNOcHpZZUFwTUxjN0FvSUJBUURreVFVT29HVjEKTzZaNXJ4KzhLbVh1S3dmbXNHNmh5cmkrNEw4MzRQakVhbHdkdFhFUmphUG8vSTVhOVFoYjNnLzF5SUdiMzdLSgpPaTFCcGVRZ1owUEVQdFgyRWI0QTNCSTA5SzExWWFybnJGM2d6WS9CVWgveEZoL1YvVEVzTTloZTZ4YlhtRm9WCktvMlY1b2NlNnQzNUJuNHVLL2xkSEJKMHJnUENQdXNKVU9JMEhtUzZkdXNQcHczRFpESkZ5SEU4anptSllwcUsKMnBEMlBBMVZ5aVBBM3MwOU12TWdNSmJXajJLQVBoYk4xQXJnTERaUTFuZ0VwaVFtM3hWdmxIVkMzN0JHM0IzcApWbmtSRE9UZnF1N1BXek40aHZnMDRaNkxBdEZuYzNnMFFxRGcvcXZlS0xuclZwOUJEUlhWb0pZZkNMaVBkajZCCmFiYnFXQ0lYTnMwTkFvSUJBUURFWllrQk9UT2t2UG44UStWOVRzSldJa0hWZ0w2cTZKaEVSNTZIOE5MdW5ta28KNGI3Ylh0anQ5dTRBdXdDKzg5Ris4dE9jRnZTZVdidm5oRWdvTzdTaSthbzcyR2RUUmsyd1BHV3UxL0VoaWI1Kwo4RURhREVJcWZ6WmYzVVNVZ0dCT3VsNXN4anQvZVNlMGdYMStnYUQxUXVCV0wvd3RjaHlZMXVtSC9RTUN3TnY3CnFNQkYyaWQ1cy9Demh2NVE0K2Q0VnoyTlVKUXArN204OENWUHpieHU1N2o2NVBDZXgydFplRDQvNzN3UmFqMU4KTmh2VFNYUVlBNnVhM1VPOUVzYnQ5REFrSFQ0cjlNTHdaWlpnNXRIRCtObmhHS21MUWpvOWxyaWpYWEVyNVhkVAppSkd2cG15Qlg1VFV5TTI4R1ZrSVd5S3I5N1ZVUFp6Qm5jTjdQb01GQW9JQkFDSWRCekFESXAyMFkwSkpwb1c4CkhLN0NvODcyQjhrQUhVUDQ1d3BCOVZYME5nQUlDZkFBR3F5bTIrTWNIajcwZ1pTNGJQcjlBL1lLUXExRE94ekoKeVFUK0NaRkRXLzFzMHhvcVVhTHJDVHk2S3RWV1VWVVdGY1V3ODFaSkJvZjh3d3FFSzBmQ1k4dzhLQmh0NHovcwo3V1F3WDZncXptZmZ3N0M2TWIxSS9HckxNSzlzeU1BMDh4L0dYUHNCZWEyR0VieGg3c1paZVlteXhXS3gyWnN0CkpOK2hXU0VDODlXYzZTRGRDR2J1MngrZHVuRnFwajZ2ZS8zVmVCYUR0UUtLTkdIZ1VMeUFIY1dwS3l3cnJBVGQKeS9ZSE4wbUZkb1VNRDBQVEM3NU5MV005ZkJlUVliZ2lnblpnMkNZdStVNTlQMlVwTzd2SWVkRjZIZGdiaEJuSwpCaEVDZ2dFQkFNZmQ2Zi90RThTQlNFUnd5YW80SThuZkhTZUlMUklvM3pqRE5vWHZ6VHlwbG00a3ovYW9QeEF0CmhPYVNmZU41bFljMXFFUkRxZzRIVGRNTUkyenZPaVFuMHNGTEJFZ0UwT0VIOEZxZVZlZGgycUJucFdXWEhwTW8KREd4TnhpUnVpSDZpOEVMVWFoaU1NQkdQVi9ONjI5MDREbnUyYVVHcHlMTzZxems3SmM5VXlITC90QVNRTFIzagp6NFBhb2xTRE5rNzJSUks0VjIyZUprM2EwYVhjRU5vM1R0OUlTUTliMG5DYVVKbFJsZTA0d2QrOWUyN0FRNUNaCkVWbUtZT1JYcW9VTGNQOEcxRm1VUGhDRkU2aEI3L1hSVWpQdmJFdm5rS05Cdko2UHppT0RMdktRK3I4TzR0WkMKQi9LTDlCdjNmdUkvQlJLeU9WZVU1VnZ5MzUveUFxVT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"mTLS authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"mTLS authentication profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"certificate\": \"client_cert\",\n \"key\": \"client_key\"\n }\n }\n ]\n }\n }\n }\n}", - "options": { - "raw": { - "language": "json" + "name": "Bearer Token, Basic and HTTP header upstream authentication", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } } - } + ], + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"Bearer token-based authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"Bearer token-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"bearer\"\n }\n },\n {\n \"name\": \"Header-based authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"token\": \"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiIsImtpZCI6IjAwMDEiLCJpc3MiOiJCYXNoIEpXVCBHZW5lcmF0b3IiLCJpYXQiOjE3MDI0ODEzNjcsImV4cCI6MTcwMjQ4MTM2OH0.eyJuYW1lIjoiQm9iIERldk9wcyIsInN1YiI6IkpXVCBzdWIgY2xhaW0iLCJpc3MiOiJKV1QgaXNzIGNsYWltIiwicm9sZXMiOlsiZGV2b3BzIl19.SKA_7MszAypMEtX5NDQ0TcUbVYx_Wt0hrtmuyTmrVKU\",\n \"type\": \"header\",\n \"location\": \"X-AUTH-TOKEN\"\n }\n },\n {\n \"name\": \"Basic authentication profile\",\n \"type\": \"token\",\n \"token\": {\n \"type\": \"basic\",\n \"username\": \"authusername\",\n \"password\": \"YXV0aHBhc3N3b3Jk\"\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] }, - "url": { - "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", - "protocol": "http", - "host": [ - "{{ncg_host}}" + { + "name": "mTLS upstream authentication", + "event": [ + { + "listen": "test", + "script": { + "exec": [ + "var respData = JSON.parse(responseBody);", + "", + "tests[\"configUid is: \" +respData.configUid] = respData.configUid;", + "", + "pm.collectionVariables.set('configUid',respData.configUid);" + ], + "type": "text/javascript", + "packages": {} + } + } ], - "port": "{{ncg_port}}", - "path": [ - "{{ngc_api_version}}", - "config" - ] + "request": { + "method": "POST", + "header": [], + "body": { + "mode": "raw", + "raw": "{\n \"output\": {\n \"type\": \"nms\",\n \"nms\": {\n \"url\": \"{{nim_host}}\",\n \"username\": \"{{nim_username}}\",\n \"password\": \"{{nim_password}}\",\n \"instancegroup\": \"{{nim_instancegroup}}\",\n \"synctime\": 0,\n \"modules\": [\n \"ngx_http_js_module\",\n \"ngx_stream_js_module\"\n ],\n \"certificates\": [\n {\n \"type\": \"certificate\",\n \"name\": \"test_cert\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.crt\",\n \"authentication\": [\n {\n \"profile\": \"Basic authentication profile\"\n }\n ]\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"test_key\",\n \"contents\": {\n \"content\": \"{{github_gitops_root}}/v2/www.online-boutique.local.key\"\n }\n },\n {\n \"type\": \"certificate\",\n \"name\": \"client_cert\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBDRVJUSUZJQ0FURS0tLS0tCk1JSUZBakNDQStxZ0F3SUJBZ0lVYXZ1aFhBOWFLVFJlYmZ2Y1BFU2Z0MjBUQXhzd0RRWUpLb1pJaHZjTkFRRUwKQlFBd1ZqRUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlZCQWdNQkVOdmNtc3hEVEFMQmdOVkJBY01CRU52Y21zeApFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUll3RkFZRFZRUUREQTEwWlhOMExtRmpiV1F1YkdGdU1CNFhEVEkwCk1EUXhOVEUzTURnMU0xb1hEVEkxTURReE5URTNNRGcxTTFvd1dERUxNQWtHQTFVRUJoTUNTVVV4RFRBTEJnTlYKQkFnTUJFTnZjbXN4RFRBTEJnTlZCQWNNQkVOdmNtc3hFVEFQQmdOVkJBb01DRUZqYldVZ1RIUmtNUmd3RmdZRApWUVFEREE5amJHbGxiblF1WVdOdFpTNXNZV0l3Z2dJaU1BMEdDU3FHU0liM0RRRUJBUVVBQTRJQ0R3QXdnZ0lLCkFvSUNBUURUQ2kydi8rc08rdTJTcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkQKUWkvL0dIZXhYS3NaelBURDUyN0ZMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOApPa3lxVFdma0xkRnF2akFGclU5czMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2CmpsV1E5NTVWUzlmZlNLU3hpQTRCQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUMKRjYrRGZmQ2FNLzVockljRkswSVFheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNOAo5UlhxQmlaNnFWdGFPQXFEK0pRVGNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4CnZvczA4TSsvOUlhWTdQeGtQNXB6bDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0QKbTlURDJKT0hTU3VmdkRkRnV1YjFwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwegpkZ1RsMDVVWjZtMFpFNUdlK1FRaG5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BCmFLZ0hTbTJROE9MS1lJdlhVN1RpME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzQKc3I0UXJ4Q2xuY2pwbUMrdkRmdHJuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJvNEhGTUlIQwpNQWtHQTFVZEV3UUNNQUF3RVFZSllJWklBWWI0UWdFQkJBUURBZ1dnTURNR0NXQ0dTQUdHK0VJQkRRUW1GaVJQCmNHVnVVMU5NSUVkbGJtVnlZWFJsWkNCRGJHbGxiblFnUTJWeWRHbG1hV05oZEdVd0hRWURWUjBPQkJZRUZGQmIKdmltZDYxekxLdlA0U2RZRFRWbndYdFVwTUI4R0ExVWRJd1FZTUJhQUZLY29GRHpMVnc1QXBiMzB5UTM0c25MZQpHQ1FWTUE0R0ExVWREd0VCL3dRRUF3SUY0REFkQmdOVkhTVUVGakFVQmdnckJnRUZCUWNEQWdZSUt3WUJCUVVICkF3UXdEUVlKS29aSWh2Y05BUUVMQlFBRGdnRUJBSmg0cm9kZEVGemJwWk5kSW5zUmdubWVnWVIxSWlzUUxRU0IKcmNydEZxVmtLb3Rsc09EUXBFMThIRjBsbVA4WE9IU0hqYytYUVF5YlFNd1ViZ2RNbnhBZE1WaGFaMXppWElnaQpiUHgwOEY0YXR5MElJY1hyOVpFVnZTd1pheURBZHErb2s5RGpoRjQwYU5iQmFBTXB2NTRCL2U3OUNMSWZ6REo4CnNZaWw3K09abWlOUTJtZUNreVBYdXdhd3hTeVRnNVhWS3Q4VEtKQVJ5aTFJeWRHWkZRMTJGWFJHTE1BSjdaYngKRzlPd1ZvazExenpWNkRQZHFuZU5ER3BQRU5WZ2VmVjI0ZU1JVXUrYzhnSThYTU1GMU9VVWR3MmlEQmU4Q01TQQppc2h2aDFWYVZPNC96czlma2dLV2FXejNwQWprN3pkbWdaUnpXRUVHR0JMekJoQk5FQTg9Ci0tLS0tRU5EIENFUlRJRklDQVRFLS0tLS0K\"\n }\n },\n {\n \"type\": \"key\",\n \"name\": \"client_key\",\n \"contents\": {\n \"content\": \"LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUpRd0lCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQ1Mwd2dna3BBZ0VBQW9JQ0FRRFRDaTJ2LytzTyt1MlMKcFF5ZVBLM3hnb3RyRUlDZGJYMmN4N0xkcnRDbE82a0xTM3IybWpxV1ptWkRRaS8vR0hleFhLc1p6UFRENTI3RgpMZlREcExIb2h1MldmeUErQy9HN2JycXlNT0dQZ3lVcEI2NWN2THU4V1BNOE9reXFUV2ZrTGRGcXZqQUZyVTlzCjMxUnljT3BQSlB0cFloRWlhQXZRWDlwRnhjalM2NUJscUFCMjY1YnlncGs2amxXUTk1NVZTOWZmU0tTeGlBNEIKQXAzMHV3Rm5YRWtMcGRCenI1TkFKeXJqdXhzbHJGVEs4SGxvRmQ2MndzQUNGNitEZmZDYU0vNWhySWNGSzBJUQpheHBpaWUyWllKaVFmNXJMeVd6VG1xMVh1QmsyaEhBWDNvQUY2bW9hcnVNODlSWHFCaVo2cVZ0YU9BcUQrSlFUCmNpYmVzekQ2Z2xoSitGM0VFTEJOWVhETmFLbGprQXRxVWFwYStXZEdVUkU4dm9zMDhNKy85SWFZN1B4a1A1cHoKbDhmaS9RaS9ESW9WLzh0UUlydFBIYzJqSTFSRkZoL3BpeDFsR2lyTlRBK0RtOVREMkpPSFNTdWZ2RGRGdXViMQpwMnF5TW5Nd3FFNklHOGpmTEg2TGNxRGFSbmcveVpVUTBqa01LSFYrd1gwemRnVGwwNVVaNm0wWkU1R2UrUVFoCm5nY0FUemdrSWp3WlJod1JLOEx1Nmtwb2Vnb0dMUkhkTVNqcUV0NkNWc25BYUtnSFNtMlE4T0xLWUl2WFU3VGkKME9DNm5OdU40RDlKdkxGQXdPMUVFU2tCUXFQeThGby8zRVNZWU1FazJ6RzRzcjRRcnhDbG5janBtQyt2RGZ0cgpuR055SVJvditOeS9zd2FSdkcvcm15ZnhEYVdNL3dJREFRQUJBb0lDQUJzWEZBK2krRmVFbW5rdE9xdHRUL1FsCk5TUlozL05WYjJuRlcyUGRqRHhKcGtUTzV2VS9qYThVd2ZmTVIrdWxCbWhSWkVmOFBweVBMc2J5USszV29iSjAKUk9JQlprVjdYd2ZDWDdEVUJDc0VtS3ZscDlvN3JVQmJqV0w1SmpJNDhYeDI3VFR0NlFMY05uVXhUZDUxanRPagpXYXFJdzNqNU1Oc0tFdDRncWlET2RhQzIvMzJaekZkTHNHNWdnRmRnUElZOUpYUXN3bHk2VnZjdzVpU0RoTnRICkcyYWxYYXdiL2s1Sk9OWTIyY3RDT0kyUnhPUGtPc3lYRkoxYTBRQStGYmxBWExKenVubE5ZNU9iOXEwWTFpa2sKSHpGeXBUcENSdmY2dzY3em00NUYvZVdkS0MzdmpnM29JK29GUS9vMTJUdjhWV3Bhc0ptSmoraUJ5aFRpOGx4YQpMempXaHNrUFZJUGJDYjFDTmIzZHNQSWVWYTVQYmRORGh2bHBQZFV4cXhKRGI4ZUFhemxSQXJVdmtaQnpkTjZtCjVwekljNGtMQnd3Wno5eGg1MzdsQ0tlZ3ZJYmdEMlQvMEM4SVFsWmFIK1M3azlWaVU2Z1RWSGNzU1RsSm52NVYKN3VCMWczVzJZRVloZFJxNlJOUXRZaUFuTktUS2ZYb3d1UUJteWRwVWRYWUljTE5GcER6eHgvRGdQSEhnU0J1dQpQZXVMZzdpemhSSndKMTQ1eW5VTlloQnROVVJaOWlRL1cwWTJOa242UWFBZDNld1pLaVg0S3lZU2k3TTF1MFJWCmFDc1BmZWJrWkRTUnRnQ2Q5N3dGSVYyTVh0UnpXNlpKUUlDWkhHTDVkYkJrNU0xQTI2NnJSYmcvTWZJSE9mdTMKRUZBVjc2aXoyVmNtSDRzblNZUHhBb0lCQVFEc0pNWElhM3ZyRXdRSkh0alRWZE9ONk9rbEExRVN4aWtjSWdSOAo3aFRWQXk3dmtORlpFVUhYczQ1TXlYQzRxTWlvUnFhZHVtOXpQWnFBQ0tQMHRWcXdDMVdOcUFrbDBQc1pZYXcrCmpKNjRDQXA4ZnJ4UDUrOVpWREtiamRWK1ZrK1lrbC9UL0Q1djNFTnZIdkVxN3ZMSE56dE9VSmFWZXNFRERQSW8KaER5SHhjRXQwTHRXTFhoNGNMSDhIQk9Kc0wxYXcxRlhNTk1qdERzazlBMTlJRFlYTHcwaWpIeC81c3hHUGcxSQpmMXRIemhpTlN5NUU5RG9pTmJxVFQvWEEyYVdCdjJ1ZlQybmw2ekticHZHNVo0VGlOUE5ESU5MaUFXOVNNQnJnCkJtaXJFVHU4MnVSa3hnaWZTM01CZ0ZFdXVtaSt2cEhwNzhGaUNOcHpZZUFwTUxjN0FvSUJBUURreVFVT29HVjEKTzZaNXJ4KzhLbVh1S3dmbXNHNmh5cmkrNEw4MzRQakVhbHdkdFhFUmphUG8vSTVhOVFoYjNnLzF5SUdiMzdLSgpPaTFCcGVRZ1owUEVQdFgyRWI0QTNCSTA5SzExWWFybnJGM2d6WS9CVWgveEZoL1YvVEVzTTloZTZ4YlhtRm9WCktvMlY1b2NlNnQzNUJuNHVLL2xkSEJKMHJnUENQdXNKVU9JMEhtUzZkdXNQcHczRFpESkZ5SEU4anptSllwcUsKMnBEMlBBMVZ5aVBBM3MwOU12TWdNSmJXajJLQVBoYk4xQXJnTERaUTFuZ0VwaVFtM3hWdmxIVkMzN0JHM0IzcApWbmtSRE9UZnF1N1BXek40aHZnMDRaNkxBdEZuYzNnMFFxRGcvcXZlS0xuclZwOUJEUlhWb0pZZkNMaVBkajZCCmFiYnFXQ0lYTnMwTkFvSUJBUURFWllrQk9UT2t2UG44UStWOVRzSldJa0hWZ0w2cTZKaEVSNTZIOE5MdW5ta28KNGI3Ylh0anQ5dTRBdXdDKzg5Ris4dE9jRnZTZVdidm5oRWdvTzdTaSthbzcyR2RUUmsyd1BHV3UxL0VoaWI1Kwo4RURhREVJcWZ6WmYzVVNVZ0dCT3VsNXN4anQvZVNlMGdYMStnYUQxUXVCV0wvd3RjaHlZMXVtSC9RTUN3TnY3CnFNQkYyaWQ1cy9Demh2NVE0K2Q0VnoyTlVKUXArN204OENWUHpieHU1N2o2NVBDZXgydFplRDQvNzN3UmFqMU4KTmh2VFNYUVlBNnVhM1VPOUVzYnQ5REFrSFQ0cjlNTHdaWlpnNXRIRCtObmhHS21MUWpvOWxyaWpYWEVyNVhkVAppSkd2cG15Qlg1VFV5TTI4R1ZrSVd5S3I5N1ZVUFp6Qm5jTjdQb01GQW9JQkFDSWRCekFESXAyMFkwSkpwb1c4CkhLN0NvODcyQjhrQUhVUDQ1d3BCOVZYME5nQUlDZkFBR3F5bTIrTWNIajcwZ1pTNGJQcjlBL1lLUXExRE94ekoKeVFUK0NaRkRXLzFzMHhvcVVhTHJDVHk2S3RWV1VWVVdGY1V3ODFaSkJvZjh3d3FFSzBmQ1k4dzhLQmh0NHovcwo3V1F3WDZncXptZmZ3N0M2TWIxSS9HckxNSzlzeU1BMDh4L0dYUHNCZWEyR0VieGg3c1paZVlteXhXS3gyWnN0CkpOK2hXU0VDODlXYzZTRGRDR2J1MngrZHVuRnFwajZ2ZS8zVmVCYUR0UUtLTkdIZ1VMeUFIY1dwS3l3cnJBVGQKeS9ZSE4wbUZkb1VNRDBQVEM3NU5MV005ZkJlUVliZ2lnblpnMkNZdStVNTlQMlVwTzd2SWVkRjZIZGdiaEJuSwpCaEVDZ2dFQkFNZmQ2Zi90RThTQlNFUnd5YW80SThuZkhTZUlMUklvM3pqRE5vWHZ6VHlwbG00a3ovYW9QeEF0CmhPYVNmZU41bFljMXFFUkRxZzRIVGRNTUkyenZPaVFuMHNGTEJFZ0UwT0VIOEZxZVZlZGgycUJucFdXWEhwTW8KREd4TnhpUnVpSDZpOEVMVWFoaU1NQkdQVi9ONjI5MDREbnUyYVVHcHlMTzZxems3SmM5VXlITC90QVNRTFIzagp6NFBhb2xTRE5rNzJSUks0VjIyZUprM2EwYVhjRU5vM1R0OUlTUTliMG5DYVVKbFJsZTA0d2QrOWUyN0FRNUNaCkVWbUtZT1JYcW9VTGNQOEcxRm1VUGhDRkU2aEI3L1hSVWpQdmJFdm5rS05Cdko2UHppT0RMdktRK3I4TzR0WkMKQi9LTDlCdjNmdUkvQlJLeU9WZVU1VnZ5MzUveUFxVT0KLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo=\"\n }\n }\n ]\n }\n },\n \"declaration\": {\n \"http\": {\n \"servers\": [\n {\n \"name\": \"Online boutique HTTPS\",\n \"names\": [\n \"www.online-boutique.lan\"\n ],\n \"listen\": {\n \"address\": \"0.0.0.0:443\",\n \"http2\": true,\n \"tls\": {\n \"certificate\": \"test_cert\",\n \"key\": \"test_key\",\n \"ciphers\": \"DEFAULT\",\n \"protocols\": [\n \"TLSv1.2\",\n \"TLSv1.3\"\n ]\n }\n },\n \"log\": {\n \"access\": \"/var/log/nginx/online_boutique_https_access_log\",\n \"error\": \"/var/log/nginx/online_boutique_https_error_log\"\n },\n \"locations\": [\n {\n \"uri\": \"/\",\n \"urimatch\": \"prefix\",\n \"upstream\": \"http://upstream_boutique\",\n \"authentication\": {\n \"server\": [\n {\n \"profile\": \"mTLS authentication profile\"\n }\n ]\n }\n }\n ]\n }\n ],\n \"upstreams\": [\n {\n \"name\": \"upstream_boutique\",\n \"origin\": [\n {\n \"server\": \"192.168.2.200:80\"\n }\n ]\n }\n ],\n \"authentication\": {\n \"server\": [\n {\n \"name\": \"mTLS authentication profile\",\n \"type\": \"mtls\",\n \"mtls\": {\n \"certificate\": \"client_cert\",\n \"key\": \"client_key\"\n }\n }\n ]\n }\n }\n }\n}", + "options": { + "raw": { + "language": "json" + } + } + }, + "url": { + "raw": "http://{{ncg_host}}:{{ncg_port}}/{{ngc_api_version}}/config", + "protocol": "http", + "host": [ + "{{ncg_host}}" + ], + "port": "{{ncg_port}}", + "path": [ + "{{ngc_api_version}}", + "config" + ] + } + }, + "response": [] } - }, - "response": [] + ] } ] } ] } - ] + ], + "description": "[https://github.com/f5devcentral/NGINX-Declarative-API/blob/main/USAGE-v5.0.md](https://github.com/f5devcentral/NGINX-Declarative-API/blob/main/USAGE-v4.2.md)" } ], "event": [ @@ -4501,7 +4605,27 @@ }, { "key": "ngc_api_version", - "value": "v4.2", + "value": "v5.0", + "type": "string" + }, + { + "key": "nginxone_url", + "value": "https://tenant.volterra.us", + "type": "string" + }, + { + "key": "nginxone_token", + "value": "APIToken TOKEN", + "type": "string" + }, + { + "key": "nginxone_namespace", + "value": "default", + "type": "string" + }, + { + "key": "nginxone_clustername", + "value": "declarativeAPITest", "type": "string" } ] diff --git a/contrib/postman/README.md b/contrib/postman/README.md index 70d1055..20a51bf 100644 --- a/contrib/postman/README.md +++ b/contrib/postman/README.md @@ -2,7 +2,7 @@ This collection contains: -API v4.2 - Latest +API v5.0 - Latest - `Configuration generation` - Declaration examples with output to plaintext, JSON, Kubernetes ConfigMap, HTTP POST - `Declarative automation examples` - Several examples and use cases - `API Gateway` - Sample API gateway requests for Swagger and OpenAPI schemas import @@ -18,16 +18,20 @@ API v4.2 - Latest - `NGINX Javascript` - NGINX configuration with `njs` javascript - `Server-side and source of truth authentication` - Requests for authentication towards upstreams and source of truth -API v4.1 +API v4.2 - `Configuration generation` - Declaration examples with output to plaintext, JSON, Kubernetes ConfigMap, HTTP POST - `Declarative automation examples` - Several examples and use cases - `API Gateway` - Sample API gateway requests for Swagger and OpenAPI schemas import - `CRUD automation` - Sample requests for CRUD-based automation - `GitOps autosync` - GitOps automation demo - `Housekeeping - common endpoints` - Miscellaneous general purpose requests + - `HTTP Headers Manipulation` - `server` and `location` level HTTP headers management + - `HTTP Server` - TLS offload with NGINX App Protect WAF - `JWT Client Authentication` - JWT-based client authentication for HTTP + - `JWT Client Authentication and Authorization` - JWT-based client authentication and authorization for HTTP - `mTLS Client Authentication` - mTLS client authentication for HTTP - `NGINX App Protect WAF` - Sample requests for declarative configuration lifecycle management + - `NGINX Javascript` - NGINX configuration with `njs` javascript - `Server-side and source of truth authentication` - Requests for authentication towards upstreams and source of truth --- diff --git a/etc/config.toml b/etc/config.toml index 659ec8e..07662dd 100644 --- a/etc/config.toml +++ b/etc/config.toml @@ -3,7 +3,7 @@ # Main variables [main] banner = "NGINX Declarative API" -version = "4.2" +version = "5.0" url = "https://github.com/f5devcentral/NGINX-Declarative-API" # Templates @@ -40,6 +40,7 @@ uri = "/v1/devportal" # Staged configuration for NGINX Instance Manager [nms] +nginx_conf = '/etc/nginx/nginx.conf' config_dir = '/etc/nginx' certs_dir = '/etc/nginx/ssl' apigw_dir = '/etc/nginx/apigateway' diff --git a/src/V4_1_CreateConfig.py b/src/V5_0_CreateConfig.py similarity index 52% rename from src/V4_1_CreateConfig.py rename to src/V5_0_CreateConfig.py index eb4856f..64746c7 100644 --- a/src/V4_1_CreateConfig.py +++ b/src/V5_0_CreateConfig.py @@ -7,6 +7,7 @@ import pickle import time import uuid +import hashlib from datetime import datetime from urllib.parse import urlparse @@ -17,31 +18,29 @@ from pydantic import ValidationError from requests.packages.urllib3.exceptions import InsecureRequestWarning -import v4_1.APIGateway -import v4_1.DevPortal -import v4_1.DeclarationPatcher -import v4_1.GitOps -import v4_1.MiscUtils +import v5_0.APIGateway +import v5_0.DevPortal +import v5_0.DeclarationPatcher +import v5_0.GitOps +import v5_0.MiscUtils +import v5_0.NMSOutput +import v5_0.NGINXOneOutput # NGINX App Protect helper functions -import v4_1.NAPUtils -import v4_1.NIMUtils +import v5_0.NAPUtils +import v5_0.NIMUtils # NGINX Declarative API modules from NcgConfig import NcgConfig from NcgRedis import NcgRedis # pydantic models -from V4_1_NginxConfigDeclaration import * +from V5_0_NginxConfigDeclaration import * # Tolerates self-signed TLS certificates requests.packages.urllib3.disable_warnings(InsecureRequestWarning) -def getuniqueid(): - return uuid.uuid4() - - def configautosync(configUid): print("Autosyncing configuid [" + configUid + "]") @@ -62,10 +61,10 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn # Building NGINX configuration for the given declaration # NGINX configuration files for staged config - configFiles = {'files': [], 'rootDir': NcgConfig.config['nms']['config_dir']} + configFiles = {'files': []} # NGINX auxiliary files for staged config - auxFiles = {'files': [], 'rootDir': NcgConfig.config['nms']['config_dir']} + auxFiles = {'files': []} try: # Pydantic JSON validation @@ -78,11 +77,11 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn j2_env = Environment(loader=FileSystemLoader(NcgConfig.config['templates']['root_dir'] + '/' + apiversion), trim_blocks=True, extensions=["jinja2_base64_filters.Base64Filters"]) - j2_env.filters['regex_replace'] = v4_1.MiscUtils.regex_replace + j2_env.filters['regex_replace'] = v5_0.MiscUtils.regex_replace if 'http' in d['declaration']: if 'snippet' in d['declaration']['http']: - status, snippet = v4_1.GitOps.getObjectFromRepo(object = d['declaration']['http']['snippet'], authProfiles = d['declaration']['http']['authentication']) + status, snippet = v5_0.GitOps.getObjectFromRepo(object = d['declaration']['http']['snippet'], authProfiles = d['declaration']['http']['authentication']) if status != 200: return {"status_code": 422, "message": {"status_code": status, "message": snippet}} @@ -99,7 +98,7 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn upstream = http['upstreams'][i] if upstream['snippet']: - status, snippet = v4_1.GitOps.getObjectFromRepo(object = upstream['snippet'], authProfiles = d['declaration']['http']['authentication']) + status, snippet = v5_0.GitOps.getObjectFromRepo(object = upstream['snippet'], authProfiles = d['declaration']['http']['authentication']) if status != 200: return {"status_code": 422, "message": {"status_code": status, "message": snippet}} @@ -112,7 +111,7 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn all_ratelimits = [] http = d['declaration']['http'] - d_rate_limit = v4_1.MiscUtils.getDictKey(d, 'declaration.http.rate_limit') + d_rate_limit = v5_0.MiscUtils.getDictKey(d, 'declaration.http.rate_limit') if d_rate_limit is not None: for i in range(len(d_rate_limit)): all_ratelimits.append(d_rate_limit[i]['name']) @@ -123,7 +122,7 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn all_auth_client_profiles = [] all_auth_server_profiles = [] - d_auth_profiles = v4_1.MiscUtils.getDictKey(d, 'declaration.http.authentication') + d_auth_profiles = v5_0.MiscUtils.getDictKey(d, 'declaration.http.authentication') if d_auth_profiles is not None: if 'client' in d_auth_profiles: # Render all client authentication profiles @@ -160,6 +159,21 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn all_auth_client_profiles.append(auth_profile['name']) auxFiles['files'].append(authProfileConfigFile) + case 'mtls': + # Add the rendered authentication configuration snippet as a config file in the staged configuration - mTLS template + templateName = NcgConfig.config['templates']['auth_client_root'] + "/mtls.tmpl" + renderedClientAuthProfile = j2_env.get_template(templateName).render( + authprofile=auth_profile, ncgconfig=NcgConfig.config) + + b64renderedClientAuthProfile = base64.b64encode( + bytes(renderedClientAuthProfile, 'utf-8')).decode('utf-8') + configFileName = NcgConfig.config['nms']['auth_client_dir'] + '/' + auth_profile[ + 'name'].replace(' ', '_') + ".conf" + authProfileConfigFile = {'contents': b64renderedClientAuthProfile, + 'name': configFileName} + + all_auth_client_profiles.append(auth_profile['name']) + auxFiles['files'].append(authProfileConfigFile) if 'server' in d_auth_profiles: # Render all server authentication profiles @@ -170,7 +184,7 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn match auth_profile['type']: case 'token': - # Add the rendered authentication configuration snippet as a config file in the staged configuration - jwt template + # Add the rendered authentication configuration snippet as a config file in the staged configuration - token template templateName = NcgConfig.config['templates']['auth_server_root']+"/token.tmpl" renderedServerAuthProfile = j2_env.get_template(templateName).render( authprofile=auth_profile, ncgconfig=NcgConfig.config) @@ -183,16 +197,142 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn all_auth_server_profiles.append(auth_profile['name']) auxFiles['files'].append(authProfileConfigFile) + case 'mtls': + # Add the rendered authentication configuration snippet as a config file in the staged configuration - mTLS template + templateName = NcgConfig.config['templates']['auth_server_root'] + "/mtls.tmpl" + renderedServerAuthProfile = j2_env.get_template(templateName).render( + authprofile=auth_profile, ncgconfig=NcgConfig.config) + + b64renderedServerAuthProfile = base64.b64encode( + bytes(renderedServerAuthProfile, 'utf-8')).decode('utf-8') + configFileName = NcgConfig.config['nms']['auth_server_dir'] + '/' + auth_profile[ + 'name'].replace(' ', '_') + ".conf" + authProfileConfigFile = {'contents': b64renderedServerAuthProfile, + 'name': configFileName} + + all_auth_server_profiles.append(auth_profile['name']) + auxFiles['files'].append(authProfileConfigFile) + + + # Check authorization profiles validity and creates authorization config files + + # List of all authorization client profile names + all_authz_client_profiles = [] + + d_authz_profiles = v5_0.MiscUtils.getDictKey(d, 'declaration.http.authorization') + if d_authz_profiles is not None: + # Render all client authorization profiles + + for i in range(len(d_authz_profiles)): + authz_profile = d_authz_profiles[i] + + match authz_profile['type']: + case 'jwt': + # Add the rendered authorization configuration snippet as a config file in the staged configuration - jwt authZ maps template + templateName = NcgConfig.config['templates']['authz_client_root']+"/jwt-authz-map.tmpl" + renderedClientAuthZProfile = j2_env.get_template(templateName).render( + authprofile=authz_profile, ncgconfig=NcgConfig.config) + + b64renderedClientAuthProfile = base64.b64encode(bytes(renderedClientAuthZProfile, 'utf-8')).decode('utf-8') + configFileName = NcgConfig.config['nms']['authz_client_dir'] + '/'+authz_profile['name'].replace(' ','_')+".maps.conf" + authProfileConfigFile = {'contents': b64renderedClientAuthProfile, + 'name': configFileName } + + all_authz_client_profiles.append(authz_profile['name']) + auxFiles['files'].append(authProfileConfigFile) + + # Add the rendered authorization configuration snippet as a config file in the staged configuration - jwt template + templateName = NcgConfig.config['templates']['authz_client_root'] + "/jwt.tmpl" + renderedClientAuthZProfile = j2_env.get_template(templateName).render( + authprofile=authz_profile, ncgconfig=NcgConfig.config) + + b64renderedClientAuthProfile = base64.b64encode(bytes(renderedClientAuthZProfile, 'utf-8')).decode( + 'utf-8') + configFileName = NcgConfig.config['nms']['authz_client_dir'] + '/' + authz_profile['name'].replace(' ', + '_') + ".conf" + authProfileConfigFile = {'contents': b64renderedClientAuthProfile, + 'name': configFileName} + + all_authz_client_profiles.append(authz_profile['name']) + auxFiles['files'].append(authProfileConfigFile) + + # NGINX Javascript profiles + all_njs_profiles = [] + d_njs_files = v5_0.MiscUtils.getDictKey(d, 'declaration.http.njs_profiles') + if d_njs_files is not None: + for i in range(len(d_njs_files)): + njs_file = d_njs_files[i] + njs_filename = njs_file['name'].replace(' ','_') + + status, content = v5_0.GitOps.getObjectFromRepo(object=njs_file['file'], + authProfiles=d['declaration']['http'][ + 'authentication']) + + if status != 200: + return {"status_code": 422, "message": {"status_code": status, "message": content}} + + njsAuxFile = {'contents': content['content'], + 'name': NcgConfig.config['nms']['njs_dir'] + '/' + njs_filename + '.js'} + auxFiles['files'].append(njsAuxFile) + all_njs_profiles.append(njs_filename) + + # HTTP level Javascript hooks + d_http_njs_hooks = v5_0.MiscUtils.getDictKey(d, 'declaration.http.njs') + if d_http_njs_hooks is not None: + for i in range(len(d_http_njs_hooks)): + if d_http_njs_hooks[i]['profile'] not in all_njs_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid njs profile [{d_http_njs_hooks[i]['profile']}] in HTTP declaration, must be one of {all_njs_profiles}"}}} + # Parse HTTP servers - d_servers = v4_1.MiscUtils.getDictKey(d, 'declaration.http.servers') + d_servers = v5_0.MiscUtils.getDictKey(d, 'declaration.http.servers') if d_servers is not None: - apiGatewaySnippet = '' - for server in d_servers: serverSnippet = '' + # Server level Javascript hooks + if server['njs']: + for i in range(len(server['njs'])): + if server['njs'][i]['profile'] not in all_njs_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid njs profile [{server['njs'][i]['profile']}] in server [{server['name']}], must be one of {all_njs_profiles}"}}} + + # Server client authentication name validity check + if 'authentication' in server and server['authentication']: + serverAuthClientProfiles = server['authentication']['client'] + + for authClientProfile in serverAuthClientProfiles: + if authClientProfile['profile'] not in all_auth_client_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid client authentication profile [{authClientProfile['profile']}] in server [{server['name']}] must be one of {all_auth_client_profiles}"}}} + + # Location client authorization name validity check + if 'authorization' in server and server['authorization']: + if server['authorization']['profile'] and server['authorization']['profile'] not in all_authz_client_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid client authorization profile [{server['authorization']['profile']}] in server [{server['name']}] must be one of {all_authz_client_profiles}"}}} + + # mTLS client authentication name validity check + if 'authentication' in server['listen']['tls']: + if 'client' in server['listen']['tls']['authentication']: + tlsAuthProfiles = server['listen']['tls']['authentication']['client'] + for mtlsClientProfile in tlsAuthProfiles: + if mtlsClientProfile['profile'] not in all_auth_client_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid client authentication profile [{mtlsClientProfile['profile']}] in server [{server['name']}] must be one of {all_auth_client_profiles}"}}} + if server['snippet']: - status, serverSnippet = v4_1.GitOps.getObjectFromRepo(object = server['snippet'], authProfiles = d['declaration']['http']['authentication'], base64Encode = False) + status, serverSnippet = v5_0.GitOps.getObjectFromRepo(object = server['snippet'], authProfiles = d['declaration']['http']['authentication'], base64Encode = False) if status != 200: return {"status_code": 422, "message": {"status_code": status, "message": serverSnippet}} @@ -200,8 +340,18 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn serverSnippet = serverSnippet['content'] for loc in server['locations']: + + # Location level Javascript hooks + if loc['njs']: + for i in range(len(loc['njs'])): + if loc['njs'][i]['profile'] not in all_njs_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, + "content": f"invalid njs profile [{loc['njs'][i]['profile']}] in location [{loc['uri']}], must be one of {all_njs_profiles}"}}} + if loc['snippet']: - status, snippet = v4_1.GitOps.getObjectFromRepo(object = loc['snippet'], authProfiles = d['declaration']['http']['authentication']) + status, snippet = v5_0.GitOps.getObjectFromRepo(object = loc['snippet'], authProfiles = d['declaration']['http']['authentication']) if status != 200: return {"status_code": 422, "message": {"status_code": status, "message": snippet}} @@ -222,7 +372,14 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn if authClientProfile['profile'] not in all_auth_client_profiles: return {"status_code": 422, "message": {"status_code": status, "message": - {"code": status, "content": f"invalid client authentication profile [{authClientProfile['profile']}] in location [{loc['uri']}]"}}} + {"code": status, "content": f"invalid client authentication profile [{authClientProfile['profile']}] in location [{loc['uri']}] must be one of {all_auth_client_profiles}"}}} + + # Location client authorization name validity check + if 'authorization' in loc and loc['authorization']: + if loc['authorization']['profile'] and loc['authorization']['profile'] not in all_authz_client_profiles: + return {"status_code": 422, + "message": {"status_code": status, "message": + {"code": status, "content": f"invalid client authorization profile [{loc['authorization']['profile']}] in location [{loc['uri']}] must be one of {all_authz_client_profiles}"}}} # Location server authentication name validity check if 'authentication' in loc and loc['authentication']: @@ -243,19 +400,27 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn {"code": status, "content": f"invalid server authentication profile [{openApiAuthProfile[0]['profile']}] for OpenAPI schema [{loc['apigateway']['openapi_schema']['content']}]"}}} - status, apiGatewayConfigDeclaration = v4_1.APIGateway.createAPIGateway(locationDeclaration = loc, authProfiles = d['declaration']['http']['authentication']) - else: - apiGatewayConfigDeclaration = '' + status, apiGatewayConfigDeclaration = v5_0.APIGateway.createAPIGateway(locationDeclaration = loc, authProfiles = d['declaration']['http']['authentication']) + + # API Gateway configuration template rendering + if apiGatewayConfigDeclaration: + apiGatewaySnippet = j2_env.get_template(NcgConfig.config['templates']['apigwconf']).render( + declaration=apiGatewayConfigDeclaration, ncgconfig=NcgConfig.config) + apiGatewaySnippetb64 = base64.b64encode(bytes(apiGatewaySnippet, 'utf-8')).decode('utf-8') + + newAuxFile = {'contents': apiGatewaySnippetb64, 'name': NcgConfig.config['nms']['apigw_dir'] + + loc['uri'] + ".conf" } + auxFiles['files'].append(newAuxFile) # API Gateway Developer portal provisioning if loc['apigateway'] and loc['apigateway']['developer_portal'] and 'enabled' in loc['apigateway']['developer_portal'] and loc['apigateway']['developer_portal']['enabled'] == True: - status, devPortalHTML = v4_1.DevPortal.createDevPortal(locationDeclaration = loc, authProfiles = d['declaration']['http']['authentication']) + status, devPortalHTML = v5_0.DevPortal.createDevPortal(locationDeclaration = loc, authProfiles = d['declaration']['http']['authentication']) if status != 200: - return {"status_code": 400, + return {"status_code": 412, "message": {"status_code": status, "message": - {"code": status, "content": f"Developer Portal creation failed for {loc['apigateway']['openapi_schema']['content']}"}}} + {"code": status, "content": f"Developer Portal creation failed for {loc['uri']}"}}} ### Add optional API Developer portal HTML files # devPortalHTML @@ -276,28 +441,23 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn "content": f"invalid rate_limit profile [{loc['rate_limit']['profile']}]"}}} - # API Gateway configuration template rendering - apiGatewaySnippet += j2_env.get_template(NcgConfig.config['templates']['apigwconf']).render( - declaration=apiGatewayConfigDeclaration, ncgconfig=NcgConfig.config)\ - if apiGatewayConfigDeclaration else '' - - server['snippet']['content'] = base64.b64encode(bytes(serverSnippet + apiGatewaySnippet, 'utf-8')).decode('utf-8') + server['snippet']['content'] = base64.b64encode(bytes(serverSnippet, 'utf-8')).decode('utf-8') if 'layer4' in d['declaration']: # Check Layer4/stream upstreams validity all_upstreams = [] - d_upstreams = v4_1.MiscUtils.getDictKey(d, 'declaration.layer4.upstreams') + d_upstreams = v5_0.MiscUtils.getDictKey(d, 'declaration.layer4.upstreams') if d_upstreams is not None: for i in range(len(d_upstreams)): all_upstreams.append(d_upstreams[i]['name']) - d_servers = v4_1.MiscUtils.getDictKey(d, 'declaration.layer4.servers') + d_servers = v5_0.MiscUtils.getDictKey(d, 'declaration.layer4.servers') if d_servers is not None: for server in d_servers: if server['snippet']: - status, snippet = v4_1.GitOps.getObjectFromRepo(object = server['snippet'], authProfiles = d['declaration']['http']['authentication']) + status, snippet = v5_0.GitOps.getObjectFromRepo(object = server['snippet'], authProfiles = d['declaration']['http']['authentication']) if status != 200: return {"status_code": 422, "message": {"status_code": status, "message": snippet}} @@ -378,319 +538,37 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn return Response(content=cmHttp + '\n---\n' + cmStream, headers={'Content-Type': 'application/x-yaml'}) elif decltype.lower() == 'nms': - # NGINX Instance Manager Staged Configuration publish + # Output to NGINX Instance Manager - nmsUsername = v4_1.MiscUtils.getDictKey(d, 'output.nms.username') - nmsPassword = v4_1.MiscUtils.getDictKey(d, 'output.nms.password') - nmsInstanceGroup = v4_1.MiscUtils.getDictKey(d, 'output.nms.instancegroup') - nmsSynctime = v4_1.MiscUtils.getDictKey(d, 'output.nms.synctime') + # NGINX configuration files for staged config + configFiles['rootDir'] = NcgConfig.config['nms']['config_dir'] - nmsUrlFromJson = v4_1.MiscUtils.getDictKey(d, 'output.nms.url') - urlCheck = urlparse(nmsUrlFromJson) + # NGINX auxiliary files for staged config + auxFiles['rootDir'] = NcgConfig.config['nms']['config_dir'] - if urlCheck.scheme not in ['http', 'https'] or urlCheck.scheme == "" or urlCheck.netloc == "": - return {"status_code": 400, - "message": {"status_code": 400, "message": {"code": 400, - "content": f"invalid NGINX Management Suite URL {nmsUrlFromJson}"}}, - "headers": {'Content-Type': 'application/json'}} + return v5_0.NMSOutput.NMSOutput(d = d, declaration = declaration, apiversion = apiversion, + b64HttpConf = b64HttpConf, b64StreamConf = b64StreamConf, + configFiles = configFiles, + auxFiles = auxFiles, + runfromautosync = runfromautosync, configUid = configUid ) - nmsUrl = f"{urlCheck.scheme}://{urlCheck.netloc}" + elif decltype.lower() == 'nginxone': + # Output to NGINX One SaaS Console - if nmsSynctime < 0: - return {"status_code": 400, - "message": {"status_code": 400, "message": {"code": 400, "content": "synctime must be >= 0"}}, - "headers": {'Content-Type': 'application/json'}} + # NGINX configuration files for staged config + configFiles['name'] = NcgConfig.config['nms']['config_dir'] - # Fetch NGINX App Protect WAF policies from source of truth if needed - d_policies = v4_1.MiscUtils.getDictKey(d, 'output.nms.policies') - if d_policies is not None: - for policy in d_policies: - if 'versions' in policy: - for policyVersion in policy['versions']: - status, content = v4_1.GitOps.getObjectFromRepo(object = policyVersion['contents'], authProfiles = d['declaration']['http']['authentication']) + # NGINX auxiliary files for staged config + # TODO + # auxFiles['name'] = NcgConfig.config['nms']['config_dir'] - if status != 200: - return {"status_code": 422, "message": {"status_code": status, "message": content}} - - policyVersion['contents'] = content - - # Check TLS items validity - all_tls = {'certificate': {}, 'key': {}} + #return v5_0.NGINXOneOutput.NGINXOneOutput(d = d, declaration = declaration, apiversion = apiversion, + # b64HttpConf = b64HttpConf, b64StreamConf = b64StreamConf, + # configFiles = configFiles, + # auxFiles = auxFiles, + # runfromautosync = runfromautosync, configUid = configUid ) - d_certs = v4_1.MiscUtils.getDictKey(d, 'output.nms.certificates') - if d_certs is not None: - for i in range(len(d_certs)): - if d_certs[i]['name']: - all_tls[d_certs[i]['type']][d_certs[i]['name']] = True - - d_servers = v4_1.MiscUtils.getDictKey(d, 'declaration.http.servers') - if d_servers is not None: - for server in d_servers: - if server['listen'] is not None: - if 'tls' in server['listen']: - cert_name = v4_1.MiscUtils.getDictKey(server, 'listen.tls.certificate') - if cert_name and cert_name not in all_tls['certificate']: - return {"status_code": 422, - "message": { - "status_code": 422, - "message": {"code": 422, - "content": "invalid TLS certificate " + - cert_name + " for server" + str( - server['names'])} - }} - - cert_key = v4_1.MiscUtils.getDictKey(server, 'listen.tls.key') - if cert_key and cert_key not in all_tls['key']: - return {"status_code": 422, - "message": { - "status_code": 422, - "message": {"code": 422, - "content": "invalid TLS key " + cert_key + " for server" + str( - server['names'])} - }} - - trusted_cert_name = v4_1.MiscUtils.getDictKey(server, 'listen.tls.trusted_ca_certificates') - if trusted_cert_name and trusted_cert_name not in all_tls['certificate']: - return {"status_code": 422, - "message": { - "status_code": 422, - "message": {"code": 422, - "content": "invalid trusted CA certificate " + - trusted_cert_name + " for server" + str(server['names'])} - }} - - if v4_1.MiscUtils.getDictKey(server, 'listen.tls.mtls.enabled') in ['optional_no_ca'] \ - and 'ocsp' in server['listen']['tls']: - return {"status_code": 422, - "message": { - "status_code": 422, - "message": {"code": 422, - "content": "OCSP is incompatible with 'optional_no_ca' client " - "mTLS verification for server" + str( - server['names'])} - }} - - client_cert_name = v4_1.MiscUtils.getDictKey(server, 'listen.tls.mtls.client_certificates') - if client_cert_name and client_cert_name not in all_tls['certificate']: - return {"status_code": 422, - "message": { - "status_code": 422, - "message": {"code": 422, - "content": f"invalid mTLS client certificates [{client_cert_name}] for server {str(server['names'])}"} - }} - - # Add optional certificates specified under output.nms.certificates - extensions_map = {'certificate': '.crt', 'key': '.key'} - - d_certificates = v4_1.MiscUtils.getDictKey(d, 'output.nms.certificates') - if d_certificates is not None: - for c in d_certificates: - status, certContent = v4_1.GitOps.getObjectFromRepo(object = c['contents'], authProfiles = d['declaration']['http']['authentication']) - - if status != 200: - return {"status_code": 422, - "message": {"status_code": status, "message": {"code": status, "content": certContent}}} - - newAuxFile = {'contents': certContent['content'], 'name': NcgConfig.config['nms']['certs_dir'] + - '/' + c['name'] + extensions_map[c['type']]} - auxFiles['files'].append(newAuxFile) - - ### / Add optional certificates specified under output.nms.certificates - - # NGINX main configuration file through template - j2_env = Environment(loader=FileSystemLoader(NcgConfig.config['templates']['root_dir'] + '/' + apiversion), - trim_blocks=True, extensions=["jinja2_base64_filters.Base64Filters"]) - - nginxMainConf = j2_env.get_template(NcgConfig.config['templates']['nginxmain']).render( - nginxconf={'modules': v4_1.MiscUtils.getDictKey(d, 'output.nms.modules')}) - - # Base64-encoded NGINX main configuration (/etc/nginx/nginx.conf) - b64NginxMain = str(base64.urlsafe_b64encode(nginxMainConf.encode("utf-8")), "utf-8") - - # Base64-encoded NGINX mime.types (/etc/nginx/mime.types) - f = open(NcgConfig.config['templates']['root_dir'] + '/' + apiversion + '/' + NcgConfig.config['templates'][ - 'mimetypes'], 'r') - nginxMimeTypes = f.read() - f.close() - - b64NginxMimeTypes = str(base64.urlsafe_b64encode(nginxMimeTypes.encode("utf-8")), "utf-8") - filesMimeType = {'contents': b64NginxMimeTypes, 'name': NcgConfig.config['nms']['config_dir'] + '/mime.types'} - auxFiles['files'].append(filesMimeType) - - # Base64-encoded NGINX HTTP service configuration - filesNginxMain = {'contents': b64NginxMain, 'name': NcgConfig.config['nms']['config_dir'] + '/nginx.conf'} - filesHttpConf = {'contents': b64HttpConf, - 'name': NcgConfig.config['nms']['config_dir'] + '/' + NcgConfig.config['nms'][ - 'staged_config_http_filename']} - filesStreamConf = {'contents': b64StreamConf, - 'name': NcgConfig.config['nms']['config_dir'] + '/' + NcgConfig.config['nms'][ - 'staged_config_stream_filename']} - - # Append config files to staged configuration - configFiles['files'].append(filesNginxMain) - configFiles['files'].append(filesHttpConf) - configFiles['files'].append(filesStreamConf) - - # Staged config - baseStagedConfig = {'auxFiles': auxFiles, 'configFiles': configFiles} - stagedConfig = {'auxFiles': auxFiles, 'configFiles': configFiles, - 'updateTime': datetime.utcnow().isoformat()[:-3] + 'Z', - 'ignoreConflict': True, 'validateConfig': False} - - currentBaseStagedConfig = NcgRedis.redis.get(f'ncg.basestagedconfig.{configUid}').decode('utf-8') if NcgRedis.redis.get(f'ncg.basestagedconfig.{configUid}') else None - newBaseStagedConfig = json.dumps(baseStagedConfig) - - if currentBaseStagedConfig is not None and newBaseStagedConfig == currentBaseStagedConfig: - print(f'Declaration [{configUid}] not changed') - return {"status_code": 200, - "message": {"status_code": 200, "message": {"code": 200, "content": "no changes"}}} - else: - # Configuration objects have changed, publish to NIM needed - print(f'Declaration [{configUid}] changed, publishing to NMS') - - # Retrieve instance group uid - try: - ig = requests.get(url=f'{nmsUrl}/api/platform/v1/instance-groups', auth=(nmsUsername, nmsPassword), - verify=False) - except Exception as e: - return {"status_code": 400, - "message": {"status_code": 400, - "message": {"code": 400, "content": f"Can't connect to {nmsUrl}"}}} - - if ig.status_code != 200: - try: - return {"status_code": ig.status_code, - "message": {"status_code": ig.status_code, - "message": {"code": ig.status_code, "content": json.loads(ig.text)}}} - except: - return {"status_code": ig.status_code, - "message": {"status_code": ig.status_code, - "message": {"code": ig.status_code, "content": ig.text}}} - - # Get the instance group id - igUid = v4_1.NIMUtils.getNIMInstanceGroupUid(nmsUrl=nmsUrl, nmsUsername=nmsUsername, - nmsPassword=nmsPassword, instanceGroupName=nmsInstanceGroup) - - # Invalid instance group - if igUid is None: - return {"status_code": 404, - "message": {"status_code": 404, "message": {"code": 404, - "content": f"instance group {nmsInstanceGroup} not found"}}, - "headers": {'Content-Type': 'application/json'}} - - ### NGINX App Protect policies support - commits policies to control plane - - # Check NGINX App Protect WAF policies configuration sanity - status, description = v4_1.NAPUtils.checkDeclarationPolicies(d) - - if status != 200: - return {"status_code": 422, "message": {"status_code": status, "message": description}} - - # Provision NGINX App Protect WAF policies to NGINX Instance Manager - provisionedNapPolicies, activePolicyUids = v4_1.NAPUtils.provisionPolicies( - nmsUrl=nmsUrl, nmsUsername=nmsUsername, nmsPassword=nmsPassword, declaration=d) - - ### / NGINX App Protect policies support - - ### Publish staged config to instance group - r = requests.post(url=nmsUrl + f"/api/platform/v1/instance-groups/{igUid}/config", - data=json.dumps(stagedConfig), - headers={'Content-Type': 'application/json'}, - auth=(nmsUsername, nmsPassword), - verify=False) - - if r.status_code != 202: - # Configuration push failed - return {"status_code": r.status_code, - "message": {"status_code": r.status_code, "message": r.text}, - "headers": {'Content-Type': 'application/json'}} - - # Fetch the deployment status - publishResponse = json.loads(r.text) - - # Wait for either NIM success or failure after pushing a staged config - isPending = True - while isPending: - time.sleep(NcgConfig.config['nms']['staged_config_publish_waittime']) - deploymentCheck = requests.get(url=nmsUrl + publishResponse['links']['rel'], - auth=(nmsUsername, nmsPassword), - verify=False) - - checkJson = json.loads(deploymentCheck.text) - - if not checkJson['details']['pending']: - isPending = False - - if len(checkJson['details']['failure']) > 0: - # Staged config publish to NIM failed - jsonResponse = checkJson['details']['failure'][0] - deploymentCheck.status_code = 422 - else: - # Staged config publish to NIM succeeded - jsonResponse = json.loads(deploymentCheck.text) - - # if nmsSynctime > 0 and runfromautosync == False: - if runfromautosync == False: - # No configuration is found, generate one - configUid = str(getuniqueid()) - - # Stores the staged config to redis - # Redis keys: - # ncg.declaration.[configUid] = original config declaration - # ncg.declarationrendered.[configUid] = original config declaration - rendered - # ncg.basestagedconfig.[configUid] = base staged configuration - # ncg.apiversion.[configUid] = ncg API version - # ncg.status.[configUid] = latest status - - NcgRedis.redis.set(f'ncg.declaration.{configUid}', pickle.dumps(declaration)) - NcgRedis.redis.set(f'ncg.declarationrendered.{configUid}', json.dumps(d)) - NcgRedis.redis.set(f'ncg.basestagedconfig.{configUid}', json.dumps(baseStagedConfig)) - NcgRedis.redis.set(f'ncg.apiversion.{configUid}', apiversion) - - # Makes NGINX App Protect policies active - doWeHavePolicies = v4_1.NAPUtils.makePolicyActive(nmsUrl=nmsUrl, nmsUsername=nmsUsername, - nmsPassword=nmsPassword, - activePolicyUids=activePolicyUids, - instanceGroupUid=igUid) - - if doWeHavePolicies: - # Clean up NGINX App Protect WAF policies not used anymore - # and not defined in the declaration just pushed - time.sleep(NcgConfig.config['nms']['staged_config_publish_waittime']) - v4_1.NAPUtils.cleanPolicyLeftovers(nmsUrl=nmsUrl, nmsUsername=nmsUsername, - nmsPassword=nmsPassword, - currentPolicies=provisionedNapPolicies) - - # If deploying a new configuration in GitOps mode start autosync - if nmsSynctime == 0: - NcgRedis.declarationsList[configUid] = "static" - elif not runfromautosync: - # GitOps autosync - print(f'Starting autosync for configUid {configUid} every {nmsSynctime} seconds') - - job = schedule.every(nmsSynctime).seconds.do(lambda: configautosync(configUid)) - # Keep track of GitOps configs, key is the threaded job - NcgRedis.declarationsList[configUid] = job - - NcgRedis.redis.set(f'ncg.apiversion.{configUid}', apiversion) - - responseContent = {'code': deploymentCheck.status_code, 'content': jsonResponse, 'configUid': configUid} - - # Configuration push completed, update redis keys - if configUid != "": - NcgRedis.redis.set('ncg.status.' + configUid, json.dumps(responseContent)) - - # if nmsSynctime > 0: - # Updates status, declaration and basestagedconfig in redis - NcgRedis.redis.set('ncg.declaration.' + configUid, pickle.dumps(declaration)) - NcgRedis.redis.set('ncg.declarationrendered.' + configUid, json.dumps(d)) - NcgRedis.redis.set('ncg.basestagedconfig.' + configUid, json.dumps(baseStagedConfig)) - - return {"status_code": deploymentCheck.status_code, - "message": {"status_code": deploymentCheck.status_code, - "message": responseContent}, - "headers": {'Content-Type': 'application/json'} - } + return {"status_code": 501, "message": {"code": 501, "content": "NGINX One support not yet available"}} else: return {"status_code": 422, "message": {"status_code": 422, "message": f"output type {decltype} unknown"}} @@ -712,51 +590,51 @@ def patch_config(declaration: ConfigDeclaration, configUid: str, apiversion: str status_code, currentDeclaration = get_declaration(configUid=configUid) # Handle policy updates - d_policies = v4_1.MiscUtils.getDictKey(declarationToPatch, 'output.nms.policies') + d_policies = v5_0.MiscUtils.getDictKey(declarationToPatch, 'output.nms.policies') if d_policies is not None: # NGINX App Protect WAF policy updates for p in d_policies: - currentDeclaration = v4_1.DeclarationPatcher.patchNAPPolicies( + currentDeclaration = v5_0.DeclarationPatcher.patchNAPPolicies( sourceDeclaration=currentDeclaration, patchedNAPPolicies=p) # Handle certificate updates - d_certificates = v4_1.MiscUtils.getDictKey(declarationToPatch, 'output.nms.certificates') + d_certificates = v5_0.MiscUtils.getDictKey(declarationToPatch, 'output.nms.certificates') if d_certificates is not None: # TLS certificate/key updates for p in d_certificates: - currentDeclaration = v4_1.DeclarationPatcher.patchCertificates( + currentDeclaration = v5_0.DeclarationPatcher.patchCertificates( sourceDeclaration=currentDeclaration, patchedCertificates=p) # Handle declaration updates if 'declaration' in declarationToPatch: # HTTP - d_upstreams = v4_1.MiscUtils.getDictKey(declarationToPatch, 'declaration.http.upstreams') + d_upstreams = v5_0.MiscUtils.getDictKey(declarationToPatch, 'declaration.http.upstreams') if d_upstreams: # HTTP upstream patch for u in d_upstreams: - currentDeclaration = v4_1.DeclarationPatcher.patchHttpUpstream( + currentDeclaration = v5_0.DeclarationPatcher.patchHttpUpstream( sourceDeclaration=currentDeclaration, patchedHttpUpstream=u) - d_servers = v4_1.MiscUtils.getDictKey(declarationToPatch, 'declaration.http.servers') + d_servers = v5_0.MiscUtils.getDictKey(declarationToPatch, 'declaration.http.servers') if d_servers: # HTTP servers patch for s in d_servers: - currentDeclaration = v4_1.DeclarationPatcher.patchHttpServer( + currentDeclaration = v5_0.DeclarationPatcher.patchHttpServer( sourceDeclaration=currentDeclaration, patchedHttpServer=s) # Stream / Layer4 - d_upstreams = v4_1.MiscUtils.getDictKey(declarationToPatch, 'declaration.layer4.upstreams') + d_upstreams = v5_0.MiscUtils.getDictKey(declarationToPatch, 'declaration.layer4.upstreams') if d_upstreams: # Stream upstream patch for u in d_upstreams: - currentDeclaration = v4_1.DeclarationPatcher.patchStreamUpstream( + currentDeclaration = v5_0.DeclarationPatcher.patchStreamUpstream( sourceDeclaration=currentDeclaration, patchedStreamUpstream=u) - d_servers = v4_1.MiscUtils.getDictKey(declarationToPatch, 'declaration.layer4.servers') + d_servers = v5_0.MiscUtils.getDictKey(declarationToPatch, 'declaration.layer4.servers') if d_servers: # Stream servers patch for s in d_servers: - currentDeclaration = v4_1.DeclarationPatcher.patchStreamServer( + currentDeclaration = v5_0.DeclarationPatcher.patchStreamServer( sourceDeclaration=currentDeclaration, patchedStreamServer=s) # Apply the updated declaration @@ -789,4 +667,4 @@ def get_declaration(configUid: str): if cfg is None: return 404, "" - return 200, pickle.loads(cfg).dict() + return 200, pickle.loads(cfg).dict() \ No newline at end of file diff --git a/src/V4_1_NginxConfigDeclaration.py b/src/V5_0_NginxConfigDeclaration.py similarity index 62% rename from src/V4_1_NginxConfigDeclaration.py rename to src/V5_0_NginxConfigDeclaration.py index df73430..685aa59 100644 --- a/src/V4_1_NginxConfigDeclaration.py +++ b/src/V5_0_NginxConfigDeclaration.py @@ -1,13 +1,15 @@ """ -JSON declaration format +JSON declaration structure """ from __future__ import annotations - from typing import List, Optional - from pydantic import BaseModel, Extra, model_validator +import re + +# Regexp to check names +alphanumRegexp = '^[a-zA-Z0-9\ \-\_]+$' class OutputConfigMap(BaseModel, extra="forbid"): name: str = "nginx-config" @@ -120,17 +122,30 @@ class OutputNMS(BaseModel, extra="forbid"): log_profiles: Optional[List[LogProfile]] = [] +class OutputNGINXOne(BaseModel, extra="forbid"): + url: str = "" + namespace: str = "" + token: str = "" + cluster: str = "" + synctime: Optional[int] = 0 + modules: Optional[List[str]] = [] + certificates: Optional[List[NmsCertificate]] = [] + policies: Optional[List[NmsPolicy]] = [] + log_profiles: Optional[List[LogProfile]] = [] + + class Output(BaseModel, extra="forbid"): type: str configmap: Optional[OutputConfigMap] = {} http: Optional[OutputHttp] = {} nms: Optional[OutputNMS] = {} + nginxone: Optional[OutputNGINXOne] = {} @model_validator(mode='after') def check_type(self) -> 'Output': - _type, configmap, http, nms = self.type, self.configmap, self.http, self.nms + _type, configmap, http, nms, nginxone = self.type, self.configmap, self.http, self.nms, self.nginxone - valid = ['plaintext', 'json', 'configmap', 'http', 'nms'] + valid = ['plaintext', 'json', 'configmap', 'http', 'nms', 'nginxone'] if _type not in valid: raise ValueError(f"Invalid output type [{_type}] must be one of {str(valid)}") @@ -142,6 +157,8 @@ def check_type(self) -> 'Output': isError = True elif _type == 'nms' and not nms: isError = True + elif _type == 'nginxone' and not nginxone: + isError = True if isError: raise ValueError(f"Invalid output data for type [{_type}]") @@ -160,13 +177,26 @@ class Ocsp(BaseModel, extra="forbid"): enabled: Optional[str] = "off" responder: Optional[str] = "" + @model_validator(mode='after') + def check_type(self) -> 'Ocsp': + _enabled = self.enabled + + valid = ['on', 'off', 'leaf'] + if _enabled not in valid: + raise ValueError(f"Invalid OCSP validation type type [{_enabled}] must be one of {str(valid)}") + + return self + -class Mtls(BaseModel, extra="forbid"): +class AuthClientMtls(BaseModel, extra="forbid"): enabled: Optional[str] = "off" client_certificates: str = "" + trusted_ca_certificates: Optional[str] = "" + ocsp: Optional[Ocsp] = {} + stapling: Optional[OcspStapling] = {} @model_validator(mode='after') - def check_type(self) -> 'Mtls': + def check_type(self) -> 'AuthClientMtls': _enabled = self.enabled valid = ['on', 'off', 'optional', 'optional_no_ca'] @@ -179,12 +209,9 @@ def check_type(self) -> 'Mtls': class Tls(BaseModel, extra="forbid"): certificate: str = "" key: str = "" - trusted_ca_certificates: str = "" ciphers: Optional[str] = "" protocols: Optional[List[str]] = [] - mtls: Optional[Mtls] = {} - ocsp: Optional[Ocsp] = {} - stapling: Optional[OcspStapling] = {} + authentication: Optional[LocationAuth] = {} class Listen(BaseModel, extra="forbid"): @@ -232,11 +259,34 @@ class LocationAuthServer(BaseModel, extra="forbid"): profile: Optional[str] = "" +class LocationHeaderToClient(BaseModel, extra="forbid"): + add: Optional[List[HTTPHeader]] = [] + delete: Optional[List[str]] = [] + replace: Optional[List[HTTPHeader]] = [] + + +class LocationHeaderToServer(BaseModel, extra="forbid"): + set: Optional[List[HTTPHeader]] = [] + delete: Optional[List[str]] = [] + + +class HTTPHeader(BaseModel, extra="forbid"): + name: str = "" + value: str = "" + + class LocationAuth(BaseModel, extra="forbid"): client: Optional[List[LocationAuthClient]] = [] server: Optional[List[LocationAuthServer]] = [] +class AuthorizationProfileReference(BaseModel, extra="forbid"): + profile: Optional[str] = "" + +class LocationHeaders(BaseModel, extra="forbid"): + to_server: Optional[LocationHeaderToServer] = {} + to_client: Optional[LocationHeaderToClient] = {} + class RateLimitApiGw(BaseModel, extra="forbid"): profile: Optional[str] = "" httpcode: Optional[int] = 429 @@ -251,18 +301,24 @@ class APIGatewayAuthentication(BaseModel, extra="forbid"): paths: Optional[List[str]] = [] +class APIGatewayAuthorization(BaseModel, extra="forbid"): + profile: str + enforceOnPaths: Optional[bool] = True + paths: Optional[List[str]] = [] + class AuthClientJWT(BaseModel, extra="forbid"): realm: str = "JWT Authentication" key: str = "" cachetime: Optional[int] = 0 jwt_type: str = "signed" + token_location: Optional[str] = "" @model_validator(mode='after') def check_type(self) -> 'AuthClientJWT': jwt_type, key = self.jwt_type, self.key - if not key.strip() : - raise ValueError(f"Invalid: JWT key must not be empty") + #if not key.strip(): + # raise ValueError(f"Invalid: JWT key must not be empty") valid = ['signed', 'encrypted', 'nested'] if jwt_type not in valid: @@ -272,23 +328,53 @@ def check_type(self) -> 'AuthClientJWT': class AuthServerToken(BaseModel, extra="forbid"): token: str = "" - type: Optional[str] = "bearer" + type: Optional[str] = "" location: Optional[str] = "" + username: Optional[str] = "" + password: Optional[str] = "" @model_validator(mode='after') def check_type(self) -> 'AuthServerToken': - location, type = self.location, self.type.lower() + tokentype, location, username, password = self.type.lower(), self.location, self.username, self.password + + valid = ['bearer', 'header', 'basic', ''] + if tokentype not in valid: + raise ValueError(f"Invalid token type [{tokentype}] must be one of {str(valid)}") + + if tokentype in ['header'] and location == "": + raise ValueError(f"Empty location for [{tokentype}] token") + + if tokentype in ['basic'] and (username == "" or password == ""): + raise ValueError(f"Missing username/password for [{tokentype}] token") + + return self + + +class AuthServerMtls(BaseModel, extra="forbid"): + certificate: str = "" + key: str = "" - valid = ['bearer', 'header'] - if type not in valid: - raise ValueError(f"Invalid token type [{type}] must be one of {str(valid)}") - if type in ['header'] and location == "": - raise ValueError(f"Empty location for [{type}] token") +class JwtAuthZNameValue(BaseModel, extra="forbid"): + name: str + value: List[str] + errorcode: Optional[int] = 401 + + @model_validator(mode='after') + def check_type(self) -> 'JwtAuthZNameValue': + errorcode = self.errorcode + + valid = [401, 403] + if errorcode not in valid: + raise ValueError(f"Invalid errorcode [{errorcode}] must be one of {str(valid)}") return self +class AuthorizationJWT(BaseModel, extra="forbid"): + claims: List[JwtAuthZNameValue] + + class HealthCheck(BaseModel, extra="forbid"): enabled: Optional[bool] = False uri: Optional[str] = "/" @@ -321,6 +407,9 @@ class Location(BaseModel, extra="forbid"): app_protect: Optional[AppProtect] = {} snippet: Optional[ObjectFromSourceOfTruth] = {} authentication: Optional[LocationAuth] = {} + authorization: Optional[AuthorizationProfileReference] = {} + headers: Optional[LocationHeaders]= {} + njs: Optional[List[NjsHookLocation]] = [] @model_validator(mode='after') def check_type(self) -> 'Location': @@ -342,6 +431,71 @@ class ObjectFromSourceOfTruth(BaseModel, extra="forbid"): content: str = "" authentication: Optional[List[LocationAuthServer]] = [] + +class NjsHook_js_body_filter(BaseModel, extra="forbid"): + buffer_type: Optional[str] = "" + + +class NjsHook_js_periodic(BaseModel, extra="forbid"): + interval: Optional[str] = "" + jitter: Optional[int] = 0 + worker_affinity: Optional[str] = "" + + +class NjsHook_js_preload_object(BaseModel, extra="forbid"): + file: str + + +class NjsHook_js_set(BaseModel, extra="forbid"): + variable: str + + +class NjsHookHttpServerDetails(BaseModel, extra="forbid"): + type: str + js_preload_object: Optional[NjsHook_js_preload_object] = {} + js_set: Optional[NjsHook_js_set] = {} + + @model_validator(mode='after') + def check_type(self) -> 'NjsHookHttpServerDetails': + _type = self.type + + valid = ['js_preload_object', 'js_set'] + if _type not in valid: + raise ValueError(f"Invalid hook [{_type}] must be one of {str(valid)}") + + return self + + +class NjsHookLocationDetails(BaseModel, extra="forbid"): + type: str + js_preload_object: Optional[NjsHook_js_preload_object] = {} + js_set: Optional[NjsHook_js_set] = {} + js_body_filter: Optional[NjsHook_js_body_filter] = {} + js_periodic: Optional[NjsHook_js_periodic] = {} + + @model_validator(mode='after') + def check_type(self) -> 'NjsHookLocationDetails': + _type = self.type + + valid = ['js_body_filter', 'js_content', 'js_header_filter', 'js_periodic', 'js_preload_object', 'js_set'] + if _type not in valid: + raise ValueError(f"Invalid hook [{_type}] must be one of {str(valid)}") + + return self + + +class NjsHookHttpServer(BaseModel, extra="forbid"): + hook: NjsHookHttpServerDetails + profile: str + function: str + + +class NjsHookLocation(BaseModel, extra="forbid"): + hook: NjsHookLocationDetails + profile: str + function: str + + class Server(BaseModel, extra="forbid"): name: str names: Optional[List[str]] = [] @@ -351,6 +505,19 @@ class Server(BaseModel, extra="forbid"): locations: Optional[List[Location]] = [] app_protect: Optional[AppProtect] = {} snippet: Optional[ObjectFromSourceOfTruth] = {} + headers: Optional[LocationHeaders] = {} + njs: Optional[List[NjsHookHttpServer]] = [] + authentication: Optional[LocationAuth] = {} + authorization: Optional[AuthorizationProfileReference] = {} + + @model_validator(mode='after') + def check_type(self) -> 'Server': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self class L4Server(BaseModel, extra="forbid"): @@ -359,6 +526,15 @@ class L4Server(BaseModel, extra="forbid"): upstream: Optional[str] = "" snippet: Optional[ObjectFromSourceOfTruth] = {} + @model_validator(mode='after') + def check_type(self) -> 'L4Server': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + class Sticky(BaseModel, extra="forbid"): cookie: str = "" @@ -393,12 +569,30 @@ class Upstream(BaseModel, extra="forbid"): sticky: Optional[Sticky] = {} snippet: Optional[ObjectFromSourceOfTruth] = {} + @model_validator(mode='after') + def check_type(self) -> 'Upstream': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + class L4Upstream(BaseModel, extra="forbid"): name: str origin: Optional[List[L4Origin]] = [] snippet: Optional[ObjectFromSourceOfTruth] = {} + @model_validator(mode='after') + def check_type(self) -> 'L4Upstream': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + class ValidItem(BaseModel, extra="forbid"): codes: Optional[List[int]] = [200] @@ -411,6 +605,15 @@ class CachingItem(BaseModel, extra="forbid"): size: Optional[str] = "10m" valid: Optional[List[ValidItem]] = [] + @model_validator(mode='after') + def check_type(self) -> 'CachingItem': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + class RateLimitItem(BaseModel, extra="forbid"): name: str @@ -418,6 +621,15 @@ class RateLimitItem(BaseModel, extra="forbid"): size: Optional[str] = "" rate: Optional[str] = "" + @model_validator(mode='after') + def check_type(self) -> 'RateLimitItem': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + class NginxPlusApi(BaseModel, extra="forbid"): write: Optional[bool] = False @@ -457,15 +669,19 @@ class Authentication_Client(BaseModel, extra="forbid"): type: str jwt: Optional[AuthClientJWT] = {} + mtls: Optional[AuthClientMtls] = {} @model_validator(mode='after') def check_type(self) -> 'Authentication_Client': _type, name = self.type, self.name - valid = ['jwt'] + valid = ['jwt', 'mtls'] if _type not in valid: raise ValueError(f"Invalid client authentication type [{_type}] for profile [{name}] must be one of {str(valid)}") + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + return self @@ -474,15 +690,19 @@ class Authentication_Server(BaseModel, extra="forbid"): type: str token: Optional[AuthServerToken] = {} + mtls: Optional[AuthServerMtls] = {} @model_validator(mode='after') def check_type(self) -> 'Authentication_Server': _type, name = self.type, self.name - valid = ['token'] + valid = ['token', 'mtls'] if _type not in valid: raise ValueError(f"Invalid server authentication type [{_type}] for profile [{name}] must be one of {str(valid)}") + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + return self @@ -491,6 +711,39 @@ class Authentication(BaseModel, extra="forbid"): server: Optional[List[Authentication_Server]] = [] +class Authorization(BaseModel, extra="forbid"): + name: str + type: str + + jwt: Optional[AuthorizationJWT] = {} + + @model_validator(mode='after') + def check_type(self) -> 'Authorization': + _type, name = self.type, self.name + + valid = ['jwt'] + if _type not in valid: + raise ValueError(f"Invalid authorization type [{_type}] for profile [{name}] must be one of {str(valid)}") + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + +class NjsFile(BaseModel, extra="forbid"): + name: str + file: ObjectFromSourceOfTruth + + @model_validator(mode='after') + def check_type(self) -> 'NjsFile': + name = self.name + + if not re.search(alphanumRegexp,name): + raise ValueError(f"Invalid name [{name}] should match regexp {alphanumRegexp}") + + return self + + class Http(BaseModel, extra="forbid"): servers: Optional[List[Server]] = [] upstreams: Optional[List[Upstream]] = [] @@ -500,6 +753,9 @@ class Http(BaseModel, extra="forbid"): maps: Optional[List[Map]] = [] snippet: Optional[ObjectFromSourceOfTruth] = {} authentication: Optional[Authentication] = {} + authorization: Optional[List[Authorization]] = [] + njs: Optional[List[NjsHookHttpServer]] = [] + njs_profiles: Optional[List[NjsFile]] = [] class Declaration(BaseModel, extra="forbid"): @@ -522,6 +778,7 @@ class APIGateway(BaseModel, extra="forbid"): developer_portal: Optional[DeveloperPortal] = {} rate_limit: Optional[List[RateLimitApiGw]] = [] authentication: Optional[APIGatewayAuthentication] = {} + authorization: Optional[List[APIGatewayAuthorization]] = [] log: Optional[Log] = {} diff --git a/src/main.py b/src/main.py index 73b9085..67451f0 100644 --- a/src/main.py +++ b/src/main.py @@ -16,12 +16,11 @@ import NcgConfig import NcgRedis -import V4_1_CreateConfig -import V4_1_NginxConfigDeclaration - import V4_2_CreateConfig import V4_2_NginxConfigDeclaration +import V5_0_CreateConfig +import V5_0_NginxConfigDeclaration cfg = NcgConfig.NcgConfig(configFile="../etc/config.toml") redis = NcgRedis.NcgRedis(host=cfg.config['redis']['host'], port=cfg.config['redis']['port']) @@ -39,10 +38,10 @@ def runScheduler(): time.sleep(1) -# Submit declaration using v4.1 API -@app.post("/v4.1/config", status_code=200, response_class=PlainTextResponse) -def post_config_v4_1(d: V4_1_NginxConfigDeclaration.ConfigDeclaration, response: Response): - output = V4_1_CreateConfig.createconfig(declaration=d, apiversion='v4.1') +# Submit declaration using v4.2 API +@app.post("/v4.2/config", status_code=200, response_class=PlainTextResponse) +def post_config_v4_2(d: V4_2_NginxConfigDeclaration.ConfigDeclaration, response: Response): + output = V4_2_CreateConfig.createconfig(declaration=d, apiversion='v4.2') if type(output) in [Response, str]: # ConfigMap or plaintext response @@ -61,10 +60,10 @@ def post_config_v4_1(d: V4_1_NginxConfigDeclaration.ConfigDeclaration, response: return JSONResponse(content=response, status_code=output['status_code'], headers=headers) -# Submit declaration using v4.2 API -@app.post("/v4.2/config", status_code=200, response_class=PlainTextResponse) -def post_config_v4_2(d: V4_2_NginxConfigDeclaration.ConfigDeclaration, response: Response): - output = V4_2_CreateConfig.createconfig(declaration=d, apiversion='v4.2') +# Submit declaration using v5.0 API +@app.post("/v5.0/config", status_code=200, response_class=PlainTextResponse) +def post_config_v5_0(d: V5_0_NginxConfigDeclaration.ConfigDeclaration, response: Response): + output = V5_0_CreateConfig.createconfig(declaration=d, apiversion='v5.0') if type(output) in [Response, str]: # ConfigMap or plaintext response @@ -83,22 +82,22 @@ def post_config_v4_2(d: V4_2_NginxConfigDeclaration.ConfigDeclaration, response: return JSONResponse(content=response, status_code=output['status_code'], headers=headers) -# Modify declaration using v4.1 API -@app.patch("/v4.1/config/{configuid}", status_code=200, response_class=PlainTextResponse) -def patch_config_v4_1(d: V4_1_NginxConfigDeclaration.ConfigDeclaration, response: Response, configuid: str): - return V4_1_CreateConfig.patch_config(declaration=d, configUid=configuid, apiversion='v4.1') - - # Modify declaration using v4.2 API @app.patch("/v4.2/config/{configuid}", status_code=200, response_class=PlainTextResponse) def patch_config_v4_2(d: V4_2_NginxConfigDeclaration.ConfigDeclaration, response: Response, configuid: str): return V4_2_CreateConfig.patch_config(declaration=d, configUid=configuid, apiversion='v4.2') -# Get declaration - v4.1 API -@app.get("/v4.1/config/{configuid}", status_code=200, response_class=PlainTextResponse) -def get_config_declaration_v4_1(configuid: str): - status_code, content = V4_1_CreateConfig.get_declaration(configUid=configuid) +# Modify declaration using v5.0 API +@app.patch("/v5.0/config/{configuid}", status_code=200, response_class=PlainTextResponse) +def patch_config_v5_0(d: V5_0_NginxConfigDeclaration.ConfigDeclaration, response: Response, configuid: str): + return V5_0_CreateConfig.patch_config(declaration=d, configUid=configuid, apiversion='v5.0') + + +# Get declaration - v4.2 API +@app.get("/v4.2/config/{configuid}", status_code=200, response_class=PlainTextResponse) +def get_config_declaration_v4_2(configuid: str): + status_code, content = V4_2_CreateConfig.get_declaration(configUid=configuid) if status_code == 404: return JSONResponse( @@ -114,11 +113,10 @@ def get_config_declaration_v4_1(configuid: str): ) - -# Get declaration - v4.2 API -@app.get("/v4.2/config/{configuid}", status_code=200, response_class=PlainTextResponse) -def get_config_declaration_v4_2(configuid: str): - status_code, content = V4_2_CreateConfig.get_declaration(configUid=configuid) +# Get declaration - v5.0 API +@app.get("/v5.0/config/{configuid}", status_code=200, response_class=PlainTextResponse) +def get_config_declaration_v5_0(configuid: str): + status_code, content = V5_0_CreateConfig.get_declaration(configUid=configuid) if status_code == 404: return JSONResponse( @@ -135,8 +133,8 @@ def get_config_declaration_v4_2(configuid: str): # Get declaration status -@app.get("/v4.1/config/{configuid}/status", status_code=200, response_class=PlainTextResponse) @app.get("/v4.2/config/{configuid}/status", status_code=200, response_class=PlainTextResponse) +@app.get("/v5.0/config/{configuid}/status", status_code=200, response_class=PlainTextResponse) def get_config_status(configuid: str): status = redis.redis.get('ncg.status.' + configuid) @@ -155,8 +153,8 @@ def get_config_status(configuid: str): # Delete declaration -@app.delete("/v4.1/config/{configuid}", status_code=200, response_class=PlainTextResponse) @app.delete("/v4.2/config/{configuid}", status_code=200, response_class=PlainTextResponse) +@app.delete("/v5.0/config/{configuid}", status_code=200, response_class=PlainTextResponse) def delete_config(configuid: str = ""): if configuid not in redis.declarationsList: return JSONResponse( diff --git a/src/v4_1/APIGateway.py b/src/v4_1/APIGateway.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/v4_1/DeclarationPatcher.py b/src/v4_1/DeclarationPatcher.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/v4_1/DevPortal.py b/src/v4_1/DevPortal.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/v4_1/OpenAPIParser.py b/src/v4_1/OpenAPIParser.py deleted file mode 100644 index e69de29..0000000 diff --git a/src/v5_0/APIGateway.py b/src/v5_0/APIGateway.py new file mode 100644 index 0000000..ca61c19 --- /dev/null +++ b/src/v5_0/APIGateway.py @@ -0,0 +1,37 @@ +""" +API Gateway support functions +""" + +import json + +import v5_0.GitOps +import v5_0.MiscUtils +from v5_0.OpenAPIParser import OpenAPIParser + +# pydantic models +from V5_0_NginxConfigDeclaration import * + + +# Builds the declarative JSON for the API Gateway configuration +# Return a tuple: status, description. If status = 200 things were successful +def createAPIGateway(locationDeclaration: dict, authProfiles: Authentication={}): + apiGwDeclaration = {} + + if locationDeclaration['apigateway']['openapi_schema']: + status, apiSchemaString = v5_0.GitOps.getObjectFromRepo(object=locationDeclaration['apigateway']['openapi_schema'], + authProfiles = authProfiles['server'] if 'server' in authProfiles else {}, base64Encode=False) + + if v5_0.MiscUtils.yaml_or_json(apiSchemaString['content']) == 'yaml': + # YAML to JSON conversion + apiSchemaString['content'] = v5_0.MiscUtils.yaml_to_json(apiSchemaString['content']) + + apiSchema = OpenAPIParser(json.loads(apiSchemaString['content'])) + + apiGwDeclaration = {} + apiGwDeclaration['location'] = locationDeclaration + apiGwDeclaration['info'] = apiSchema.info() + apiGwDeclaration['servers'] = apiSchema.servers() + apiGwDeclaration['paths'] = apiSchema.paths() + apiGwDeclaration['version'] = apiSchema.version() + + return 200, apiGwDeclaration \ No newline at end of file diff --git a/src/v5_0/DeclarationPatcher.py b/src/v5_0/DeclarationPatcher.py new file mode 100644 index 0000000..6e27285 --- /dev/null +++ b/src/v5_0/DeclarationPatcher.py @@ -0,0 +1,238 @@ +""" +Declaration parsing functions +""" + + +# Returns the patched declaration based on the patchedHttpServer +def patchHttpServer(sourceDeclaration: dict, patchedHttpServer: dict): + allTargetServers = [] + + haveWePatched = False + + if 'declaration' not in sourceDeclaration: + sourceDeclaration['declaration'] = {} + + if 'http' not in sourceDeclaration['declaration']: + sourceDeclaration['declaration']['http'] = {} + + if 'servers' not in sourceDeclaration['declaration']['http']: + sourceDeclaration['declaration']['http']['servers'] = [] + + # HTTP server patch + for s in sourceDeclaration['declaration']['http']['servers']: + if s['name'] == patchedHttpServer['name']: + # Patching an existing HTTP server, 'name' is the key + if len(patchedHttpServer) > 1: + # Patching HTTP server specifying only 'name' (len == 1) means delete + # If further fields are specified HTTP server is patched + allTargetServers.append(patchedHttpServer) + + haveWePatched = True + else: + # Unmodified HTTP server + allTargetServers.append(s) + + if not haveWePatched: + # The HTTP server being patched is a new one, let's add it + allTargetServers.append(patchedHttpServer) + + sourceDeclaration['declaration']['http']['servers'] = allTargetServers + + return sourceDeclaration + + +# Returns the patched declaration based on the patchedHttpUpstream +def patchHttpUpstream(sourceDeclaration: dict, patchedHttpUpstream: dict): + allTargetUpstreams = [] + + haveWePatched = False + + if 'declaration' not in sourceDeclaration: + sourceDeclaration['declaration'] = {} + + if 'http' not in sourceDeclaration['declaration']: + sourceDeclaration['declaration']['http'] = {} + + if 'upstreams' not in sourceDeclaration['declaration']['http']: + sourceDeclaration['declaration']['http']['upstreams'] = [] + + # HTTP upstreams patch + for s in sourceDeclaration['declaration']['http']['upstreams']: + if s['name'] == patchedHttpUpstream['name']: + # Patching an existing HTTP upstream, 'name' is the key + if len(patchedHttpUpstream) > 1: + # Patching HTTP upstream specifying only 'name' (len == 1) means delete + # If further fields are specified HTTP upstream is patched + allTargetUpstreams.append(patchedHttpUpstream) + + haveWePatched = True + else: + # Unmodified HTTP upstream + allTargetUpstreams.append(s) + + if not haveWePatched: + # The HTTP upstream being patched is a new one, let's add it + allTargetUpstreams.append(patchedHttpUpstream) + + sourceDeclaration['declaration']['http']['upstreams'] = allTargetUpstreams + + return sourceDeclaration + + +# Returns the patched declaration based on the patchedStreamServer +def patchStreamServer(sourceDeclaration: dict, patchedStreamServer: dict): + allTargetServers = [] + + haveWePatched = False + + if 'declaration' not in sourceDeclaration: + sourceDeclaration['declaration'] = {} + + if 'layer4' not in sourceDeclaration['declaration']: + sourceDeclaration['declaration']['layer4'] = {} + + if 'servers' not in sourceDeclaration['declaration']['layer4']: + sourceDeclaration['declaration']['layer4']['servers'] = [] + + # HTTP server patch + for s in sourceDeclaration['declaration']['layer4']['servers']: + if s['name'] == patchedStreamServer['name']: + # Patching an existing Stream server, 'name' is the key + if len(patchedStreamServer) > 1: + # Patching Stream server specifying only 'name' (len == 1) means delete + # If further fields are specified HTTP server is patched + allTargetServers.append(patchedStreamServer) + + haveWePatched = True + else: + # Unmodified HTTP server + allTargetServers.append(s) + + if not haveWePatched: + # The Stream server being patched is a new one, let's add it + allTargetServers.append(patchedStreamServer) + + sourceDeclaration['declaration']['layer4']['servers'] = allTargetServers + + return sourceDeclaration + + +# Returns the patched declaration based on the patchedStreamUpstream +def patchStreamUpstream(sourceDeclaration: dict, patchedStreamUpstream: dict): + allTargetUpstreams = [] + + haveWePatched = False + + if 'declaration' not in sourceDeclaration: + sourceDeclaration['declaration'] = {} + + if 'layer4' not in sourceDeclaration['declaration']: + sourceDeclaration['declaration']['layer4'] = {} + + if 'upstreams' not in sourceDeclaration['declaration']['layer4']: + sourceDeclaration['declaration']['layer4']['upstreams'] = [] + + # HTTP upstreams patch + for s in sourceDeclaration['declaration']['layer4']['upstreams']: + if s['name'] == patchedStreamUpstream['name']: + # Patching an existing Stream upstream, 'name' is the key + if len(patchedStreamUpstream) > 1: + # Patching Stream upstream specifying only 'name' (len == 1) means delete + # If further fields are specified HTTP upstream is patched + allTargetUpstreams.append(patchedStreamUpstream) + + haveWePatched = True + else: + # Unmodified HTTP upstream + allTargetUpstreams.append(s) + + if not haveWePatched: + # The Stream upstream being patched is a new one, let's add it + allTargetUpstreams.append(patchedStreamUpstream) + + sourceDeclaration['declaration']['layer4']['upstreams'] = allTargetUpstreams + + return sourceDeclaration + + +# Returns the patched declaration based on the patchedNAPPolicies +def patchNAPPolicies(sourceDeclaration: dict, patchedNAPPolicies: dict): + allTargetPolicies = [] + + haveWePatched = False + + if 'output' not in sourceDeclaration: + return sourceDeclaration + + if 'nms' not in sourceDeclaration['output']: + return sourceDeclaration + + if 'policies' not in sourceDeclaration['output']['nms']: + return sourceDeclaration + + # NGINX App Protect WAF policies patch + for p in sourceDeclaration['output']['nms']['policies']: + if 'type' in p and p['type'] == 'app_protect' \ + and 'name' in p and p['name'] \ + and p['type'] == patchedNAPPolicies['type'] \ + and p['name'] == patchedNAPPolicies['name']: + + # Patching an existing NGINX App Protect WAF policy, 'name' is the key + if patchedNAPPolicies['versions'] and patchedNAPPolicies['active_tag']: + # Patching NAP policy specifying 'versions' and 'active_tag' means updating + # If 'versions' and 'active_tag' are missing then it's a deletion + allTargetPolicies.append(patchedNAPPolicies) + + haveWePatched = True + else: + # Unmodified HTTP upstream + allTargetPolicies.append(p) + + if not haveWePatched: + # The NAP policy being patched is a new one, let's add it + allTargetPolicies.append(patchedNAPPolicies) + + sourceDeclaration['output']['nms']['policies'] = allTargetPolicies + + return sourceDeclaration + + +# Returns the patched declaration based on patchedCertificates +def patchCertificates(sourceDeclaration: dict, patchedCertificates: dict): + allTargetCertificates = [] + + haveWePatched = False + + if 'output' not in sourceDeclaration: + return sourceDeclaration + + if 'nms' not in sourceDeclaration['output']: + return sourceDeclaration + + if 'certificates' not in sourceDeclaration['output']['nms']: + return sourceDeclaration + + # TLS certificates patch + for c in sourceDeclaration['output']['nms']['certificates']: + if 'type' in c and c['type'] in ['certificate', 'key', 'chain'] \ + and 'name' in c and c['name'] \ + and c['type'] == patchedCertificates['type'] \ + and c['name'] == patchedCertificates['name']: + + if 'contents' in c and c['contents']: + # Patching an existing TLS certificate/key/chain, 'name' is the key. + # If content is empty the certificate is deleted + allTargetCertificates.append(patchedCertificates) + + haveWePatched = True + else: + # Unmodified HTTP upstream + allTargetCertificates.append(c) + + if not haveWePatched: + # The TLS certificate/key/chain being patched is a new one, let's add it + allTargetCertificates.append(patchedCertificates) + + sourceDeclaration['output']['nms']['certificates'] = allTargetCertificates + + return sourceDeclaration diff --git a/src/v5_0/DevPortal.py b/src/v5_0/DevPortal.py new file mode 100644 index 0000000..85e75c1 --- /dev/null +++ b/src/v5_0/DevPortal.py @@ -0,0 +1,46 @@ +""" +API Gateway Developer Portal support functions +""" + +import json +import requests +import base64 + +# NGINX Declarative API modules +from NcgConfig import NcgConfig +import v5_0.GitOps +import v5_0.MiscUtils + +# pydantic models +from V5_0_NginxConfigDeclaration import * + +def buildDevPortal(openapischema): + try: + response = requests.post(f"http://{NcgConfig.config['devportal']['host']}:" + f"{NcgConfig.config['devportal']['port']}{NcgConfig.config['devportal']['uri']}", + headers={'Content-Type': 'application/json'}, data=openapischema) + except Exception as e: + return 400, "" + + return response.status_code, json.loads(response.text) + + +# Builds the declarative JSON for the API Gateway configuration +# Return a tuple: status, description. If status = 200 things were successful +def createDevPortal(locationDeclaration: dict, authProfiles: Authentication={}): + if locationDeclaration['apigateway']['openapi_schema']: + status, apiSchemaString = v5_0.GitOps.getObjectFromRepo( + object = locationDeclaration['apigateway']['openapi_schema'], authProfiles = authProfiles['server'] if 'server' in authProfiles else {}, base64Encode = False) + + if v5_0.MiscUtils.yaml_or_json(apiSchemaString['content']) == 'yaml': + # YAML to JSON conversion + status, devportalJSON = buildDevPortal(openapischema = v5_0.MiscUtils.yaml_to_json(apiSchemaString['content'])) + else: + status, devportalJSON = buildDevPortal(openapischema = apiSchemaString['content']) + + if status == 200: + devportalHTML = base64.b64encode(bytes(devportalJSON['devportal'], 'utf-8')).decode('utf-8') + else: + devportalHTML = "" + + return status, devportalHTML \ No newline at end of file diff --git a/src/v4_1/GitOps.py b/src/v5_0/GitOps.py similarity index 75% rename from src/v4_1/GitOps.py rename to src/v5_0/GitOps.py index 5a1be5b..3b56ea7 100644 --- a/src/v4_1/GitOps.py +++ b/src/v5_0/GitOps.py @@ -6,10 +6,9 @@ import requests from requests import ReadTimeout, HTTPError, Timeout, ConnectionError, ConnectTimeout -from typing import List # pydantic models -from V4_1_NginxConfigDeclaration import * +from V5_0_NginxConfigDeclaration import * # Fetches a URL content @@ -37,18 +36,27 @@ def getObjectFromRepo(object: ObjectFromSourceOfTruth, authProfiles: Authenticat headers = {} # Set server authentication if needed - if authProfiles and 'server' in authProfiles: + if authProfiles and 'server' in authProfiles and len(object['authentication'])>0: for authP in authProfiles['server']: if object['authentication'][0]['profile'] == authP['name']: # Sets up authentication if authP['type'].lower() == 'token': + + print(f"===> {authP['name']} {authP['token']['username']} {authP['token']['password']}") + authToken = authP['token']['token'] authTokenType = authP['token']['type'] - authTokenLocation = authP['token']['location'] if authTokenType.lower() == 'bearer': headers['Authorization'] = f"Bearer {authToken}" + elif authTokenType.lower() == 'basic': + authTokenUsername = authP['token']['username'] + authTokenPassword = base64.b64decode(authP['token']['password']).decode('utf-8') + + headers['Authorization'] = f"Basic {base64.b64encode(str.encode(authTokenUsername + ':' + authTokenPassword)).decode('utf-8')}" elif authTokenType.lower() == 'header': + authTokenLocation = authP['token']['location'] + headers[authTokenLocation] = authToken status_code, fetchedContent = __fetchfromsourceoftruth__(url = object['content'], headers = headers) diff --git a/src/v4_1/MiscUtils.py b/src/v5_0/MiscUtils.py similarity index 87% rename from src/v4_1/MiscUtils.py rename to src/v5_0/MiscUtils.py index a5ea547..0d8349a 100644 --- a/src/v4_1/MiscUtils.py +++ b/src/v5_0/MiscUtils.py @@ -5,6 +5,8 @@ import re import json import yaml +import uuid + def getDictKey(_dict: dict, key_lookup: str, separator='.'): """ @@ -41,4 +43,11 @@ def yaml_or_json(document: str): YAML to JSON conversion """ def yaml_to_json(document: str): - return json.dumps(yaml.safe_load(document)) \ No newline at end of file + return json.dumps(yaml.safe_load(document)) + + +""" +Returns a unique ID +""" +def getuniqueid(): + return uuid.uuid4() \ No newline at end of file diff --git a/src/v4_1/NAPUtils.py b/src/v5_0/NAPUtils.py similarity index 99% rename from src/v4_1/NAPUtils.py rename to src/v5_0/NAPUtils.py index bdfcea8..0d8ab65 100644 --- a/src/v4_1/NAPUtils.py +++ b/src/v5_0/NAPUtils.py @@ -5,7 +5,7 @@ import requests import json -import v4_1.GitOps +import v5_0.GitOps from fastapi.responses import Response, JSONResponse @@ -171,7 +171,7 @@ def provisionPolicies(nmsUrl: str, nmsUsername: str, nmsPassword: str, declarati if p['type'] == 'app_protect': # Iterates over all policy versions for policyVersion in p['versions']: - status, policyBody = v4_1.GitOps.getObjectFromRepo(policyVersion['contents']) + status, policyBody = v5_0.GitOps.getObjectFromRepo(policyVersion['contents']) if status != 200: return JSONResponse( diff --git a/src/v5_0/NGINXOneOutput.py b/src/v5_0/NGINXOneOutput.py new file mode 100644 index 0000000..0d3b2ff --- /dev/null +++ b/src/v5_0/NGINXOneOutput.py @@ -0,0 +1,3 @@ +""" +Placeholder +""" \ No newline at end of file diff --git a/src/v5_0/NGINXOneUtils.py b/src/v5_0/NGINXOneUtils.py new file mode 100644 index 0000000..a6dc62b --- /dev/null +++ b/src/v5_0/NGINXOneUtils.py @@ -0,0 +1,28 @@ +""" +NGINX One support functions +""" + +import requests +import json + + +# Fetch a cluster ID from NGINX One +# Return None if not found +def getClusterId(nOneUrl: str, nOneToken: str, nameSpace: str, clusterName: str): + # Retrieve instance group uid + print(f"URL {nOneUrl} namespace {nameSpace} cluster {clusterName} token {nOneToken}") + + cluster = requests.get(url=f'{nOneUrl}/api/nginx/one/namespaces/{nameSpace}/clusters', + verify=False, headers = {"Authorization": f"Bearer {nOneToken}"}) + + if cluster.status_code != 200: + return None + + # Get the instance group id + igUid = None + igJson = json.loads(cluster.text) + for i in igJson['items']: + if i['name'] == clusterName: + igUid = i['object_id'] + + return igUid diff --git a/src/v4_1/NIMUtils.py b/src/v5_0/NIMUtils.py similarity index 90% rename from src/v4_1/NIMUtils.py rename to src/v5_0/NIMUtils.py index 8472ba7..de1b9a9 100644 --- a/src/v4_1/NIMUtils.py +++ b/src/v5_0/NIMUtils.py @@ -5,10 +5,6 @@ import requests import json -import v4_1.GitOps - -from fastapi.responses import Response, JSONResponse - # Fetch an instance group UID from NGINX Instance Manager # Return None if not found diff --git a/src/v5_0/NMSOutput.py b/src/v5_0/NMSOutput.py new file mode 100644 index 0000000..4610a38 --- /dev/null +++ b/src/v5_0/NMSOutput.py @@ -0,0 +1,321 @@ +""" +Output to NGINX Instance Manager +""" + +import base64 +import requests +import json +import pickle +import time +import schedule + +from jinja2 import Environment, FileSystemLoader +from urllib.parse import urlparse +from datetime import datetime + +import V5_0_CreateConfig + +import v5_0.APIGateway +import v5_0.DevPortal +import v5_0.DeclarationPatcher +import v5_0.GitOps +import v5_0.MiscUtils +import v5_0.NMSOutput + +# pydantic models +from V5_0_NginxConfigDeclaration import * + +# NGINX App Protect helper functions +import v5_0.NAPUtils +import v5_0.NIMUtils +import v5_0.MiscUtils + +# NGINX Declarative API modules +from NcgConfig import NcgConfig +from NcgRedis import NcgRedis + +def NMSOutput(d, declaration: ConfigDeclaration, apiversion: str, b64HttpConf: str, + b64StreamConf: str,configFiles = {}, auxFiles = {}, + runfromautosync: bool = False, + configUid: str = ""): + # NGINX Instance Manager Staged Configuration publish + + nmsUsername = v5_0.MiscUtils.getDictKey(d, 'output.nms.username') + nmsPassword = v5_0.MiscUtils.getDictKey(d, 'output.nms.password') + nmsInstanceGroup = v5_0.MiscUtils.getDictKey(d, 'output.nms.instancegroup') + nmsSynctime = v5_0.MiscUtils.getDictKey(d, 'output.nms.synctime') + + nmsUrlFromJson = v5_0.MiscUtils.getDictKey(d, 'output.nms.url') + urlCheck = urlparse(nmsUrlFromJson) + + if urlCheck.scheme not in ['http', 'https'] or urlCheck.scheme == "" or urlCheck.netloc == "": + return {"status_code": 400, + "message": {"status_code": 400, "message": {"code": 400, + "content": f"invalid NGINX Instance Manager URL {nmsUrlFromJson}"}}, + "headers": {'Content-Type': 'application/json'}} + + nmsUrl = f"{urlCheck.scheme}://{urlCheck.netloc}" + + if nmsSynctime < 0: + return {"status_code": 400, + "message": {"status_code": 400, "message": {"code": 400, "content": "synctime must be >= 0"}}, + "headers": {'Content-Type': 'application/json'}} + + # Fetch NGINX App Protect WAF policies from source of truth if needed + d_policies = v5_0.MiscUtils.getDictKey(d, 'output.nms.policies') + if d_policies is not None: + for policy in d_policies: + if 'versions' in policy: + for policyVersion in policy['versions']: + status, content = v5_0.GitOps.getObjectFromRepo(object=policyVersion['contents'], + authProfiles=d['declaration']['http'][ + 'authentication']) + + if status != 200: + return {"status_code": 422, "message": {"status_code": status, "message": content}} + + policyVersion['contents'] = content + + # Check TLS items validity + all_tls = {'certificate': {}, 'key': {}} + + d_certs = v5_0.MiscUtils.getDictKey(d, 'output.nms.certificates') + if d_certs is not None: + for i in range(len(d_certs)): + if d_certs[i]['name']: + all_tls[d_certs[i]['type']][d_certs[i]['name']] = True + + d_servers = v5_0.MiscUtils.getDictKey(d, 'declaration.http.servers') + if d_servers is not None: + for server in d_servers: + if server['listen'] is not None: + if 'tls' in server['listen']: + cert_name = v5_0.MiscUtils.getDictKey(server, 'listen.tls.certificate') + if cert_name and cert_name not in all_tls['certificate']: + return {"status_code": 422, + "message": { + "status_code": 422, + "message": {"code": 422, + "content": "invalid TLS certificate " + + cert_name + " for server" + str( + server['names'])} + }} + + cert_key = v5_0.MiscUtils.getDictKey(server, 'listen.tls.key') + if cert_key and cert_key not in all_tls['key']: + return {"status_code": 422, + "message": { + "status_code": 422, + "message": {"code": 422, + "content": "invalid TLS key " + cert_key + " for server" + str( + server['names'])} + }} + + trusted_cert_name = v5_0.MiscUtils.getDictKey(server, 'listen.tls.trusted_ca_certificates') + if trusted_cert_name and trusted_cert_name not in all_tls['certificate']: + return {"status_code": 422, + "message": { + "status_code": 422, + "message": {"code": 422, + "content": "invalid trusted CA certificate " + + trusted_cert_name + " for server" + str(server['names'])} + }} + + # Add optional certificates specified under output.nms.certificates + extensions_map = {'certificate': '.crt', 'key': '.key'} + + d_certificates = v5_0.MiscUtils.getDictKey(d, 'output.nms.certificates') + if d_certificates is not None: + for c in d_certificates: + status, certContent = v5_0.GitOps.getObjectFromRepo(object=c['contents'], + authProfiles=d['declaration']['http']['authentication']) + + if status != 200: + return {"status_code": 422, + "message": {"status_code": status, "message": {"code": status, "content": certContent}}} + + newAuxFile = {'contents': certContent['content'], 'name': NcgConfig.config['nms']['certs_dir'] + + '/' + c['name'] + extensions_map[c['type']]} + auxFiles['files'].append(newAuxFile) + + ### / Add optional certificates specified under output.nms.certificates + + # NGINX main configuration file through template + j2_env = Environment(loader=FileSystemLoader(NcgConfig.config['templates']['root_dir'] + '/' + apiversion), + trim_blocks=True, extensions=["jinja2_base64_filters.Base64Filters"]) + + nginxMainConf = j2_env.get_template(NcgConfig.config['templates']['nginxmain']).render( + nginxconf={'modules': v5_0.MiscUtils.getDictKey(d, 'output.nms.modules')}) + + # Base64-encoded NGINX main configuration (/etc/nginx/nginx.conf) + b64NginxMain = str(base64.urlsafe_b64encode(nginxMainConf.encode("utf-8")), "utf-8") + + # Base64-encoded NGINX mime.types (/etc/nginx/mime.types) + f = open(NcgConfig.config['templates']['root_dir'] + '/' + apiversion + '/' + NcgConfig.config['templates'][ + 'mimetypes'], 'r') + nginxMimeTypes = f.read() + f.close() + + b64NginxMimeTypes = str(base64.urlsafe_b64encode(nginxMimeTypes.encode("utf-8")), "utf-8") + filesMimeType = {'contents': b64NginxMimeTypes, 'name': NcgConfig.config['nms']['config_dir'] + '/mime.types'} + auxFiles['files'].append(filesMimeType) + + # Base64-encoded NGINX HTTP service configuration + filesNginxMain = {'contents': b64NginxMain, 'name': NcgConfig.config['nms']['config_dir'] + '/nginx.conf'} + filesHttpConf = {'contents': b64HttpConf, + 'name': NcgConfig.config['nms']['config_dir'] + '/' + NcgConfig.config['nms'][ + 'staged_config_http_filename']} + filesStreamConf = {'contents': b64StreamConf, + 'name': NcgConfig.config['nms']['config_dir'] + '/' + NcgConfig.config['nms'][ + 'staged_config_stream_filename']} + + # Append config files to staged configuration + configFiles['files'].append(filesNginxMain) + configFiles['files'].append(filesHttpConf) + configFiles['files'].append(filesStreamConf) + + # Staged config + baseStagedConfig = {'auxFiles': auxFiles, 'configFiles': configFiles} + stagedConfig = {'auxFiles': auxFiles, 'configFiles': configFiles, + 'updateTime': datetime.utcnow().isoformat()[:-3] + 'Z', + 'ignoreConflict': True, 'validateConfig': False} + + currentBaseStagedConfig = NcgRedis.redis.get(f'ncg.basestagedconfig.{configUid}').decode( + 'utf-8') if NcgRedis.redis.get(f'ncg.basestagedconfig.{configUid}') else None + newBaseStagedConfig = json.dumps(baseStagedConfig) + + if currentBaseStagedConfig is not None and newBaseStagedConfig == currentBaseStagedConfig: + print(f'Declaration [{configUid}] not changed') + return {"status_code": 200, + "message": {"status_code": 200, "message": {"code": 200, "content": "no changes"}}} + else: + # Configuration objects have changed, publish to NIM needed + print( + f'Declaration [{configUid}] changed, publishing' if configUid else f'New declaration created, publishing') + + # Get the instance group id + igUid = v5_0.NIMUtils.getNIMInstanceGroupUid(nmsUrl=nmsUrl, nmsUsername=nmsUsername, + nmsPassword=nmsPassword, instanceGroupName=nmsInstanceGroup) + + # Invalid instance group + if igUid is None: + return {"status_code": 404, + "message": {"status_code": 404, "message": {"code": 404, + "content": f"instance group {nmsInstanceGroup} not found"}}, + "headers": {'Content-Type': 'application/json'}} + + ### NGINX App Protect policies support - commits policies to control plane + + # Check NGINX App Protect WAF policies configuration sanity + status, description = v5_0.NAPUtils.checkDeclarationPolicies(d) + + if status != 200: + return {"status_code": 422, + "message": {"status_code": status, "message": {"code": status, "content": description}}, + "headers": {'Content-Type': 'application/json'}} + + # Provision NGINX App Protect WAF policies to NGINX Instance Manager + provisionedNapPolicies, activePolicyUids = v5_0.NAPUtils.provisionPolicies( + nmsUrl=nmsUrl, nmsUsername=nmsUsername, nmsPassword=nmsPassword, declaration=d) + + ### / NGINX App Protect policies support + + ### Publish staged config to instance group + r = requests.post(url=nmsUrl + f"/api/platform/v1/instance-groups/{igUid}/config", + data=json.dumps(stagedConfig), + headers={'Content-Type': 'application/json'}, + auth=(nmsUsername, nmsPassword), + verify=False) + + if r.status_code != 202: + # Configuration push failed + return {"status_code": r.status_code, + "message": {"status_code": r.status_code, "message": r.text}, + "headers": {'Content-Type': 'application/json'}} + + # Fetch the deployment status + publishResponse = json.loads(r.text) + + # Wait for either NIM success or failure after pushing a staged config + isPending = True + while isPending: + time.sleep(NcgConfig.config['nms']['staged_config_publish_waittime']) + deploymentCheck = requests.get(url=nmsUrl + publishResponse['links']['rel'], + auth=(nmsUsername, nmsPassword), + verify=False) + + checkJson = json.loads(deploymentCheck.text) + + if not checkJson['details']['pending']: + isPending = False + + if len(checkJson['details']['failure']) > 0: + # Staged config publish to NIM failed + jsonResponse = checkJson['details']['failure'][0] + deploymentCheck.status_code = 422 + else: + # Staged config publish to NIM succeeded + jsonResponse = json.loads(deploymentCheck.text) + + # if nmsSynctime > 0 and runfromautosync == False: + if runfromautosync == False: + # No configuration is found, generate one + configUid = str(v5_0.MiscUtils.getuniqueid()) + + # Stores the staged config to redis + # Redis keys: + # ncg.declaration.[configUid] = original config declaration + # ncg.declarationrendered.[configUid] = original config declaration - rendered + # ncg.basestagedconfig.[configUid] = base staged configuration + # ncg.apiversion.[configUid] = ncg API version + # ncg.status.[configUid] = latest status + + NcgRedis.redis.set(f'ncg.declaration.{configUid}', pickle.dumps(declaration)) + NcgRedis.redis.set(f'ncg.declarationrendered.{configUid}', json.dumps(d)) + NcgRedis.redis.set(f'ncg.basestagedconfig.{configUid}', json.dumps(baseStagedConfig)) + NcgRedis.redis.set(f'ncg.apiversion.{configUid}', apiversion) + + # Makes NGINX App Protect policies active + doWeHavePolicies = v5_0.NAPUtils.makePolicyActive(nmsUrl=nmsUrl, nmsUsername=nmsUsername, + nmsPassword=nmsPassword, + activePolicyUids=activePolicyUids, + instanceGroupUid=igUid) + + if doWeHavePolicies: + # Clean up NGINX App Protect WAF policies not used anymore + # and not defined in the declaration just pushed + time.sleep(NcgConfig.config['nms']['staged_config_publish_waittime']) + v5_0.NAPUtils.cleanPolicyLeftovers(nmsUrl=nmsUrl, nmsUsername=nmsUsername, + nmsPassword=nmsPassword, + currentPolicies=provisionedNapPolicies) + + # If deploying a new configuration in GitOps mode start autosync + if nmsSynctime == 0: + NcgRedis.declarationsList[configUid] = "static" + elif not runfromautosync: + # GitOps autosync + print(f'Starting autosync for configUid {configUid} every {nmsSynctime} seconds') + + job = schedule.every(nmsSynctime).seconds.do(lambda: V5_0_CreateConfig.configautosync(configUid)) + # Keep track of GitOps configs, key is the threaded job + NcgRedis.declarationsList[configUid] = job + + NcgRedis.redis.set(f'ncg.apiversion.{configUid}', apiversion) + + responseContent = {'code': deploymentCheck.status_code, 'content': jsonResponse, 'configUid': configUid} + + # Configuration push completed, update redis keys + if configUid != "": + NcgRedis.redis.set('ncg.status.' + configUid, json.dumps(responseContent)) + + # if nmsSynctime > 0: + # Updates status, declaration and basestagedconfig in redis + NcgRedis.redis.set('ncg.declaration.' + configUid, pickle.dumps(declaration)) + NcgRedis.redis.set('ncg.declarationrendered.' + configUid, json.dumps(d)) + NcgRedis.redis.set('ncg.basestagedconfig.' + configUid, json.dumps(baseStagedConfig)) + + return {"status_code": deploymentCheck.status_code, + "message": {"status_code": deploymentCheck.status_code, + "message": responseContent}, + "headers": {'Content-Type': 'application/json'} + } \ No newline at end of file diff --git a/src/v5_0/OpenAPIParser.py b/src/v5_0/OpenAPIParser.py new file mode 100644 index 0000000..0e4e355 --- /dev/null +++ b/src/v5_0/OpenAPIParser.py @@ -0,0 +1,71 @@ +""" +OpenAPI schema parser support functions +""" + +import json + +class OpenAPIParser: + httpMethods = ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'CONNECT', 'OPTIONS', 'TRACE', 'PATCH'] + + def __init__(self, openAPISchema): + self.openAPISchema = openAPISchema + + def version(self): + if 'openapi' in self.openAPISchema: + return self.openAPISchema['openapi'] + elif 'swagger' in self.openAPISchema: + return self.openAPISchema['swagger'] + + return None + + def info(self): + return self.openAPISchema['info'] + + def servers(self): + self.allServers = [] + + # Loop over OpenAPI schema servers + if 'servers' in self.openAPISchema: + for server in self.openAPISchema['servers']: + urlName = server['url'] + self.s = {} + self.s['url'] = urlName + + if 'description' in server: + self.s['description'] = server['description'] + + self.allServers.append(self.s) + + return self.allServers + + def paths(self): + self.allPaths = [] + + # Loop over OpenAPI schema paths + if 'paths' in self.openAPISchema: + for path in self.openAPISchema['paths'].keys(): + #print(f"- {path}") + self.p = {} + self.p['path'] = path + self.p['methods'] = [] + + # Loop over path HTTP methods found in schema + for method in self.openAPISchema['paths'][path].keys(): + methodInfo = self.openAPISchema['paths'][path][method] + + if method.upper() in self.httpMethods: + #print(f" - {method} - {methodInfo['description'] if 'description' in methodInfo else ''}") + self.m = {} + self.m['method'] = method + self.m['details'] = {} + + if 'description' in methodInfo and methodInfo['description']: + self.m['details']['description'] = methodInfo['description'] + if 'operationId' in methodInfo and methodInfo['operationId']: + self.m['details']['operationId'] = methodInfo['operationId'] + + self.p['methods'].append(self.m) + + self.allPaths.append(self.p) + + return self.allPaths \ No newline at end of file diff --git a/templates/v4.1/apigateway.tmpl b/templates/v4.1/apigateway.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/auth/client/jwks.tmpl b/templates/v4.1/auth/client/jwks.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/auth/client/jwt.tmpl b/templates/v4.1/auth/client/jwt.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/auth/server/token.tmpl b/templates/v4.1/auth/server/token.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/configmap.tmpl b/templates/v4.1/configmap.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/http.tmpl b/templates/v4.1/http.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/logformat.tmpl b/templates/v4.1/logformat.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/nginx-conf/mime.types b/templates/v4.1/nginx-conf/mime.types deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/nginx-conf/nginx.conf b/templates/v4.1/nginx-conf/nginx.conf deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v4.1/stream.tmpl b/templates/v4.1/stream.tmpl deleted file mode 100644 index e69de29..0000000 diff --git a/templates/v5.0/apigateway.tmpl b/templates/v5.0/apigateway.tmpl new file mode 100644 index 0000000..292c814 --- /dev/null +++ b/templates/v5.0/apigateway.tmpl @@ -0,0 +1,138 @@ +{% if declaration.servers %} + {# --- OpenAPI schema contains server details --- #} + {% if declaration.servers[0].url.lower().startswith('http://') or declaration.servers[0].url.lower().startswith('https://') %} + {# --- OpenAPI schema contains a full server URL --- #} + {% set destination_server = declaration.servers[0].url %} + {% else %} + {# --- OpenAPI schema contains a server URI --- #} + {% set destination_server = declaration.location.apigateway.api_gateway.server_url + declaration.servers[0].url %} + {% endif %} +{% else %} + {# --- OpenAPI schema contains no server details --- #} + {% set destination_server = declaration.location.apigateway.api_gateway.server_url %} +{% endif %} + +# API Gateway: {{ declaration.info.title }} {{ declaration.info.version }} +# OpenAPI version: {{ declaration.version }} +# Base URI: {{ declaration.location.uri }} +# Strip base URI: {{ declaration.location.apigateway.api_gateway.strip_uri }} +# Destination server: {{ destination_server }} + +{% if declaration.paths -%} +{% for path in declaration.paths %} +location {% if '{' not in path.path %}={% else %}~{% endif %} {{ declaration.location.uri }}{{ path.path | regex_replace('{(.*?)}','(.*)') }} { + {% for method in path.methods -%} + # {{ method.method|upper }} - operationId: {{ method.details.operationId }} + {% endfor -%} + {% set method_names = path.methods|map(attribute='method')|list %} + + {% if declaration.location.apigateway.log.access %}access_log {{ declaration.location.apigateway.log.access }} main;{% endif %} + + {% if declaration.location.apigateway.log.error %}error_log {{ declaration.location.apigateway.log.error }};{% endif %} + + + limit_except {{ method_names|join(' ')|upper }} { deny all; } + + {# --- Rate limiting start --- #} + {%- for rl in declaration.location.apigateway.rate_limit -%} + {%- set enforceRL = namespace(toBeEnforced = False) -%} + {%- if rl.enforceOnPaths == False -%} + {%- set enforceRL.toBeEnforced = True -%} + {%- endif -%} + {%- for rlPath in rl.paths -%} + {%- if path.path == rlPath -%} + {%- if rl.enforceOnPaths == True -%} + {%- set enforceRL.toBeEnforced = True -%} + {%- else -%} + {%- set enforceRL.toBeEnforced = False -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + + {%- if enforceRL.toBeEnforced == True -%} + {%- if rl.profile %}limit_req zone={{ rl.profile }}{% if rl.burst %} burst={{ rl.burst }}{% endif %}{% if rl.delay == 0 %} nodelay;{% else %} delay={{ rl.delay }};{% endif %}{% endif %} + + {% if rl.httpcode %}limit_req_status {{ rl.httpcode }};{% endif %} + {%- endif -%} + {%- endfor -%} + + {# --- Rate limiting end --- #} + + + {# --- Authentication start --- #} + {%- if declaration.location.apigateway.authentication -%} + {%- set enforceAuth = namespace(toBeEnforced = False) -%} + {%- if declaration.location.apigateway.authentication.enforceOnPaths == False -%} + {%- set enforceAuth.toBeEnforced = True -%} + {%- endif -%} + {%- for authPath in declaration.location.apigateway.authentication.paths -%} + {%- if path.path == authPath -%} + {%- if declaration.location.apigateway.authentication.enforceOnPaths == True -%} + {%- set enforceAuth.toBeEnforced = True -%} + {%- else -%} + {%- set enforceAuth.toBeEnforced = False -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + + {# --- Client authentication --- #} + {%- if enforceAuth.toBeEnforced == True -%} + {%- if declaration.location.apigateway.authentication and declaration.location.apigateway.authentication.client -%} + {%- for clientAuthProfile in declaration.location.apigateway.authentication.client -%} + include "{{ ncgconfig.nms.auth_client_dir }}/{{ clientAuthProfile.profile | replace(" ", "_") }}.conf"; + {% endfor -%} + {%- endif -%} + {%- endif -%} + + {%- endif %} + + {# --- Authentication end --- #} + + + {# --- Authorization start --- #} + {%- if declaration.location.apigateway.authorization -%} + {%- for authZentry in declaration.location.apigateway.authorization %} + {%- set enforceAuthZ = namespace(toBeEnforced = False) -%} + {%- if authZentry.enforceOnPaths == False -%} + {%- set enforceAuthZ.toBeEnforced = True -%} + {%- endif -%} + {%- for authPath in authZentry.paths -%} + {%- if path.path == authPath -%} + {%- if authZentry.enforceOnPaths == True -%} + {%- set enforceAuthZ.toBeEnforced = True -%} + {%- else -%} + {%- set enforceAuthZ.toBeEnforced = False -%} + {%- endif -%} + {%- endif -%} + {%- endfor -%} + + {# --- Client authorization --- #} + {%- if enforceAuthZ.toBeEnforced == True -%} + include "{{ ncgconfig.nms.authz_client_dir }}/{{ authZentry.profile | replace(" ", "_") }}.conf"; + {%- endif -%} + + {%- endfor -%} + {%- endif %} + + {# --- Authorization end --- #} + + {% if declaration.location.apigateway.api_gateway.strip_uri -%} + rewrite ^{{ declaration.location.uri }}/(.*)$ /$1 break; + {% endif %} + + {% if declaration.location.apigateway.api_gateway.server_url -%} + proxy_set_header Host {{ declaration.location.apigateway.api_gateway.server_url.split('://')[1].split('/')[0] }}; + {% endif -%} + + proxy_pass {{ destination_server }}$uri; +} + +{% endfor %} + +{% if declaration.location.apigateway.developer_portal.enabled == True -%} +location = {{ declaration.location.uri }}{{ declaration.location.apigateway.developer_portal.uri }} { + rewrite ^{{ declaration.location.uri }}/(.*)$ /$1 break; + root {{ ncgconfig.nms.devportal_dir }}; +} +{% endif %} +{% endif %} diff --git a/templates/v5.0/authn/client/jwks.tmpl b/templates/v5.0/authn/client/jwks.tmpl new file mode 100644 index 0000000..aea3a35 --- /dev/null +++ b/templates/v5.0/authn/client/jwks.tmpl @@ -0,0 +1,11 @@ +location = /_auth/jwt/{{ authprofile.name | replace(" ", "_") }}/_jwks_uri { + internal; + + {% if authprofile.jwt.key.startswith('http://') or authprofile.jwt.key.startswith('https://') -%} + proxy_method GET; + proxy_pass {{ authprofile.jwt.key }}; + {% else -%} + return 200 '{{ authprofile.jwt.key }}'; + {%- endif %} + +} diff --git a/templates/v5.0/authn/client/jwt.tmpl b/templates/v5.0/authn/client/jwt.tmpl new file mode 100644 index 0000000..14e9719 --- /dev/null +++ b/templates/v5.0/authn/client/jwt.tmpl @@ -0,0 +1,6 @@ +auth_jwt "{{ authprofile.jwt.realm }}"{% if authprofile.jwt.token_location %} token={{ authprofile.jwt.token_location }}{% endif %}; +auth_jwt_type {{ authprofile.jwt.jwt_type }}; +auth_jwt_key_request /_auth/jwt/{{ authprofile.name | replace(" ", "_") }}/_jwks_uri; +{% if authprofile.jwt.cachetime != 0 %} +auth_jwt_key_cache {{ authprofile.jwt.cachetime }}; +{% endif %} diff --git a/templates/v5.0/authn/client/mtls.tmpl b/templates/v5.0/authn/client/mtls.tmpl new file mode 100644 index 0000000..683facc --- /dev/null +++ b/templates/v5.0/authn/client/mtls.tmpl @@ -0,0 +1,29 @@ +{%- if authprofile.mtls.enabled|lower != "off" -%} +ssl_verify_client {{ authprofile.mtls.enabled }}; +{% if authprofile.mtls.client_certificates -%} +ssl_client_certificate {{ ncgconfig.nms.certs_dir }}/{{ authprofile.mtls.client_certificates }}.crt; +{% endif %} + +{% if authprofile.mtls.trusted_ca_certificates -%} +ssl_trusted_certificate {{ ncgconfig.nms.certs_dir }}/{{ authprofile.mtls.trusted_ca_certificates }}.crt; +{% endif %} + +{# --- OCSP section start --- #} +{%- if authprofile.mtls.ocsp and authprofile.mtls.ocsp.enabled|lower != "off" -%} +ssl_ocsp {{ authprofile.mtls.ocsp.enabled }}; +{% if authprofile.mtls.ocsp.responder -%} +ssl_ocsp_responder {{ authprofile.mtls.ocsp.responder }}; +{% endif %} +{% endif %} +{# --- OCSP section end --- #} + +{# --- TLS stapling section start --- #} +{%- if authprofile.mtls.stapling and authprofile.mtls.stapling.enabled == True -%} +ssl_stapling on; +ssl_stapling_verify {% if authprofile.mtls.stapling.verify == True %}on{% else %}off{% endif %}; +{% if authprofile.mtls.stapling.responder -%} +ssl_stapling_responder {{ authprofile.mtls.stapling.responder }}; +{% endif -%} +{%- endif %} +{# --- TLS stapling section end --- #} +{% endif %} \ No newline at end of file diff --git a/templates/v5.0/authn/server/mtls.tmpl b/templates/v5.0/authn/server/mtls.tmpl new file mode 100644 index 0000000..5b0ae18 --- /dev/null +++ b/templates/v5.0/authn/server/mtls.tmpl @@ -0,0 +1,2 @@ +proxy_ssl_certificate {{ ncgconfig.nms.certs_dir }}/{{ authprofile.mtls.certificate }}.crt; +proxy_ssl_certificate_key {{ ncgconfig.nms.certs_dir }}/{{ authprofile.mtls.certificate }}.key; diff --git a/templates/v5.0/authn/server/token.tmpl b/templates/v5.0/authn/server/token.tmpl new file mode 100644 index 0000000..7acbd60 --- /dev/null +++ b/templates/v5.0/authn/server/token.tmpl @@ -0,0 +1,7 @@ +{% if authprofile.token.type == "bearer" %} +proxy_set_header Authorization "Bearer {{ authprofile.token.token }}"; +{% elif authprofile.token.type == "basic" %} +proxy_set_header Authorization "Basic {{ (authprofile.token.username + ':' + (authprofile.token.password | b64decode) ) | b64encode }}"; +{% elif authprofile.token.type == "header" %} +proxy_set_header {{ authprofile.token.location }} "{{ authprofile.token.token }}"; +{% endif %} \ No newline at end of file diff --git a/templates/v5.0/authz/client/jwt-authz-map.tmpl b/templates/v5.0/authz/client/jwt-authz-map.tmpl new file mode 100644 index 0000000..047da21 --- /dev/null +++ b/templates/v5.0/authz/client/jwt-authz-map.tmpl @@ -0,0 +1,14 @@ +{% for claim in authprofile.jwt.claims %} +auth_jwt_claim_set $authz_match_jwt_claim_{{ claim.name }}_{{ authprofile.name | replace(" ", "_") }} {{ claim.name }}; +{% endfor %} + +{% for claim in authprofile.jwt.claims %} +# JWT claim {{ claim.name }} validation for profile "{{ authprofile.name }}" +map $authz_match_jwt_claim_{{ claim.name }}_{{ authprofile.name | replace(" ", "_") }} $jwt_authz_claim_{{ claim.name }}_{{ authprofile.name | replace(" ", "_") }} { +{% for value in claim.value %} + "{{ value }}" 1; +{% endfor %} + default 0; +} + +{% endfor %} \ No newline at end of file diff --git a/templates/v5.0/authz/client/jwt.tmpl b/templates/v5.0/authz/client/jwt.tmpl new file mode 100644 index 0000000..f6b4d91 --- /dev/null +++ b/templates/v5.0/authz/client/jwt.tmpl @@ -0,0 +1,3 @@ +{% for claim in authprofile.jwt.claims %} +auth_jwt_require $jwt_authz_claim_{{ claim.name }}_{{ authprofile.name | replace(" ", "_") }} error={{ claim.errorcode }}; +{% endfor %} \ No newline at end of file diff --git a/templates/v5.0/configmap.tmpl b/templates/v5.0/configmap.tmpl new file mode 100644 index 0000000..29c3973 --- /dev/null +++ b/templates/v5.0/configmap.tmpl @@ -0,0 +1,13 @@ +apiVersion: v1 +kind: ConfigMap +metadata: + name: {{ name }} + {% if namespace -%} + namespace: {{ namespace }} + {% endif %} + +data: + {{ filename }}: |- +{% filter indent(width = 4) %} + {{ nginxconfig }} +{% endfilter %} \ No newline at end of file diff --git a/templates/v5.0/http.tmpl b/templates/v5.0/http.tmpl new file mode 100644 index 0000000..e62ef0e --- /dev/null +++ b/templates/v5.0/http.tmpl @@ -0,0 +1,418 @@ +# NGINX configuration file - HTTP servers - generated by https://github.com/f5devcentral/NGINX-Declarative-API + +{# --- njs import section --- #} +{% if declaration.njs_profiles %} +js_path "{{ ncgconfig.nms.njs_dir }}"; +{% for njsp in declaration.njs_profiles %} +js_import {{ njsp.name | replace(" ", "_") }} from {{ njsp.name | replace(" ", "_") }}.js; +{% endfor %} +{% endif %} + +{# --- njs functions section - HTTP level --- #} +{% if declaration.njs %} +{% for njshook in declaration.njs %} +{% if njshook.hook.type|lower == "js_set" %} +{{ njshook.hook.type }} {{ njshook.hook.js_set.variable }} {{ njshook.profile }}.{{ njshook.function }}; +{% elif njshook.hook.type|lower == "js_preload_object" %} +{{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_preload_object.file %}from {{ njshook.hook.js_preload_object.file }}{% endif%}; +{% endif %} +{% endfor %} +{% endif %} + +{# --- Maps section --- #} + +{% if declaration.maps %} +{% for m in declaration.maps %} + +map {{ m.match }} {{ m.variable }} { + {% for e in m.entries %} + {%- if e.keymatch|lower == "exact" %}{% endif -%} + {%- if e.keymatch|lower == "regex" %} ~^ {%- endif -%} + {%- if e.keymatch|lower == "iregex" %} ~*^ {%- endif -%} + {{ e.key }} {{ e.value }}; + {% endfor -%} + +} +{% endfor %} +{% endif %} + +{# --- Maps section for authorization --- #} +{%- if declaration.authorization -%} +variables_hash_bucket_size 512; +{% for authzprofile in declaration.authorization -%} +include "{{ ncgconfig.nms.authz_client_dir }}/{{ authzprofile.name | replace(" ", "_") }}.maps.conf"; +{% endfor -%} +{%- endif -%} + +{# --- Snippets section --- #} +{% if declaration.snippet and declaration.snippet.content %}{{ declaration.snippet.content | b64decode }}{% endif %} + + +{# --- Upstreams section --- #} +{% if declaration.upstreams %} +{% for u in declaration.upstreams %} +{% if u.name %} +{% if u.origin %} +upstream {{ u.name }} { + zone {{ u.name }} 64k; + {% for o in u.origin -%} + server {{ o.server }}{% if o.weight %} weight={{ o.weight }}{% endif %}{% if o.max_fails %} max_fails={{ o.max_fails }}{% endif %}{% if o.fail_timeout %} fail_timeout={{ o.fail_timeout }}{% endif %}{% if o.max_conns %} max_conns={{ o.max_conns }}{% endif %}{% if o.slow_start %} slow_start={{ o.slow_start }}{% endif %}{% if o.backup and o.backup == True %} backup{% endif %}; + {% endfor %} + + {% if u.sticky and u.sticky.cookie and u.sticky.expires and u.sticky.domain and u.sticky.path -%} + sticky cookie {{ u.sticky.cookie }}{% if u.sticky.expires %} expires={{ u.sticky.expires }}{% endif %}{% if u.sticky.domain %} domain={{ u.sticky.domain }}{% endif %}{% if u.sticky.path %} path={{ u.sticky.path }}{% endif %}; + {% endif -%} + + {% if u.snippet and u.snippet.content %}{{ u.snippet.content | b64decode }}{% endif %} + +} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} + +{# --- Rate limit section --- #} + +{% if declaration.rate_limit %} +{% for rl in declaration.rate_limit %} +limit_req_zone {{ rl.key }} zone={{ rl.name }}:{{ rl.size }} rate={{ rl.rate }}; +{% endfor %} +{% endif %} + +{# --- Server section for NGINX Plus API --- #} + +{% if declaration.nginx_plus_api %} +{% if declaration.nginx_plus_api.listen %} +server { + listen {{ declaration.nginx_plus_api.listen }}; + + location /api { + {% if declaration.nginx_plus_api.write == True %}api write=on;{% else %}api write=off;{% endif %} + + {% if declaration.nginx_plus_api.allow_acl -%} + allow {{ declaration.nginx_plus_api.allow_acl }}; + deny all; + {% else %} + allow all; + {% endif %} + + } + + location / { + root /usr/share/nginx/html; + index dashboard.html; + } +} +{% endif %} +{% endif %} + +{# --- Server section --- #} + +{% for s in declaration.servers %} +server { + # {{ s.name }} + {# --- Listen section start --- #} + {%- if s.listen -%} + {% if s.listen.address %} + + listen {{ s.listen.address }}{% if s.listen.tls and s.listen.tls.certificate %} ssl{% endif %}; + {% if s.listen.http2 and s.listen.http2 == True -%}http2 on;{% endif -%} + {%- endif %} + + {# --- TLS section start --- #} + {%- if s.listen.tls -%} + + {%- if s.listen.tls.certificate -%} + ssl_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.certificate }}.crt; + {% endif -%} + {%- if s.listen.tls.key -%} + ssl_certificate_key {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.key }}.key; + {% endif -%} + {% if s.listen.tls.ciphers -%} + ssl_ciphers {{ s.listen.tls.ciphers }}; + {% endif -%} + {% if s.listen.tls.protocols -%} + ssl_protocols{% for p in s.listen.tls.protocols %} {{ p }}{% endfor %}; + {% endif -%} + + {# --- client authentication section --- #} + {%- if s.listen.tls and s.listen.tls.authentication and s.listen.tls.authentication.client[0] and s.listen.tls.authentication.client[0].profile -%} + include "{{ ncgconfig.nms.auth_client_dir }}/{{ s.listen.tls.authentication.client[0].profile | replace(" ", "_") }}.conf"; + {% endif %} + + {%- endif %} + {# --- TLS section end --- #} + + {%- endif -%} + {# --- Listen section end --- #} + + {# --- njs functions section start - server level --- #} + {%- if s.njs -%} + {%- for njshook in s.njs -%} + {% if njshook.hook.type|lower == "js_set" %} + {{ njshook.hook.type }} {{ njshook.hook.js_set.variable }} {{ njshook.profile }}.{{ njshook.function }}; + {% elif njshook.hook.type|lower == "js_preload_object" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_preload_object.file %}from {{ njshook.hook.js_preload_object.file }}{% endif%}; + {% elif njshook.hook.type|lower == "js_periodic" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_periodic.interval %}interval={{ njshook.hook.js_periodic.interval }}{% endif%} {% if njshook.hook.js_periodic.jitter %}interval={{ njshook.hook.js_periodic.jitter }}{% endif%} {% if njshook.hook.js_periodic.worker_affinity %}interval={{ njshook.hook.js_periodic.worker_affinity }}{% endif%}; + {% elif njshook.hook.type|lower == "js_body_filter" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_body_filter.buffer_type %}{{ njshook.hook.js_body_filter.buffer_type }}{% endif%}; + {% elif njshook.hook.type|lower == "js_header_filter" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }}; + {% elif njshook.hook.type|lower == "js_content" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }}; + {% endif %} + {%- endfor -%} + {%- endif -%} + + {# --- njs functions section end - server level --- #} + + {% if s.names -%} + server_name{% for svrname in s.names %} {{ svrname }}{% endfor -%}; + status_zone {{ s.names[0] }}; + proxy_ssl_server_name on; + {% endif -%} + + {% if s.resolver -%} + resolver {{ s.resolver }}; + {% endif -%} + + {# --- Server NGINX App Protect WAF section start --- #} + + {% if s.app_protect -%} + {% if s.app_protect.enabled == True -%} + app_protect_enable on; + {% endif -%} + {% if s.app_protect.policy -%} + app_protect_policy_file {{ ncgconfig.nms.nap_policies_dir_pum }}/{{ s.app_protect.policy }}.tgz; + {% endif -%} + {%- if s.app_protect.log -%} + {%- if s.app_protect.log.enabled == True -%} + app_protect_security_log_enable on; + {%- if s.app_protect.log.profile_name -%} + app_protect_security_log "{{ ncgconfig.nms.nap_logformats_dir_pum }}/{{ s.app_protect.log.profile_name }}.tgz" syslog:server={{ s.app_protect.log.destination }}; + {% endif -%} + {% endif %} + {% endif %} + {% endif %} + {# --- Server NGINX App Protect WAF section end --- #} + + {# --- HTTP headers manipulation section start --- #} + {%- if s.headers -%} + {%- if s.headers.to_server -%} + + {%- if s.headers.to_server.set -%} + + {%- for hSet in s.headers.to_server.set -%} + proxy_set_header {{ hSet.name }} "{{ hSet.value }}"; + {% endfor -%} + {%- endif %} + {% if s.headers.to_server.delete -%} + {% for hDel in s.headers.to_server.delete -%} + proxy_set_header {{ hDel }} ""; + {% endfor -%} + {% endif -%} + + {% endif %} + + {% if s.headers.to_client -%} + + {% if s.headers.to_client.add -%} + {% for hAdd in s.headers.to_client.add -%} + add_header {{ hAdd.name }} "{{ hAdd.value }}"; + {% endfor %} + {% endif %} + + {% if s.headers.to_client.delete -%} + {% for hDel in s.headers.to_client.delete -%} + proxy_hide_header {{ hDel }}; + {% endfor %} + {% endif %} + + {% if s.headers.to_client.replace -%} + {% for hDel in s.headers.to_client.replace -%} + proxy_hide_header {{ hDel.name }}; + add_header {{ hDel.name }} "{{ hDel.value }}"; + {% endfor %} + {% endif %} + + {% endif %} + {% endif %} + + {# --- HTTP headers manipulation section end --- #} + + {% if s.log.access %}access_log {{ s.log.access }} main;{% endif %} + + {% if s.log.error %}error_log {{ s.log.error }};{% endif %} + + {# --- Client authentication at server {} level --- #} + {%- if s.authentication and s.authentication.client -%} + {%- for clientAuthProfile in s.authentication.client -%} + include "{{ ncgconfig.nms.auth_client_dir }}/{{ clientAuthProfile.profile | replace(" ", "_") }}.conf"; + {% endfor -%} + {%- endif -%} + + {# --- Client authorization at server {} level --- #} + {%- if s.authorization and s.authorization.profile -%} + include "{{ ncgconfig.nms.authz_client_dir }}/{{ s.authorization.profile | replace(" ", "_") }}.conf"; + {%- endif -%} + + {% filter indent(width=4) %} +{% if s.snippet and s.snippet.content %}{{ s.snippet.content | b64decode }}{% endif %} + {% endfilter %} + + {# --- Server location section start --- #} + {% for loc in s.locations %} + + location + {%- if loc.urimatch -%} + {# location URI match types: prefix (default), exact (=), casesens_regex (~), caseinsens_regex (~*), best_nonregex (^~) #} + {%- if loc.urimatch|lower == "prefix" %} {% endif %} + {%- if loc.urimatch|lower == "exact" %} = {% endif %} + {%- if loc.urimatch|lower == "regex" %} ~ {% endif %} + {%- if loc.urimatch|lower == "iregex" %} ~* {% endif %} + {%- if loc.urimatch|lower == "best" %} ^~ {% endif %} + {%- endif -%} + {{ loc.uri }} { + {% if loc.authentication and loc.authentication.server and loc.authentication.server[0].profile -%} + include "{{ ncgconfig.nms.auth_server_dir }}/{{ loc.authentication.server[0].profile | replace(" ", "_") }}.conf"; + {% endif %} + + {% if loc.upstream %}proxy_pass {{ loc.upstream }};{% endif %} + + {% if loc.log.access %}access_log {{ loc.log.access }} main;{% endif %} + + {% if loc.log.error %}error_log {{ loc.log.error }};{% endif %} + + {# --- Active healthchecks --- #} + + {% if loc.health_check -%} + {% if loc.health_check.enabled == True -%} + health_check{% if loc.health_check.uri %} uri={{ loc.health_check.uri }}{% endif %}{% if loc.health_check.interval %} interval={{ loc.health_check.interval }}{% endif %}{% if loc.health_check.fails %} fails={{ loc.health_check.fails }}{% endif %}{% if loc.health_check.passes %} passes={{ loc.health_check.passes }}{% endif %}; + {% endif %} + {% endif %} + + {# --- njs functions section start - location level --- #} + {%- if loc.njs -%} + {%- for njshook in loc.njs -%} + {% if njshook.hook.type|lower == "js_set" %} + {{ njshook.hook.type }} {{ njshook.hook.js_set.variable }} {{ njshook.profile }}.{{ njshook.function }}; + {% elif njshook.hook.type|lower == "js_preload_object" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_preload_object.file %}from {{ njshook.hook.js_preload_object.file }}{% endif%}; + {% elif njshook.hook.type|lower == "js_periodic" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_periodic.interval %}interval={{ njshook.hook.js_periodic.interval }}{% endif%} {% if njshook.hook.js_periodic.jitter %}interval={{ njshook.hook.js_periodic.jitter }}{% endif%} {% if njshook.hook.js_periodic.worker_affinity %}interval={{ njshook.hook.js_periodic.worker_affinity }}{% endif%}; + {% elif njshook.hook.type|lower == "js_body_filter" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }} {% if njshook.hook.js_body_filter.buffer_type %}{{ njshook.hook.js_body_filter.buffer_type }}{% endif%}; + {% elif njshook.hook.type|lower == "js_header_filter" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }}; + {% elif njshook.hook.type|lower == "js_content" %} + {{ njshook.hook.type }} {{ njshook.profile }}.{{ njshook.function }}; + {% endif %} + {%- endfor -%} + {%- endif -%} + + {# --- njs functions section end - server level --- #} + + {# --- HTTP headers manipulation section @ location start --- #} + {%- if loc.headers -%} + {% if loc.headers.to_server -%} + + {% if loc.headers.to_server.set -%} + {% for hSet in loc.headers.to_server.set -%} + proxy_set_header {{ hSet.name }} "{{ hSet.value }}"; + {% endfor %} + {% endif %} + + {% if loc.headers.to_server.delete -%} + {% for hDel in loc.headers.to_server.delete -%} + proxy_set_header {{ hDel }} ""; + {% endfor %} + {% endif %} + + {% endif %} + + {% if loc.headers.to_client -%} + + {% if loc.headers.to_client.add -%} + {% for hAdd in loc.headers.to_client.add -%} + add_header {{ hAdd.name }} "{{ hAdd.value }}"; + {% endfor %} + {% endif %} + + {% if loc.headers.to_client.delete -%} + {% for hDel in loc.headers.to_client.delete -%} + proxy_hide_header {{ hDel }}; + {% endfor %} + {% endif %} + + {% if loc.headers.to_client.replace -%} + {% for hDel in loc.headers.to_client.replace -%} + proxy_hide_header {{ hDel.name }}; + add_header {{ hDel.name }} "{{ hDel.value }}"; + {% endfor %} + {% endif %} + + {% endif %} + {% endif %} + {# --- HTTP headers manipulation section @ location end --- #} + + {# --- Rate limiting --- #} + + {% if loc.rate_limit -%} + {% if loc.rate_limit.profile %}limit_req zone={{ loc.rate_limit.profile }}{% if loc.rate_limit.burst %} burst={{ loc.rate_limit.burst }}{% endif %}{% if loc.rate_limit.delay == 0 %} nodelay;{% else %} delay={{ loc.rate_limit.delay }};{% endif %} + + {% if loc.rate_limit.httpcode %}limit_req_status {{ loc.rate_limit.httpcode }};{% endif %}{% endif %} + {% endif %} + + {# --- Client authentication at location level --- #} + {%- if loc.authentication and loc.authentication.client -%} + {%- for clientAuthProfile in loc.authentication.client -%} + include "{{ ncgconfig.nms.auth_client_dir }}/{{ clientAuthProfile.profile | replace(" ", "_") }}.conf"; + {% endfor -%} + {%- endif -%} + + {# --- Client authorization at location level --- #} + {%- if loc.authorization and loc.authorization.profile -%} + include "{{ ncgconfig.nms.authz_client_dir }}/{{ loc.authorization.profile | replace(" ", "_") }}.conf"; + {%- endif -%} + + {# --- Location NGINX App Protect WAF --- #} + + {% if loc.app_protect -%} + {% if loc.app_protect.enabled == True -%} + app_protect_enable on; + {% endif -%} + {% if loc.app_protect.policy -%} + app_protect_policy_file {{ ncgconfig.nms.nap_policies_dir_pum }}/{{ loc.app_protect.policy }}.tgz; + {% endif %} + {% if loc.app_protect.log -%} + {%- if loc.app_protect.log.enabled == True -%} + app_protect_security_log_enable on; + {% if loc.app_protect.log.profile_name -%} + app_protect_security_log "{{ ncgconfig.nms.nap_logformats_dir_pum }}/{{ loc.app_protect.log.profile_name }}.tgz" syslog:server={{ loc.app_protect.log.destination }}; + {% endif %} + {% endif %} + {% endif %} + {% endif %} + + {% if loc.apigateway and loc.apigateway.api_gateway.enabled == True %} + include "{{ ncgconfig.nms.apigw_dir }}{{ loc.uri }}.conf"; + {% endif %} + + {# --- Location snippets --- #} + {% if loc.snippet and loc.snippet.content %}{{ loc.snippet.content | b64decode }}{% endif %} + + } + {% endfor %} + + {# --- JWT authentication JWKS endpoints --- #} + {%- if declaration.authentication and declaration.authentication.client -%} + {%- for clientAuthProfile in declaration.authentication.client -%} + {%- if clientAuthProfile.type == "jwt" -%} + include "{{ ncgconfig.nms.auth_client_dir }}/jwks_{{ clientAuthProfile.name | replace(" ", "_") }}.conf"; + {% endif -%} + {%- endfor -%} + {%- endif %} + +} +{% endfor -%} \ No newline at end of file diff --git a/templates/v5.0/logformat.tmpl b/templates/v5.0/logformat.tmpl new file mode 100644 index 0000000..db9863e --- /dev/null +++ b/templates/v5.0/logformat.tmpl @@ -0,0 +1,12 @@ +{ + "filter": { + "request_type": "{{ log.type }}" + }, + + "content": { + "format": "{{ log.format }}", + "format_string": "{{ log.format_string }}", + "max_request_size": "{{ log.max_request_size }}", + "max_message_size": "{{ log.max_message_size }}" + } +} diff --git a/templates/v5.0/nginx-conf/mime.types b/templates/v5.0/nginx-conf/mime.types new file mode 100644 index 0000000..d4e08df --- /dev/null +++ b/templates/v5.0/nginx-conf/mime.types @@ -0,0 +1,97 @@ +types { + text/html html htm shtml; + text/css css; + text/xml xml; + image/gif gif; + image/jpeg jpeg jpg; + application/javascript js; + application/atom+xml atom; + application/rss+xml rss; + + text/mathml mml; + text/plain txt; + text/vnd.sun.j2me.app-descriptor jad; + text/vnd.wap.wml wml; + text/x-component htc; + + image/png png; + image/svg+xml svg svgz; + image/tiff tif tiff; + image/vnd.wap.wbmp wbmp; + image/webp webp; + image/x-icon ico; + image/x-jng jng; + image/x-ms-bmp bmp; + + font/woff woff; + font/woff2 woff2; + + application/java-archive jar war ear; + application/json json; + application/mac-binhex40 hqx; + application/msword doc; + application/pdf pdf; + application/postscript ps eps ai; + application/rtf rtf; + application/vnd.apple.mpegurl m3u8; + application/vnd.google-earth.kml+xml kml; + application/vnd.google-earth.kmz kmz; + application/vnd.ms-excel xls; + application/vnd.ms-fontobject eot; + application/vnd.ms-powerpoint ppt; + application/vnd.oasis.opendocument.graphics odg; + application/vnd.oasis.opendocument.presentation odp; + application/vnd.oasis.opendocument.spreadsheet ods; + application/vnd.oasis.opendocument.text odt; + application/vnd.openxmlformats-officedocument.presentationml.presentation + pptx; + application/vnd.openxmlformats-officedocument.spreadsheetml.sheet + xlsx; + application/vnd.openxmlformats-officedocument.wordprocessingml.document + docx; + application/vnd.wap.wmlc wmlc; + application/wasm wasm; + application/x-7z-compressed 7z; + application/x-cocoa cco; + application/x-java-archive-diff jardiff; + application/x-java-jnlp-file jnlp; + application/x-makeself run; + application/x-perl pl pm; + application/x-pilot prc pdb; + application/x-rar-compressed rar; + application/x-redhat-package-manager rpm; + application/x-sea sea; + application/x-shockwave-flash swf; + application/x-stuffit sit; + application/x-tcl tcl tk; + application/x-x509-ca-cert der pem crt; + application/x-xpinstall xpi; + application/xhtml+xml xhtml; + application/xspf+xml xspf; + application/zip zip; + + application/octet-stream bin exe dll; + application/octet-stream deb; + application/octet-stream dmg; + application/octet-stream iso img; + application/octet-stream msi msp msm; + + audio/midi mid midi kar; + audio/mpeg mp3; + audio/ogg ogg; + audio/x-m4a m4a; + audio/x-realaudio ra; + + video/3gpp 3gpp 3gp; + video/mp2t ts; + video/mp4 mp4; + video/mpeg mpeg mpg; + video/quicktime mov; + video/webm webm; + video/x-flv flv; + video/x-m4v m4v; + video/x-mng mng; + video/x-ms-asf asx asf; + video/x-ms-wmv wmv; + video/x-msvideo avi; +} \ No newline at end of file diff --git a/templates/v5.0/nginx-conf/nginx.conf b/templates/v5.0/nginx-conf/nginx.conf new file mode 100644 index 0000000..6ee69c9 --- /dev/null +++ b/templates/v5.0/nginx-conf/nginx.conf @@ -0,0 +1,40 @@ +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +{% for m in nginxconf.modules %} +load_module modules/{{m}}.so; +{% endfor %} + +events { + worker_connections 1024; +} + +http { + include /etc/nginx/mime.types; + default_type application/octet-stream; + + log_format main '$remote_addr - $remote_user [$time_local] "$request" ' + '$status $body_bytes_sent "$http_referer" ' + '"$http_user_agent" "$http_x_forwarded_for"'; + + access_log /var/log/nginx/access.log main; + + sendfile on; + #tcp_nopush on; + keepalive_timeout 65; + #gzip on; + include /etc/nginx/conf.d/*.conf; +} + + +# TCP/UDP proxy and load balancing block +stream { + log_format stream-main '$remote_addr [$time_local] ' + '$protocol $status $bytes_sent $bytes_received ' + '$session_time "$ssl_preread_server_name"'; + #access_log /dev/stdout stream-main; + include /etc/nginx/stream-conf.d/*.conf; +} \ No newline at end of file diff --git a/templates/v5.0/stream.tmpl b/templates/v5.0/stream.tmpl new file mode 100644 index 0000000..3ae64f0 --- /dev/null +++ b/templates/v5.0/stream.tmpl @@ -0,0 +1,66 @@ +# NGINX configuration file - Stream servers - generated by https://github.com/f5devcentral/NGINX-Declarative-API + +{# --- Upstreams section --- #} + +{% if declaration.upstreams %} +{% for u in declaration.upstreams %} +{% if u.name %} +{% if u.origin %} +upstream {{ u.name }} { + zone {{ u.name }} 64k; + {% for o in u.origin -%} + server {{ o.server }}{% if o.weight %} weight={{ o.weight }}{% endif %}{% if o.max_fails %} max_fails={{ o.max_fails }}{% endif %}{% if o.fail_timeout %} fail_timeout={{ o.fail_timeout }}{% endif %}{% if o.max_conns %} max_conns={{ o.max_conns }}{% endif %}{% if o.slow_start %} slow_start={{ o.slow_start }}{% endif %}{% if o.backup and o.backup == True %} backup{% endif %}; + {% endfor %} + + {% if u.snippet and u.snippet.content %}{{ u.snippet.content }}{% endif %} + +} +{% endif %} +{% endif %} +{% endfor %} +{% endif %} + + +{# --- Stream server section --- #} + +{% for s in declaration.servers %} + {%- if s.listen %} + {% if s.listen.address %} + +server { + listen {{ s.listen.address }}{% if s.listen.protocol == "udp" %} {{ s.listen.protocol }}{% endif %}; + status_zone {{ s.name }}; + {% endif -%} + {% endif -%} + + + {# --- TLS section --- #} + {%- if s.listen.tls -%} + {%- if s.listen.tls.certificate -%} + ssl_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.certificate }}.crt; + {% endif -%} + {%- if s.listen.tls.key -%} + ssl_certificate_key {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.key }}.key; + {% endif -%} + {% if s.listen.tls.ciphers -%} + ssl_ciphers {{ s.listen.tls.ciphers }}; + {% endif -%} + {% if s.listen.tls.protocols -%} + ssl_protocols{% for p in s.listen.tls.protocols %} {{ p }}{% endfor %}; + {% endif %} + {% endif %} + + {% if s.upstream -%} + proxy_pass {{ s.upstream }}; + {% endif %} + + {% if s.snippet and s.snippet.content %}{{ s.snippet.content | b64decode }}{% endif %} + + {%- if s.listen %} + {%- if s.listen.address %} + +} + {% endif -%} + {% endif -%} + +{%- endfor %}