| Version | Supported |
|---|---|
| 0.1.x (latest) | ✅ Security fixes |
| < 0.1.0 | ❌ |
Do not open a public GitHub issue for security vulnerabilities.
Email: emanthen@gmail.com
Subject line: [SECURITY] Stacklift — <brief description>
Include:
- Description of the vulnerability
- Steps to reproduce or a proof-of-concept
- Potential impact
- Any suggested fix (optional)
Response SLA: Acknowledgement within 48 hours. Fix timeline depends on severity:
| Severity | Fix timeline |
|---|---|
| Critical (data exposure, credential leak) | 48 hours |
| High (privilege escalation, IAM bypass) | 7 days |
| Medium (information disclosure) | 30 days |
| Low | Next release |
You will receive credit in the CHANGELOG unless you prefer to remain anonymous.
Stacklift is built around these security defaults. Understanding them helps you evaluate risk.
The cicd module creates a GitHub Actions OIDC provider and IAM role. GitHub exchanges a short-lived OIDC token for temporary AWS credentials scoped to a single repository and branch. Nothing is stored in GitHub Secrets.
Trust policy uses StringLike on the sub claim:
token.actions.githubusercontent.com:sub → repo:ORG/REPO:ref:refs/heads/BRANCH
All passwords and API keys are stored in AWS Secrets Manager. ECS injects them as environment variables at task startup — the application reads os.environ["DATABASE_URL"], never fetches secrets itself.
RDS passwords are generated via random_password with lifecycle { ignore_changes = [password] } so Terraform never rotates a live database password on apply.
All ECS Fargate tasks run in private subnets with assign_public_ip = false. Outbound traffic routes through a NAT Gateway. Inbound traffic reaches the container only through the ALB security group.
Two independent guards:
deletion_protection = trueon the RDS instance (AWS-level protection)lifecycle { prevent_destroy = true }in Terraform (plan-level protection)
Both must be removed before terraform destroy will succeed. This is intentional.
Each ECS service gets two IAM roles:
- Execution role: ECR pull + Secrets Manager read + CloudWatch Logs write. Only the specific secret ARNs are allowed.
- Task role: empty by default. Extend via
task_role_policy_arnsfor exactly the permissions your app needs.
The cicd IAM role uses iam:PassRole scoped to the specific task execution and task role ARNs — not iam:PassRole: *.
The HTTPS listener uses ELBSecurityPolicy-TLS13-1-2-2021-06 — TLS 1.3 preferred, TLS 1.2 minimum, TLS 1.0/1.1 rejected.
-
Terraform state contains resource IDs and ARNs. State is stored in S3 with SSE-AES256. It does not contain secret values (passwords are in Secrets Manager), but it does contain subnet IDs, security group IDs, and other infrastructure metadata. Ensure your S3 backend bucket is private with versioning enabled.
-
random_passwordresult is in Terraform state. The RDS password is stored in state (base64-encoded) in addition to Secrets Manager. The state is encrypted at rest. For higher security, consider a secrets rotation Lambda (available in the Pro tier). -
Container image scanning is basic. ECR Basic Scanning is enabled by default. For production, consider enabling ECR Enhanced Scanning (Amazon Inspector) for continuous vulnerability assessment.