Appearance
Set up AWS CloudTrail to track API calls, monitor security events, and maintain compliance audit trails for Pocketbook deployments on AWS.
Overview
AWS CloudTrail provides:
- Audit logging: Record all API calls and actions
- Compliance: Meet regulatory requirements (SOC 2, HIPAA, PCI-DSS)
- Security monitoring: Detect unauthorized access
- Incident investigation: Track events for forensic analysis
- Governance: Monitor resource changes
Prerequisites
- AWS Account with admin access
- IAM permissions for CloudTrail, S3, and CloudWatch
- S3 bucket for log storage
- (Optional) CloudWatch Logs for real-time monitoring
Basic CloudTrail Setup
Create S3 Bucket for Logs
bash
# Create S3 bucket
aws s3api create-bucket \
--bucket pocketbook-cloudtrail-logs \
--region us-east-1
# Enable versioning
aws s3api put-bucket-versioning \
--bucket pocketbook-cloudtrail-logs \
--versioning-configuration Status=Enabled
# Block public access
aws s3api put-public-access-block \
--bucket pocketbook-cloudtrail-logs \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true"Create S3 Bucket Policy
json
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AWSCloudTrailAclCheck",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:GetBucketAcl",
"Resource": "arn:aws:s3:::pocketbook-cloudtrail-logs"
},
{
"Sid": "AWSCloudTrailWrite",
"Effect": "Allow",
"Principal": {
"Service": "cloudtrail.amazonaws.com"
},
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::pocketbook-cloudtrail-logs/AWSLogs/123456789012/*",
"Condition": {
"StringEquals": {
"s3:x-amz-acl": "bucket-owner-full-control"
}
}
}
]
}Apply policy:
bash
aws s3api put-bucket-policy \
--bucket pocketbook-cloudtrail-logs \
--policy file://bucket-policy.jsonCreate CloudTrail
bash
aws cloudtrail create-trail \
--name pocketbook-audit-trail \
--s3-bucket-name pocketbook-cloudtrail-logs \
--is-multi-region-trail \
--enable-log-file-validation
# Start logging
aws cloudtrail start-logging \
--name pocketbook-audit-trailCloudTrail Configuration
Enable Specific Events
Management Events
bash
aws cloudtrail put-event-selectors \
--trail-name pocketbook-audit-trail \
--event-selectors '[
{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": []
}
]'Data Events (S3)
Track S3 object-level operations:
bash
aws cloudtrail put-event-selectors \
--trail-name pocketbook-audit-trail \
--event-selectors '[
{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": [
{
"Type": "AWS::S3::Object",
"Values": [
"arn:aws:s3:::pocketbook-uploads/*"
]
}
]
}
]'Data Events (Lambda)
Track Lambda function invocations:
bash
aws cloudtrail put-event-selectors \
--trail-name pocketbook-audit-trail \
--event-selectors '[
{
"ReadWriteType": "All",
"IncludeManagementEvents": true,
"DataResources": [
{
"Type": "AWS::Lambda::Function",
"Values": [
"arn:aws:lambda:us-east-1:123456789012:function/*"
]
}
]
}
]'CloudFormation Template
yaml
AWSTemplateFormatVersion: '2010-09-09'
Description: 'CloudTrail setup for Pocketbook'
Resources:
CloudTrailBucket:
Type: 'AWS::S3::Bucket'
Properties:
BucketName: pocketbook-cloudtrail-logs
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
LifecycleConfiguration:
Rules:
- Id: DeleteOldLogs
Status: Enabled
ExpirationInDays: 2555 # 7 years for compliance
Transitions:
- TransitionInDays: 90
StorageClass: GLACIER
- TransitionInDays: 365
StorageClass: DEEP_ARCHIVE
CloudTrailBucketPolicy:
Type: 'AWS::S3::BucketPolicy'
Properties:
Bucket: !Ref CloudTrailBucket
PolicyDocument:
Version: '2012-10-17'
Statement:
- Sid: AWSCloudTrailAclCheck
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: 's3:GetBucketAcl'
Resource: !GetAtt CloudTrailBucket.Arn
- Sid: AWSCloudTrailWrite
Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: 's3:PutObject'
Resource: !Sub '${CloudTrailBucket.Arn}/AWSLogs/${AWS::AccountId}/*'
Condition:
StringEquals:
's3:x-amz-acl': 'bucket-owner-full-control'
CloudTrail:
Type: 'AWS::CloudTrail::Trail'
DependsOn: CloudTrailBucketPolicy
Properties:
TrailName: pocketbook-audit-trail
S3BucketName: !Ref CloudTrailBucket
IsLogging: true
IsMultiRegionTrail: true
EnableLogFileValidation: true
IncludeGlobalServiceEvents: true
EventSelectors:
- ReadWriteType: All
IncludeManagementEvents: true
CloudTrailLogGroup:
Type: 'AWS::Logs::LogGroup'
Properties:
LogGroupName: /aws/cloudtrail/pocketbook
RetentionInDays: 90
CloudTrailRole:
Type: 'AWS::IAM::Role'
Properties:
AssumeRolePolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Principal:
Service: cloudtrail.amazonaws.com
Action: 'sts:AssumeRole'
Policies:
- PolicyName: CloudTrailLogPolicy
PolicyDocument:
Version: '2012-10-17'
Statement:
- Effect: Allow
Action:
- 'logs:CreateLogStream'
- 'logs:PutLogEvents'
Resource: !GetAtt CloudTrailLogGroup.ArnDeploy:
bash
aws cloudformation create-stack \
--stack-name pocketbook-cloudtrail \
--template-body file://cloudtrail.yaml \
--capabilities CAPABILITY_IAMCloudWatch Integration
Send Logs to CloudWatch
bash
# Create CloudWatch Logs group
aws logs create-log-group \
--log-group-name /aws/cloudtrail/pocketbook
# Create IAM role for CloudTrail
aws iam create-role \
--role-name CloudTrailRole \
--assume-role-policy-document file://trust-policy.json
# Attach policy
aws iam put-role-policy \
--role-name CloudTrailRole \
--policy-name CloudTrailLogPolicy \
--policy-document file://log-policy.json
# Update trail
aws cloudtrail update-trail \
--name pocketbook-audit-trail \
--cloud-watch-logs-log-group-arn arn:aws:logs:us-east-1:123456789012:log-group:/aws/cloudtrail/pocketbook:* \
--cloud-watch-logs-role-arn arn:aws:iam::123456789012:role/CloudTrailRoleCreate CloudWatch Alarms
bash
# Alert on root account usage
aws cloudwatch put-metric-alarm \
--alarm-name pocketbook-root-account-usage \
--alarm-description "Alert when root account is used" \
--metric-name RootAccountUsage \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 1 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alerts
# Alert on unauthorized API calls
aws cloudwatch put-metric-alarm \
--alarm-name pocketbook-unauthorized-api-calls \
--alarm-description "Alert on unauthorized API calls" \
--metric-name UnauthorizedAPICalls \
--namespace CloudTrailMetrics \
--statistic Sum \
--period 300 \
--threshold 5 \
--comparison-operator GreaterThanOrEqualToThreshold \
--evaluation-periods 1 \
--alarm-actions arn:aws:sns:us-east-1:123456789012:security-alertsMetric Filters
Track Security Events
bash
# Filter pattern for failed console logins
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/pocketbook \
--filter-name FailedConsoleLogins \
--filter-pattern '{ ($.eventName = ConsoleLogin) && ($.errorMessage = "Failed authentication") }' \
--metric-transformations \
metricName=FailedConsoleLogins,metricNamespace=CloudTrailMetrics,metricValue=1
# Filter pattern for IAM policy changes
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/pocketbook \
--filter-name IAMPolicyChanges \
--filter-pattern '{ ($.eventName = PutUserPolicy) || ($.eventName = PutRolePolicy) || ($.eventName = PutGroupPolicy) }' \
--metric-transformations \
metricName=IAMPolicyChanges,metricNamespace=CloudTrailMetrics,metricValue=1
# Filter pattern for S3 bucket changes
aws logs put-metric-filter \
--log-group-name /aws/cloudtrail/pocketbook \
--filter-name S3BucketChanges \
--filter-pattern '{ ($.eventSource = s3.amazonaws.com) && (($.eventName = PutBucketAcl) || ($.eventName = PutBucketPolicy) || ($.eventName = DeleteBucket)) }' \
--metric-transformations \
metricName=S3BucketChanges,metricNamespace=CloudTrailMetrics,metricValue=1Query CloudTrail Logs
Using AWS CLI
bash
# Look up recent events
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=EventName,AttributeValue=RunInstances \
--max-results 10
# Filter by user
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=Username,AttributeValue=admin@pocketbook.com \
--start-time 2024-01-01T00:00:00Z \
--end-time 2024-01-31T23:59:59Z
# Filter by resource
aws cloudtrail lookup-events \
--lookup-attributes AttributeKey=ResourceName,AttributeValue=pocketbook-productionUsing CloudWatch Logs Insights
sql
-- Find all failed authentication attempts
fields @timestamp, userIdentity.principalId, errorMessage
| filter eventName = "ConsoleLogin" and errorMessage = "Failed authentication"
| sort @timestamp desc
| limit 100
-- Track IAM changes
fields @timestamp, userIdentity.arn, eventName, requestParameters
| filter eventSource = "iam.amazonaws.com"
| sort @timestamp desc
-- S3 access patterns
fields @timestamp, userIdentity.principalId, eventName, requestParameters.bucketName
| filter eventSource = "s3.amazonaws.com"
| stats count() by eventNameUsing Athena
Create Athena table:
sql
CREATE EXTERNAL TABLE cloudtrail_logs (
eventversion STRING,
useridentity STRUCT<
type:STRING,
principalid:STRING,
arn:STRING,
accountid:STRING,
invokedby:STRING,
accesskeyid:STRING,
userName:STRING,
sessioncontext:STRUCT<
attributes:STRUCT<
mfaauthenticated:STRING,
creationdate:STRING>,
sessionissuer:STRUCT<
type:STRING,
principalId:STRING,
arn:STRING,
accountId:STRING,
userName:STRING>>>,
eventtime STRING,
eventsource STRING,
eventname STRING,
awsregion STRING,
sourceipaddress STRING,
useragent STRING,
errorcode STRING,
errormessage STRING,
requestparameters STRING,
responseelements STRING,
additionaleventdata STRING,
requestid STRING,
eventid STRING,
resources ARRAY<STRUCT<
ARN:STRING,
accountId:STRING,
type:STRING>>,
eventtype STRING,
apiversion STRING,
readonly STRING,
recipientaccountid STRING,
serviceeventdetails STRING,
sharedeventid STRING,
vpcendpointid STRING
)
ROW FORMAT SERDE 'com.amazon.emr.hive.serde.CloudTrailSerde'
STORED AS INPUTFORMAT 'com.amazon.emr.cloudtrail.CloudTrailInputFormat'
OUTPUTFORMAT 'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION 's3://pocketbook-cloudtrail-logs/AWSLogs/123456789012/CloudTrail/';Query with Athena:
sql
-- Find all API calls by specific user in last 7 days
SELECT eventtime, eventname, eventsource, sourceipaddress
FROM cloudtrail_logs
WHERE useridentity.username = 'admin@pocketbook.com'
AND eventtime > date_format(date_add('day', -7, now()), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventtime DESC;
-- Track resource deletions
SELECT eventtime, eventname, useridentity.arn, requestparameters
FROM cloudtrail_logs
WHERE eventname LIKE '%Delete%'
AND eventtime > date_format(date_add('day', -30, now()), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY eventtime DESC;Security Monitoring
Common Security Queries
sql
-- Detect root account usage
SELECT eventtime, sourceipaddress, useragent
FROM cloudtrail_logs
WHERE useridentity.type = 'Root'
AND eventtime > date_format(date_add('day', -1, now()), '%Y-%m-%dT%H:%i:%sZ');
-- Detect failed login attempts
SELECT eventtime, sourceipaddress, COUNT(*) as attempts
FROM cloudtrail_logs
WHERE eventname = 'ConsoleLogin'
AND errormessage = 'Failed authentication'
AND eventtime > date_format(date_add('hour', -1, now()), '%Y-%m-%dT%H:%i:%sZ')
GROUP BY eventtime, sourceipaddress
HAVING COUNT(*) > 5;
-- Track privilege escalation attempts
SELECT eventtime, useridentity.arn, eventname, requestparameters
FROM cloudtrail_logs
WHERE (eventname LIKE 'Put%Policy' OR eventname LIKE 'Attach%Policy')
AND eventtime > date_format(date_add('day', -7, now()), '%Y-%m-%dT%H:%i:%sZ');Compliance Reporting
Generate Compliance Reports
typescript
// scripts/generate-compliance-report.ts
import { CloudTrailClient, LookupEventsCommand } from '@aws-sdk/client-cloudtrail';
import { writeFileSync } from 'fs';
const client = new CloudTrailClient({ region: 'us-east-1' });
async function generateComplianceReport(startDate: Date, endDate: Date) {
const events = await client.send(
new LookupEventsCommand({
StartTime: startDate,
EndTime: endDate,
MaxResults: 1000,
})
);
const report = {
period: {
start: startDate.toISOString(),
end: endDate.toISOString(),
},
summary: {
totalEvents: events.Events?.length || 0,
userActions: events.Events?.filter(
(e) => e.Username !== 'AWSService'
).length,
automatedActions: events.Events?.filter(
(e) => e.Username === 'AWSService'
).length,
},
events: events.Events?.map((e) => ({
time: e.EventTime,
name: e.EventName,
user: e.Username,
resource: e.Resources?.[0]?.ResourceName,
})),
};
writeFileSync(
`compliance-report-${startDate.toISOString().split('T')[0]}.json`,
JSON.stringify(report, null, 2)
);
console.log('Compliance report generated');
}
// Generate monthly report
const now = new Date();
const lastMonth = new Date(now);
lastMonth.setMonth(lastMonth.getMonth() - 1);
generateComplianceReport(lastMonth, now);Best Practices
- Enable multi-region trails: Capture events from all regions
- Enable log file validation: Detect log tampering
- Use separate S3 bucket: Dedicated bucket for CloudTrail logs
- Enable MFA delete: Protect logs from deletion
- Set up lifecycle policies: Archive old logs to Glacier
- Monitor critical events: Alert on security-relevant events
- Regular log analysis: Review logs weekly for anomalies
- Integrate with SIEM: Forward logs to security tools
- Restrict access: Limit who can access CloudTrail logs
- Document procedures: Maintain runbooks for incident response
Cost Optimization
bash
# Enable S3 Intelligent-Tiering
aws s3api put-bucket-intelligent-tiering-configuration \
--bucket pocketbook-cloudtrail-logs \
--id cloudtrail-tiering \
--intelligent-tiering-configuration '{
"Id": "cloudtrail-tiering",
"Status": "Enabled",
"Tierings": [
{
"Days": 90,
"AccessTier": "ARCHIVE_ACCESS"
},
{
"Days": 180,
"AccessTier": "DEEP_ARCHIVE_ACCESS"
}
]
}'Troubleshooting
Logs Not Appearing
- Check CloudTrail is enabled and logging
- Verify S3 bucket policy allows CloudTrail writes
- Check IAM permissions
- Verify bucket exists and is in correct region
Query Athena for Missing Logs
sql
SELECT DISTINCT date_format(from_iso8601_timestamp(eventtime), '%Y-%m-%d') as log_date
FROM cloudtrail_logs
WHERE eventtime > date_format(date_add('day', -30, now()), '%Y-%m-%dT%H:%i:%sZ')
ORDER BY log_date DESC;Next Steps
- Security Guide - Additional security measures
- Secrets Manager - Secure credentials
- Monitoring (internal deployment documentation) - Infrastructure monitoring
- Incident Response - Security incident procedures
