diff --git a/.gitignore b/.gitignore index 66652bf..6fb564d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,11 @@ *~ \#* +####################### +# Terraform variables # +####################### +*.tfvars + ########################## # Backup/temporary files # ########################## diff --git a/NginxOneConsole-demo/readme.md b/NginxOneConsole-demo/readme.md deleted file mode 100644 index 55d9b29..0000000 --- a/NginxOneConsole-demo/readme.md +++ /dev/null @@ -1,5 +0,0 @@ -# NGINX One Console Demo - -The material for this demo can be found within the NGINX One Workshop. Below is the github repository link. - -## https://github.com/nginxinc/nginx-one-workshops \ No newline at end of file diff --git a/README.md b/README.md index e1f17b0..403e593 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,28 @@ -# nginx_demos +[![Project Status: Concept – Minimal or no implementation has been done yet, or the repository is only intended to be a limited example, demo, or proof-of-concept.](https://www.repostatus.org/badges/latest/concept.svg)](https://www.repostatus.org/#concept) +[![OpenSSF Scorecard](https://api.securityscorecards.dev/projects/github.com/nginx/nginx-demos/badge)](https://securityscorecards.dev/viewer/?uri=github.com/nginx/nginx-demos) +[![Community Support](https://badgen.net/badge/support/community/cyan?icon=awesome)](/SUPPORT.md) +[![Community Forum](https://img.shields.io/badge/community-forum-009639?logo=discourse&link=https%3A%2F%2Fcommunity.nginx.org)](https://community.nginx.org) +[![License](https://img.shields.io/badge/License-Apache%202.0-blue.svg)](https://opensource.org/licenses/Apache-2.0) +[![Contributor Covenant](https://img.shields.io/badge/Contributor%20Covenant-2.1-4baaaa.svg)](/CODE_OF_CONDUCT.md) -## Requirements +# NGINX Demos -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam elit turpis, varius et arcu elementum, viverra rhoncus sem. Aliquam nec sodales magna, et egestas enim. Mauris lobortis ultrices euismod. Pellentesque in arcu lacus. Mauris cursus laoreet nulla, ac vehicula est. Vestibulum eu mauris quis lorem consectetur aliquam ac nec quam. Vestibulum commodo pharetra mi, at bibendum neque faucibus ut. Mauris et tortor sed sem consectetur eleifend ut non magna. Praesent feugiat placerat nibh, varius viverra orci bibendum sed. Vestibulum dapibus ex ut pulvinar facilisis. Quisque sodales enim et augue tempor mattis. Suspendisse finibus congue felis, ac blandit ligula. Praesent condimentum ultrices odio quis semper. Nunc ultrices, nibh quis mattis pellentesque, elit nulla bibendum felis, quis dapibus erat turpis ac urna. +This repository contains a collection of curated and updated NGINX demos covering NGINX offerings. -## Getting Started +## Repository Structure -Duis sit amet sapien vel velit ornare vulputate. Nulla rutrum euismod risus ac efficitur. Curabitur in sagittis elit, a semper leo. Suspendisse malesuada aliquam velit, eu suscipit lorem vehicula at. Proin turpis lacus, semper in placerat in, accumsan non ipsum. Cras euismod, elit eget pretium laoreet, tortor nulla finibus tortor, nec hendrerit elit turpis ut eros. Quisque congue nisi id mauris molestie, eu condimentum dolor rutrum. Nullam eleifend elit ac lobortis tristique. Pellentesque nec tellus non mauris aliquet commodo a eu elit. Ut at feugiat metus, at tristique mauris. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; +The demos are divided by NGINX product offering into unique distinct folders. Each folder then contains one or more demos covering various use cases within the respective product offering. -## How to Use +Each demo might have unique deployment requirements. Please refer to each individual README for more details. -Maecenas at vehicula justo. Suspendisse posuere elementum elit vel posuere. Etiam quis pulvinar massa. Integer tempor semper risus, vitae maximus eros ullamcorper vitae. In egestas, ex vitae gravida sodales, ipsum dolor varius est, et cursus lorem dui a mi. Morbi faucibus ut nisi id faucibus. Sed quis ullamcorper ex. In et dolor id nunc interdum suscipit. +## Available Demos + +|Title|Description| +|-----|-----------| +|NGINX API gateway|Configure NGINX as an API gateway| +|NGINX Gateway Fabric|Simple overview of configuring NGINX Gateway Fabric to route traffic within Kubernetes| +|NGINX Ingress Controller|Simple overview of deploying and configuring NGINX Ingress Controller| +|NGINX One|Simple overview of NGINX One and its capabilities| ## Contributing diff --git a/nginx-gateway-fabric/appworld/README.md b/nginx-gateway-fabric/appworld/README.md new file mode 100644 index 0000000..89f989e --- /dev/null +++ b/nginx-gateway-fabric/appworld/README.md @@ -0,0 +1,3 @@ +# NGINX Gateway Fabric Demo + +This demo contains the files used in the NGINX Gateway Fabric AppWorld demo. diff --git a/ngf/appworld/cafe-route-equal-weight.yaml b/nginx-gateway-fabric/appworld/cafe-route-equal-weight.yaml similarity index 100% rename from ngf/appworld/cafe-route-equal-weight.yaml rename to nginx-gateway-fabric/appworld/cafe-route-equal-weight.yaml diff --git a/ngf/appworld/cafe-route-v2.yaml b/nginx-gateway-fabric/appworld/cafe-route-v2.yaml similarity index 100% rename from ngf/appworld/cafe-route-v2.yaml rename to nginx-gateway-fabric/appworld/cafe-route-v2.yaml diff --git a/ngf/appworld/cafe-route.yaml b/nginx-gateway-fabric/appworld/cafe-route.yaml similarity index 100% rename from ngf/appworld/cafe-route.yaml rename to nginx-gateway-fabric/appworld/cafe-route.yaml diff --git a/ngf/appworld/coffee-app.yaml b/nginx-gateway-fabric/appworld/coffee-app.yaml similarity index 100% rename from ngf/appworld/coffee-app.yaml rename to nginx-gateway-fabric/appworld/coffee-app.yaml diff --git a/ngf/appworld/gateway.yaml b/nginx-gateway-fabric/appworld/gateway.yaml similarity index 100% rename from ngf/appworld/gateway.yaml rename to nginx-gateway-fabric/appworld/gateway.yaml diff --git a/nic/appworld/README.md b/nginx-ingress-controller/appworld/README.md similarity index 100% rename from nic/appworld/README.md rename to nginx-ingress-controller/appworld/README.md diff --git a/nic/appworld/nginx-deployment.yaml b/nginx-ingress-controller/appworld/nginx-deployment.yaml similarity index 100% rename from nic/appworld/nginx-deployment.yaml rename to nginx-ingress-controller/appworld/nginx-deployment.yaml diff --git a/nic/appworld/nginx-ingress.yaml b/nginx-ingress-controller/appworld/nginx-ingress.yaml similarity index 100% rename from nic/appworld/nginx-ingress.yaml rename to nginx-ingress-controller/appworld/nginx-ingress.yaml diff --git a/nginx-one/appworld/README.md b/nginx-one/appworld/README.md new file mode 100644 index 0000000..5ab7f60 --- /dev/null +++ b/nginx-one/appworld/README.md @@ -0,0 +1,3 @@ +# NGINX One Console Demo + +The material for this demo can be found within the NGINX One Workshop GitHub repository at . diff --git a/nginx/api-gateway/README.md b/nginx/api-gateway/README.md new file mode 100644 index 0000000..fddac9b --- /dev/null +++ b/nginx/api-gateway/README.md @@ -0,0 +1,204 @@ +# NGINX API Gateway Demo + +## Overview + +This demo uses Terraform to automate the setup of an NGINX API gateway pseudo-production environment that includes a mock API backend database. + +## Requirements + +### Terraform + +This demo has been developed and tested with Terraform `0.13` through `1.1.5`. + +Instructions on how to install Terraform can be found in the [Terraform website](https://www.terraform.io/downloads.html). + +### AWS R53 + +You will need to create R53 hosted zone beforehand. Make sure you own the domain you are using through the R53 hosted zone or you risk running into DNS issues. You should specify the R53 hosted zone `id` as well as a FQDN for the NGINX Plus API gateway and backend API in the corresponding Terraform variables. + +## Deployment + +To use the provided Terraform scripts, you need to: + +1. Export your AWS credentials as environment variables (or alternatively, tweak the AWS provider in [`terraform/provider.tf`](terraform/provider.tf)). +2. Set up default values for variables missing a value in [`terraform/variables.tf`](terraform/variables.tf) (you can find example values commented out in the file). Alternatively, you can input those variables at runtime (beware of dictionary values if you do the latter). + +Once you have configured your Terraform environment, you can either: + +* Run [`./setup.sh`](setup.sh) to initialize the AWS Terraform provider and start a Terraform deployment on AWS. +* Run `terraform init` and `terraform apply`. + +And finally, once you are done playing with the demo, you can destroy the AWS infrastructure by either: + +* Run [`./cleanup.sh`](cleanup.sh) to destroy your Terraform deployment. +* Run `terraform destroy`. + +## Demo Overview + +You will find a series of NGINX configuration files in the [`nginx_api_gateway_config`](nginx_api_gateway_config/) folder. The folder is divided into individual steps, meant to be copied into their respective directory in order. By default, the folder is uploaded to your NGINX API gateway instance. + +Do note that you will have to replace the `` placeholder value found in the API backends NGINX configuration file in Step 3 with the corresponding value you used when deploying the Terraform environment (see [`nginx_api_gateway_config/step_3/api_backends.conf`](nginx_api_gateway_config/step_3/api_backends.conf) for more details). + +A deployment script to help you copy the configuration files, [`deploy.sh`](nginx_api_gateway_config/deploy.sh), is also provided. To run the script, use the step number as a parameter, e.g. `./deploy.sh 1` for step 1. You might need to make the deployment script executable by running `sudo chmod +x deploy.sh`. + +### Step 1 -> Define the entry point of the NGINX API gateway + +To deploy: + +`./deploy.sh 1` + +To test: + +`curl -s http://localhost:8080` + +Expected response: + +```html + +400 Bad Request + +

