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.ps1 lists 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

ServiceWhat it doesTurn on
Amazon GuardDutyThreat 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 monitoringEvery account and region, from a delegated administrator account
AWS Security Hub CSPMPosture checks against standards: AWS Foundational Security Best Practices, CIS AWS Foundations Benchmark, NIST, PCI DSS. A score per standard and a list of failed controlsEvery account and region, with central configuration
AWS Security HubCorrelates findings from GuardDuty, Inspector and Security Hub CSPM into prioritised exposures and attack paths, with ticketing integrationsOrganisation-wide, from the security tooling account
Amazon InspectorVulnerability scanning of EC2 instances, container images and Lambda functionsProduction accounts first
IAM Access AnalyzerResources shared outside the organisation, and unused accessOrganisation 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:

SettingWhy
Key policy that separates key administrators from key usersWhoever manages the key shouldn't automatically be able to decrypt the data
Automatic rotation onYearly by default (the period is adjustable); old versions still decrypt old data
One key per application and data classificationA compromised or misused key exposes less
Deletion waiting period (7–30 days) and an alarm on ScheduleKeyDeletionDeleting 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

AreaBaselineHow to check
IdentityIdentity Center with MFA; no root credentials or keys; break-glass users alarmedGet-AwsIamCredentialReport
Management portsNo SSH or RDP from the internet; Session Manager insteadGet-AwsSecurityGroupReport
Data exposureS3 Block Public Access on at account level; no public snapshots or AMIsGet-AwsS3PublicAccessReport
InstancesIMDSv2 required; EBS encryption by defaultThe PowerShell above; Security Hub controls EC2.8 and EC2.7
DetectionGuardDuty and Security Hub CSPM in every account and region, delegated adminSecurity Hub console
LoggingOrganisation CloudTrail to a locked log archive bucket; Config everywhereSecurity Hub controls CloudTrail.1 and Config.1
SecretsRoles instead of keys; secrets in Secrets Manager with rotationIAM Access Analyzer; Secrets Manager console
BackupAWS Backup with a locked vault and cross-account copies for productionGuide 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.