Skip to content

Conversation

@rudransh-shrivastava
Copy link
Collaborator

@rudransh-shrivastava rudransh-shrivastava commented Nov 18, 2025

Resolves #2568

Proposed change

  • Add a security group for ECS tasks.

Note PR depends on another PR, will be rebased once it's merged.

Checklist

  • I've read and followed the contributing guidelines.
  • I've run make check-test locally; all checks and tests passed.

@github-actions github-actions bot added docs Improvements or additions to documentation backend backend-tests makefile ci labels Nov 18, 2025
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 18, 2025

Summary by CodeRabbit

  • Documentation

    • Updated infrastructure documentation to clarify security group setup and network configuration requirements.
  • Chores

    • Reorganized infrastructure security configuration to strengthen network isolation between containerized services, database systems, and caching infrastructure.
    • Removed deprecated code references and internal comments.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Replaces Lambda security-group usage with a dedicated ECS security group across Terraform modules, adds ECS/RDS/RDS-proxy/Redis security groups and rules, updates module inputs/outputs and README; also removes a single in-code comment in a backend management command.

Changes

Cohort / File(s) Summary
Backend comment
backend/apps/common/management/commands/load_data.py
Removes a single comment inside the handle method (no logic change).
Docs
infrastructure/README.md
Updates security-group selection instructions to reference the ECS SG (example: owasp-nest-staging-ecs-sg).
Root module inputs
infrastructure/main.tf
Replaces passing lambda_sg_id to the ecs module with ecs_sg_id.
ECS module changes
infrastructure/modules/ecs/...
infrastructure/modules/ecs/main.tf, infrastructure/modules/ecs/variables.tf
Adds ecs_sg_id variable, removes lambda_sg_id, and updates ECS task modules to use var.ecs_sg_id instead of var.lambda_sg_id across task definitions.
Security module resources
infrastructure/modules/security/main.tf
Adds aws_security_group resources for ecs, rds, rds_proxy, and redis; adds multiple ingress/egress aws_security_group_rule resources and conditional rules gated by var.create_rds_proxy.
Security outputs
infrastructure/modules/security/outputs.tf
Adds ecs_sg_id output; updates rds_proxy_sg_id to return the proxy SG id conditionally (var.create_rds_proxy ? ... : null).

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Pay attention to:
    • correctness of conditional logic around var.create_rds_proxy in security rules and outputs
    • that all ECS task modules receive the new ecs_sg_id and no references to removed lambda_sg_id remain
    • network path correctness (ECS ↔ RDS / RDS-proxy, ECS ↔ Redis, Lambda ↔ others)

Possibly related PRs

Suggested reviewers

  • kasya

Pre-merge checks and finishing touches

❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning Most changes are in-scope, focused on ECS security groups and infrastructure. However, the removal of a single comment in load_data.py appears unrelated to the security group improvements objective. Consider removing the unrelated comment change from backend/apps/common/management/commands/load_data.py or clarifying its relevance to the security group improvements.
Docstring Coverage ⚠️ Warning Docstring coverage is 66.67% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Improve Security Groups for Infrastructure' is directly related to the main change, which adds an ECS security group and updates infrastructure security configurations.
Description check ✅ Passed The description mentions resolving #2568 and proposes adding a security group for ECS tasks, which aligns with the actual code changes made in this PR.
Linked Issues check ✅ Passed The PR successfully addresses the primary coding requirement from #2568: creating a separate Security Group for ECS tasks. The changes add an ECS security group and update all ECS task configurations to use it instead of the Lambda security group.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

📜 Recent review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d56de4d and ce166c5.

