100% Free Forever
AI-Powered Learning
Industry Expert Content
Certificates & Badges
Learn At Your Own Pace
CI/CD, GitOps, DevSecOps & Observability
55 minintermediate

Practice — audit AWS account with Security Hub and fix findings

What You'll Build

In this exercise you will enable AWS Security Hub on a fresh AWS account, evaluate its initial CIS AWS Foundations Benchmark compliance score, identify the highest-severity failing controls, and remediate four critical gaps: deleting root access keys, enabling account-level S3 public access block, enforcing the IAM password policy, and revoking unrestricted SSH security group rules. You will then enable GuardDuty and Inspector, re-run the compliance check, and document the improved score.

By the end, the account will have addressed the most critical CIS Level 1 failures and will have continuous threat detection and vulnerability scanning active, establishing the security monitoring baseline that all subsequent production workloads will run within. The improved compliance score and resolved finding count serve as evidence that the account meets the minimum security baseline required for production use.

Analogy🏏Cricket
Think of it like cricket: Picture setting up a remote ground management system for a cricket ground in a new city. First, the infrastructure must be installed: the pitch sensors, the scoreboard network, and the broadcast uplink—equivalent to installing ArgoCD on the EKS cluster. Then, the venue configuration must be committed to the central venue management database: the pitch dimensions, the lighting schedule, the boundary positions—equivalent to committing the Kubernetes manifests to the GitOps repository. Then, the venue must be registered with the central management system, which then automatically enforces the declared configuration at the ground—equivalent to creating the ArgoCD Application that connects the repository to the cluster. When a ground manager moves a boundary rope by hand, the sensors detect the drift and alert the management system to restore the declared position—equivalent to ArgoCD detecting and reverting the manual replica scale. This reveals why the lab sequence matters: you cannot verify GitOps until all three components—operator, repository, and Application—are connected and working together.

Prerequisites

  • An AWS account where you have administrator access—either an IAM user with AdministratorAccess or an AWS Organization member account.
  • AWS CLI configured with credentials that have securityhub:*, guardduty:*, inspector2:*, iam:*, s3control:*, and ec2:* permissions.
  • Python 3.x and the AWS CLI v2 installed locally for the audit and remediation commands.
  • Awareness that enabling GuardDuty, Inspector, and Security Hub in a fresh account will incur small but non-zero costs—approximately $2-5 for the duration of this exercise.
  • Access to the AWS Console as the root user for the root access key deletion step, since AWS CLI cannot delete root access keys.

Setup — Enable Security Hub and Baseline Audit

Enable Security Hub with both the CIS Benchmark and AWS Foundational Security Best Practices standards, then run the initial compliance query to generate the findings list. The initial enable may produce dozens of CRITICAL and HIGH findings for a fresh account—this is expected and reflects the gap between default account configuration and the CIS baseline.

Analogy🏏Cricket
🏏 Think of it like cricket: The first full safety inspection of a newly built ground almost always returns a long list of critical faults — missing exit signs, uncertified floodlights, unmarked boundaries — and that's expected, because a raw venue simply hasn't been brought up to standard yet. That is exactly what happens when you enable Security Hub with the CIS Benchmark and AWS Foundational Security Best Practices standards and run the first compliance query: a fresh account produces dozens of CRITICAL and HIGH findings. Just as the inspector's initial list is not a disaster but a measured baseline of the gap to the standard, the findings measure the distance between the account's default configuration and the CIS baseline. The payoff: running the baseline audit turns a vague sense of 'is this secure?' into a concrete, prioritised list of gaps you can actually work down control by control.
bash
# Prerequisites: AWS CLI configured with an IAM user or role that has
# SecurityHub:*, Config:*, GuardDuty:*, and ReadOnly permissions.

# 1. Enable Security Hub with CIS and AWS Foundational standards
aws securityhub enable-security-hub \
  --enable-default-standards \
  --region ap-south-1

# 2. Wait 2-3 minutes for initial findings to populate, then check standards
aws securityhub describe-standards-controls \
  --standards-subscription-arn \
    $(aws securityhub get-enabled-standards \
      --query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
      --output text) \
  --query 'Controls[?ComplianceStatus==`FAILED`].{ID:ControlId,Title:Title,Severity:SeverityRating}' \
  --output table

# 3. Check overall compliance summary
aws securityhub get-summary-counts \
  --query 'ResourceCount'

# 4. List all CRITICAL and HIGH NEW findings
aws securityhub get-findings \
  --filters '{
    "SeverityLabel": [{"Value":"CRITICAL","Comparison":"EQUALS"},{"Value":"HIGH","Comparison":"EQUALS"}],
    "WorkflowStatus": [{"Value":"NEW","Comparison":"EQUALS"}],
    "RecordState":    [{"Value":"ACTIVE","Comparison":"EQUALS"}]
  }' \
  --query 'Findings[*].{Title:Title,Severity:Severity.Label,Resource:Resources[0].Id}' \
  --output table

Step 1 — Remediate Critical IAM and S3 Findings

