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." + } +}