400 Bad Request

+
nginx/1.19.5
+ + +``` + +### Step 2 -> Define default JSON error codes + +To deploy: + +`./deploy.sh 2` + +To test: + +`curl -s http://localhost:8080 | jq` + +Expected response: + +```json +{"status":400,"message":"Bad request"} +``` + +To test (headers): + +`curl -sI http://localhost:8080` + +Expected response: + +```text +HTTP/1.1 400 Bad Request +... +``` + +### Step 3 -> Define the API endpoints and upstream/backend servers + +To deploy: + +`./deploy.sh 3` + +To test: + +`curl -s http://localhost:8080/api/f1/drivers/alonso | jq` + +Expected response: + +```json +{ + "MRData": { + "xmlns": "http://ergast.com/mrd/1.5", + "series": "f1", + "url": "http://ergast.com/api/f1/drivers/alonso", + "limit": "30", + "offset": "0", + "total": "1", + "DriverTable": { + "driverId": "alonso", + "Drivers": [ + { + "driverId": "alonso", + "permanentNumber": "14", + "code": "ALO", + "url": "http://en.wikipedia.org/wiki/Fernando_Alonso", + "givenName": "Fernando", + "familyName": "Alonso", + "dateOfBirth": "1981-07-29", + "nationality": "Spanish" + } + ] + } + } +} +``` + +### Step 4 -> Enable rate limiting + +To deploy: + +`./deploy.sh 4` + +To test (run multiple times in quick succession): + +`curl -s http://localhost:8080/api/f1/drivers/alonso | jq` + +Expected response: + +```json +{"status":429,"message":"API rate limit exceeded"} +``` + +### Step 5 -> Set up API Key authentication + +To deploy: + +`./deploy.sh 5` + +To test (unauthorized requests): + +`curl -s http://localhost:8080/api/f1/drivers/alonso | jq` + +Expected response (unauthorized requests): + +```json +{"status":401,"message":"Unauthorized"} +``` + +To test (authorized requests): + +`curl -sH "apikey: 7B5zIqmRGXmrJTFmKa99vcit" http://localhost:8080/api/f1/drivers/alonso | jq` + +Expected response (authorized requests): + +```json +{"MRData": { + "xmlns": "http://ergast.com/mrd/1.4", + "series": "f1", + "url": "http://ergast.com/api/f1/drivers/alonso", + ... +}} +``` + +### Step 6 -> Set up JSON body validation using NJS + +To deploy: + +`./deploy.sh 6` + +To test (incorrect JSON): + +`curl -sH "apikey: 7B5zIqmRGXmrJTFmKa99vcit" -i -X POST -d 'garbage123' http://localhost:8080/api/f1/seasons` + +Expected response (incorrect JSON): + +```text +HTTP/1.1 415 Unsupported Media Type +``` + +To test (correct JSON): + +`curl -sH "apikey: 7B5zIqmRGXmrJTFmKa99vcit" -i -X POST -d '{"season":"2020"}' http://localhost:8080/api/f1/seasons | jq` + +Expected response (correct JSON): + +```text +HTTP/1.1 200 OK +``` diff --git a/nginx/api-gateway/cleanup.sh b/nginx/api-gateway/cleanup.sh new file mode 100755 index 0000000..67bdf88 --- /dev/null +++ b/nginx/api-gateway/cleanup.sh @@ -0,0 +1,4 @@ +cd terraform \ +&& terraform destroy -auto-approve \ +&& rm -f terraform.tfstate terraform.tfstate.backup \ +&& rm -rf .terraform diff --git a/nginx/api-gateway/nginx_api_gateway_config/deploy.sh b/nginx/api-gateway/nginx_api_gateway_config/deploy.sh new file mode 100755 index 0000000..fd6ff50 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/deploy.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +if [ -z $1 ]; then + echo "You need to pass a step number" + exit 1; +fi + +if (($1 < 1 || $1 > 8)); then + echo "Only steps 1 through 8 exist" + exit 1; +fi + +sudo cp -r step_$1/* /etc/nginx +sudo nginx -s reload + +if [ $? = 0 ]; then + echo "NGINX successfully reloaded" +fi + diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_1/conf.d/api_gateway.conf b/nginx/api-gateway/nginx_api_gateway_config/step_1/conf.d/api_gateway.conf new file mode 100644 index 0000000..98f19b2 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_1/conf.d/api_gateway.conf @@ -0,0 +1,11 @@ +server { + listen 8080; + + include conf.d/my_apis/*.conf; + + location / { + return 400; + } + + default_type application/json; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_2/conf.d/my_apis/api_json_errors.conf b/nginx/api-gateway/nginx_api_gateway_config/step_2/conf.d/my_apis/api_json_errors.conf new file mode 100644 index 0000000..3fa9323 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_2/conf.d/my_apis/api_json_errors.conf @@ -0,0 +1,20 @@ +error_page 400 = @400; +location @400 { + return 400 '{"status":400,"message":"Bad request"}\n'; +} +error_page 401 = @401; +location @401 { + return 401 '{"status":401,"message":"Unauthorized"}\n'; +} +error_page 403 = @403; +location @403 { + return 403 '{"status":403,"message":"Forbidden"}\n'; +} +error_page 405 = @405; +location @405 { + return 405 '{"status":405,"message":"Method not allowed"}\n'; +} +error_page 429 = @429; +location @429 { + return 429 '{"status":429,"message":"API rate limit exceeded"}\n'; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/api_backends.conf b/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/api_backends.conf new file mode 100644 index 0000000..3411ed6 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/api_backends.conf @@ -0,0 +1,7 @@ +upstream f1-admin { + server ; +} + +upstream f1-data { + server :8000; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/my_apis/f1.conf b/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/my_apis/f1.conf new file mode 100644 index 0000000..c35f720 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_3/conf.d/my_apis/f1.conf @@ -0,0 +1,13 @@ +location /api/f1/ { + location = /api/f1/seasons { + proxy_pass http://f1-admin; + } + + location ~ /api/f1/[12][0-9]+ { + proxy_pass http://f1-data; + } + + location /api/f1/drivers { + proxy_pass http://f1-data; + } +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/api_gateway.conf b/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/api_gateway.conf new file mode 100644 index 0000000..ffefef6 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/api_gateway.conf @@ -0,0 +1,13 @@ +limit_req_zone $remote_addr zone=perip:1m rate=1r/s; + +server { + listen 8080; + + include conf.d/my_apis/*.conf; + + location / { + return 400; + } + + default_type application/json; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/my_apis/f1.conf b/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/my_apis/f1.conf new file mode 100644 index 0000000..7fe7693 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_4/conf.d/my_apis/f1.conf @@ -0,0 +1,16 @@ +location /api/f1/ { + limit_req zone=perip nodelay; + limit_req_status 429; + + location = /api/f1/seasons { + proxy_pass http://f1-admin; + } + + location ~ /api/f1/[12][0-9]+ { + proxy_pass http://f1-data; + } + + location /api/f1/drivers { + proxy_pass http://f1-data; + } +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/api_key.conf b/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/api_key.conf new file mode 100644 index 0000000..17ad82f --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/api_key.conf @@ -0,0 +1,5 @@ +map $http_apikey $api_client_name { + "7B5zIqmRGXmrJTFmKa99vcit" "client_one"; + "QzVV6y1EmQFbbxOfRCwyJs35" "client_two"; + default ""; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/my_apis/f1.conf b/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/my_apis/f1.conf new file mode 100644 index 0000000..179ce6b --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_5/conf.d/my_apis/f1.conf @@ -0,0 +1,32 @@ +location /api/f1/ { + auth_request /_validate_apikey; + + limit_req zone=perip nodelay; + limit_req_status 429; + + location = /api/f1/seasons { + proxy_pass http://f1-admin; + } + + location ~ /api/f1/[12][0-9]+ { + proxy_pass http://f1-data; + } + + location /api/f1/drivers { + proxy_pass http://f1-data; + } +} + +location = /_validate_apikey { + internal; + + if ($http_apikey = "") { + return 401; + } + + if ($api_client_name = "") { + return 403; + } + + return 204; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/api_gateway.conf b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/api_gateway.conf new file mode 100644 index 0000000..3dffe59 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/api_gateway.conf @@ -0,0 +1,18 @@ +limit_req_zone $remote_addr zone=perip:1m rate=2r/s; + +server { + listen 8080; + + include conf.d/my_apis/*.conf; + + location / { + return 400; + } + + location /_get_request_body { + internal; + return 204; + } + + default_type application/json; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.conf b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.conf new file mode 100644 index 0000000..50a5b19 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.conf @@ -0,0 +1,8 @@ +js_import conf.d/json_validation.js; +js_set $json_validated json_validation.parseRequestBody; + +server { + listen 127.0.0.1:10415; + return 415; + include conf.d/my_apis/api_json_errors.conf; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.js b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.js new file mode 100644 index 0000000..f180d86 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/json_validation.js @@ -0,0 +1,13 @@ +function parseRequestBody(r) { + try { + if (r.variables.request_body) { + JSON.parse(r.variables.request_body); + } + return r.variables.upstream; + } catch (e) { + r.error('JSON.parse exception'); + return '127.0.0.1:10415'; + } +} + +export default { parseRequestBody }; diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/my_apis/f1.conf b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/my_apis/f1.conf new file mode 100644 index 0000000..d8a9754 --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_6/conf.d/my_apis/f1.conf @@ -0,0 +1,34 @@ +location /api/f1/ { + auth_request /_validate_apikey; + + limit_req zone=perip nodelay; + limit_req_status 429; + + location = /api/f1/seasons { + set $upstream f1-admin; + mirror /_get_request_body; + proxy_pass http://$json_validated$request_uri; + } + + location ~ /api/f1/[12][0-9]+ { + proxy_pass http://f1-data; + } + + location /api/f1/drivers { + proxy_pass http://f1-data; + } +} + +location = /_validate_apikey { + internal; + + if ($http_apikey = "") { + return 401; + } + + if ($api_client_name = "") { + return 403; + } + + return 204; +} diff --git a/nginx/api-gateway/nginx_api_gateway_config/step_6/nginx.conf b/nginx/api-gateway/nginx_api_gateway_config/step_6/nginx.conf new file mode 100644 index 0000000..cf1b9bd --- /dev/null +++ b/nginx/api-gateway/nginx_api_gateway_config/step_6/nginx.conf @@ -0,0 +1,28 @@ +load_module modules/ngx_http_js_module.so; + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + +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; + + keepalive_timeout 65; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/api-gateway/setup.sh b/nginx/api-gateway/setup.sh new file mode 100755 index 0000000..dc43757 --- /dev/null +++ b/nginx/api-gateway/setup.sh @@ -0,0 +1,3 @@ +cd terraform \ +&& terraform init \ +&& terraform apply -auto-approve diff --git a/nginx/api-gateway/terraform/.terraform.lock.hcl b/nginx/api-gateway/terraform/.terraform.lock.hcl new file mode 100644 index 0000000..4c81218 --- /dev/null +++ b/nginx/api-gateway/terraform/.terraform.lock.hcl @@ -0,0 +1,45 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.87.0" + constraints = ">= 4.0.0" + hashes = [ + "h1:IYq3by7O/eJuXzJwOF920z2nZEkw08PkDFdw2xkyhrs=", + "zh:017f237466875c919330b9e214fb33af14fffbff830d3755e8976d8fa3c963c2", + "zh:0776d1e60aa93c85ecbb01144aed2789c8e180bb0f1c811a0aba17ca7247b26c", + "zh:0dfa5c6cfb3724494fdc73f7d042515e88a20da8968959f48b3ec0b937bd8c8f", + "zh:1707a5ead36a7980cb3f83e8b69a67a14ae725bfc990ddfcc209b59400b57b04", + "zh:1c71f54fdd6adcbe547d6577dbb843d72a30fef0ab882d0afbeb8a7b348bc442", + "zh:3563c850a29790957ec3f4d3ba203bfa2e084ac7319035b3f43b91f818a2c9b4", + "zh:520bf6cef53785a92226651d5bebacbbf9314bdbc3211d0bf0903bce4e45149d", + "zh:56f9778575830f6e5c23462c2eccbf2c9afaddb00a69275fcfb33cd1a6d17f4d", + "zh:73e381cb0b1e76d471d7b0952f3d2a80350b507d15bda9b7041ea69077e3b5b5", + "zh:7da74b48f8fa088be758a92407980400cb4b039a8d9ba3c108907e4055e9ad6f", + "zh:8dacfa9623ba2e0197fe7db6faaaa0820a3b91fe00ba9e5d8a646340522bc8dd", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:9c2ebd21d697e1a611fe201788dc9e1678949a088afc85d4589563bca484d835", + "zh:ac5d0bbf36f9a6cedbfb63993f6baf0aabdaf21c8d7fc3b1e69ba8cbf344b5f3", + "zh:c2329644179f78a0458b6cf2dd5eaadca4c610fc3577a1b50620544d92df13e8", + ] +} + +provider "registry.terraform.io/hashicorp/null" { + version = "3.2.3" + constraints = ">= 3.0.0" + hashes = [ + "h1:I0Um8UkrMUb81Fxq/dxbr3HLP2cecTH2WMJiwKSrwQY=", + "zh:22d062e5278d872fe7aed834f5577ba0a5afe34a3bdac2b81f828d8d3e6706d2", + "zh:23dead00493ad863729495dc212fd6c29b8293e707b055ce5ba21ee453ce552d", + "zh:28299accf21763ca1ca144d8f660688d7c2ad0b105b7202554ca60b02a3856d3", + "zh:55c9e8a9ac25a7652df8c51a8a9a422bd67d784061b1de2dc9fe6c3cb4e77f2f", + "zh:756586535d11698a216291c06b9ed8a5cc6a4ec43eee1ee09ecd5c6a9e297ac1", + "zh:78d5eefdd9e494defcb3c68d282b8f96630502cac21d1ea161f53cfe9bb483b3", + "zh:9d5eea62fdb587eeb96a8c4d782459f4e6b73baeece4d04b4a40e44faaee9301", + "zh:a6355f596a3fb8fc85c2fb054ab14e722991533f87f928e7169a486462c74670", + "zh:b5a65a789cff4ada58a5baffc76cb9767dc26ec6b45c00d2ec8b1b027f6db4ed", + "zh:db5ab669cf11d0e9f81dc380a6fdfcac437aea3d69109c7aef1a5426639d2d65", + "zh:de655d251c470197bcbb5ac45d289595295acb8f829f6c781d4a75c8c8b7c7dd", + "zh:f5c68199f2e6076bce92a12230434782bf768103a427e9bb9abee99b116af7b5", + ] +} diff --git a/nginx/api-gateway/terraform/api_db_vm.tf b/nginx/api-gateway/terraform/api_db_vm.tf new file mode 100644 index 0000000..a8d19d6 --- /dev/null +++ b/nginx/api-gateway/terraform/api_db_vm.tf @@ -0,0 +1,96 @@ +# Mock NGINX Plus Sports API containing mock Baseball, Golf, and Tennis data +# Also launches an Ergast F1 API container +resource "aws_instance" "backend_api" { + ami = data.aws_ami.ubuntu.id + instance_type = var.backend_api_machine_type + key_name = var.key_data["name"] + vpc_security_group_ids = [ + aws_security_group.backend_api.id, + ] + subnet_id = aws_subnet.main.id + user_data = < /dev/null +apt install -y docker-ce docker-ce-cli docker-compose-plugin containerd.io +git clone https://github.com/lcrilly/ergast-f1-api /home/ubuntu/ergast-f1-api +# sed -i -e 's/FROM mysql/FROM mysql:5.6/g' /home/ubuntu/ergast-f1-api/ergastdb/Dockerfile +sed -i 's/$format = "xml";/$format = "json";/' /home/ubuntu/ergast-f1-api/webroot/php/api/index.php +docker compose -f /home/ubuntu/ergast-f1-api/docker-compose.yaml up --build -d --remove-orphans + +apt update +apt install -y ca-certificates curl gnupg2 lsb-release ubuntu-keyring +curl https://nginx.org/keys/nginx_signing.key | gpg --dearmor | tee /usr/share/keyrings/nginx-archive-keyring.gpg >/dev/null +gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list +printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | tee /etc/apt/preferences.d/99nginx +apt update +apt install -y nginx nginx-module-njs +sed -i '1iload_module modules/ngx_http_js_module.so;' /etc/nginx/nginx.conf +cat > /etc/nginx/conf.d/default.conf <<'EOL' +${templatefile("backend_api/default.conf", { + fqdn = 0, +})} +EOL +cat > /etc/nginx/conf.d/echo.js </dev/null +gpg --dry-run --quiet --no-keyring --import --import-options import-show /usr/share/keyrings/nginx-archive-keyring.gpg +echo "deb [signed-by=/usr/share/keyrings/nginx-archive-keyring.gpg] https://nginx.org/packages/mainline/ubuntu `lsb_release -cs` nginx" | tee /etc/apt/sources.list.d/nginx.list +printf "Package: *\nPin: origin nginx.org\nPin: release o=nginx\nPin-Priority: 900\n" | tee /etc/apt/preferences.d/99nginx +apt update +apt install -y nginx nginx-module-njs jq +service nginx start +EOF + tags = { + Name = "nginx_api_gateway", + Owner = var.owner, + user = "ubuntu", + } +} + +# Enable SSL on NGINX API gateway +resource "null_resource" "tweak_nginx_api_gateway_config" { + count = var.nginx_api_gateway_certbot ? 1 : 0 + provisioner "remote-exec" { + inline = [ + "set -ex", + "while [ ! -f /etc/nginx/nginx.conf ]; do sleep 5; done", + "sudo snap install core", + "sudo snap refresh core", + "sudo snap install --classic certbot", + "sudo ln -s /certbot /usr/bin/certbot", + "sudo certbot --nginx --redirect --agree-tos --register-unsafely-without-email -d ${aws_route53_record.nginx_api_gateway.fqdn}", + ] + } + connection { + type = "ssh" + user = "ubuntu" + private_key = file(var.key_data["location"]) + host = aws_route53_record.nginx_api_gateway.fqdn + } + depends_on = [ + aws_instance.nginx_api_gateway, + ] +} + +# Upload NGINX configuration files to NGINX API gateway +resource "null_resource" "upload_nginx_api_gateway_config_files" { + count = var.upload_nginx_api_gateway_config_files ? 1 : 0 + provisioner "remote-exec" { + inline = [ + "set -ex", + "while [ ! -f /etc/nginx/nginx.conf ]; do sleep 5; done", + ] + } + provisioner "file" { + source = "../nginx_api_gateway_config" + destination = "/home/ubuntu" + } + connection { + type = "ssh" + user = "ubuntu" + private_key = file(var.key_data["location"]) + host = aws_route53_record.nginx_api_gateway.fqdn + } + depends_on = [ + aws_instance.nginx_api_gateway, + ] +} diff --git a/nginx/api-gateway/terraform/backend_api/default.conf b/nginx/api-gateway/terraform/backend_api/default.conf new file mode 100644 index 0000000..16122ed --- /dev/null +++ b/nginx/api-gateway/terraform/backend_api/default.conf @@ -0,0 +1,88 @@ +js_import conf.d/echo.js; + +map $request_method $return_code { + "POST" 201; + "DELETE" 204; + default 200; +} + +%{ if fqdn != 0} +server { + listen 80; + return 301 https://$host$request_uri; +} +%{ endif } + +server { +%{ if fqdn != 0 } + listen 443 ssl; + ssl_certificate /etc/letsencrypt/live/${fqdn}/fullchain.pem; # managed by Certbot + ssl_certificate_key /etc/letsencrypt/live/${fqdn}/privkey.pem; # managed by Certbot + include /etc/letsencrypt/options-ssl-nginx.conf; # managed by Certbot + ssl_dhparam /etc/letsencrypt/ssl-dhparams.pem; # managed by Certbot +%{ else } + listen 80; +%{ endif } + default_type application/json; + mirror /_null; + + location / { + js_content echo.echo; + } + + location /api/f1 { + proxy_pass http://localhost:8000; + } + + location /api/baseball/players { + js_content echo.baseballPlayers; + } + + location /api/baseball/stadiums { + js_content echo.baseballStadiums; + } + + location /api/baseball/leagues { + js_content echo.baseballLeagues; + } + + location /api/baseball/seasons { + return 500; + } + + location /api/tennis/players { + js_content echo.tennisPlayers; + } + + location /api/tennis/courts { + js_content echo.tennisCourts; + } + + location /api/tennis/tournaments { + js_content echo.tennisTournaments; + } + + location /api/golf/players { + js_content echo.golfPlayers; + } + + location /api/golf/courses { + js_content echo.golfCourses; + } + + location /api/golf/tournaments { + js_content echo.golfTournaments; + } + + location /_null { + internal; + return 204; + } + + # redirect server error pages to the static page /50x.html + # + error_page 500 502 503 504 /50x.html; + location = /50x.html { + root /usr/share/nginx/html; + } +} diff --git a/nginx/api-gateway/terraform/backend_api/echo.js b/nginx/api-gateway/terraform/backend_api/echo.js new file mode 100644 index 0000000..28c7465 --- /dev/null +++ b/nginx/api-gateway/terraform/backend_api/echo.js @@ -0,0 +1,202 @@ +function echo(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + + var req = { + "client": r.variables.remote_addr, + "port": Number(r.variables.server_port), + "host": r.variables.host, + "method": r.variables.request_method, + "uri": r.uri, + "httpVersion": r.httpVersion, + "headers": headers, + "body": r.variables.request_body + } + var res = { + "status": Number(r.variables.return_code), + "timestamp": r.variables.time_iso8601 + } + + r.return(Number(r.variables.return_code), JSON.stringify({ "request": req, "response": res }) + '\n'); +} + +function baseballLeagues(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var leagues = [ + { + "id": 0, + "leagueName": "Major League Baseball" + }, + { + "id": 1, + "leagueName": "Nippon Professional Baseball" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "leagues": leagues }) + '\n'); +} + +function baseballPlayers(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var players = [ + { + "id": 0, + "firstName": "Babe", + "lastName": "Ruth" + }, + { + "id": 1, + "firstName": "Roger", + "lastName": "Clemens" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "players": players }) + '\n'); +} + +function baseballStadiums(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var stadiums = [ + { + "id": 0, + "stadiumName": "AT&T Park" + }, + { + "id": 1, + "stadiumName": "Wrigley Field" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "stadiums": stadiums }) + '\n'); +} + +function golfCourses(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var courses = [ + { + "id": 0, + "courseName": "Royal County Down" + }, + { + "id": 1, + "courseName": "Augusta National G.C." + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "courses": courses }) + '\n'); +} + +function golfPlayers(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var players = [ + { + "id": 0, + "firstName": "Rory", + "lastName": "McIlroy" + }, + { + "id": 1, + "firstName": "Jon", + "lastName": "Rahm" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "players": players }) + '\n'); +} + +function golfTournaments(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var tournaments = [ + { + "id": 0, + "tournamentName": "Masters Tournament" + }, + { + "id": 1, + "tournamentName": "PGA Championship" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "tournaments": tournaments }) + '\n'); +} + +function tennisCourts(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var courts = [ + { + "id": 0, + "courtName": "Centre Court" + }, + { + "id": 1, + "courtName": "Court Philippe Chatrier" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "courts": courts }) + '\n'); +} + +function tennisPlayers(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var players = [ + { + "id": 0, + "firstName": "Rafael", + "lastName": "Nadal" + }, + { + "id": 1, + "firstName": "Roger", + "lastName": "Federer" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "players": players }) + '\n'); +} + +function tennisTournaments(r) { + var headers = {}; + for (var h in r.headersIn) { + headers[h] = r.headersIn[h]; + } + var tournaments = [ + { + "id": 0, + "tournamentName": "Wimbledon" + }, + { + "id": 1, + "tournamentName": "Roland Garros" + } + ] + + r.return(Number(r.variables.return_code), JSON.stringify({ "tournaments": tournaments }) + '\n'); +} + +export default { echo, baseballLeagues, baseballPlayers, baseballStadiums, golfCourses, golfPlayers, golfTournaments, tennisCourts, tennisPlayers, tennisTournaments }; diff --git a/nginx/api-gateway/terraform/backend_api/nginx.conf b/nginx/api-gateway/terraform/backend_api/nginx.conf new file mode 100644 index 0000000..00886a2 --- /dev/null +++ b/nginx/api-gateway/terraform/backend_api/nginx.conf @@ -0,0 +1,30 @@ +load_module modules/ngx_http_js_module.so; + +user nginx; +worker_processes auto; + +error_log /var/log/nginx/error.log notice; +pid /var/run/nginx.pid; + + +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; + + keepalive_timeout 65; + + include /etc/nginx/conf.d/*.conf; +} diff --git a/nginx/api-gateway/terraform/infrastructure.tf b/nginx/api-gateway/terraform/infrastructure.tf new file mode 100644 index 0000000..485cbbd --- /dev/null +++ b/nginx/api-gateway/terraform/infrastructure.tf @@ -0,0 +1,200 @@ +# Create AWS main VPC +resource "aws_vpc" "main" { + cidr_block = "10.0.0.0/16" + enable_dns_hostnames = true + tags = { + Name = "nginx_api_gateway_vpc" + Owner = var.owner, + } +} + +# Create AWS main subnet +resource "aws_subnet" "main" { + vpc_id = aws_vpc.main.id + cidr_block = "10.0.0.0/24" + map_public_ip_on_launch = true + tags = { + Name = "nginx_api_gateway_subnet" + Owner = var.owner, + } +} + +# Create AWS security group for NGINX API gateway instances +resource "aws_security_group" "nginx_api_gateway" { + name = "nginx-api-gateway-sg" + description = "Security group for NGINX API gateway" + vpc_id = aws_vpc.main.id + tags = { + Name = "nginx_api_gateway_sg" + Owner = var.owner, + } +} + +resource "aws_security_group_rule" "nginx_api_gateway_ingress_ssh" { + type = "ingress" + from_port = 22 + to_port = 22 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_ingress_http" { + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_ingress_https" { + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_ingress_api" { + type = "ingress" + from_port = 8080 + to_port = 8080 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_egress_http" { + type = "egress" + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_egress_https" { + type = "egress" + from_port = 443 + to_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "nginx_api_gateway_egress_f1" { + type = "egress" + from_port = 8000 + to_port = 8000 + protocol = "tcp" + security_group_id = aws_security_group.nginx_api_gateway.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group" "backend_api" { + name = "backend-api-sg" + description = "Security group for backend API" + vpc_id = aws_vpc.main.id + tags = { + Owner = var.owner, + Name = "backend_api_sg" + } +} + +resource "aws_security_group_rule" "backend_api_ingress_ssh" { + type = "ingress" + from_port = 22 + to_port = 22 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "backend_api_ingress_http" { + type = "ingress" + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "backend_api_ingress_https" { + type = "ingress" + from_port = 443 + to_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "backend_api_ingress_ergast" { + type = "ingress" + from_port = 8000 + to_port = 8000 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "backend_api_egress_http" { + type = "egress" + from_port = 80 + to_port = 80 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +resource "aws_security_group_rule" "backend_api_egress_https" { + type = "egress" + from_port = 443 + to_port = 443 + protocol = "tcp" + security_group_id = aws_security_group.backend_api.id + cidr_blocks = [ + "0.0.0.0/0", + ] +} + +# Create AWS internet gateway +resource "aws_internet_gateway" "main" { + vpc_id = aws_vpc.main.id + tags = { + Owner = var.owner, + Name = "nginx_api_gateway_gtwy" + } +} + +# Configure AWS network route +resource "aws_route" "main" { + route_table_id = aws_vpc.main.main_route_table_id + destination_cidr_block = "0.0.0.0/0" + gateway_id = aws_internet_gateway.main.id +} diff --git a/nginx/api-gateway/terraform/providers.tf b/nginx/api-gateway/terraform/providers.tf new file mode 100644 index 0000000..908b084 --- /dev/null +++ b/nginx/api-gateway/terraform/providers.tf @@ -0,0 +1,5 @@ +provider "aws" { + region = var.region +} + +provider "null" {} diff --git a/nginx/api-gateway/terraform/r53.tf b/nginx/api-gateway/terraform/r53.tf new file mode 100644 index 0000000..b40726c --- /dev/null +++ b/nginx/api-gateway/terraform/r53.tf @@ -0,0 +1,41 @@ +# Create Elastic IP for the NGINX API gateway +resource "aws_eip" "nginx_api_gateway" { + instance = aws_instance.nginx_api_gateway.id + domain = "vpc" + tags = { + Name = "nginx_api_gateway", + Owner = var.owner, + } +} + +# Create Elastic IP for the backend API +resource "aws_eip" "backend_api" { + instance = aws_instance.backend_api.id + domain = "vpc" + tags = { + Name = "backend_api", + Owner = var.owner, + } +} + +# Create an A record for the NGINX API gateway EIP +resource "aws_route53_record" "nginx_api_gateway" { + zone_id = var.r53_zone + name = var.nginx_api_gateway_fqdn + type = "A" + ttl = "300" + records = [ + aws_eip.nginx_api_gateway.public_ip + ] +} + +# Create an A record for the backend API EIP +resource "aws_route53_record" "backend_api" { + zone_id = var.r53_zone + name = var.backend_api_fqdn + type = "A" + ttl = "300" + records = [ + aws_eip.backend_api.public_ip + ] +} diff --git a/nginx/api-gateway/terraform/variables.tf b/nginx/api-gateway/terraform/variables.tf new file mode 100644 index 0000000..c652471 --- /dev/null +++ b/nginx/api-gateway/terraform/variables.tf @@ -0,0 +1,87 @@ +variable "region" { + description = "Your target AWS region" + default = "us-west-1" + type = string +} + +variable "owner" { + description = "Owner of resources" + default = "NGINX APIg demo runner" + type = string +} + +variable "key_data" { + description = "The key used to ssh into your AWS instance" + # default = { + # name = "johndoe" + # location = "~/.ssh/johndoe.pem" + # } + type = map(string) +} + +variable "r53_zone" { + description = "R53 hosted zone ID" + # default = "LAK92116148SDMXK85713" + type = string +} + +variable "nginx_api_gateway_machine_type" { + description = "The AWS machine type for your NGINX API gateway" + default = "t2.medium" + type = string +} + +variable "nginx_api_gateway_fqdn" { + description = "NGINX API gateway FQDN" + # default = "gateway.nginx.com" + type = string +} + +variable "nginx_api_gateway_certbot" { + description = "Use certbot to automate cert generation for the NGINX API gateway" + default = false + type = bool +} + +variable "upload_nginx_api_gateway_config_files" { + description = "Upload NGINX API gateway sample files" + default = true + type = bool +} + +variable "backend_api_machine_type" { + description = "The AWS machine type for your API workload backend" + default = "t2.medium" + type = string +} + +variable "backend_api_fqdn" { + description = "Backend API gateway FQDN" + # default = "backend.nginx.com" + type = string +} + +variable "backend_api_certbot" { + description = "Use certbot to automate cert generation for the backend API" + default = false + type = bool +} + +data "aws_ami" "ubuntu" { + most_recent = true + + filter { + name = "name" + values = ["ubuntu/images/hvm-ssd-gp3/ubuntu-noble-24.04-amd64-server*"] + } + filter { + name = "virtualization-type" + values = ["hvm"] + } + filter { + name = "architecture" + values = ["x86_64"] + } + + owners = ["099720109477"] +} diff --git a/nginx/api-gateway/terraform/versions.tf b/nginx/api-gateway/terraform/versions.tf new file mode 100644 index 0000000..4828ac7 --- /dev/null +++ b/nginx/api-gateway/terraform/versions.tf @@ -0,0 +1,13 @@ +terraform { + required_version = ">= 1.0" + required_providers { + aws = { + source = "hashicorp/aws" + version = ">= 4.0" + } + null = { + source = "hashicorp/null" + version = ">= 3.0" + } + } +}