AWS / Guide 6
Security baseline: identity, detection and encryption
Root and IAM hygiene, IMDSv2, CloudTrail, GuardDuty, Security Hub, KMS, Secrets Manager and the logs you need.
Network controls limit where an attacker can go. Most AWS incidents, though, start with credentials: an access key committed to a repository, a phished administrator, an IAM user nobody removed. This guide covers the baseline that sits alongside the network: identity, CloudTrail, GuardDuty, Security Hub, encryption with KMS, Secrets Manager, and the logs you'll need on the day something happens.
Identity comes first
- Root users: MFA is now required on every root user, in every account type. With AWS Organizations, remove member accounts' root credentials altogether with centralised root access management (Guide 2), and make sure no root user has access keys.
- People sign in through IAM Identity Center, with MFA enforced by your identity provider or by Identity Center itself. Phishing-resistant methods (FIDO2 security keys, passkeys) for anyone with admin permission sets.
- No long-lived access keys for people. Identity Center gives the CLI and PowerShell short-lived credentials (Guide 1).
- Two break-glass IAM users in the management account (or a dedicated account), with hardware MFA, no access keys, and an alarm on every sign-in, for when Identity Center or the identity provider is down.
- Review access quarterly.
Get-AwsIamCredentialReport.ps1lists IAM users without MFA, old and unused access keys, and root user status. IAM Access Analyzer's unused access findings show roles and permissions nobody uses.
Roles over access keys
Every access key in a config file is a password that doesn't expire on its own and can't use MFA. For anything running in AWS, use a role: an EC2 instance profile, a Lambda or ECS task role, an EKS pod identity. AWS issues and rotates the credentials, and nothing is stored in code.
For things outside AWS: OIDC federation for CI/CD platforms (GitHub Actions, GitLab, Azure DevOps), IAM Roles Anywhere with certificates for on-premises servers, and IAM user access keys only as a last resort, with an owner, a rotation date and a policy that restricts them to known source IPs.
Instance roles have their own risk: any process on the instance can read the credentials from the instance metadata service. Require IMDSv2 (session tokens), which blocks the server-side request forgery attacks that used IMDSv1 to steal them. Set it as the account default for new instances, and with a declarative policy across the organisation:
# New instances in this region default to IMDSv2 only
Edit-EC2InstanceMetadataDefault -HttpToken required -HttpPutResponseHopLimit 2 -Region us-east-1
# Existing instances still allowing IMDSv1
(Get-EC2Instance).Instances | Where-Object { $_.MetadataOptions.HttpTokens -ne 'required' } |
Select-Object InstanceId, @{n='Name';e={ ($_.Tags | Where-Object Key -eq 'Name').Value }}
CloudTrail for the whole organisation
CloudTrail records every API call: who did what, from where, and when. The console's event history keeps 90 days of management events per account and region. That isn't enough for an investigation. Create one organisation trail from the management account (Control Tower does this for you):
- All regions, all accounts, management events (read and write), delivered to an S3 bucket in the log archive account.
- That bucket with versioning, S3 Object Lock (or at least an SCP and bucket policy that deny deletes), and a lifecycle rule to move older logs to a cheaper storage class.
- Log file validation on, so you can prove logs weren't altered.
- Data events (S3 object reads and writes, Lambda invocations) only for sensitive buckets and functions: they're charged per event and add up quickly.
- Optionally, a copy to CloudWatch Logs or CloudTrail Lake for querying.
GuardDuty and Security Hub
| Service | What it does | Turn on |
|---|---|---|
| Amazon GuardDuty | Threat detection from CloudTrail, VPC flow logs and DNS logs: compromised credentials, crypto-mining, calls from known-bad IPs. Optional protection plans for S3, EKS, RDS, Lambda, malware scanning and runtime monitoring | Every account and region, from a delegated administrator account |
| AWS Security Hub CSPM | Posture checks against standards: AWS Foundational Security Best Practices, CIS AWS Foundations Benchmark, NIST, PCI DSS. A score per standard and a list of failed controls | Every account and region, with central configuration |
| AWS Security Hub | Correlates findings from GuardDuty, Inspector and Security Hub CSPM into prioritised exposures and attack paths, with ticketing integrations | Organisation-wide, from the security tooling account |
| Amazon Inspector | Vulnerability scanning of EC2 instances, container images and Lambda functions | Production accounts first |
| IAM Access Analyzer | Resources shared outside the organisation, and unused access | Organisation zone of trust, free for external access |
Make the security tooling account the delegated administrator for all of them, so findings from every account land in one place and member accounts can't turn the services off.
# Is GuardDuty on in this region, and what are the 20 most severe findings?
$detectorId = Get-GDDetectorList
$sort = New-Object Amazon.GuardDuty.Model.SortCriteria -Property @{ AttributeName = 'severity'; OrderBy = 'DESC' }
$ids = Get-GDFindingList -DetectorId $detectorId -SortCriterion $sort -MaxResult 20 -NoAutoIteration
Get-GDFinding -DetectorId $detectorId -FindingId $ids | Select-Object Severity, Type, Title, UpdatedAt
# Failed Security Hub CSPM controls with critical or high severity, grouped by control
function New-SHFilter($Value) { New-Object Amazon.SecurityHub.Model.StringFilter -Property @{ Comparison = 'EQUALS'; Value = $Value } }
$filter = New-Object Amazon.SecurityHub.Model.AwsSecurityFindingFilters
$filter.ComplianceStatus = New-SHFilter 'FAILED'
$filter.RecordState = New-SHFilter 'ACTIVE'
$filter.SeverityLabel = @((New-SHFilter 'CRITICAL'), (New-SHFilter 'HIGH'))
Get-SHUBFinding -Filter $filter | Group-Object Title | Sort-Object Count -Descending | Select-Object Count, Name -First 20
Work the findings
- Fix by control, not by resource. A handful of controls (MFA, open security groups, public S3, IMDSv2, EBS encryption, CloudTrail) cover most of the risk and map to these guides.
- Fix with a guardrail, so it stays fixed: an SCP, a declarative policy, an account-level default or a Config remediation, rather than hand-fixing each resource.
- Suppress with a reason. Disable a control that doesn't apply, or set a finding's workflow status to Suppressed with a note, so the score reflects real risk.
- Route high-severity findings to people: an EventBridge rule on GuardDuty and Security Hub findings, to an SNS topic, chat channel or ticket queue (Guide 9).
Encryption and secrets
Turn on EBS encryption by default in every region; new volumes and snapshot copies are then encrypted automatically. S3 encrypts new objects by default. For data that needs control over who can decrypt, use customer managed KMS keys:
| Setting | Why |
|---|---|
| Key policy that separates key administrators from key users | Whoever manages the key shouldn't automatically be able to decrypt the data |
| Automatic rotation on | Yearly by default (the period is adjustable); old versions still decrypt old data |
| One key per application and data classification | A compromised or misused key exposes less |
Deletion waiting period (7–30 days) and an alarm on ScheduleKeyDeletion | Deleting a key makes everything encrypted with it unrecoverable |
Secrets (database passwords, API keys) belong in AWS Secrets Manager, not in environment variables, user data or code. Turn on automatic rotation for database credentials, grant apps secretsmanager:GetSecretValue on their own secrets only, and use RDS-managed master passwords so nobody ever sees the admin password. Systems Manager Parameter Store SecureString parameters are a cheaper option for configuration values that don't need rotation.
# EBS encryption by default, per region
Enable-EC2EbsEncryptionByDefault -Region us-east-1
Get-EC2EbsEncryptionByDefault -Region us-east-1
# Customer managed keys without automatic rotation
Get-KMSKeyList | ForEach-Object {
$k = Get-KMSKey -KeyId $_.KeyId
if ($k.KeyManager -eq 'CUSTOMER' -and $k.KeyState -eq 'Enabled' -and -not (Get-KMSKeyRotationStatus -KeyId $_.KeyId)) { $k.Arn }
}
Logs you will need later
Investigations fail most often because the logs were never collected, or were kept for too short a time. Keep these in the log archive account, for at least a year (longer if your auditors ask):
- CloudTrail organisation trail (above).
- AWS Config configuration history and snapshots: what a resource looked like at any point in time.
- VPC flow logs for production VPCs (Guide 4).
- Load balancer, CloudFront and WAF access logs for internet-facing services.
- Route 53 Resolver query logs: which instance looked up which domain, invaluable after a malware finding.
- Session Manager session logs.
For searching and correlating across all of them, Amazon Security Lake normalises these sources into one format (OCSF) in your own S3 buckets, ready for Athena or a SIEM such as Microsoft Sentinel or Splunk.
A security baseline checklist
| Area | Baseline | How to check |
|---|---|---|
| Identity | Identity Center with MFA; no root credentials or keys; break-glass users alarmed | Get-AwsIamCredentialReport |
| Management ports | No SSH or RDP from the internet; Session Manager instead | Get-AwsSecurityGroupReport |
| Data exposure | S3 Block Public Access on at account level; no public snapshots or AMIs | Get-AwsS3PublicAccessReport |
| Instances | IMDSv2 required; EBS encryption by default | The PowerShell above; Security Hub controls EC2.8 and EC2.7 |
| Detection | GuardDuty and Security Hub CSPM in every account and region, delegated admin | Security Hub console |
| Logging | Organisation CloudTrail to a locked log archive bucket; Config everywhere | Security Hub controls CloudTrail.1 and Config.1 |
| Secrets | Roles instead of keys; secrets in Secrets Manager with rotation | IAM Access Analyzer; Secrets Manager console |
| Backup | AWS Backup with a locked vault and cross-account copies for production | Guide 8; Get-AwsBackupCoverage |
Examples to try
From aws.ravindran.in: CloudTrail (and CloudTrail to CloudWatch), Secrets Manager (including database credentials), EBS encryption and S3 encryption.