From b42d21d558a5a116ef8686b48dd50f7a436c90cb Mon Sep 17 00:00:00 2001 From: jdettmannnava <145699825+jdettmannnava@users.noreply.github.com> Date: Tue, 17 Sep 2024 10:04:30 -0400 Subject: [PATCH] DPC-3767 Add infrastructure for DPC WAF-sync lambda (#127) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## 🎫 Ticket https://jira.cms.gov/browse/DPC-3767 ## 🛠 Changes - Added wav-sync as service - Added plan and apply wav-sync github actions ## ℹ️ Context DPC needs to synchronize ip addresses in a database with an AWS WAF IP Set. We [decided](https://confluence.cms.gov/x/1ZUZQQ) to use a cron-triggered lambda to manage this. This ticket creates the infrastructure for automated deployment of the lambda. The aws_waf_access inline policy seems to be the minimum necessary for the lambda to run. It may require updating (the lambda has not been written). The name of the IP Set was intuited by browsing the WAF IP Sets in AWS Console. The terraform scripts and github actions are modified versions of the opt-out-import service. ## 🧪 Validation - Comparison of the plan output to opt-out-import plan output show that the permissions necessary are present. - Plan applied successfully --- .github/workflows/api-waf-sync-apply.yml | 49 +++++++++++++++ .github/workflows/api-waf-sync-plan.yml | 47 +++++++++++++++ .../services/api-waf-sync/.terraform.lock.hcl | 25 ++++++++ terraform/services/api-waf-sync/README.md | 17 ++++++ terraform/services/api-waf-sync/main.tf | 60 +++++++++++++++++++ terraform/services/api-waf-sync/outputs.tf | 7 +++ terraform/services/api-waf-sync/terraform.tf | 18 ++++++ terraform/services/api-waf-sync/variables.tf | 8 +++ 8 files changed, 231 insertions(+) create mode 100644 .github/workflows/api-waf-sync-apply.yml create mode 100644 .github/workflows/api-waf-sync-plan.yml create mode 100644 terraform/services/api-waf-sync/.terraform.lock.hcl create mode 100644 terraform/services/api-waf-sync/README.md create mode 100644 terraform/services/api-waf-sync/main.tf create mode 100644 terraform/services/api-waf-sync/outputs.tf create mode 100644 terraform/services/api-waf-sync/terraform.tf create mode 100644 terraform/services/api-waf-sync/variables.tf diff --git a/.github/workflows/api-waf-sync-apply.yml b/.github/workflows/api-waf-sync-apply.yml new file mode 100644 index 00000000..045e0fa7 --- /dev/null +++ b/.github/workflows/api-waf-sync-apply.yml @@ -0,0 +1,49 @@ +name: api-waf-sync apply terraform + +on: + push: + branches: + - main + paths: + - .github/workflows/api-waf-sync-apply.yml + - terraform/modules/bucket/** + - terraform/modules/key/** + - terraform/modules/function/** + - terraform/modules/queue/** + - terraform/modules/subnets/** + - terraform/modules/vpc/** + - terraform/services/api-waf-sync/** + workflow_dispatch: # Allow manual trigger + +jobs: + terraform-apply: + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./terraform/services/api-waf-sync + strategy: + fail-fast: false + matrix: + env: [dev] + env: + TF_VAR_env: ${{ matrix.env }} + steps: + - uses: actions/checkout@v4 + - uses: ./actions/setup-tfenv-terraform + - uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.BCDA_ACCOUNT }}:role/delegatedadmin/developer/dpc-${{ matrix.env }}-github-actions + aws-region: ${{ vars.AWS_REGION }} + - run: terraform init -reconfigure -backend-config=../../backends/dpc-$TF_VAR_env.s3.tfbackend + - run: terraform apply -auto-approve + - uses: slackapi/slack-github-action@v1.26.0 + if: ${{ failure() }} + with: + channel-id: 'C04UG13JF9B' + slack-message: "Terraform apply failure: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}" + env: + SLACK_BOT_TOKEN: ${{ secrets.SLACK_BOT_TOKEN }} + diff --git a/.github/workflows/api-waf-sync-plan.yml b/.github/workflows/api-waf-sync-plan.yml new file mode 100644 index 00000000..7e8c8df6 --- /dev/null +++ b/.github/workflows/api-waf-sync-plan.yml @@ -0,0 +1,47 @@ +name: api-waf-sync plan terraform + +on: + pull_request: + paths: + - .github/workflows/api-waf-sync-plan.yml + - terraform/modules/bucket/** + - terraform/modules/key/** + - terraform/modules/function/** + - terraform/modules/queue/** + - terraform/modules/subnets/** + - terraform/modules/vpc/** + - terraform/services/api-waf-sync/** + workflow_dispatch: # Allow manual trigger + +jobs: + check-terraform-fmt: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: ./actions/setup-tfenv-terraform + - run: terraform fmt -check -diff -recursive terraform/services/api-waf-sync + + terraform-plan: + needs: check-terraform-fmt + permissions: + contents: read + id-token: write + runs-on: ubuntu-latest + defaults: + run: + working-directory: ./terraform/services/api-waf-sync + strategy: + fail-fast: false + matrix: + env: [dev] + env: + TF_VAR_env: ${{ matrix.env }} + steps: + - uses: actions/checkout@v4 + - uses: ./actions/setup-tfenv-terraform + - uses: aws-actions/configure-aws-credentials@v4 + with: + role-to-assume: arn:aws:iam::${{ secrets.BCDA_ACCOUNT }}:role/delegatedadmin/developer/dpc-${{ matrix.env }}-github-actions + aws-region: ${{ vars.AWS_REGION }} + - run: terraform init -reconfigure -backend-config=../../backends/dpc-$TF_VAR_env.s3.tfbackend + - run: terraform plan diff --git a/terraform/services/api-waf-sync/.terraform.lock.hcl b/terraform/services/api-waf-sync/.terraform.lock.hcl new file mode 100644 index 00000000..5f3051f2 --- /dev/null +++ b/terraform/services/api-waf-sync/.terraform.lock.hcl @@ -0,0 +1,25 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/aws" { + version = "5.8.0" + constraints = "~> 5.8.0" + hashes = [ + "h1:vnjWfeuf4AflWsRq3ivVig8dR8PAg8BHTVyAtOzJ1yQ=", + "zh:0974311d5e1becfdcbdae43d022d52689fdad32a4145659e56ac534bcb8cba02", + "zh:100dc64a90fc0d36cf6e2882b4358fde17705edd8ab3c5f2c06d219c36b21565", + "zh:467a86de8a7d77cde5c3386f9e82d7f1bf5972d1b3d177e797d1d9d2e87fd357", + "zh:4ad1f8ef5c5522f81d271b93594a43a7666b3409ca201a1911cd950e489ef12b", + "zh:540a50ab7061c6df2057ec9580890a9e86a687233120af738985fa84dde2a20a", + "zh:6e7b73b770e92891da94751c3e0cff1e1b852f5121da8c4a689056833eeb7d94", + "zh:879d42721e86331b05ff77bd219ca9a062485cdb2fa803d2dcf63084f25d484c", + "zh:980563e615fbba127c02df6dc8872ce60f7137df45fdb8cd801cdcbae6cf192a", + "zh:9b12af85486a96aedd8d7984b0ff811a4b42e3d88dad1a3fb4c0b580d04fa425", + "zh:a6ad25c4d3edde466ea68731097aedad4b68278af0742fc1ab71d2c30491f92e", + "zh:af8df9e06f576c11ce67ac2b675d0d8db4aac618fec95d27c10aa59436feebbf", + "zh:b625ca7c4b99c6b3af34041b9773ccd9d80b0dde264c40b5d163a6abd73793af", + "zh:c9e0ca6aa48ebaa0892ac438392c49052a86605f490950d5317855f35ab7d74a", + "zh:dc500a03d3ed6b1fed3f118a55a7fb93bf172965ae6b2f25cc7f4a152e44edd7", + "zh:e0438bf67d93a29f0d56f9a4544297155ca85c0f10626778d4c3aa68c7e93581", + ] +} diff --git a/terraform/services/api-waf-sync/README.md b/terraform/services/api-waf-sync/README.md new file mode 100644 index 00000000..6137935a --- /dev/null +++ b/terraform/services/api-waf-sync/README.md @@ -0,0 +1,17 @@ +# Terraform for api-waf-sync function and associated infra + +This service sets up the infrastructure for the api-waf-sync lambda function in dev for dpc. + +## Manual deploy + +Pass in a backend file when running terraform init. See variables.tf for variables to include. Example: + +```bash +export TF_VAR_env=dev +terraform init -backend-config=../../backends/dpc-dev.s3.tfbackend +terraform apply +``` + +## Automated deploy + +This terraform is automatically applied on merge to main by the waf-sync-apply.yml workflow. diff --git a/terraform/services/api-waf-sync/main.tf b/terraform/services/api-waf-sync/main.tf new file mode 100644 index 00000000..d9720dc2 --- /dev/null +++ b/terraform/services/api-waf-sync/main.tf @@ -0,0 +1,60 @@ +locals { + full_name = "dpc-${var.env}-api-waf-sync" + db_sg_name = "dpc-${var.env}-db" +} + +module "api_waf_sync_function" { + source = "../../modules/function" + + app = "dpc" + env = var.env + + name = local.full_name + description = "Synchronizes the IP whitelist in DPC with the WAF IP Set" + + handler = "bootstrap" + runtime = "provided.al2" + + function_role_inline_policies = { + waf-access = data.aws_iam_policy_document.aws_waf_access.json + } + + schedule_expression = "cron(0/10 * * * ? *)" + + environment_variables = { + ENV = var.env + APP_NAME = "dpc-${var.env}-api-waf-sync" + WAF_IP_SET_NAME = "DPC_${upper(var.env)}_Implementer_IP_Set" + } +} + +# Add a rule to the database security group to allow access from the function + +data "aws_security_group" "db" { + name = local.db_sg_name +} + +resource "aws_security_group_rule" "function_access" { + type = "ingress" + from_port = 5432 + to_port = 5432 + protocol = "tcp" + description = "api-waf-sync function access" + + security_group_id = data.aws_security_group.db.id + source_security_group_id = module.api_waf_sync_function.security_group_id +} + +# Because we inline policies, we cannot just link to aws:policy/AWSWAFFullAccess +data "aws_iam_policy_document" "aws_waf_access" { + statement { + effect = "Allow" + resources = ["*"] + + actions = [ + "wafv2:GetIpSet", + "wafv2:UpdateIpSet", + ] + } +} + diff --git a/terraform/services/api-waf-sync/outputs.tf b/terraform/services/api-waf-sync/outputs.tf new file mode 100644 index 00000000..d36460e8 --- /dev/null +++ b/terraform/services/api-waf-sync/outputs.tf @@ -0,0 +1,7 @@ +output "function_role_arn" { + value = module.api_waf_sync_function.role_arn +} + +output "zip_bucket" { + value = module.api_waf_sync_function.zip_bucket +} diff --git a/terraform/services/api-waf-sync/terraform.tf b/terraform/services/api-waf-sync/terraform.tf new file mode 100644 index 00000000..725e78ce --- /dev/null +++ b/terraform/services/api-waf-sync/terraform.tf @@ -0,0 +1,18 @@ +provider "aws" { + default_tags { + tags = { + application = "dpc" + business = "oeda" + code = "https://github.com/CMSgov/ab2d-bcda-dpc-platform/tree/main/terraform/services/api-waf-sync" + component = "api-waf-sync" + environment = var.env + terraform = true + } + } +} + +terraform { + backend "s3" { + key = "api-waf-sync/terraform.tfstate" + } +} diff --git a/terraform/services/api-waf-sync/variables.tf b/terraform/services/api-waf-sync/variables.tf new file mode 100644 index 00000000..9325e5d5 --- /dev/null +++ b/terraform/services/api-waf-sync/variables.tf @@ -0,0 +1,8 @@ +variable "env" { + description = "The application environment (dev, test, prod)" + type = string + validation { + condition = contains(["dev", "test", "prod"], var.env) + error_message = "Valid value for env is dev, test, or prod." + } +}