📒 Files selected for processing (1)
  • infrastructure/README.md (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • infrastructure/README.md
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run pre-commit checks
  • GitHub Check: CodeQL (javascript-typescript)

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@rudransh-shrivastava rudransh-shrivastava changed the base branch from main to feature/nest-zappa-migration November 18, 2025 11:39
@rudransh-shrivastava rudransh-shrivastava changed the title Feature/nest zappa migration security groups Improve Security Groups for Infrastructure Nov 18, 2025
@github-actions github-actions bot removed the ci label Nov 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🧹 Nitpick comments (7)
backend/docker/Dockerfile (1)

46-47: Symlink workaround is functional but consider refactoring paths in the future.

The symlink enables make targets to reference backend/* paths correctly. This is a pragmatic solution, though a cleaner long-term approach might involve refactoring the Makefile includes to avoid needing the symlink.

infrastructure/modules/database/outputs.tf (1)

7-10: Consider renaming output for clarity.

The output db_proxy_endpoint now conditionally returns either the RDS Proxy endpoint or the direct database address. Consider renaming to db_endpoint to better reflect its dual purpose and avoid confusion.

Apply this diff:

-output "db_proxy_endpoint" {
-  description = "The endpoint of the RDS proxy"
+output "db_endpoint" {
+  description = "The database endpoint (RDS Proxy if enabled, otherwise direct DB address)"
   value       = var.create_rds_proxy ? aws_db_proxy.main[0].endpoint : aws_db_instance.main.address
 }
infrastructure/README.md (2)

106-109: Document the critical deployment dependency for secrets.

The warning about invalid DJANGO_SLACK_BOT_TOKEN causing silent deployment failures is valuable. Consider adding similar warnings for other critical secrets (e.g., DJANGO_SECRET_KEY, DJANGO_ALLOWED_HOSTS) to prevent deployment issues from incomplete parameter setup.


161-167: ECS task security group guidance is correct but could be more explicit.

The instruction to "select the ECS security group (e.g. owasp-nest-staging-ecs-sg)" is correct, but should emphasize that selecting the wrong security group will break database/Redis connectivity for tasks. Consider adding a note: "⚠️ Ensure the ECS security group is selected; using the Lambda security group will cause task failures."

infrastructure/modules/ecs/main.tf (1)

209-237: S3 fixture copy with inline AWS CLI installation could be optimized.

Installing awscli at task runtime (lines 218-219) adds ~30-60s per execution. Consider pre-installing in the Docker image or using a lighter alternative (e.g., AWS Lambda Web Adapter for S3 access). For now, this is acceptable for infrequent fixture loads but flag for future optimization.

infrastructure/modules/security/main.tf (1)

85-108: Minor: Inconsistent Redis ingress rule definition style.

Redis ingress for Lambda is defined inline within the security group (lines 102-107), while Redis ingress for ECS is defined as a separate security_group_rule resource (lines 165-172). Both are functionally correct, but using a consistent style (all inline or all separate resources) would improve maintainability.

Consider refactoring both to use separate security_group_rule resources for consistency:

# In the redis security group (remove inline rule):
- ingress {
-   description     = "Redis from Lambda"
-   from_port       = var.redis_port
-   protocol        = "tcp"
-   security_groups = [aws_security_group.lambda.id]
-   to_port         = var.redis_port
- }

Then both Lambda and ECS Redis access use separate security_group_rule resources.

infrastructure/modules/parameters/variables.tf (1)

35-38: Minor: db_port variable should be type number, not string.

Line 36 declares db_port as type = string, but ports are numeric and should be type = number. This will require conversion downstream when the port is used in SSM parameter values or Docker environment variables.

While Terraform will coerce between types, declaring the correct type prevents confusion and enables validation. Consider changing to:

 variable "db_port" {
   description = "The port of the database."
-  type        = string
+  type        = number
 }

Then convert to string when serializing to SSM parameters (e.g., tostring(var.db_port)).

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2175602 and da55db6.

📒 Files selected for processing (27)
  • backend/Makefile (1 hunks)
  • backend/apps/common/management/commands/load_data.py (1 hunks)
  • backend/docker/Dockerfile (1 hunks)
  • backend/settings/staging.py (0 hunks)
  • backend/tests/apps/common/management/commands/load_data_test.py (4 hunks)
  • backend/wsgi.py (1 hunks)
  • backend/zappa_settings.example.json (1 hunks)
  • infrastructure/README.md (4 hunks)
  • infrastructure/main.tf (4 hunks)
  • infrastructure/modules/cache/main.tf (2 hunks)
  • infrastructure/modules/cache/variables.tf (0 hunks)
  • infrastructure/modules/database/main.tf (4 hunks)
  • infrastructure/modules/database/outputs.tf (1 hunks)
  • infrastructure/modules/database/variables.tf (2 hunks)
  • infrastructure/modules/ecs/main.tf (11 hunks)
  • infrastructure/modules/ecs/modules/task/main.tf (1 hunks)
  • infrastructure/modules/ecs/modules/task/variables.tf (1 hunks)
  • infrastructure/modules/ecs/variables.tf (6 hunks)
  • infrastructure/modules/parameters/main.tf (1 hunks)
  • infrastructure/modules/parameters/outputs.tf (1 hunks)
  • infrastructure/modules/parameters/variables.tf (1 hunks)
  • infrastructure/modules/security/main.tf (4 hunks)
  • infrastructure/modules/security/outputs.tf (1 hunks)
  • infrastructure/modules/security/variables.tf (1 hunks)
  • infrastructure/outputs.tf (0 hunks)
  • infrastructure/terraform.tfvars.example (1 hunks)
  • infrastructure/variables.tf (2 hunks)
💤 Files with no reviewable changes (3)
  • backend/settings/staging.py
  • infrastructure/outputs.tf
  • infrastructure/modules/cache/variables.tf
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2551
File: infrastructure/modules/parameters/main.tf:1-191
Timestamp: 2025-11-08T11:16:25.725Z
Learning: The parameters module in infrastructure/modules/parameters/ is currently configured for staging environment only. The `configuration` and `settings_module` variables default to "Staging" and "settings.staging" respectively, and users can update parameter values via the AWS Parameter Store console. The lifecycle.ignore_changes blocks on these parameters support manual console updates without Terraform reverting them.
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2551
File: infrastructure/modules/parameters/main.tf:16-26
Timestamp: 2025-11-08T11:43:19.276Z
Learning: KMS CMK encryption for SSM SecureString parameters in infrastructure/modules/parameters/ is planned to be implemented after S3 state management is completed. Currently using AWS-managed keys for the testing infrastructure.
📚 Learning: 2025-10-26T12:50:50.512Z
Learnt from: ahmedxgouda
Repo: OWASP/Nest PR: 2429
File: backend/Makefile:30-32
Timestamp: 2025-10-26T12:50:50.512Z
Learning: The `exec-backend-e2e-command` and `exec-db-e2e-command` Makefile targets in the backend/Makefile are intended for local development and debugging only, not for CI/CD execution, so the `-it` flags are appropriate.

Applied to files:

  • backend/Makefile
📚 Learning: 2025-10-23T19:22:23.811Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2431
File: infrastructure/main.tf:0-0
Timestamp: 2025-10-23T19:22:23.811Z
Learning: In Zappa-based serverless deployments, Lambda functions and IAM execution roles are managed by Zappa at application deployment time (via `zappa deploy`/`zappa update`), not via Terraform. Terraform provisions the supporting infrastructure (VPC, RDS, S3, security groups, RDS Proxy, Secrets Manager), while Zappa handles the Lambda orchestration layer.

Applied to files:

  • backend/zappa_settings.example.json
  • infrastructure/README.md
  • infrastructure/modules/security/main.tf
📚 Learning: 2025-11-08T11:16:25.725Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2551
File: infrastructure/modules/parameters/main.tf:1-191
Timestamp: 2025-11-08T11:16:25.725Z
Learning: The parameters module in infrastructure/modules/parameters/ is currently configured for staging environment only. The `configuration` and `settings_module` variables default to "Staging" and "settings.staging" respectively, and users can update parameter values via the AWS Parameter Store console. The lifecycle.ignore_changes blocks on these parameters support manual console updates without Terraform reverting them.

Applied to files:

  • backend/zappa_settings.example.json
  • infrastructure/modules/parameters/main.tf
  • infrastructure/main.tf
  • infrastructure/modules/parameters/variables.tf
📚 Learning: 2025-10-17T15:25:34.963Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2431
File: infrastructure/modules/cache/main.tf:30-30
Timestamp: 2025-10-17T15:25:34.963Z
Learning: The infrastructure/Terraform code in the OWASP Nest repository under the `infrastructure/` directory is intended for quick testing purposes only, not for production deployment.

Applied to files:

  • infrastructure/terraform.tfvars.example
📚 Learning: 2025-10-17T15:25:53.713Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2431
File: infrastructure/modules/database/main.tf:22-60
Timestamp: 2025-10-17T15:25:53.713Z
Learning: The infrastructure code in the `infrastructure/` directory is intended for quick testing purposes only, not production-grade deployment. Production-grade security hardening controls (such as IAM database authentication, deletion protection, Performance Insights KMS encryption) are not required for this testing infrastructure.

Applied to files:

  • infrastructure/terraform.tfvars.example
  • infrastructure/README.md
📚 Learning: 2025-11-08T11:43:19.276Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2551
File: infrastructure/modules/parameters/main.tf:16-26
Timestamp: 2025-11-08T11:43:19.276Z
Learning: KMS CMK encryption for SSM SecureString parameters in infrastructure/modules/parameters/ is planned to be implemented after S3 state management is completed. Currently using AWS-managed keys for the testing infrastructure.

Applied to files:

  • infrastructure/modules/parameters/main.tf
🧬 Code graph analysis (1)
backend/apps/common/management/commands/load_data.py (3)
backend/apps/github/management/commands/github_update_related_organizations.py (2)
  • add_arguments (20-32)
  • handle (34-86)
backend/apps/common/management/commands/restore_backup.py (1)
  • handle (13-17)
backend/apps/core/utils/index.py (1)
  • disable_indexing (74-92)
🪛 Checkov (3.2.334)
infrastructure/modules/parameters/main.tf

[high] 16-26: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 28-38: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 80-86: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 104-114: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 124-130: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 132-138: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 152-162: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 164-174: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)


[high] 176-186: Ensure SSM parameters are using KMS CMK

(CKV_AWS_337)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: Run frontend e2e tests
  • GitHub Check: Run frontend unit tests
🔇 Additional comments (39)
backend/Makefile (1)

24-36: LGTM! Clean conditional execution support.

The conditional execution paths for Docker and direct modes are well-implemented and consistent across both targets. This enables flexible command execution for different deployment contexts (local Docker vs. ECS/production).

backend/docker/Dockerfile (2)

41-41: LGTM!

The Makefile copy is correctly positioned and necessary to support make target execution within the container.


54-54: LGTM! Proper make installation with cache optimization.

The --no-cache flag prevents unnecessary cache buildup while installing the required make utility.

infrastructure/modules/database/variables.tf (2)

7-11: LGTM! Well-defined conditional feature flag.

The create_rds_proxy variable with a safe default of false enables opt-in RDS Proxy provisioning, aligning with the PR's infrastructure improvements.


87-90: Variable rename is complete and properly propagated.

The rename from db_username to db_user has been successfully implemented across the infrastructure. No references to the old variable name remain in the codebase, and all parent module usages in infrastructure/main.tf correctly reference the new variable name.

backend/apps/common/management/commands/load_data.py (2)

13-26: LGTM! Well-implemented CLI argument.

The --fixture-path argument follows Django command conventions and maintains backward compatibility with the default value. The implementation is clean and well-documented.


28-34: LGTM! Clean integration of fixture path parameter.

The parameter is correctly extracted from options and passed to the loaddata command while preserving the transactional and indexing behavior.

backend/wsgi.py (1)

24-24: LGTM! Correct placement for environment setup.

The function is correctly invoked before Django settings initialization, ensuring SSM parameters are available when settings are loaded.

backend/zappa_settings.example.json (1)

4-6: LGTM! SSM Parameter Store path correctly configured.

The AWS_SYSTEMS_MANAGER_PARAM_STORE_PATH environment variable aligns with the wsgi.py implementation and uses the correct SSM parameter path format.

backend/tests/apps/common/management/commands/load_data_test.py (3)

8-36: LGTM! Clean migration to call_command.

The test correctly uses call_command and validates the default fixture path behavior with proper mocking of transaction and indexing contexts.


38-68: LGTM! Comprehensive test for custom fixture path.

The new test correctly validates that the --fixture-path argument is properly passed through to the loaddata command, with appropriate mocking of transaction and indexing behavior.


70-94: LGTM! Exception handling test preserved.

The test correctly validates that indexing is re-enabled even when call_command raises an exception, maintaining proper cleanup behavior.

infrastructure/modules/ecs/modules/task/variables.tf (1)

17-21: Variable rename verified and properly implemented across the module hierarchy.

The change from container_environment to container_parameters_arns is complete. No references to the old variable name remain, and the new variable is correctly threaded through the module hierarchy (root main.tf → ecs module → task module) and actively used in the task definition's secrets block on line 46, where it iterates over the SSM parameter ARNs map. The security improvement through AWS SSM Parameter Store integration is properly implemented.

infrastructure/modules/parameters/main.tf (3)

1-14: LGTM!

The Terraform and provider version constraints are appropriate.


16-186: KMS CMK encryption planned for future implementation.

The static analysis correctly identifies that SecureString parameters are using AWS-managed keys instead of customer-managed KMS keys. This is acceptable for the current testing infrastructure, with KMS CMK encryption planned for implementation after S3 state management is completed.

Based on learnings.


188-191: LGTM!

The random string generation for Django secret key is configured appropriately with sufficient length and special characters enabled.

infrastructure/modules/security/variables.tf (1)

7-11: LGTM!

The create_rds_proxy variable is well-defined with an appropriate default value of false, enabling optional RDS Proxy support as intended by the PR objectives.

infrastructure/modules/security/outputs.tf (2)

1-4: LGTM!

The new ecs_sg_id output correctly exposes the ECS security group ID, aligning with the PR objective to create a separate security group for ECS tasks.


11-14: LGTM!

The conditional logic for rds_proxy_sg_id correctly returns the security group ID when RDS Proxy is enabled, or null otherwise. The array index notation is appropriate for the count-based resource.

infrastructure/terraform.tfvars.example (1)

1-10: LGTM!

The migration from Django-specific variables to infrastructure configuration variables aligns with the Parameter Store integration. The removed Django variables are now managed through SSM parameters, improving secret management and supporting least-privilege IAM policies as per the PR objectives.

The default values are appropriate for testing infrastructure:

  • create_rds_proxy = false provides optional RDS Proxy support
  • db_backup_retention_period = 0 is acceptable for testing (not production)
  • force_destroy_bucket = true facilitates testing cleanup

Based on learnings.

infrastructure/modules/cache/main.tf (1)

16-36: LGTM!

The change to unconditional Redis auth token generation simplifies the logic and improves security by always requiring authentication. The addition of special = true with the existing override_special configuration ensures strong token entropy while meeting Redis-specific requirements.

infrastructure/modules/parameters/outputs.tf (1)

1-22: LGTM!

The ssm_parameter_arns output provides a clean interface for consuming modules to reference SSM parameters. The map structure with environment variable names as keys aligns well with the ECS secrets configuration pattern.

infrastructure/modules/ecs/modules/task/main.tf (1)

46-49: Execution role SSM permissions verified and correctly configured.

The infrastructure code properly implements SSM parameter access for ECS tasks. The parent ECS module creates aws_iam_role.ecs_tasks_execution_role with an attached policy granting ssm:GetParameters action on the resource pattern arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/${var.environment}/*. This role ARN is passed to all task modules via the ecs_tasks_execution_role_arn parameter and used in the task definitions. The wildcard pattern covers environment-specific SSM parameters, supporting the migration from environment variables to secrets.

The security improvement is correctly implemented with appropriate IAM permissions in place.

infrastructure/README.md (1)

56-60: Parameter Store setup instructions are clear.

The new steps correctly direct users to populate DJANGO_* secrets via AWS Console after terraform apply, which aligns with the SSM-based secrets architecture.

infrastructure/modules/ecs/main.tf (3)

119-138: ECS security group integration is correct; verify cross-module wiring.

Task modules correctly reference var.ecs_sg_id instead of the previous lambda_sg_id, implementing proper separation of concerns. Ensure that infrastructure/main.tf passes module.security.ecs_sg_id to this module and that the security module outputs this ID.


140-167: Multi-line shell command in owasp_update_project_health_metrics_task is well-structured.

Using heredoc syntax with set -e ensures early exit on command failure, which is appropriate for critical health update tasks. Verify that both make targets exist in the backend Makefile.


46-61: No changes needed — SSM policy is correct as written.

ECS reads SSM parameters referenced via containerDefinition.secrets using ssm:GetParameters (not GetParameter). The policy in lines 46-61 correctly uses ssm:GetParameters with appropriate resource ARN scoping for the project/environment parameters. The batch API is the correct and only required action for this use case.

infrastructure/modules/ecs/variables.tf (2)

12-21: New variables properly implement SSM-based secrets and separate ECS security group.

container_parameters_arns (map format) correctly replaces inline environment variables, enabling secure parameter injection via ECS secrets block. ecs_sg_id required parameter ensures explicit wiring to correct security group.


70-120: Justify memory reductions before merging.

Four task memory defaults were reduced from 2048 MiB to 1024 MiB (migrate, sync-data, health metrics, health scores tasks). While this reduces costs, it may impact throughput or cause OOM errors if task workloads increase.

Please clarify:

  • Was this change load-tested or is it based on actual observed usage?
  • What is the OOM behavior for these tasks if they exceed 1024 MiB?
  • Should index_data_task also be reduced from 2048 MiB for consistency?
infrastructure/main.tf (2)

42-70: Module invocations correctly implement conditional RDS Proxy and ECS security group separation.

The refactoring properly passes create_rds_proxy to database and security modules, and wires ecs_sg_id to the ECS module. Variable renames (db_username -> db_user) are consistent across modules.

Ensure that:

  1. infrastructure/modules/security/outputs.tf exports ecs_sg_id
  2. infrastructure/modules/cache/outputs.tf exports redis_auth_token (referenced on line 96)
  3. infrastructure/modules/database/outputs.tf exports db_proxy_endpoint with conditional null handling

84-97: Parameter module wiring is correctly implemented. Verification confirms:

  • The db_proxy_endpoint reference properly handles both create_rds_proxy = true (proxy endpoint) and create_rds_proxy = false (direct RDS address) cases via conditional logic in the database module output
  • All SSM parameter names follow the pattern /${var.project_name}/${var.environment}/*, matching the ARN pattern required by the ECS SSM policy (arn:aws:ssm:${var.aws_region}:${data.aws_caller_identity.current.account_id}:parameter/${var.project_name}/${var.environment}/*)
  • The ssm_parameter_arns output correctly maps parameter names to their ARNs, all of which will satisfy the ECS policy resource constraint
infrastructure/modules/database/main.tf (1)

79-158: Conditional RDS Proxy implementation with count is correct and follows best practices.

All proxy resources properly gate creation with count = var.create_rds_proxy ? 1 : 0, and dependent resources use index-based access (e.g., aws_iam_role.rds_proxy[0].arn). The IAM policy correctly limits Secrets Manager access to only the db_credentials secret, and require_tls = true on the proxy enforces encryption.

Verify that infrastructure/modules/database/outputs.tf exports db_proxy_endpoint with proper null-coalescing logic (e.g., db_proxy_endpoint = var.create_rds_proxy ? aws_db_proxy.main[0].endpoint : aws_db_instance.main.endpoint) to support both proxy and direct modes in the parameters module.

infrastructure/modules/security/main.tf (2)

16-48: ECS and Lambda security groups are now properly separated.

The creation of a distinct ECS security group (ecs-sg) separate from Lambda's security group (lambda-sg) correctly implements the PR objective. Both groups have identical egress policies (allow all outbound), which is appropriate for workloads that need outbound access to multiple services.


110-163: Conditional RDS access rules properly implement direct-vs-proxy access patterns.

When create_rds_proxy = false: ECS and Lambda connect directly to RDS (lines 110-130).
When create_rds_proxy = true: Both connect to RDS Proxy instead (lines 132-163), which then connects to RDS.

This logic correctly prevents direct RDS access when a proxy is configured, which is a best practice for connection pooling and audit trails.

infrastructure/variables.tf (3)

13-17: New create_rds_proxy variable correctly defaults to false for backward compatibility.

The optional RDS Proxy flag allows existing deployments to work without changes while enabling proxy-based configurations for new deployments. Default false is appropriate for incremental adoption.


68-82: Environment validation and db_user rename improve consistency and prevent configuration errors.

The validation block (lines 78-80) prevents typos like environment = "test" from silently creating misnamed resources. Renaming db_username to db_user aligns with AWS RDS terminology and matches the parameters module variable naming.


1-148: All verifications passed—no issues identified.

The reference to module.cache.redis_auth_token on line 96 is correctly implemented:

  • Cache module properly outputs this value via infrastructure/modules/cache/outputs.tf
  • No dangling references to removed var.django_* variables exist
  • Django-specific variables have been successfully migrated to SSM Parameter Store via the parameters module

The refactoring correctly redirects secret management away from tfvars (which could be accidentally committed) to SSM Parameter Store.

infrastructure/modules/parameters/variables.tf (2)

13-17: Configuration defaults correctly align with staging environment focus.

The configuration = "Staging" and settings_module = "settings.staging" defaults match the learned behavior that the parameters module is currently configured for staging only. This is appropriate for the current infrastructure scope.


1-70: Parameter module variables comprehensively cover Django configuration needs.

All essential database, cache, and Django settings are captured as inputs. Sensitive fields are properly marked. The module follows good practice of accepting derived values (db_host from RDS Proxy endpoint, redis_host from cache endpoint) rather than requiring hardcoded IPs.

backend/wsgi.py Outdated
Comment on lines 6 to 22
def _populate_environ_from_ssm():
ssm_param_path = os.getenv("AWS_SYSTEMS_MANAGER_PARAM_STORE_PATH")
if not ssm_param_path:
return

from pathlib import Path

import boto3

client = boto3.client("ssm")
paginator = client.get_paginator("get_parameters_by_path")
response_iterator = paginator.paginate(Path=ssm_param_path, WithDecryption=True)

for page in response_iterator:
for param in page["Parameters"]:
os.environ[Path(param["Name"]).name] = param["Value"]

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Add error handling and clarify boto3 region configuration.

The SSM parameter retrieval lacks error handling, which could cause application startup failures if AWS API calls fail. Additionally, the boto3 client is created without an explicit region, relying on the default AWS SDK resolution chain (environment variables, credentials file, or EC2 instance metadata).

Consider adding error handling and region specification:

 def _populate_environ_from_ssm():
     ssm_param_path = os.getenv("AWS_SYSTEMS_MANAGER_PARAM_STORE_PATH")
     if not ssm_param_path:
         return
 
     from pathlib import Path
 
     import boto3
 
-    client = boto3.client("ssm")
+    # Region will be resolved from AWS_DEFAULT_REGION env var or EC2 instance metadata
+    try:
+        client = boto3.client("ssm")
+    except Exception as e:
+        # Log but don't crash on boto3 initialization failure
+        print(f"Warning: Failed to initialize SSM client: {e}")
+        return
+
     paginator = client.get_paginator("get_parameters_by_path")
-    response_iterator = paginator.paginate(Path=ssm_param_path, WithDecryption=True)
+    try:
+        response_iterator = paginator.paginate(Path=ssm_param_path, WithDecryption=True)
+    except Exception as e:
+        # Log but don't crash on SSM API failure
+        print(f"Warning: Failed to fetch SSM parameters from {ssm_param_path}: {e}")
+        return
 
     for page in response_iterator:
         for param in page["Parameters"]:
             os.environ[Path(param["Name"]).name] = param["Value"]
🤖 Prompt for AI Agents
In backend/wsgi.py around lines 6 to 22, the SSM parameter fetch lacks error
handling and doesn't set a boto3 region; wrap boto3 client creation and
pagination in a try/except, determine region_name from os.getenv("AWS_REGION")
or os.getenv("AWS_DEFAULT_REGION") (fallback to boto3's default if neither
present) and pass it into boto3.client("ssm", region_name=region_name), catch
and handle relevant exceptions (botocore.exceptions.ClientError, NoRegionError,
EndpointConnectionError, generic Exception), log a clear error message with
exception details via the existing logger, and on failure either return early
(so startup continues) or re-raise only if this must be fatal; also validate
each parameter before writing into os.environ (skip or log malformed names) to
avoid crashing on unexpected API responses.

Comment on lines 8 to 20
"extra_permissions": [
{
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameter"
],
"Resource": [
"arn:aws:ssm:${AWS_REGION}:${AWS_ACCOUNT_ID}:parameter/owasp-nest/staging",
"arn:aws:ssm:${AWS_REGION}:${AWS_ACCOUNT_ID}:parameter/owasp-nest/staging/*"
]
}
],
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify Zappa supports environment variable placeholder substitution in extra_permissions.

The IAM permissions correctly grant least-privilege SSM access, but the ARN Resource strings use ${AWS_REGION} and ${AWS_ACCOUNT_ID} placeholders. Ensure Zappa substitutes these at deployment time, as raw placeholders in IAM policy resources will not match actual ARN patterns.


🌐 Web query:

Does Zappa support ${AWS_REGION} and ${AWS_ACCOUNT_ID} placeholder substitution in extra_permissions IAM resource ARNs?

💡 Result:

Short answer: No — Zappa does not perform substitution of ${AWS_REGION} or ${AWS_ACCOUNT_ID} inside extra_permissions ARNs. Use concrete ARNs (or generate them yourself) — the docs only show example placeholders like and <account_id> in snippets, they are not automatic runtime substitutions. [1][2]

Sources:

  • Zappa README / settings docs (shows example ARN placeholders like arn:aws:lambda::<account_id>:function:...) — examples only. [1]
  • PyPI Zappa docs / extra_permissions example. [2]

Remove placeholder syntax from IAM Resource ARNs — Zappa does not substitute ${AWS_REGION} and ${AWS_ACCOUNT_ID}.

Zappa does not perform substitution of ${AWS_REGION} or ${AWS_ACCOUNT_ID} inside extra_permissions ARNs. The documentation examples showing these placeholders are illustrative only. Lines 16-17 will be interpreted as literal strings, causing the IAM policy to fail silently — the Lambda execution role will not actually grant SSM parameter access.

Replace with concrete ARN values, environment-specific configuration, or generate ARNs dynamically outside of the Zappa settings file.

🤖 Prompt for AI Agents
In backend/zappa_settings.example.json around lines 8 to 20, the IAM resource
ARNs use placeholder syntax (${AWS_REGION}, ${AWS_ACCOUNT_ID}) that Zappa will
not substitute, so the policy will be treated as literal strings and fail to
grant SSM access; fix by replacing those placeholder ARNs with concrete ARNs
(e.g., arn:aws:ssm:<region>:<account-id>:parameter/owasp-nest/staging and
arn:aws:ssm:<region>:<account-id>:parameter/owasp-nest/staging/*), or instead
remove hardcoded ARNs and generate/populate correct ARNs from your deployment
pipeline or environment-specific config (CloudFormation/CI variables) before
writing zappa_settings, ensuring the final file contains real region and account
values rather than ${...} placeholders.

Comment on lines 1 to 5
variable "allowed_hosts" {
description = "The Django allowed hosts."
type = string
default = "*"
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

⚠️ allowed_hosts default of "*" is a security risk for production.

Setting allowed_hosts to "*" disables Host header validation in Django, which is vulnerable to Host-header attacks and cache poisoning. This default is acceptable for development/staging but dangerous for production.

Recommend:

  • Change default to an empty string or require explicit specification
  • Add validation warning in documentation
  • For production deployments, override with specific hostnames
 variable "allowed_hosts" {
   description = "The Django allowed hosts."
   type        = string
-  default     = "*"
+  default     = ""
 }

Add a comment warning users to configure this properly for production.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
variable "allowed_hosts" {
description = "The Django allowed hosts."
type = string
default = "*"
}
variable "allowed_hosts" {
description = "The Django allowed hosts."
type = string
default = ""
}
🤖 Prompt for AI Agents
in infrastructure/modules/parameters/variables.tf lines 1-5, the variable
"allowed_hosts" currently defaults to "*" which is unsafe for production; update
the variable to require explicit configuration (remove the "*" default or change
default to an empty string) and add an inline comment above the variable warning
that "*" disables Django host header validation and must be overridden with
explicit hostnames in production; also ensure documentation/README is updated to
call out this requirement and suggest production override examples.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
backend/wsgi.py (1)

3-23: Add error handling, logging, and explicit region handling around SSM access.

As written, any boto3/SSM issue (missing region/credentials, network, IAM denial, malformed response) will raise during module import and take down WSGI startup, with no logging for diagnosis; the region is also implicit. Wrapping client creation and pagination in targeted try/except, resolving region from environment, and logging failures would make this far more robust.

A concrete direction:

-import os
-from pathlib import Path
-
-import boto3
-
-
-def populate_environ_from_ssm():
-    """Populate environment variables from AWS Systems Manager Parameter Store."""
-    if not (ssm_param_path := os.getenv("AWS_SYSTEMS_MANAGER_PARAM_STORE_PATH")):
-        return
-
-    client = boto3.client("ssm")
-    paginator = client.get_paginator("get_parameters_by_path")
-    response_iterator = paginator.paginate(Path=ssm_param_path, WithDecryption=True)
-
-    for page in response_iterator:
-        for param in page["Parameters"]:
-            os.environ[Path(param["Name"]).name] = param["Value"]
+import os
+from pathlib import Path
+import logging
+
+import boto3
+from botocore.exceptions import ClientError, NoRegionError, EndpointConnectionError
+
+logger = logging.getLogger(__name__)
+
+
+def populate_environ_from_ssm():
+    """Populate environment variables from AWS Systems Manager Parameter Store."""
+    ssm_param_path = os.getenv("AWS_SYSTEMS_MANAGER_PARAM_STORE_PATH")
+    if not ssm_param_path:
+        return
+
+    region_name = os.getenv("AWS_REGION") or os.getenv("AWS_DEFAULT_REGION") or None
+
+    try:
+        client = boto3.client("ssm", region_name=region_name) if region_name else boto3.client("ssm")
+        paginator = client.get_paginator("get_parameters_by_path")
+        response_iterator = paginator.paginate(Path=ssm_param_path, WithDecryption=True)
+    except (NoRegionError, EndpointConnectionError, ClientError) as exc:
+        logger.error("Failed to initialize or call SSM for path %s: %s", ssm_param_path, exc)
+        # Decide if this should be fatal for your deployment; currently we fail closed:
+        raise
+    except Exception as exc:  # defensive catch‑all
+        logger.error("Unexpected error while preparing SSM client for path %s: %s", ssm_param_path, exc)
+        raise
+
+    for page in response_iterator:
+        for param in page.get("Parameters", []):
+            name = param.get("Name")
+            value = param.get("Value")
+            if not name or value is None:
+                logger.warning("Skipping malformed SSM parameter entry in path %s: %r", ssm_param_path, param)
+                continue
+            os.environ[Path(name).name] = value

If you prefer non‑fatal behavior in some environments, you can return instead of raise in the exception handlers; just be explicit about that policy.

🧹 Nitpick comments (1)
backend/wsgi.py (1)

23-30: Late import of get_wsgi_application with # noqa: E402 is appropriate.

Importing get_wsgi_application only after SSM‑backed env population and Django settings defaults is correct here; the # noqa: E402 suppression is justified and keeps lint noise down.

If you later add more WSGI‑time initialization, consider grouping it into a small helper (e.g., _bootstrap_settings_from_env_and_ssm()) above this import to keep the module‑level flow easy to scan.

📜 Review details

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da55db6 and c520124.

📒 Files selected for processing (3)
  • backend/apps/common/management/commands/load_data.py (1 hunks)
  • backend/wsgi.py (1 hunks)
  • infrastructure/README.md (4 hunks)
🚧 Files skipped from review as they are similar to previous changes (1)
  • infrastructure/README.md
🧰 Additional context used
🧠 Learnings (2)
📓 Common learnings
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2551
File: infrastructure/modules/parameters/main.tf:1-191
Timestamp: 2025-11-08T11:16:25.725Z
Learning: The parameters module in infrastructure/modules/parameters/ is currently configured for staging environment only. The `configuration` and `settings_module` variables default to "Staging" and "settings.staging" respectively, and users can update parameter values via the AWS Parameter Store console. The lifecycle.ignore_changes blocks on these parameters support manual console updates without Terraform reverting them.
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2699
File: backend/wsgi.py:13-13
Timestamp: 2025-11-23T11:52:15.447Z
Learning: In the OWASP Nest project, the SSM parameter store setup in backend/wsgi.py (using boto3 to fetch parameters from AWS Systems Manager) is designed for staging and production environments, not just for testing purposes.
📚 Learning: 2025-11-23T11:52:15.447Z
Learnt from: rudransh-shrivastava
Repo: OWASP/Nest PR: 2699
File: backend/wsgi.py:13-13
Timestamp: 2025-11-23T11:52:15.447Z
Learning: In the OWASP Nest project, the SSM parameter store setup in backend/wsgi.py (using boto3 to fetch parameters from AWS Systems Manager) is designed for staging and production environments, not just for testing purposes.

Applied to files:

  • backend/wsgi.py
🧬 Code graph analysis (1)
backend/apps/common/management/commands/load_data.py (2)
backend/apps/owasp/management/commands/owasp_enrich_events.py (2)
  • add_arguments (17-24)
  • handle (26-60)
backend/apps/common/management/commands/restore_backup.py (1)
  • handle (13-17)
🔇 Additional comments (1)
backend/apps/common/management/commands/load_data.py (1)

13-27: Dynamic fixture path wiring looks correct and backward compatible

add_arguments correctly registers --fixture-path with a sensible default, and using _options["fixture_path"] in handle matches Django’s option naming (hyphens → underscores). The updated call_command("loaddata", _options["fixture_path"], "-v", "3") preserves existing behavior while allowing overrides, and stays consistent with the pattern in restore_backup.py. I don’t see any correctness or transactional issues here.

Also applies to: 31-31

rudransh-shrivastava and others added 13 commits November 23, 2025 14:50
* Use AWS SSM Parameter Store to handle environment variables

* Use focused policy for read access

* Update documentation

* Add flag for create_rds_proxy

* set default value of create_rds_proxy to false

* Populate Zappa/Lambda environment variables from ssm/parameter store

* Update documentation

* Update example

* add default configurations

* add security group db from lambda

* fix load-data task by adding a --fixture-path flag

* fix ecs tasks by introducing ecs-* make targets

* change ecs run steps

* remove ecs-* and clean code

* add --no-cache

* use call_command

* add test for --fixture-path

* Update code

* Update backend/wsgi.py

---------

Co-authored-by: Arkadii Yakovets <[email protected]>
Co-authored-by: Arkadii Yakovets <[email protected]>
@arkid15r arkid15r force-pushed the feature/nest-zappa-migration-security-groups branch from c520124 to d56de4d Compare November 23, 2025 22:54
@arkid15r arkid15r marked this pull request as ready for review November 23, 2025 22:55
@arkid15r arkid15r enabled auto-merge (squash) November 23, 2025 22:55
@sonarqubecloud
Copy link

@arkid15r arkid15r merged commit 1020b29 into OWASP:feature/nest-zappa-migration Nov 23, 2025
26 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

backend docs Improvements or additions to documentation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Improve Security Groups and IAM policies for Infrastructure/Zappa

2 participants