Address the two most critical findings first: CIS 1.4 (root access keys must not exist) and CIS 2.1.1 (account-level S3 public access block). The root access key deletion requires console access as the root user—this is the one security operation that cannot be performed via CLI unless you are already authenticated as root, which is the very problem the control is trying to prevent. After deletion, Security Hub re-evaluates the control automatically within minutes.

Analogy🏏Cricket
Think of it like cricket: The root access key deletion is like the ICC's requirement to destroy any unofficial score-keeping system the venue has been using in parallel with the official ICC scoring system. There is no middle ground: the unofficial system must be destroyed, not simply disconnected. Just as the venue cannot justify keeping the unofficial system 'for backup purposes', the AWS account cannot justify keeping root access keys 'for emergencies'—the emergency use case is handled by root console login with MFA, not by a long-lived access key.
bash
# Step 1: Fix CIS 1.4 — delete root access keys.

# Check if root access keys exist
aws iam get-account-summary \
  --query 'SummaryMap.AccountAccessKeysPresent'
# If output is 1, root access keys exist — must delete.

# To delete root access keys, log in to the AWS Console as root:
# IAM → Security credentials → Access keys → Delete
# (Cannot be done via CLI unless using root credentials — which is the problem)

# After deletion, verify the finding is resolved:
aws securityhub get-findings \
  --filters '{
    "GeneratorId":[{"Value":"arn:aws:securityhub:::ruleset/cis-aws-foundations-benchmark/v/1.2.0/rule/1.4",
                   "Comparison":"PREFIX"}]
  }' \
  --query 'Findings[0].{Status:WorkflowStatus,Title:Title}'
# Expected: after deletion, the finding transitions to RESOLVED within ~5 minutes.

# Step 1b: Fix CIS 2.1.1 — enable account-level S3 block public access.
aws s3control put-public-access-block \
  --account-id $(aws sts get-caller-identity --query Account --output text) \
  --public-access-block-configuration \
    'BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true'

# Verify:
aws s3control get-public-access-block \
  --account-id $(aws sts get-caller-identity --query Account --output text)
# Expected: all four fields true.

Step 2 — Password Policy and Security Group Remediation

Configure the IAM account password policy to meet CIS 1.8 requirements: minimum 14 characters, all character classes required, 90-day maximum age, 24-password reuse prevention. Then identify all security groups with port 22 open to the public internet and remove the unrestricted rules. For any instance that requires administrative access, replace the open SSH rule with AWS Systems Manager Session Manager access—no inbound rules required.

Analogy🏏Cricket
Think of it like cricket: Fixing the password policy is like the ICC updating the ground access badge specifications after a security review: longer badges, harder to copy, must be renewed every 90 days, cannot reuse the last 24 designs. The update is a single administrative action that applies to all future badge issuance without requiring existing badges to be immediately replaced. Revoking unrestricted SSH rules is like closing the unguarded gate from the public stands to the playing area and routing all official access through the controlled main entrance instead—not restricting access, but ensuring it goes through the right channel.
bash
# Step 2: Fix CIS 1.8 — enforce IAM password policy.

aws iam update-account-password-policy \
  --minimum-password-length 14 \
  --require-uppercase-characters \
  --require-lowercase-characters \
  --require-numbers \
  --require-symbols \
  --max-password-age 90 \
  --password-reuse-prevention 24 \
  --allow-users-to-change-password

# Verify:
aws iam get-account-password-policy
# Expected: all 6 requirements true, maxPasswordAge 90, reusePreventionCount 24.

# Step 2b: Fix CIS 4.1 — identify and restrict security groups with public SSH.

# Find all security groups with port 22 open to 0.0.0.0/0 or ::/0
aws ec2 describe-security-groups \
  --filters \
    'Name=ip-permission.from-port,Values=22' \
    'Name=ip-permission.to-port,Values=22' \
    'Name=ip-permission.cidr,Values=0.0.0.0/0' \
  --query 'SecurityGroups[*].{ID:GroupId,Name:GroupName,VPC:VpcId}' \
  --output table

# For each offending security group, remove the unrestricted rule
# and replace with a specific IP range or remove SSH entirely:
aws ec2 revoke-security-group-ingress \
  --group-id sg-XXXXXXXXXX \
  --protocol tcp \
  --port 22 \
  --cidr 0.0.0.0/0

# Best practice: replace with AWS Systems Manager Session Manager
# (no SSH required — all access through IAM-authenticated SSM sessions).

Step 3 — Enable Continuous Threat Detection

Enable GuardDuty with S3 logs, Kubernetes audit log, and malware protection data sources, and enable Inspector for EC2 and ECR resource types. These services begin generating findings immediately: Inspector will scan any ECR images already in the account's registry, and GuardDuty will begin analysing CloudTrail events for threat patterns. Check for any existing Inspector CRITICAL findings against ECR images as a bonus compliance check.

