Repository navigation
feat(monitoring): enable account security guardrails - #163
Conversation
Terraform Plan🚧 Changes detected — Plan: 10 to add, 2 to change, 2 to destroy. Plan outputLLM ReviewRisk: 🟡 MEDIUM Plan adds account-level security guardrails (Access Analyzer, EBS/AMI/snapshot public-access blocks, Inspector, SSM public sharing disabled, a tampering-detection EventBridge rule) while replacing the AWS Config IAM role with a service-linked role and changing the Config S3 bucket policy; the Config role and its attachment are destroyed and the Config recorder's role_arn is changing, so AWS Config delivery must be verified before apply.
|
Terraform Plan🚧 Changes detected — Plan: 10 to add, 2 to change, 2 to destroy. Plan outputLLM ReviewRisk: 🟡 MEDIUM Plan adds security-monitoring and guardrail resources (Access Analyzer, EventBridge tampering alerts, EBS/AMI/snapshot public-access blocks, Inspector, SSM public sharing disabled) while modifying AWS Config recorder and S3 delivery policy, and destroying and replacing the Config IAM role and its policy attachment. |
|
Review fixes: the security-tampering rule now also matches GuardDuty CreateFilter/UpdateFilter, Security Hub CreateAutomationRules/BatchUpdateAutomationRules/UpdateStandardsControl/BatchUpdateStandardsControlAssociations, Config PutConfigurationRecorder and EnableSnapshotBlockPublicAccess. Docs note that UpdateDetector, UpdateServiceSetting and these events also fire on benign changes (including platform applies), and that enabling Inspector for ECR switches registry scanning from BASIC to ENHANCED, so scan_on_push no longer has an effect; expect a first burst of Inspector findings from the legacy Lambdas. |
EBS snapshot block-public-access, EBS default encryption, SSM document sharing off, AMI BPA codified, Access Analyzer, Inspector (ECR+Lambda), security-tampering EventBridge alert. Config recorder moves to the service-linked role with global types; Config bucket gets TLS-only policy and 2-year lifecycle. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…der changes Add GuardDuty filter, Security Hub automation rule and control, Config recorder and snapshot block-public-access changes to the tamper rule. Document the ECR BASIC to ENHANCED scanning switch from Inspector. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
d033f0c to
ae46e0f
Compare
Terraform Plan🚧 Changes detected — Plan: 10 to add, 2 to change, 2 to destroy. Plan outputLLM ReviewRisk: 🟡 MEDIUM Plan adds security-monitoring and hardening resources (EventBridge tampering alerts, Access Analyzer, Inspector, EBS/AMI/SSM public-sharing blocks) while replacing the AWS Config IAM role with a service-linked role and changing the Config S3 bucket policy; it contains no database or security-group destruction, but the Config role and bucket policy changes need verification before apply.
|
… patterns (#164) ## Why Security Hub and GuardDuty keep sending findings to `javabin-security` that the audit verified as benign or as accepted risk. They drown out the real signal: - **GuardDuty `Impact:IAMUser/AnomalousBehavior` (sev 8):** `javabin-team-provisioner` calls `cognito-idp:UpdateGroup` during its scheduled group sync. CloudTrail confirms the caller is the Lambda. - **GuardDuty `CredentialAccess:IAMUser/AnomalousBehavior` (sev 5) on team SSO `ssm:GetParameter` is deliberately NOT filtered.** The team permission set is `Action "*"` scoped by `StringEqualsIfExists aws:ResourceTag/team`, so it does not limit reads to the team's path, and this is the finding a stolen SSO session produces. One of the two live findings (jzwatch) came from Hungary. A comment in `detection-tuning.tf` and the docs records why. - **CIS 1.2.0 was enabled by accident:** `aws_securityhub_account` defaults `enable_default_standards` to true. Its 14 CloudWatch.* metric-filter controls need CloudTrail to send logs to CloudWatch Logs, which the platform does not do (cost). - **FSBP controls that do not apply:** EKS (none), Macie, interface endpoints, VPC block-public-access, and Inspector EC2/Lambda code scanning. - **Accepted findings:** `dbmoresleep` is public on purpose (RDS.2, RDS.46). The platform ALB's port-80 listener only redirects to HTTPS (ELB.18). ## What New `monitoring/detection-tuning.tf`. Every entry states its reason in code. **GuardDuty ARCHIVE filters** - `team-provisioner-cognito-sync`: matches that finding type, that principal and `cognito-idp` only. - Other anomaly types for this principal still alert. **Security Hub controls disabled, each with `updated_reason`** - CIS 1.2.0: CloudWatch.1-14 and CloudTrail.5. - FSBP: CloudTrail.5, GuardDuty.7, Macie.1, EC2.10/55/56/57/58/60, EC2.172, Inspector.1 and Inspector.3. **Security Hub automation rules (set to SUPPRESSED with a note)** - RDS.2 and RDS.46 on `dbmoresleep`. EC2.18 and EC2.19 on its security group stay open on purpose, because narrowing that group is still an open audit item. - ELB.18 on the platform ALB's port-80 listener. The new ingress output `http_listener_arn` is passed into monitoring for this. **Docs:** a new "Detection tuning" section in `docs/platform-modules.md` lists every suppression and its reason. The RDS `MaliciousIPCaller` findings on dbmoresleep are not suppressed. They are real signal. ## How verified - `terraform fmt -recursive -check` and `terraform validate` pass. Pytest passes (107). - Every control ID exists in its standard: checked with `list-security-control-definitions` for both enabled standards. - There are no existing automation rules or GuardDuty filters to conflict with. - The live GuardDuty findings match the filter fields exactly: - `resource.accessKeyDetails.userName` = `javabin-team-provisioner`, service `cognito-idp.amazonaws.com`, api `UpdateGroup`. - Expected plan: - Creates: 1 GuardDuty filter, 27 control associations and 2 automation rules. - No changes to existing resources beyond the new output and variable wiring. ## Review fixes - Dropped the `team-sso-own-ssm-params` filter: its justification about the permission set was false, and it would archive the stolen-SSO-session signal regardless of source country. ## Follow-ups - Merge after #163 and rebase: both edit the monitoring section of `docs/platform-modules.md`. - GuardDuty filters only apply to new findings. After apply, archive the existing provisioner finding once in the console. Confirm the two team-SSO GetParameter findings with their users (jzwatch: Hungary, 2026-10-24 10:19 UTC) before archiving them. - Re-enable the matching controls if CloudTrail later goes to CloudWatch Logs, or if VPC endpoints or Macie are adopted. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… control Budgets, Cost Anomaly Detection, Config recording ENI/VPC/Subnet daily (not excluded, so Security Hub EC2.180 and Config.1 keep working), and bounded retention for every platform Lambda log group (existing groups adopted with import blocks). Rebased onto #163 (which already expires Config history after two years, so this PR's duplicate lifecycle rule is dropped) and #158 (adds a log group for the new javabin-auth-claims-external trigger). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Why
The security audit found these account-level guardrails off:
unblockedEnableWhat
New
monitoring/guardrails.tf:aws_ebs_snapshot_block_public_accessset toblock-all-sharing. No public snapshots exist.aws_ec2_image_block_public_accessset toblock-new-sharing. This codifies the current live value, so nothing changes.aws_ebs_encryption_by_defaultwithaws/ebs. It applies to new volumes only.aws_ssm_service_settingsets SSM document public sharing toDisable. The account shares no documents.aws_accessanalyzer_analyzernamedjavabin-external-access, type ACCOUNT. This analyzer type is free.aws_inspector2_enablerfor ECR and LAMBDA, about $5-10/month. EC2 scanning and Lambda code scanning stay off.javabin-security-tampering, targeting thejavabin-securitySNS topic. It is a cheap replacement for the CIS CloudWatch metric-filter alarms, and fires on:monitoring/main.tf(Config block only):aws_iam_service_linked_roleforconfig.amazonaws.com. It does not exist today.include_global_resource_types = true. It is the only recorder in the account.aws_iam_role.config_roleand itsAWS_ConfigRoleattachment. The service-linked role replaces them. The state records the dependency, so Terraform updates the recorder before it destroys the old role.AWSConfigBucketExistenceCheck.AWSLogs/<account>/Config/*, the only prefix in use (checked).aws:SourceAccountconditions.DenyInsecureTransport.Docs:
platform-modules.md(monitoring) and the security-services line inCLAUDE.md.How verified
terraform fmt -recursive -checkandterraform validatepass with aws 5.100.0. All resource types used exist in that version.unblockedand AMI BPA isblock-new-sharing.falseand SSM sharing isEnable.DISABLED.AWSServiceRoleForConfigdoes not exist, and the recorder usesjavabin-config-role.javabin-ci-infradoes not deny any action used here. It denies Stop/Delete on the Config recorder, notPutConfigurationRecorder.iam:CreateServiceLinkedRoleis notiam:CreateRole, so the CreateRole deny does not apply.aws_iam_role.config_roleand its policy attachment.Cost
Follow-ups
🤖 Generated with Claude Code