From 912854e80c7b8c2c2ed04303f29ef1437a823169 Mon Sep 17 00:00:00 2001 From: 65397 Date: Wed, 27 Mar 2024 17:06:41 +0000 Subject: [PATCH] NGINX Declarative API 4.2.3 (#44) * 20240311-01 Commit * 20240311-02 Commit - added regexp checks for names * 20240311-02 Commit - added regexp checks for names * 20240327 - Refactored API Gateway auxfiles * 20240327-01 commit Refactored API Gateway configuration files Regexp check for object descriptions --- .../v4.2/nap-policy-xss-allowed.json | 39 +++ .../v4.2/nap-policy-xss-blocked.json | 23 ++ ...NX Declarative API.postman_collection.json | 92 ++++-- etc/config.toml | 1 + src/V4_2_CreateConfig.py | 24 +- src/V4_2_NginxConfigDeclaration.py | 82 +++++- src/v4_0/APIGateway.py | 34 --- src/v4_0/DeclarationPatcher.py | 238 --------------- src/v4_0/DevPortal.py | 43 --- src/v4_0/MiscUtils.py | 44 --- src/v4_1/APIGateway.py | 37 --- src/v4_1/DeclarationPatcher.py | 238 --------------- src/v4_1/DevPortal.py | 46 --- src/v4_1/OpenAPIParser.py | 71 ----- templates/v4.0/apigateway.tmpl | 110 ------- templates/v4.0/auth/client/jwks.tmpl | 11 - templates/v4.0/auth/client/jwt.tmpl | 4 - templates/v4.0/auth/server/jwt.tmpl | 2 - templates/v4.0/configmap.tmpl | 13 - templates/v4.0/http.tmpl | 270 ----------------- templates/v4.0/logformat.tmpl | 12 - templates/v4.0/nginx-conf/mime.types | 97 ------- templates/v4.0/nginx-conf/nginx.conf | 40 --- templates/v4.0/stream.tmpl | 66 ----- templates/v4.1/apigateway.tmpl | 110 ------- templates/v4.1/auth/client/jwks.tmpl | 11 - templates/v4.1/auth/client/jwt.tmpl | 6 - templates/v4.1/auth/server/token.tmpl | 5 - templates/v4.1/configmap.tmpl | 13 - templates/v4.1/http.tmpl | 274 ------------------ templates/v4.1/logformat.tmpl | 12 - templates/v4.1/nginx-conf/mime.types | 97 ------- templates/v4.1/nginx-conf/nginx.conf | 40 --- templates/v4.1/stream.tmpl | 66 ----- templates/v4.2/http.tmpl | 14 +- 35 files changed, 236 insertions(+), 2049 deletions(-) create mode 100644 contrib/gitops-examples/v4.2/nap-policy-xss-allowed.json create mode 100644 contrib/gitops-examples/v4.2/nap-policy-xss-blocked.json diff --git a/contrib/gitops-examples/v4.2/nap-policy-xss-allowed.json b/contrib/gitops-examples/v4.2/nap-policy-xss-allowed.json new file mode 100644 index 0000000..2fe91c8 --- /dev/null +++ b/contrib/gitops-examples/v4.2/nap-policy-xss-allowed.json @@ -0,0 +1,39 @@ +{ + "policy": { + "name": "prod-policy", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ], + "signatures": [ + { + "signatureId": 200001834, + "enabled": false + }, + { + "signatureId": 200001475, + "enabled": false + }, + { + "signatureId": 200000098, + "enabled": false + }, + { + "signatureId": 200001088, + "enabled": false + }, + { + "signatureId": 200101609, + "enabled": false + } + ] + } +} diff --git a/contrib/gitops-examples/v4.2/nap-policy-xss-blocked.json b/contrib/gitops-examples/v4.2/nap-policy-xss-blocked.json new file mode 100644 index 0000000..7b683ab --- /dev/null +++ b/contrib/gitops-examples/v4.2/nap-policy-xss-blocked.json @@ -0,0 +1,23 @@ +{ + "policy": { + "name": "prod-policy", + "template": { + "name": "POLICY_TEMPLATE_NGINX_BASE" + }, + "applicationLanguage": "utf-8", + "enforcementMode": "blocking", + "signature-sets": [ + { + "name": "All Signatures", + "block": true, + "alarm": true + } + ], + "signatures": [ + { + "signatureId": 200001834, + "enabled": false + } + ] + } +} \ 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 365e760..6305097 100644 --- a/contrib/postman/NGINX Declarative API.postman_collection.json +++ b/contrib/postman/NGINX Declarative API.postman_collection.json @@ -1,10 +1,11 @@ { "info": { - "_postman_id": "7a9da224-2aa4-47a4-8864-183ddbd12e55", + "_postman_id": "fd602864-1fe0-4ba2-b87f-e0efcf9ccaa1", "name": "NGINX Declarative API", "description": "Declarative REST API and GitOps automation layer for NGINX Instance Manager", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json", - "_exporter_id": "30973250" + "_exporter_id": "1667416", + "_collection_link": "https://orange-rocket-1353.postman.co/workspace/NGINX~3b744358-53c9-4664-be10-f7d30ab89f84/collection/1667416-fd602864-1fe0-4ba2-b87f-e0efcf9ccaa1?action=share&source=collection_link&creator=1667416" }, "item": [ { @@ -4463,7 +4464,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -4472,7 +4474,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" @@ -4512,7 +4514,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -4521,7 +4524,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" @@ -4544,7 +4547,7 @@ "response": [] }, { - "name": "Petstore API Gateway RateLimit + JWT Authentication", + "name": "Petstore API Gateway RateLimit + JWT AuthN/AuthZ", "event": [ { "listen": "test", @@ -4556,7 +4559,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -4565,7 +4569,52 @@ "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 \"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 \"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.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\": \"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 \"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 \"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 \"errorcode\": 403\n }\n ]\n }\n }\n ]\n }\n }\n}", "options": { "raw": { "language": "json" @@ -4602,7 +4651,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -4611,7 +4661,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" @@ -5754,7 +5804,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5763,7 +5814,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 \"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 \"ngx_http_app_protect_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}", "options": { "raw": { "language": "json" @@ -5798,7 +5849,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5807,7 +5859,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 \"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.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}", "options": { "raw": { "language": "json" @@ -5843,7 +5895,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5852,7 +5905,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 \"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 \"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.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}", "options": { "raw": { "language": "json" @@ -5888,7 +5941,8 @@ "", "pm.collectionVariables.set('configUid',respData.configUid);" ], - "type": "text/javascript" + "type": "text/javascript", + "packages": {} } } ], @@ -5897,7 +5951,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\": \"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_app_protect_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}", "options": { "raw": { "language": "json" diff --git a/etc/config.toml b/etc/config.toml index 2ed131e..659ec8e 100644 --- a/etc/config.toml +++ b/etc/config.toml @@ -42,6 +42,7 @@ uri = "/v1/devportal" [nms] config_dir = '/etc/nginx' certs_dir = '/etc/nginx/ssl' +apigw_dir = '/etc/nginx/apigateway' devportal_dir = '/etc/nginx/devportal' auth_client_dir = '/etc/nginx/authn/client' auth_server_dir = '/etc/nginx/authn/server' diff --git a/src/V4_2_CreateConfig.py b/src/V4_2_CreateConfig.py index d26346d..51385dd 100644 --- a/src/V4_2_CreateConfig.py +++ b/src/V4_2_CreateConfig.py @@ -7,6 +7,7 @@ import pickle import time import uuid +import hashlib from datetime import datetime from urllib.parse import urlparse @@ -274,8 +275,6 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn # Parse HTTP servers d_servers = v4_2.MiscUtils.getDictKey(d, 'declaration.http.servers') if d_servers is not None: - apiGatewaySnippet = '' - for server in d_servers: serverSnippet = '' @@ -377,8 +376,16 @@ def createconfig(declaration: ConfigDeclaration, apiversion: str, runfromautosyn "content": f"invalid server authentication profile [{openApiAuthProfile[0]['profile']}] for OpenAPI schema [{loc['apigateway']['openapi_schema']['content']}]"}}} status, apiGatewayConfigDeclaration = v4_2.APIGateway.createAPIGateway(locationDeclaration = loc, authProfiles = d['declaration']['http']['authentication']) - else: - apiGatewayConfigDeclaration = '' + + # 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: @@ -409,12 +416,7 @@ 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 @@ -922,4 +924,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_2_NginxConfigDeclaration.py b/src/V4_2_NginxConfigDeclaration.py index e09b5f0..f2085fe 100644 --- a/src/V4_2_NginxConfigDeclaration.py +++ b/src/V4_2_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" @@ -290,7 +292,7 @@ class AuthClientJWT(BaseModel, extra="forbid"): def check_type(self) -> 'AuthClientJWT': jwt_type, key = self.jwt_type, self.key - if not key.strip() : + if not key.strip(): raise ValueError(f"Invalid: JWT key must not be empty") valid = ['signed', 'encrypted', 'nested'] @@ -478,6 +480,15 @@ class Server(BaseModel, extra="forbid"): 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"): name: str @@ -485,6 +496,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 = "" @@ -519,12 +539,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] @@ -537,6 +575,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 @@ -544,6 +591,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 @@ -593,6 +649,9 @@ def check_type(self) -> 'Authentication_Client': 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 @@ -610,6 +669,9 @@ def check_type(self) -> 'Authentication_Server': 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 @@ -632,12 +694,24 @@ def check_type(self) -> 'Authorization': 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]] = [] diff --git a/src/v4_0/APIGateway.py b/src/v4_0/APIGateway.py index 93ad474..e69de29 100644 --- a/src/v4_0/APIGateway.py +++ b/src/v4_0/APIGateway.py @@ -1,34 +0,0 @@ -""" -API Gateway support functions -""" - -import json -import base64 - -import v4_0.GitOps -import v4_0.MiscUtils -from v4_0.OpenAPIParser import OpenAPIParser - - -# Builds the declarative JSON for the API Gateway configuration -# Return a tuple: status, description. If status = 200 things were successful -def createAPIGateway(locationDeclaration: dict): - apiGwDeclaration = {} - - if locationDeclaration['apigateway']['openapi_schema']: - status, apiSchemaString = v4_0.GitOps.getObjectFromRepo(content=locationDeclaration['apigateway']['openapi_schema'], base64Encode=False) - - if v4_0.MiscUtils.yaml_or_json(apiSchemaString) == 'yaml': - # YAML to JSON conversion - apiSchemaString = v4_0.MiscUtils.yaml_to_json(apiSchemaString) - - apiSchema = OpenAPIParser(json.loads(apiSchemaString)) - - 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/v4_0/DeclarationPatcher.py b/src/v4_0/DeclarationPatcher.py index 6e27285..e69de29 100644 --- a/src/v4_0/DeclarationPatcher.py +++ b/src/v4_0/DeclarationPatcher.py @@ -1,238 +0,0 @@ -""" -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/v4_0/DevPortal.py b/src/v4_0/DevPortal.py index 0815c2d..e69de29 100644 --- a/src/v4_0/DevPortal.py +++ b/src/v4_0/DevPortal.py @@ -1,43 +0,0 @@ -""" -API Gateway Developer Portal support functions -""" - -import json -import requests -import base64 - -# NGINX Declarative API modules -from NcgConfig import NcgConfig -import v4_0.GitOps -import v4_0.MiscUtils - - -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): - if locationDeclaration['apigateway']['openapi_schema']: - status, apiSchemaString = v4_0.GitOps.getObjectFromRepo( - content=locationDeclaration['apigateway']['openapi_schema'], base64Encode=False) - - if v4_0.MiscUtils.yaml_or_json(apiSchemaString) == 'yaml': - # YAML to JSON conversion - apiSchemaString = v4_0.MiscUtils.yaml_to_json(apiSchemaString) - - status, devportalJSON = buildDevPortal(openapischema=apiSchemaString) - 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_0/MiscUtils.py b/src/v4_0/MiscUtils.py index a5ea547..e69de29 100644 --- a/src/v4_0/MiscUtils.py +++ b/src/v4_0/MiscUtils.py @@ -1,44 +0,0 @@ -""" -Support functions -""" - -import re -import json -import yaml - -def getDictKey(_dict: dict, key_lookup: str, separator='.'): - """ - Searches for a nested key in a dictionary and returns its value, or None if nothing was found. - key_lookup must be a string where each key is deparated by a given "separator" character, which by default is a dot - """ - keys = key_lookup.split(separator) - subdict = _dict - - for k in keys: - subdict = subdict[k] if k in subdict else None - if subdict is None: - return None - - return subdict - -""" -Jinja2 regexp filter -""" -def regex_replace(s, find, replace): - return re.sub(find, replace, s) - -""" -JSON/YAML detection -""" -def yaml_or_json(document: str): - try: - json.load(document) - return 'json' - except Exception: - return 'yaml' - -""" -YAML to JSON conversion -""" -def yaml_to_json(document: str): - return json.dumps(yaml.safe_load(document)) \ No newline at end of file diff --git a/src/v4_1/APIGateway.py b/src/v4_1/APIGateway.py index 0c87e87..e69de29 100644 --- a/src/v4_1/APIGateway.py +++ b/src/v4_1/APIGateway.py @@ -1,37 +0,0 @@ -""" -API Gateway support functions -""" - -import json - -import v4_1.GitOps -import v4_1.MiscUtils -from v4_1.OpenAPIParser import OpenAPIParser - -# pydantic models -from V4_1_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 = v4_1.GitOps.getObjectFromRepo(object=locationDeclaration['apigateway']['openapi_schema'], - authProfiles = authProfiles['server'] if 'server' in authProfiles else {}, base64Encode=False) - - if v4_1.MiscUtils.yaml_or_json(apiSchemaString['content']) == 'yaml': - # YAML to JSON conversion - apiSchemaString['content'] = v4_1.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/v4_1/DeclarationPatcher.py b/src/v4_1/DeclarationPatcher.py index 6e27285..e69de29 100644 --- a/src/v4_1/DeclarationPatcher.py +++ b/src/v4_1/DeclarationPatcher.py @@ -1,238 +0,0 @@ -""" -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/v4_1/DevPortal.py b/src/v4_1/DevPortal.py index 7f508cc..e69de29 100644 --- a/src/v4_1/DevPortal.py +++ b/src/v4_1/DevPortal.py @@ -1,46 +0,0 @@ -""" -API Gateway Developer Portal support functions -""" - -import json -import requests -import base64 - -# NGINX Declarative API modules -from NcgConfig import NcgConfig -import v4_1.GitOps -import v4_1.MiscUtils - -# pydantic models -from V4_1_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 = v4_1.GitOps.getObjectFromRepo( - object = locationDeclaration['apigateway']['openapi_schema'], authProfiles = authProfiles['server'] if 'server' in authProfiles else {}, base64Encode = False) - - if v4_1.MiscUtils.yaml_or_json(apiSchemaString['content']) == 'yaml': - # YAML to JSON conversion - status, devportalJSON = buildDevPortal(openapischema = v4_1.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/OpenAPIParser.py b/src/v4_1/OpenAPIParser.py index 0e4e355..e69de29 100644 --- a/src/v4_1/OpenAPIParser.py +++ b/src/v4_1/OpenAPIParser.py @@ -1,71 +0,0 @@ -""" -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.0/apigateway.tmpl b/templates/v4.0/apigateway.tmpl index 9a0bd2a..e69de29 100644 --- a/templates/v4.0/apigateway.tmpl +++ b/templates/v4.0/apigateway.tmpl @@ -1,110 +0,0 @@ -{% 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 enforceRL.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 --- #} - - {% 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/v4.0/auth/client/jwks.tmpl b/templates/v4.0/auth/client/jwks.tmpl index aea3a35..e69de29 100644 --- a/templates/v4.0/auth/client/jwks.tmpl +++ b/templates/v4.0/auth/client/jwks.tmpl @@ -1,11 +0,0 @@ -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/v4.0/auth/client/jwt.tmpl b/templates/v4.0/auth/client/jwt.tmpl index 4aef347..e69de29 100644 --- a/templates/v4.0/auth/client/jwt.tmpl +++ b/templates/v4.0/auth/client/jwt.tmpl @@ -1,4 +0,0 @@ -auth_jwt "{{ authprofile.jwt.realm }}"; -auth_jwt_type {{ authprofile.jwt.jwt_type }}; -auth_jwt_key_request /_auth/jwt/{{ authprofile.name | replace(" ", "_") }}/_jwks_uri; -auth_jwt_key_cache {{ authprofile.jwt.cachetime }}; \ No newline at end of file diff --git a/templates/v4.0/auth/server/jwt.tmpl b/templates/v4.0/auth/server/jwt.tmpl index d144fdc..e69de29 100644 --- a/templates/v4.0/auth/server/jwt.tmpl +++ b/templates/v4.0/auth/server/jwt.tmpl @@ -1,2 +0,0 @@ -# Placeholder -# JWT TOKEN: {{ authprofile.token }} \ No newline at end of file diff --git a/templates/v4.0/configmap.tmpl b/templates/v4.0/configmap.tmpl index 29c3973..e69de29 100644 --- a/templates/v4.0/configmap.tmpl +++ b/templates/v4.0/configmap.tmpl @@ -1,13 +0,0 @@ -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/v4.0/http.tmpl b/templates/v4.0/http.tmpl index f4e957e..e69de29 100644 --- a/templates/v4.0/http.tmpl +++ b/templates/v4.0/http.tmpl @@ -1,270 +0,0 @@ -# NGINX configuration file - HTTP servers - generated by https://github.com/f5devcentral/NGINX-Declarative-API - -{# --- 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 %} - -{# --- Snippets section --- #} -{% if declaration.snippet %}{{ declaration.snippet | 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 %}{{ u.snippet | 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 -%} - {% if s.listen.tls.trusted_ca_certificates -%} - ssl_trusted_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.trusted_ca_certificates }}.crt; - {% endif %} - - {# --- mTLS section --- #} - {%- if s.listen.tls.mtls -%} - {%- if s.listen.tls.mtls.enabled|lower != "off" -%} - ssl_verify_client {{ s.listen.tls.mtls.enabled }}; - {% endif %} - {% if s.listen.tls.mtls.client_certificates -%} - ssl_client_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.mtls.client_certificates }}.crt; - {% endif %} - {% endif %} - - {# --- OCSP section start --- #} - {%- if s.listen.tls.ocsp and s.listen.tls.ocsp.enabled|lower != "off" -%} - ssl_ocsp {{ s.listen.tls.ocsp.enabled }}; - {% if s.listen.tls.ocsp.responder -%} - ssl_ocsp_responder {{ s.listen.tls.ocsp.responder }}; - {% endif %} - {% endif %} - {# --- OCSP section end --- #} - - {# --- TLS stapling section start --- #} - {%- if s.listen.tls.stapling and s.listen.tls.stapling.enabled == True -%} - ssl_stapling on; - ssl_stapling_verify {% if s.listen.tls.stapling.verify == True %}on{% else %}off{% endif %}; - {% if s.listen.tls.stapling.responder -%} - ssl_stapling_responder {{ s.listen.tls.stapling.responder }}; - - {% endif -%} - {%- endif %} - {# --- TLS stapling section end --- #} - - {%- endif %} - {# --- TLS section end --- #} - - {%- endif %} - {# --- Listen section end --- #} - - {% if s.names -%} - server_name{% for svrname in s.names %} {{ svrname }}{% endfor -%}; - status_zone {{ s.names[0] }}; - proxy_set_header Host $host; - 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 --- #} - - {% if s.log.access %}access_log {{ s.log.access }} main;{% endif %} - - {% if s.log.error %}error_log {{ s.log.error }};{% endif %} - - - {% filter indent(width=4) %} -{% if s.snippet %}{{ s.snippet | 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.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 %} - - {# --- 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 --- #} - {%- 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 -%} - - {# --- 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 %} - - {# --- Location snipptes --- #} - - {% if loc.snippet %}{{ loc.snippet | 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 -%} - -{%- if s.listen.address -%} -{%- endif -%} -} -{% endfor -%} \ No newline at end of file diff --git a/templates/v4.0/logformat.tmpl b/templates/v4.0/logformat.tmpl index db9863e..e69de29 100644 --- a/templates/v4.0/logformat.tmpl +++ b/templates/v4.0/logformat.tmpl @@ -1,12 +0,0 @@ -{ - "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/v4.0/nginx-conf/mime.types b/templates/v4.0/nginx-conf/mime.types index d4e08df..e69de29 100644 --- a/templates/v4.0/nginx-conf/mime.types +++ b/templates/v4.0/nginx-conf/mime.types @@ -1,97 +0,0 @@ -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/v4.0/nginx-conf/nginx.conf b/templates/v4.0/nginx-conf/nginx.conf index 6ee69c9..e69de29 100644 --- a/templates/v4.0/nginx-conf/nginx.conf +++ b/templates/v4.0/nginx-conf/nginx.conf @@ -1,40 +0,0 @@ -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/v4.0/stream.tmpl b/templates/v4.0/stream.tmpl index 79c38f9..e69de29 100644 --- a/templates/v4.0/stream.tmpl +++ b/templates/v4.0/stream.tmpl @@ -1,66 +0,0 @@ -# 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 %}{{ u.snippet }}{% 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 %}{{ s.snippet | b64decode }}{% endif %} - - {%- if s.listen %} - {%- if s.listen.address %} - -} - {% endif -%} - {% endif -%} - -{%- endfor %} diff --git a/templates/v4.1/apigateway.tmpl b/templates/v4.1/apigateway.tmpl index 9a0bd2a..e69de29 100644 --- a/templates/v4.1/apigateway.tmpl +++ b/templates/v4.1/apigateway.tmpl @@ -1,110 +0,0 @@ -{% 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 enforceRL.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 --- #} - - {% 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/v4.1/auth/client/jwks.tmpl b/templates/v4.1/auth/client/jwks.tmpl index aea3a35..e69de29 100644 --- a/templates/v4.1/auth/client/jwks.tmpl +++ b/templates/v4.1/auth/client/jwks.tmpl @@ -1,11 +0,0 @@ -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/v4.1/auth/client/jwt.tmpl b/templates/v4.1/auth/client/jwt.tmpl index 9700a9d..e69de29 100644 --- a/templates/v4.1/auth/client/jwt.tmpl +++ b/templates/v4.1/auth/client/jwt.tmpl @@ -1,6 +0,0 @@ -auth_jwt "{{ authprofile.jwt.realm }}"; -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 %} \ No newline at end of file diff --git a/templates/v4.1/auth/server/token.tmpl b/templates/v4.1/auth/server/token.tmpl index 862630b..e69de29 100644 --- a/templates/v4.1/auth/server/token.tmpl +++ b/templates/v4.1/auth/server/token.tmpl @@ -1,5 +0,0 @@ -{% if authprofile.token.type == "bearer" %} -proxy_set_header Authorization "Bearer {{ authprofile.token.token }}"; -{% 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/v4.1/configmap.tmpl b/templates/v4.1/configmap.tmpl index 29c3973..e69de29 100644 --- a/templates/v4.1/configmap.tmpl +++ b/templates/v4.1/configmap.tmpl @@ -1,13 +0,0 @@ -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/v4.1/http.tmpl b/templates/v4.1/http.tmpl index 4fb3331..e69de29 100644 --- a/templates/v4.1/http.tmpl +++ b/templates/v4.1/http.tmpl @@ -1,274 +0,0 @@ -# NGINX configuration file - HTTP servers - generated by https://github.com/f5devcentral/NGINX-Declarative-API - -{# --- 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 %} - -{# --- 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 -%} - {% if s.listen.tls.trusted_ca_certificates -%} - ssl_trusted_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.trusted_ca_certificates }}.crt; - {% endif %} - - {# --- mTLS section --- #} - {%- if s.listen.tls.mtls -%} - {%- if s.listen.tls.mtls.enabled|lower != "off" -%} - ssl_verify_client {{ s.listen.tls.mtls.enabled }}; - {% endif %} - {% if s.listen.tls.mtls.client_certificates -%} - ssl_client_certificate {{ ncgconfig.nms.certs_dir }}/{{ s.listen.tls.mtls.client_certificates }}.crt; - {% endif %} - {% endif %} - - {# --- OCSP section start --- #} - {%- if s.listen.tls.ocsp and s.listen.tls.ocsp.enabled|lower != "off" -%} - ssl_ocsp {{ s.listen.tls.ocsp.enabled }}; - {% if s.listen.tls.ocsp.responder -%} - ssl_ocsp_responder {{ s.listen.tls.ocsp.responder }}; - {% endif %} - {% endif %} - {# --- OCSP section end --- #} - - {# --- TLS stapling section start --- #} - {%- if s.listen.tls.stapling and s.listen.tls.stapling.enabled == True -%} - ssl_stapling on; - ssl_stapling_verify {% if s.listen.tls.stapling.verify == True %}on{% else %}off{% endif %}; - {% if s.listen.tls.stapling.responder -%} - ssl_stapling_responder {{ s.listen.tls.stapling.responder }}; - - {% endif -%} - {%- endif %} - {# --- TLS stapling section end --- #} - - {%- endif %} - {# --- TLS section end --- #} - - {%- endif %} - {# --- Listen section end --- #} - - {% if s.names -%} - server_name{% for svrname in s.names %} {{ svrname }}{% endfor -%}; - status_zone {{ s.names[0] }}; - proxy_set_header Host $host; - 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 --- #} - - {% if s.log.access %}access_log {{ s.log.access }} main;{% endif %} - - {% if s.log.error %}error_log {{ s.log.error }};{% 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 %} - - {# --- 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 --- #} - {%- 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 -%} - - {# --- 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 %} - - {# --- Location snipptes --- #} - - {% 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 -%} - -{%- if s.listen.address -%} -{%- endif -%} -} -{% endfor -%} \ No newline at end of file diff --git a/templates/v4.1/logformat.tmpl b/templates/v4.1/logformat.tmpl index db9863e..e69de29 100644 --- a/templates/v4.1/logformat.tmpl +++ b/templates/v4.1/logformat.tmpl @@ -1,12 +0,0 @@ -{ - "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/v4.1/nginx-conf/mime.types b/templates/v4.1/nginx-conf/mime.types index d4e08df..e69de29 100644 --- a/templates/v4.1/nginx-conf/mime.types +++ b/templates/v4.1/nginx-conf/mime.types @@ -1,97 +0,0 @@ -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/v4.1/nginx-conf/nginx.conf b/templates/v4.1/nginx-conf/nginx.conf index 6ee69c9..e69de29 100644 --- a/templates/v4.1/nginx-conf/nginx.conf +++ b/templates/v4.1/nginx-conf/nginx.conf @@ -1,40 +0,0 @@ -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/v4.1/stream.tmpl b/templates/v4.1/stream.tmpl index 3ae64f0..e69de29 100644 --- a/templates/v4.1/stream.tmpl +++ b/templates/v4.1/stream.tmpl @@ -1,66 +0,0 @@ -# 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 %} diff --git a/templates/v4.2/http.tmpl b/templates/v4.2/http.tmpl index d5122a0..325a2f5 100644 --- a/templates/v4.2/http.tmpl +++ b/templates/v4.2/http.tmpl @@ -209,10 +209,10 @@ server { {% 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 -%} + {%- 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 -%} + {%- 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 %} @@ -409,7 +409,7 @@ server { 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 -%} + {%- 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 }}; @@ -418,7 +418,11 @@ server { {% endif %} {% endif %} - {# --- Location snipptes --- #} + {# --- Location snippets --- #} + + {% if loc.apigateway.api_gateway.enabled == True %} + include "{{ ncgconfig.nms.apigw_dir }}{{ loc.uri }}.conf"; + {% endif %} {% if loc.snippet and loc.snippet.content %}{{ loc.snippet.content | b64decode }}{% endif %}