Analogy🏏Cricket
Think of it like cricket: Enabling GuardDuty is like installing the ICC's ball-tracking cameras and crowd monitoring systems at the venue. The systems begin recording and analysing immediately after installation; they do not need a specific event to begin monitoring. Just as the cameras provide continuous surveillance coverage from the moment they are installed rather than only during official matches, GuardDuty provides continuous threat detection coverage from the moment it is enabled rather than only during scheduled security reviews.
bash
# Step 3: Enable and verify GuardDuty, Inspector, and CloudTrail.

# Enable GuardDuty if not already enabled
aws guardduty create-detector \
  --enable \
  --data-sources '{
    "S3Logs":{"Enable":true},
    "Kubernetes":{"AuditLogs":{"Enable":true}},
    "MalwareProtection":{"ScanEc2InstanceWithFindings":{"EbsVolumes":{"Enable":true}}}
  }' \
  --region ap-south-1

# Verify GuardDuty is enabled:
aws guardduty list-detectors
# Expected: at least one detector ID returned.

# Enable Inspector v2 for EC2 and ECR
aws inspector2 enable \
  --resource-types EC2 ECR

# Verify Inspector is enabled:
aws inspector2 describe-organization-configuration 2>/dev/null || \
  aws inspector2 get-configuration

# Check for Inspector ECR findings (may take a few minutes after enablement):
aws inspector2 list-findings \
  --filter-criteria '{
    "resourceType":[{"comparison":"EQUALS","value":"AWS_ECR_CONTAINER_IMAGE"}],
    "severity":[{"comparison":"EQUALS","value":"CRITICAL"}]
  }' \
  --query 'findings[*].{Title:title,Severity:severity,Image:resources[0].id}'

Step 4 — Re-audit and Document Improvement

Wait 5-10 minutes for Security Hub to re-evaluate all controls after the remediations, then re-run the compliance count query to measure improvement. Mark the resolved findings as RESOLVED in the Security Hub workflow with a note documenting the remediation. The remaining failing controls form the backlog for the next remediation sprint, prioritised by severity.

Analogy🏏Cricket
🏏 Think of it like cricket: After the ground crew fixes the flagged faults, the inspector returns, re-checks every item, records the newly passing ones as resolved with a signed note, and hands over a shorter list of remaining issues ranked by how dangerous they are. That is exactly the re-audit step: wait 5–10 minutes for Security Hub to re-evaluate the controls, re-run the compliance count to measure the improvement, mark the fixed findings RESOLVED with a remediation note, and treat the remaining failures as a severity-prioritised backlog. Just as the signed note gives auditors an evidence trail of what was fixed and when, the workflow note documents your remediation for compliance. Just as the ranked remaining list tells the crew what to tackle first before the next match, the backlog drives the next remediation sprint. The payoff: measuring, documenting and re-ranking turns security from a one-off scramble into a steady, evidenced improvement loop.
bash
# Step 4: Re-run the compliance check and record the improved score.

# Wait 5-10 minutes for Security Hub to re-evaluate all controls after fixes.

# Check the CIS compliance score after remediation:
aws securityhub describe-standards-controls \
  --standards-subscription-arn \
    $(aws securityhub get-enabled-standards \
      --query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
      --output text) \
  --query '{
    TotalControls: length(Controls),
    PassingControls: length(Controls[?ComplianceStatus==`PASSED`]),
    FailingControls: length(Controls[?ComplianceStatus==`FAILED`])
  }'

# List remaining failures for the next remediation round:
aws securityhub describe-standards-controls \
  --standards-subscription-arn \
    $(aws securityhub get-enabled-standards \
      --query 'StandardsSubscriptions[?contains(StandardsArn,`cis-aws`)].StandardsSubscriptionArn' \
      --output text) \
  --query 'Controls[?ComplianceStatus==`FAILED`].{ID:ControlId,Severity:SeverityRating,Title:Title}' \
  --output table

# Mark the fixed findings as RESOLVED in Security Hub workflow:
# (Use the finding IDs from Step 1 to update their workflow status)
aws securityhub batch-update-findings \
  --finding-identifiers '[{"Id":"<finding-id>","ProductArn":"<product-arn>"}]' \
  --workflow '{"Status":"RESOLVED"}' \
  --note '{"Text":"Remediated in Exercise 27","UpdatedBy":"india-squad-security"}'

Warning: Enabling Security Hub generates findings immediately for all existing account resources. In a mature AWS account with many resources, this initial finding flood can reach thousands of findings. Do not attempt to suppress all findings to achieve a clean dashboard; this defeats the purpose of the exercise. Instead, focus on remediating the CRITICAL and HIGH findings first, using the --filters parameter on the get-findings API to work through findings by severity tier. Medium and Low findings can be addressed in subsequent sprints after the critical gaps are closed.

Extension Challenge: Create a Terraform module that encodes all the CIS Level 1 remediations from this exercise as infrastructure-as-code—password policy, S3 account block, Config rules for SSH/RDP, GuardDuty detector, Security Hub standards subscriptions—so that every new AWS account in the organisation can be bootstrapped to CIS Level 1 compliance in a single Terraform apply. Store the module in the GitOps repository and deploy it via ArgoCD's Terraform controller or Atlantis for GitOps-managed infrastructure.

Lesson 20 of 33
0% complete