mirror of
https://github.com/BagelHole/DevOps-Security-Agent-Skills.git
synced 2026-08-22 12:49:53 +02:00
V2
This commit is contained in:
@@ -9,74 +9,445 @@ metadata:
|
||||
|
||||
# Audit Logging
|
||||
|
||||
Implement comprehensive audit logging for compliance.
|
||||
Implement comprehensive audit logging for compliance, security monitoring, and forensic analysis across infrastructure and applications.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Setting up centralized logging for compliance frameworks (SOC 2, HIPAA, PCI DSS)
|
||||
- Implementing security event monitoring and alerting
|
||||
- Building audit trails for regulatory requirements
|
||||
- Configuring log retention and tamper-proof storage
|
||||
- Integrating application logs with SIEM platforms
|
||||
|
||||
## Log Categories
|
||||
|
||||
```yaml
|
||||
audit_events:
|
||||
authentication:
|
||||
- Login attempts
|
||||
- MFA events
|
||||
- Session management
|
||||
|
||||
- Login attempts (success and failure)
|
||||
- MFA enrollment and verification events
|
||||
- Session creation, renewal, and termination
|
||||
- Password changes and resets
|
||||
- API key and token generation
|
||||
|
||||
authorization:
|
||||
- Access grants
|
||||
- Permission changes
|
||||
- Role assignments
|
||||
|
||||
- Access grants and denials
|
||||
- Permission changes and role assignments
|
||||
- Privilege escalation events
|
||||
- Resource sharing modifications
|
||||
- Policy evaluation results
|
||||
|
||||
data_access:
|
||||
- Read operations
|
||||
- Write operations
|
||||
- Delete operations
|
||||
|
||||
- Read operations on sensitive data
|
||||
- Write and update operations
|
||||
- Delete and purge operations
|
||||
- Bulk export and download events
|
||||
- Data classification changes
|
||||
|
||||
administrative:
|
||||
- Configuration changes
|
||||
- User management
|
||||
- System changes
|
||||
- User and group management
|
||||
- System startup and shutdown
|
||||
- Backup and restore operations
|
||||
- Network and firewall rule changes
|
||||
|
||||
system:
|
||||
- Service health state changes
|
||||
- Resource provisioning and deprovisioning
|
||||
- Certificate and key rotation events
|
||||
- Scheduled job execution results
|
||||
- Integration and webhook events
|
||||
```
|
||||
|
||||
## Application Logging
|
||||
## Rsyslog Configuration for Centralized Logging
|
||||
|
||||
```bash
|
||||
# /etc/rsyslog.d/50-audit.conf
|
||||
|
||||
# Load imfile module to read application logs
|
||||
module(load="imfile")
|
||||
|
||||
# Forward auth logs
|
||||
input(type="imfile"
|
||||
File="/var/log/auth.log"
|
||||
Tag="auth"
|
||||
Severity="info"
|
||||
Facility="auth"
|
||||
)
|
||||
|
||||
# Forward application audit logs
|
||||
input(type="imfile"
|
||||
File="/var/log/app/audit.log"
|
||||
Tag="app-audit"
|
||||
Severity="info"
|
||||
Facility="local0"
|
||||
)
|
||||
|
||||
# Structured JSON template
|
||||
template(name="json-audit" type="list") {
|
||||
constant(value="{")
|
||||
constant(value="\"@timestamp\":\"") property(name="timereported" dateFormat="rfc3339")
|
||||
constant(value="\",\"host\":\"") property(name="hostname")
|
||||
constant(value="\",\"severity\":\"") property(name="syslogseverity-text")
|
||||
constant(value="\",\"facility\":\"") property(name="syslogfacility-text")
|
||||
constant(value="\",\"tag\":\"") property(name="syslogtag" format="json")
|
||||
constant(value="\",\"message\":\"") property(name="msg" format="json")
|
||||
constant(value="\"}\n")
|
||||
}
|
||||
|
||||
# Forward to central syslog server over TLS
|
||||
action(
|
||||
type="omfwd"
|
||||
target="syslog.internal.example.com"
|
||||
port="6514"
|
||||
protocol="tcp"
|
||||
StreamDriver="gtls"
|
||||
StreamDriverMode="1"
|
||||
StreamDriverAuthMode="x509/name"
|
||||
template="json-audit"
|
||||
queue.type="LinkedList"
|
||||
queue.size="50000"
|
||||
queue.filename="fwd_audit"
|
||||
queue.saveonshutdown="on"
|
||||
action.resumeRetryCount="-1"
|
||||
)
|
||||
```
|
||||
|
||||
## Journald Configuration for Persistent Logging
|
||||
|
||||
```ini
|
||||
# /etc/systemd/journald.conf
|
||||
[Journal]
|
||||
Storage=persistent
|
||||
Compress=yes
|
||||
Seal=yes
|
||||
SplitMode=uid
|
||||
MaxRetentionSec=365d
|
||||
MaxFileSec=30d
|
||||
SystemMaxUse=10G
|
||||
SystemKeepFree=2G
|
||||
ForwardToSyslog=yes
|
||||
```
|
||||
|
||||
```bash
|
||||
# Query journald for audit events
|
||||
journalctl _TRANSPORT=audit --since "24 hours ago" --output json-pretty
|
||||
|
||||
# Filter by specific audit types
|
||||
journalctl _AUDIT_TYPE=1112 --since today # user login events
|
||||
journalctl _AUDIT_TYPE=1100 --since today # user auth events
|
||||
|
||||
# Export for offline analysis
|
||||
journalctl --since "7 days ago" --output export > /backup/journal-export.bin
|
||||
```
|
||||
|
||||
## Application Logging with Structured JSON
|
||||
|
||||
```python
|
||||
import logging
|
||||
import json
|
||||
import hashlib
|
||||
from datetime import datetime, timezone
|
||||
from functools import wraps
|
||||
|
||||
class AuditLogger:
|
||||
def log_event(self, event_type, user, resource, action, result):
|
||||
def __init__(self, service_name, logger_name="audit"):
|
||||
self.service = service_name
|
||||
self.logger = logging.getLogger(logger_name)
|
||||
handler = logging.FileHandler("/var/log/app/audit.log")
|
||||
handler.setFormatter(logging.Formatter("%(message)s"))
|
||||
self.logger.addHandler(handler)
|
||||
self.logger.setLevel(logging.INFO)
|
||||
self._prev_hash = None
|
||||
|
||||
def log_event(self, event_type, user, resource, action, result,
|
||||
metadata=None, source_ip=None):
|
||||
log_entry = {
|
||||
'timestamp': datetime.utcnow().isoformat(),
|
||||
'event_type': event_type,
|
||||
'user': user,
|
||||
'resource': resource,
|
||||
'action': action,
|
||||
'result': result,
|
||||
'source_ip': request.remote_addr
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"service": self.service,
|
||||
"event_type": event_type,
|
||||
"user": user,
|
||||
"resource": resource,
|
||||
"action": action,
|
||||
"result": result,
|
||||
"source_ip": source_ip,
|
||||
"metadata": metadata or {},
|
||||
}
|
||||
logger.info(json.dumps(log_entry))
|
||||
# Chain hash for tamper detection
|
||||
raw = json.dumps(log_entry, sort_keys=True)
|
||||
log_entry["prev_hash"] = self._prev_hash
|
||||
log_entry["hash"] = hashlib.sha256(
|
||||
f"{self._prev_hash}:{raw}".encode()
|
||||
).hexdigest()
|
||||
self._prev_hash = log_entry["hash"]
|
||||
self.logger.info(json.dumps(log_entry))
|
||||
|
||||
def log_auth(self, user, action, success, source_ip=None, mfa=False):
|
||||
self.log_event(
|
||||
event_type="authentication",
|
||||
user=user,
|
||||
resource="auth-service",
|
||||
action=action,
|
||||
result="success" if success else "failure",
|
||||
metadata={"mfa_used": mfa},
|
||||
source_ip=source_ip,
|
||||
)
|
||||
|
||||
def log_data_access(self, user, resource, operation, record_count=0,
|
||||
source_ip=None):
|
||||
self.log_event(
|
||||
event_type="data_access",
|
||||
user=user,
|
||||
resource=resource,
|
||||
action=operation,
|
||||
result="success",
|
||||
metadata={"record_count": record_count},
|
||||
source_ip=source_ip,
|
||||
)
|
||||
|
||||
|
||||
def audit_trail(audit_logger, resource_name):
|
||||
"""Decorator to automatically audit function calls."""
|
||||
def decorator(func):
|
||||
@wraps(func)
|
||||
def wrapper(*args, **kwargs):
|
||||
user = kwargs.get("current_user", "system")
|
||||
try:
|
||||
result = func(*args, **kwargs)
|
||||
audit_logger.log_event(
|
||||
event_type="operation",
|
||||
user=user,
|
||||
resource=resource_name,
|
||||
action=func.__name__,
|
||||
result="success",
|
||||
)
|
||||
return result
|
||||
except Exception as e:
|
||||
audit_logger.log_event(
|
||||
event_type="operation",
|
||||
user=user,
|
||||
resource=resource_name,
|
||||
action=func.__name__,
|
||||
result="failure",
|
||||
metadata={"error": str(e)},
|
||||
)
|
||||
raise
|
||||
return wrapper
|
||||
return decorator
|
||||
```
|
||||
|
||||
## Centralized Logging
|
||||
## Fluentd / Fluent Bit Log Aggregation
|
||||
|
||||
```yaml
|
||||
# Fluentd configuration
|
||||
<source>
|
||||
@type tail
|
||||
path /var/log/audit/*.log
|
||||
tag audit.*
|
||||
</source>
|
||||
# fluent-bit.conf - lightweight agent on each node
|
||||
[SERVICE]
|
||||
Flush 5
|
||||
Daemon Off
|
||||
Log_Level info
|
||||
Parsers_File parsers.conf
|
||||
|
||||
<match audit.**>
|
||||
@type elasticsearch
|
||||
host elasticsearch.example.com
|
||||
index_name audit-logs
|
||||
</match>
|
||||
[INPUT]
|
||||
Name tail
|
||||
Path /var/log/app/audit.log
|
||||
Parser json
|
||||
Tag audit.app
|
||||
Refresh_Interval 5
|
||||
Rotate_Wait 30
|
||||
|
||||
[INPUT]
|
||||
Name systemd
|
||||
Tag audit.system
|
||||
Systemd_Filter _TRANSPORT=audit
|
||||
|
||||
[FILTER]
|
||||
Name modify
|
||||
Match audit.*
|
||||
Add cluster ${CLUSTER_NAME}
|
||||
Add node ${NODE_NAME}
|
||||
|
||||
[OUTPUT]
|
||||
Name es
|
||||
Match audit.*
|
||||
Host elasticsearch.internal.example.com
|
||||
Port 9200
|
||||
Index audit-logs
|
||||
Type _doc
|
||||
tls On
|
||||
tls.verify On
|
||||
Retry_Limit 5
|
||||
|
||||
[OUTPUT]
|
||||
Name s3
|
||||
Match audit.*
|
||||
region us-east-1
|
||||
bucket audit-logs-archive
|
||||
total_file_size 50M
|
||||
upload_timeout 10m
|
||||
s3_key_format /logs/%Y/%m/%d/$TAG/%H_%M_%S.gz
|
||||
compression gzip
|
||||
```
|
||||
|
||||
## Elasticsearch Index Lifecycle for Retention
|
||||
|
||||
```json
|
||||
{
|
||||
"policy": {
|
||||
"phases": {
|
||||
"hot": {
|
||||
"min_age": "0ms",
|
||||
"actions": {
|
||||
"rollover": {
|
||||
"max_size": "50gb",
|
||||
"max_age": "1d"
|
||||
},
|
||||
"set_priority": { "priority": 100 }
|
||||
}
|
||||
},
|
||||
"warm": {
|
||||
"min_age": "7d",
|
||||
"actions": {
|
||||
"shrink": { "number_of_shards": 1 },
|
||||
"forcemerge": { "max_num_segments": 1 },
|
||||
"set_priority": { "priority": 50 }
|
||||
}
|
||||
},
|
||||
"cold": {
|
||||
"min_age": "30d",
|
||||
"actions": {
|
||||
"freeze": {},
|
||||
"set_priority": { "priority": 0 }
|
||||
}
|
||||
},
|
||||
"delete": {
|
||||
"min_age": "365d",
|
||||
"actions": { "delete": {} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Retention Policy by Compliance Framework
|
||||
|
||||
```yaml
|
||||
retention_requirements:
|
||||
soc2:
|
||||
minimum: 1 year
|
||||
recommended: 3 years
|
||||
notes: "Based on audit period and report requirements"
|
||||
|
||||
hipaa:
|
||||
minimum: 6 years
|
||||
notes: "From date of creation or last effective date"
|
||||
|
||||
pci_dss:
|
||||
minimum: 1 year
|
||||
immediately_available: 3 months
|
||||
notes: "Req 10.7 - retain for at least one year, 3 months immediately available"
|
||||
|
||||
gdpr:
|
||||
minimum: "As long as necessary for processing purpose"
|
||||
notes: "Apply data minimization; delete when no longer needed"
|
||||
|
||||
fedramp:
|
||||
minimum: 3 years
|
||||
notes: "AU-11 control requirement"
|
||||
|
||||
iso27001:
|
||||
minimum: "Defined by organization policy"
|
||||
recommended: 3 years
|
||||
notes: "A.12.4.1 - retention period must be defined"
|
||||
```
|
||||
|
||||
## Log Integrity Verification Script
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# verify-log-integrity.sh - Verify log file checksums against stored hashes
|
||||
|
||||
LOG_DIR="/var/log/app"
|
||||
HASH_FILE="/var/log/app/.checksums"
|
||||
ALERT_WEBHOOK="${ALERT_WEBHOOK_URL}"
|
||||
|
||||
verify_logs() {
|
||||
local failures=0
|
||||
while IFS=' ' read -r stored_hash filename; do
|
||||
if [ -f "$filename" ]; then
|
||||
current_hash=$(sha256sum "$filename" | awk '{print $1}')
|
||||
if [ "$stored_hash" != "$current_hash" ]; then
|
||||
echo "TAMPER DETECTED: $filename"
|
||||
failures=$((failures + 1))
|
||||
curl -s -X POST "$ALERT_WEBHOOK" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"text\":\"ALERT: Audit log tamper detected on $(hostname): $filename\"}"
|
||||
fi
|
||||
else
|
||||
echo "MISSING: $filename"
|
||||
failures=$((failures + 1))
|
||||
fi
|
||||
done < "$HASH_FILE"
|
||||
|
||||
return $failures
|
||||
}
|
||||
|
||||
update_checksums() {
|
||||
find "$LOG_DIR" -name "*.log" -type f -exec sha256sum {} \; > "$HASH_FILE"
|
||||
chmod 440 "$HASH_FILE"
|
||||
}
|
||||
|
||||
case "${1:-verify}" in
|
||||
verify) verify_logs ;;
|
||||
update) update_checksums ;;
|
||||
*) echo "Usage: $0 {verify|update}" ;;
|
||||
esac
|
||||
```
|
||||
|
||||
## SIEM Integration Checklist
|
||||
|
||||
```yaml
|
||||
siem_integration:
|
||||
log_sources:
|
||||
- [ ] Operating system auth logs (syslog, journald)
|
||||
- [ ] Application audit logs (structured JSON)
|
||||
- [ ] Cloud provider audit trails (CloudTrail, Activity Log, Audit Logs)
|
||||
- [ ] Database query and access logs
|
||||
- [ ] Network flow logs and firewall logs
|
||||
- [ ] Container and orchestrator logs (Kubernetes audit)
|
||||
- [ ] WAF and CDN access logs
|
||||
- [ ] VPN and remote access logs
|
||||
|
||||
normalization:
|
||||
- [ ] Common event format (CEF) or OCSF schema
|
||||
- [ ] Consistent timestamp format (ISO 8601 / UTC)
|
||||
- [ ] Unified user identity fields
|
||||
- [ ] Standardized severity levels
|
||||
|
||||
alerting_rules:
|
||||
- [ ] Multiple failed login attempts (brute force)
|
||||
- [ ] Login from unusual location or device
|
||||
- [ ] Privilege escalation events
|
||||
- [ ] Sensitive data bulk export
|
||||
- [ ] Administrative action outside change window
|
||||
- [ ] Service account anomalous activity
|
||||
- [ ] Log forwarding gap or interruption
|
||||
|
||||
operational:
|
||||
- [ ] Log pipeline health monitoring
|
||||
- [ ] Storage capacity alerting
|
||||
- [ ] Retention policy enforcement verified
|
||||
- [ ] Backup of log archives confirmed
|
||||
- [ ] Access to log systems restricted and audited
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Structured logging (JSON)
|
||||
- Centralized collection
|
||||
- Tamper-proof storage
|
||||
- Retention policies
|
||||
- Alerting on anomalies
|
||||
- Use structured logging (JSON) with consistent field names across all services
|
||||
- Ship logs to a centralized platform with write-once storage for tamper protection
|
||||
- Implement hash chaining or digital signatures for log integrity verification
|
||||
- Define and enforce retention policies per compliance framework requirements
|
||||
- Set up real-time alerting for high-severity security events
|
||||
- Separate audit logs from application debug logs to reduce noise
|
||||
- Never log sensitive data (passwords, tokens, PII) in audit entries
|
||||
- Monitor the logging pipeline itself to detect gaps in coverage
|
||||
- Regularly test log restoration from archives to verify recoverability
|
||||
- Rotate and compress logs to manage storage while meeting retention windows
|
||||
|
||||
@@ -9,56 +9,455 @@ metadata:
|
||||
|
||||
# AWS CloudTrail
|
||||
|
||||
Audit AWS account activity with CloudTrail.
|
||||
Audit AWS account activity with CloudTrail for compliance, security investigation, and operational troubleshooting.
|
||||
|
||||
## Create Trail
|
||||
## When to Use
|
||||
|
||||
- Enabling organization-wide audit logging across all AWS accounts
|
||||
- Investigating security incidents or unauthorized API activity
|
||||
- Meeting compliance requirements for SOC 2, HIPAA, PCI DSS, or FedRAMP
|
||||
- Setting up automated alerting on sensitive AWS API calls
|
||||
- Querying historical AWS activity for forensic analysis
|
||||
|
||||
## Create an Organization Trail
|
||||
|
||||
```bash
|
||||
# Create organization trail
|
||||
# Create the S3 bucket for log storage
|
||||
aws s3api create-bucket \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--region us-east-1
|
||||
|
||||
# Apply bucket policy allowing CloudTrail to write
|
||||
aws s3api put-bucket-policy \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--policy '{
|
||||
"Version": "2012-10-17",
|
||||
"Statement": [
|
||||
{
|
||||
"Sid": "AWSCloudTrailAclCheck",
|
||||
"Effect": "Allow",
|
||||
"Principal": {"Service": "cloudtrail.amazonaws.com"},
|
||||
"Action": "s3:GetBucketAcl",
|
||||
"Resource": "arn:aws:s3:::org-cloudtrail-audit-logs"
|
||||
},
|
||||
{
|
||||
"Sid": "AWSCloudTrailWrite",
|
||||
"Effect": "Allow",
|
||||
"Principal": {"Service": "cloudtrail.amazonaws.com"},
|
||||
"Action": "s3:PutObject",
|
||||
"Resource": "arn:aws:s3:::org-cloudtrail-audit-logs/AWSLogs/*",
|
||||
"Condition": {
|
||||
"StringEquals": {"s3:x-amz-acl": "bucket-owner-full-control"}
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
# Block public access on the audit bucket
|
||||
aws s3api put-public-access-block \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--public-access-block-configuration \
|
||||
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
|
||||
|
||||
# Enable versioning for tamper protection
|
||||
aws s3api put-bucket-versioning \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--versioning-configuration Status=Enabled
|
||||
|
||||
# Enable server-side encryption
|
||||
aws s3api put-bucket-encryption \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--server-side-encryption-configuration '{
|
||||
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "alias/cloudtrail-key"}}]
|
||||
}'
|
||||
|
||||
# Set lifecycle policy for log retention
|
||||
aws s3api put-bucket-lifecycle-configuration \
|
||||
--bucket org-cloudtrail-audit-logs \
|
||||
--lifecycle-configuration '{
|
||||
"Rules": [
|
||||
{
|
||||
"ID": "TransitionToGlacier",
|
||||
"Status": "Enabled",
|
||||
"Filter": {"Prefix": "AWSLogs/"},
|
||||
"Transitions": [
|
||||
{"Days": 90, "StorageClass": "GLACIER"}
|
||||
]
|
||||
},
|
||||
{
|
||||
"ID": "ExpireOldLogs",
|
||||
"Status": "Enabled",
|
||||
"Filter": {"Prefix": "AWSLogs/"},
|
||||
"Expiration": {"Days": 2555}
|
||||
}
|
||||
]
|
||||
}'
|
||||
|
||||
# Create the organization trail
|
||||
aws cloudtrail create-trail \
|
||||
--name org-audit-trail \
|
||||
--s3-bucket-name audit-logs-bucket \
|
||||
--s3-bucket-name org-cloudtrail-audit-logs \
|
||||
--is-organization-trail \
|
||||
--is-multi-region-trail \
|
||||
--enable-log-file-validation \
|
||||
--kms-key-id arn:aws:kms:...
|
||||
--kms-key-id arn:aws:kms:us-east-1:123456789012:alias/cloudtrail-key \
|
||||
--cloud-watch-logs-log-group-arn arn:aws:logs:us-east-1:123456789012:log-group:CloudTrail:* \
|
||||
--cloud-watch-logs-role-arn arn:aws:iam::123456789012:role/CloudTrail-CWLogs-Role
|
||||
|
||||
# Start logging
|
||||
aws cloudtrail start-logging --name org-audit-trail
|
||||
```
|
||||
|
||||
## Event Selectors
|
||||
## Event Selectors for Management and Data Events
|
||||
|
||||
```bash
|
||||
# Log all management and data events
|
||||
# Configure advanced event selectors for granular control
|
||||
aws cloudtrail put-event-selectors \
|
||||
--trail-name org-audit-trail \
|
||||
--event-selectors '[{
|
||||
"ReadWriteType": "All",
|
||||
"IncludeManagementEvents": true,
|
||||
"DataResources": [{
|
||||
"Type": "AWS::S3::Object",
|
||||
"Values": ["arn:aws:s3:::sensitive-bucket/"]
|
||||
}]
|
||||
}]'
|
||||
--advanced-event-selectors '[
|
||||
{
|
||||
"Name": "AllManagementEvents",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Management"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "S3DataEventsForSensitiveBuckets",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Data"]},
|
||||
{"Field": "resources.type", "Equals": ["AWS::S3::Object"]},
|
||||
{"Field": "resources.ARN", "StartsWith": [
|
||||
"arn:aws:s3:::sensitive-data-bucket/",
|
||||
"arn:aws:s3:::pii-bucket/",
|
||||
"arn:aws:s3:::financial-data/"
|
||||
]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "LambdaInvocations",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Data"]},
|
||||
{"Field": "resources.type", "Equals": ["AWS::Lambda::Function"]}
|
||||
]
|
||||
},
|
||||
{
|
||||
"Name": "DynamoDBDataEvents",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Data"]},
|
||||
{"Field": "resources.type", "Equals": ["AWS::DynamoDB::Table"]}
|
||||
]
|
||||
}
|
||||
]'
|
||||
```
|
||||
|
||||
## CloudTrail Lake
|
||||
## CloudWatch Alerts for Sensitive Activity
|
||||
|
||||
```bash
|
||||
# Create metric filter for unauthorized API calls
|
||||
aws logs put-metric-filter \
|
||||
--log-group-name CloudTrail \
|
||||
--filter-name UnauthorizedAPICalls \
|
||||
--filter-pattern '{ ($.errorCode = "*UnauthorizedAccess*") || ($.errorCode = "AccessDenied*") }' \
|
||||
--metric-transformations \
|
||||
metricName=UnauthorizedAPICalls,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
|
||||
# Create alarm for unauthorized calls
|
||||
aws cloudwatch put-metric-alarm \
|
||||
--alarm-name UnauthorizedAPICallsAlarm \
|
||||
--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-alerts
|
||||
|
||||
# Root account usage alarm
|
||||
aws logs put-metric-filter \
|
||||
--log-group-name CloudTrail \
|
||||
--filter-name RootAccountUsage \
|
||||
--filter-pattern '{ ($.userIdentity.type = "Root") && ($.userIdentity.invokedBy NOT EXISTS) && ($.eventType != "AwsServiceEvent") }' \
|
||||
--metric-transformations \
|
||||
metricName=RootAccountUsage,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
|
||||
aws cloudwatch put-metric-alarm \
|
||||
--alarm-name RootAccountUsageAlarm \
|
||||
--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
|
||||
|
||||
# Console login without MFA
|
||||
aws logs put-metric-filter \
|
||||
--log-group-name CloudTrail \
|
||||
--filter-name ConsoleLoginWithoutMFA \
|
||||
--filter-pattern '{ ($.eventName = "ConsoleLogin") && ($.additionalEventData.MFAUsed != "Yes") && ($.userIdentity.type = "IAMUser") }' \
|
||||
--metric-transformations \
|
||||
metricName=ConsoleLoginWithoutMFA,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
|
||||
# IAM policy changes
|
||||
aws logs put-metric-filter \
|
||||
--log-group-name CloudTrail \
|
||||
--filter-name IAMPolicyChanges \
|
||||
--filter-pattern '{ ($.eventName=CreatePolicy) || ($.eventName=DeletePolicy) || ($.eventName=AttachRolePolicy) || ($.eventName=DetachRolePolicy) || ($.eventName=AttachUserPolicy) || ($.eventName=PutUserPolicy) }' \
|
||||
--metric-transformations \
|
||||
metricName=IAMPolicyChanges,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
|
||||
# Security group changes
|
||||
aws logs put-metric-filter \
|
||||
--log-group-name CloudTrail \
|
||||
--filter-name SecurityGroupChanges \
|
||||
--filter-pattern '{ ($.eventName=AuthorizeSecurityGroupIngress) || ($.eventName=RevokeSecurityGroupIngress) || ($.eventName=CreateSecurityGroup) || ($.eventName=DeleteSecurityGroup) }' \
|
||||
--metric-transformations \
|
||||
metricName=SecurityGroupChanges,metricNamespace=CloudTrailMetrics,metricValue=1
|
||||
```
|
||||
|
||||
## Athena Queries for CloudTrail Analysis
|
||||
|
||||
```sql
|
||||
-- Query events
|
||||
SELECT eventTime, userIdentity.userName, eventName, sourceIPAddress
|
||||
-- Create Athena table for CloudTrail logs
|
||||
CREATE EXTERNAL TABLE IF NOT EXISTS 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,
|
||||
readOnly STRING,
|
||||
resources ARRAY<STRUCT<arn: STRING, accountId: STRING, type: STRING>>,
|
||||
eventType STRING,
|
||||
recipientAccountId STRING
|
||||
)
|
||||
PARTITIONED BY (region STRING, year STRING, month STRING, day STRING)
|
||||
ROW FORMAT SERDE 'org.apache.hive.hcatalog.data.JsonSerDe'
|
||||
LOCATION 's3://org-cloudtrail-audit-logs/AWSLogs/123456789012/CloudTrail/';
|
||||
|
||||
-- Find all delete operations in the last 7 days
|
||||
SELECT eventTime, userIdentity.arn, eventName, sourceIPAddress,
|
||||
requestParameters
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventTime > '2024-01-01'
|
||||
AND eventName LIKE '%Delete%'
|
||||
WHERE eventName LIKE '%Delete%'
|
||||
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
|
||||
ORDER BY eventTime DESC
|
||||
LIMIT 100
|
||||
LIMIT 100;
|
||||
|
||||
-- Identify console logins from unusual IP addresses
|
||||
SELECT eventTime, userIdentity.userName, sourceIPAddress,
|
||||
additionalEventData
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventName = 'ConsoleLogin'
|
||||
AND sourceIPAddress NOT IN ('198.51.100.0/24', '203.0.113.0/24')
|
||||
AND eventTime > date_format(date_add('day', -30, current_date), '%Y-%m-%dT%H:%i:%sZ')
|
||||
ORDER BY eventTime DESC;
|
||||
|
||||
-- Access key usage patterns per principal
|
||||
SELECT userIdentity.arn,
|
||||
count(*) AS api_call_count,
|
||||
count(DISTINCT eventName) AS unique_actions,
|
||||
count(DISTINCT sourceIPAddress) AS unique_ips,
|
||||
min(eventTime) AS first_seen,
|
||||
max(eventTime) AS last_seen
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventTime > date_format(date_add('day', -30, current_date), '%Y-%m-%dT%H:%i:%sZ')
|
||||
GROUP BY userIdentity.arn
|
||||
ORDER BY api_call_count DESC
|
||||
LIMIT 50;
|
||||
|
||||
-- Failed API calls indicating permission issues or reconnaissance
|
||||
SELECT eventTime, userIdentity.arn, eventName, errorCode, errorMessage,
|
||||
sourceIPAddress
|
||||
FROM cloudtrail_logs
|
||||
WHERE errorCode IN ('AccessDenied', 'UnauthorizedAccess', 'Client.UnauthorizedAccess')
|
||||
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
|
||||
ORDER BY eventTime DESC
|
||||
LIMIT 200;
|
||||
|
||||
-- Track KMS key usage
|
||||
SELECT eventTime, userIdentity.arn, eventName, requestParameters,
|
||||
resources[1].arn AS key_arn
|
||||
FROM cloudtrail_logs
|
||||
WHERE eventSource = 'kms.amazonaws.com'
|
||||
AND eventName IN ('Decrypt', 'Encrypt', 'GenerateDataKey', 'DisableKey', 'ScheduleKeyDeletion')
|
||||
AND eventTime > date_format(date_add('day', -7, current_date), '%Y-%m-%dT%H:%i:%sZ')
|
||||
ORDER BY eventTime DESC;
|
||||
```
|
||||
|
||||
## CloudTrail Lake (Event Data Store)
|
||||
|
||||
```bash
|
||||
# Create an event data store for long-term queryable storage
|
||||
aws cloudtrail create-event-data-store \
|
||||
--name org-audit-event-store \
|
||||
--multi-region-enabled \
|
||||
--organization-enabled \
|
||||
--retention-period 2555 \
|
||||
--advanced-event-selectors '[
|
||||
{
|
||||
"Name": "AllManagementEvents",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Management"]}
|
||||
]
|
||||
}
|
||||
]'
|
||||
```
|
||||
|
||||
```sql
|
||||
-- CloudTrail Lake SQL queries (run in console or via StartQuery API)
|
||||
-- Investigate a specific user's activity
|
||||
SELECT eventTime, eventName, eventSource, sourceIPAddress,
|
||||
errorCode, requestParameters
|
||||
FROM EVENT_DATA_STORE_ID
|
||||
WHERE userIdentity.arn = 'arn:aws:iam::123456789012:user/suspicious-user'
|
||||
AND eventTime > '2024-01-01 00:00:00'
|
||||
ORDER BY eventTime DESC;
|
||||
|
||||
-- Cross-account activity summary
|
||||
SELECT recipientAccountId, userIdentity.arn,
|
||||
count(*) AS event_count
|
||||
FROM EVENT_DATA_STORE_ID
|
||||
WHERE eventTime > '2024-01-01 00:00:00'
|
||||
GROUP BY recipientAccountId, userIdentity.arn
|
||||
ORDER BY event_count DESC;
|
||||
```
|
||||
|
||||
## Validate Trail Integrity
|
||||
|
||||
```bash
|
||||
# Validate log file integrity for a date range
|
||||
aws cloudtrail validate-logs \
|
||||
--trail-arn arn:aws:cloudtrail:us-east-1:123456789012:trail/org-audit-trail \
|
||||
--start-time "2024-01-01T00:00:00Z" \
|
||||
--end-time "2024-01-31T23:59:59Z"
|
||||
|
||||
# Check trail status
|
||||
aws cloudtrail get-trail-status --name org-audit-trail
|
||||
|
||||
# Describe the trail configuration
|
||||
aws cloudtrail describe-trails --trail-name-list org-audit-trail
|
||||
```
|
||||
|
||||
## Terraform Configuration
|
||||
|
||||
```hcl
|
||||
resource "aws_cloudtrail" "org_trail" {
|
||||
name = "org-audit-trail"
|
||||
s3_bucket_name = aws_s3_bucket.cloudtrail.id
|
||||
is_organization_trail = true
|
||||
is_multi_region_trail = true
|
||||
enable_log_file_validation = true
|
||||
kms_key_id = aws_kms_key.cloudtrail.arn
|
||||
cloud_watch_logs_group_arn = "${aws_cloudwatch_log_group.cloudtrail.arn}:*"
|
||||
cloud_watch_logs_role_arn = aws_iam_role.cloudtrail_cw.arn
|
||||
include_global_service_events = true
|
||||
|
||||
advanced_event_selector {
|
||||
name = "AllManagementEvents"
|
||||
field_selector {
|
||||
field = "eventCategory"
|
||||
equals = ["Management"]
|
||||
}
|
||||
}
|
||||
|
||||
advanced_event_selector {
|
||||
name = "SensitiveS3DataEvents"
|
||||
field_selector {
|
||||
field = "eventCategory"
|
||||
equals = ["Data"]
|
||||
}
|
||||
field_selector {
|
||||
field = "resources.type"
|
||||
equals = ["AWS::S3::Object"]
|
||||
}
|
||||
field_selector {
|
||||
field = "resources.ARN"
|
||||
starts_with = ["arn:aws:s3:::sensitive-data-bucket/"]
|
||||
}
|
||||
}
|
||||
|
||||
tags = {
|
||||
Environment = "production"
|
||||
Compliance = "soc2,hipaa"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setup Checklist
|
||||
|
||||
```yaml
|
||||
cloudtrail_checklist:
|
||||
trail_configuration:
|
||||
- [ ] Organization trail enabled across all accounts
|
||||
- [ ] Multi-region trail enabled
|
||||
- [ ] Log file validation enabled
|
||||
- [ ] KMS encryption configured with dedicated key
|
||||
- [ ] CloudWatch Logs integration active
|
||||
- [ ] S3 bucket policy restricts access to CloudTrail service only
|
||||
|
||||
s3_bucket_hardening:
|
||||
- [ ] Public access blocked
|
||||
- [ ] Versioning enabled
|
||||
- [ ] Server-side encryption enabled
|
||||
- [ ] Lifecycle policy set for retention and archival
|
||||
- [ ] Access logging enabled on the bucket itself
|
||||
- [ ] Object Lock enabled for WORM compliance (if required)
|
||||
|
||||
monitoring_and_alerting:
|
||||
- [ ] Metric filters for unauthorized API calls
|
||||
- [ ] Alarm on root account usage
|
||||
- [ ] Alarm on console login without MFA
|
||||
- [ ] Alarm on IAM policy changes
|
||||
- [ ] Alarm on security group and NACL changes
|
||||
- [ ] Alarm on CloudTrail configuration changes
|
||||
- [ ] Alarm on S3 bucket policy changes
|
||||
|
||||
analysis:
|
||||
- [ ] Athena table created for ad-hoc queries
|
||||
- [ ] CloudTrail Lake event data store for long-term queries
|
||||
- [ ] Regular review of high-risk API patterns
|
||||
- [ ] Automated reports for compliance evidence
|
||||
|
||||
operational:
|
||||
- [ ] Trail status health check automated
|
||||
- [ ] Log delivery latency monitored
|
||||
- [ ] Log file validation run periodically
|
||||
- [ ] SNS notification for trail configuration changes
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Organization-wide trails
|
||||
- Enable log file validation
|
||||
- Encrypt with KMS
|
||||
- CloudWatch Logs integration
|
||||
- Event alerting
|
||||
- Enable organization-wide trails from the management account for full coverage
|
||||
- Always enable log file validation to detect tampering
|
||||
- Encrypt logs with a customer-managed KMS key and restrict key usage
|
||||
- Use advanced event selectors to capture data events on sensitive resources without logging everything
|
||||
- Integrate with CloudWatch Logs for real-time metric filters and alarms
|
||||
- Set up Athena or CloudTrail Lake for efficient querying during investigations
|
||||
- Apply S3 lifecycle policies to transition old logs to Glacier and enforce retention
|
||||
- Monitor the trail itself (delivery errors, configuration changes) as a meta-control
|
||||
- Validate log integrity periodically as part of compliance evidence collection
|
||||
- Restrict access to the CloudTrail S3 bucket and KMS key with least-privilege IAM policies
|
||||
|
||||
@@ -9,49 +9,348 @@ metadata:
|
||||
|
||||
# Azure Monitor Audit
|
||||
|
||||
Audit Azure activity with Monitor and Activity Logs.
|
||||
Audit Azure activity with Monitor, Activity Logs, and Log Analytics for compliance, security, and operational visibility.
|
||||
|
||||
## Diagnostic Settings
|
||||
## When to Use
|
||||
|
||||
- Enabling centralized audit logging across Azure subscriptions
|
||||
- Meeting compliance requirements for SOC 2, HIPAA, PCI DSS, or ISO 27001
|
||||
- Investigating security incidents or unauthorized activity in Azure
|
||||
- Setting up alerting on administrative and security events
|
||||
- Building compliance dashboards and automated evidence collection
|
||||
|
||||
## Create Log Analytics Workspace
|
||||
|
||||
```bash
|
||||
# Enable diagnostic settings
|
||||
az monitor diagnostic-settings create \
|
||||
--name audit-logs \
|
||||
--resource /subscriptions/{sub}/resourceGroups/{rg}/providers/... \
|
||||
--logs '[{"category":"AuditEvent","enabled":true}]' \
|
||||
--workspace /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.OperationalInsights/workspaces/{workspace}
|
||||
# Create resource group for audit resources
|
||||
az group create \
|
||||
--name rg-audit \
|
||||
--location eastus
|
||||
|
||||
# Create Log Analytics workspace
|
||||
az monitor log-analytics workspace create \
|
||||
--resource-group rg-audit \
|
||||
--workspace-name audit-workspace \
|
||||
--location eastus \
|
||||
--retention-time 365 \
|
||||
--sku PerGB2018
|
||||
|
||||
# Get workspace ID for later use
|
||||
WORKSPACE_ID=$(az monitor log-analytics workspace show \
|
||||
--resource-group rg-audit \
|
||||
--workspace-name audit-workspace \
|
||||
--query id -o tsv)
|
||||
|
||||
# Enable audit solutions
|
||||
az monitor log-analytics solution create \
|
||||
--resource-group rg-audit \
|
||||
--solution-type SecurityCenterFree \
|
||||
--workspace audit-workspace
|
||||
```
|
||||
|
||||
## Activity Log Export
|
||||
## Configure Diagnostic Settings for Subscription Activity Log
|
||||
|
||||
```bash
|
||||
# Export activity log to Log Analytics
|
||||
# Export subscription activity log to Log Analytics
|
||||
az monitor diagnostic-settings subscription create \
|
||||
--name activity-log-export \
|
||||
--name activity-log-to-workspace \
|
||||
--location global \
|
||||
--logs '[{"category":"Administrative","enabled":true},{"category":"Security","enabled":true}]' \
|
||||
--workspace /subscriptions/.../workspaces/audit-workspace
|
||||
--workspace "$WORKSPACE_ID" \
|
||||
--logs '[
|
||||
{"category": "Administrative", "enabled": true},
|
||||
{"category": "Security", "enabled": true},
|
||||
{"category": "ServiceHealth", "enabled": true},
|
||||
{"category": "Alert", "enabled": true},
|
||||
{"category": "Recommendation", "enabled": true},
|
||||
{"category": "Policy", "enabled": true},
|
||||
{"category": "Autoscale", "enabled": true},
|
||||
{"category": "ResourceHealth", "enabled": true}
|
||||
]'
|
||||
|
||||
# Also archive to storage account for long-term retention
|
||||
az storage account create \
|
||||
--name auditlogsarchive \
|
||||
--resource-group rg-audit \
|
||||
--location eastus \
|
||||
--sku Standard_GRS \
|
||||
--kind StorageV2 \
|
||||
--min-tls-version TLS1_2 \
|
||||
--allow-blob-public-access false
|
||||
|
||||
az monitor diagnostic-settings subscription create \
|
||||
--name activity-log-to-storage \
|
||||
--location global \
|
||||
--storage-account /subscriptions/{sub}/resourceGroups/rg-audit/providers/Microsoft.Storage/storageAccounts/auditlogsarchive \
|
||||
--logs '[
|
||||
{"category": "Administrative", "enabled": true, "retentionPolicy": {"enabled": true, "days": 2555}},
|
||||
{"category": "Security", "enabled": true, "retentionPolicy": {"enabled": true, "days": 2555}}
|
||||
]'
|
||||
```
|
||||
|
||||
## Log Analytics Queries
|
||||
## Resource-Level Diagnostic Settings
|
||||
|
||||
```bash
|
||||
# Enable diagnostics for Azure Key Vault
|
||||
az monitor diagnostic-settings create \
|
||||
--name keyvault-audit \
|
||||
--resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.KeyVault/vaults/{vault} \
|
||||
--workspace "$WORKSPACE_ID" \
|
||||
--logs '[
|
||||
{"category": "AuditEvent", "enabled": true, "retentionPolicy": {"enabled": true, "days": 365}},
|
||||
{"category": "AzurePolicyEvaluationDetails", "enabled": true}
|
||||
]' \
|
||||
--metrics '[
|
||||
{"category": "AllMetrics", "enabled": true}
|
||||
]'
|
||||
|
||||
# Enable diagnostics for Azure SQL Database
|
||||
az monitor diagnostic-settings create \
|
||||
--name sql-audit \
|
||||
--resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Sql/servers/{server}/databases/{db} \
|
||||
--workspace "$WORKSPACE_ID" \
|
||||
--logs '[
|
||||
{"category": "SQLSecurityAuditEvents", "enabled": true},
|
||||
{"category": "SQLInsights", "enabled": true},
|
||||
{"category": "AutomaticTuning", "enabled": true}
|
||||
]'
|
||||
|
||||
# Enable diagnostics for Azure App Service
|
||||
az monitor diagnostic-settings create \
|
||||
--name appservice-audit \
|
||||
--resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Web/sites/{app} \
|
||||
--workspace "$WORKSPACE_ID" \
|
||||
--logs '[
|
||||
{"category": "AppServiceHTTPLogs", "enabled": true},
|
||||
{"category": "AppServiceAuditLogs", "enabled": true},
|
||||
{"category": "AppServiceIPSecAuditLogs", "enabled": true},
|
||||
{"category": "AppServicePlatformLogs", "enabled": true}
|
||||
]'
|
||||
|
||||
# Enable diagnostics for Network Security Groups
|
||||
az monitor diagnostic-settings create \
|
||||
--name nsg-flow-logs \
|
||||
--resource /subscriptions/{sub}/resourceGroups/{rg}/providers/Microsoft.Network/networkSecurityGroups/{nsg} \
|
||||
--workspace "$WORKSPACE_ID" \
|
||||
--logs '[
|
||||
{"category": "NetworkSecurityGroupEvent", "enabled": true},
|
||||
{"category": "NetworkSecurityGroupRuleCounter", "enabled": true}
|
||||
]'
|
||||
```
|
||||
|
||||
## Azure Policy for Diagnostic Settings Enforcement
|
||||
|
||||
```bash
|
||||
# Assign built-in policy to require diagnostic settings on Key Vaults
|
||||
az policy assignment create \
|
||||
--name require-kv-diagnostics \
|
||||
--policy "951af2fa-529b-416e-ab6e-066fd85ac459" \
|
||||
--scope /subscriptions/{sub} \
|
||||
--params '{
|
||||
"logAnalytics": {"value": "'$WORKSPACE_ID'"},
|
||||
"effect": {"value": "DeployIfNotExists"}
|
||||
}'
|
||||
|
||||
# Assign policy to require diagnostic settings on SQL databases
|
||||
az policy assignment create \
|
||||
--name require-sql-diagnostics \
|
||||
--policy "b79fa14e-238a-4c2d-b376-442ce508fc84" \
|
||||
--scope /subscriptions/{sub} \
|
||||
--params '{
|
||||
"logAnalyticsWorkspaceId": {"value": "'$WORKSPACE_ID'"}
|
||||
}'
|
||||
```
|
||||
|
||||
## KQL Queries for Security Investigation
|
||||
|
||||
```kusto
|
||||
// Failed login attempts
|
||||
AuditLogs
|
||||
// Failed sign-in attempts with location and device details
|
||||
SigninLogs
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where ResultType != "0"
|
||||
| project TimeGenerated, Identity, ResultDescription, IPAddress
|
||||
| summarize FailureCount = count(),
|
||||
DistinctIPs = dcount(IPAddress),
|
||||
Locations = make_set(LocationDetails.city)
|
||||
by UserPrincipalName, ResultDescription, AppDisplayName
|
||||
| where FailureCount > 5
|
||||
| order by FailureCount desc
|
||||
|
||||
// Administrative changes
|
||||
// Successful sign-ins from unusual locations
|
||||
SigninLogs
|
||||
| where TimeGenerated > ago(7d)
|
||||
| where ResultType == "0"
|
||||
| extend City = tostring(LocationDetails.city),
|
||||
Country = tostring(LocationDetails.countryOrRegion)
|
||||
| summarize LoginCount = count(),
|
||||
Cities = make_set(City),
|
||||
Countries = make_set(Country)
|
||||
by UserPrincipalName
|
||||
| where array_length(Countries) > 2
|
||||
|
||||
// Risky sign-ins requiring investigation
|
||||
SigninLogs
|
||||
| where TimeGenerated > ago(7d)
|
||||
| where RiskLevelDuringSignIn in ("medium", "high")
|
||||
| project TimeGenerated, UserPrincipalName, IPAddress,
|
||||
LocationDetails.city, RiskLevelDuringSignIn,
|
||||
RiskEventTypes_V2, AppDisplayName
|
||||
| order by TimeGenerated desc
|
||||
|
||||
// Administrative operations across subscriptions
|
||||
AzureActivity
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where CategoryValue == "Administrative"
|
||||
| where OperationNameValue contains "write" or OperationNameValue contains "delete"
|
||||
| project TimeGenerated, Caller, OperationNameValue, ResourceGroup
|
||||
| where ActivityStatusValue == "Success"
|
||||
| project TimeGenerated, Caller, OperationNameValue,
|
||||
ResourceGroup, Resource, SubscriptionId
|
||||
| order by TimeGenerated desc
|
||||
|
||||
// Key Vault access patterns
|
||||
AzureDiagnostics
|
||||
| where ResourceType == "VAULTS"
|
||||
| where TimeGenerated > ago(24h)
|
||||
| where OperationName in ("SecretGet", "SecretSet", "SecretDelete",
|
||||
"KeySign", "KeyDecrypt", "CertificateGet")
|
||||
| project TimeGenerated, CallerIPAddress, identity_claim_upn_s,
|
||||
OperationName, id_s, ResultType
|
||||
| order by TimeGenerated desc
|
||||
|
||||
// Detect changes to Network Security Groups
|
||||
AzureActivity
|
||||
| where TimeGenerated > ago(7d)
|
||||
| where OperationNameValue has_any ("securityRules/write", "securityRules/delete",
|
||||
"networkSecurityGroups/write")
|
||||
| where ActivityStatusValue == "Success"
|
||||
| project TimeGenerated, Caller, OperationNameValue,
|
||||
ResourceGroup, Properties
|
||||
| order by TimeGenerated desc
|
||||
|
||||
// Azure Policy compliance drift
|
||||
PolicyInsights
|
||||
| where TimeGenerated > ago(7d)
|
||||
| where ComplianceState == "NonCompliant"
|
||||
| summarize NonCompliantCount = count() by PolicyDefinitionName, ResourceType
|
||||
| order by NonCompliantCount desc
|
||||
|
||||
// Privileged role assignments (PIM)
|
||||
AuditLogs
|
||||
| where TimeGenerated > ago(30d)
|
||||
| where OperationName has_any ("Add member to role", "Add eligible member to role")
|
||||
| extend RoleName = tostring(TargetResources[0].displayName),
|
||||
AssignedUser = tostring(TargetResources[2].displayName),
|
||||
AssignedBy = InitiatedBy.user.userPrincipalName
|
||||
| project TimeGenerated, AssignedBy, AssignedUser, RoleName, OperationName
|
||||
| order by TimeGenerated desc
|
||||
```
|
||||
|
||||
## Alert Rules
|
||||
|
||||
```bash
|
||||
# Create action group for security notifications
|
||||
az monitor action-group create \
|
||||
--resource-group rg-audit \
|
||||
--name security-team \
|
||||
--short-name SecTeam \
|
||||
--email-receivers name=SecurityLead email=security@example.com \
|
||||
--webhook-receivers name=PagerDuty uri=https://events.pagerduty.com/integration/{key}/enqueue
|
||||
|
||||
# Alert on multiple failed sign-ins (brute force detection)
|
||||
az monitor scheduled-query create \
|
||||
--resource-group rg-audit \
|
||||
--name brute-force-detection \
|
||||
--scopes "$WORKSPACE_ID" \
|
||||
--condition "count > 10" \
|
||||
--condition-query "SigninLogs | where ResultType != '0' | summarize count() by UserPrincipalName, bin(TimeGenerated, 5m) | where count_ > 10" \
|
||||
--evaluation-frequency 5m \
|
||||
--window-size 5m \
|
||||
--severity 2 \
|
||||
--action-groups /subscriptions/{sub}/resourceGroups/rg-audit/providers/Microsoft.Insights/actionGroups/security-team
|
||||
|
||||
# Alert on Key Vault secret access outside business hours
|
||||
az monitor scheduled-query create \
|
||||
--resource-group rg-audit \
|
||||
--name keyvault-offhours-access \
|
||||
--scopes "$WORKSPACE_ID" \
|
||||
--condition "count > 0" \
|
||||
--condition-query "AzureDiagnostics | where ResourceType == 'VAULTS' | where OperationName in ('SecretGet','SecretList') | where hourofday(TimeGenerated) < 6 or hourofday(TimeGenerated) > 22" \
|
||||
--evaluation-frequency 15m \
|
||||
--window-size 15m \
|
||||
--severity 3 \
|
||||
--action-groups /subscriptions/{sub}/resourceGroups/rg-audit/providers/Microsoft.Insights/actionGroups/security-team
|
||||
|
||||
# Alert on subscription-level administrative changes
|
||||
az monitor activity-log alert create \
|
||||
--resource-group rg-audit \
|
||||
--name critical-admin-changes \
|
||||
--condition category=Administrative and operationName="Microsoft.Authorization/roleAssignments/write" \
|
||||
--action-group /subscriptions/{sub}/resourceGroups/rg-audit/providers/Microsoft.Insights/actionGroups/security-team \
|
||||
--description "Alert on new role assignments"
|
||||
```
|
||||
|
||||
## Workbook for Compliance Dashboard (ARM Template Snippet)
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "Microsoft.Insights/workbooks",
|
||||
"apiVersion": "2022-04-01",
|
||||
"name": "[guid('compliance-dashboard')]",
|
||||
"location": "[resourceGroup().location]",
|
||||
"kind": "shared",
|
||||
"properties": {
|
||||
"displayName": "Compliance Audit Dashboard",
|
||||
"serializedData": "{\"version\":\"Notebook/1.0\",\"items\":[{\"type\":1,\"content\":{\"json\":\"## Compliance Audit Dashboard\"},\"name\":\"title\"},{\"type\":3,\"content\":{\"version\":\"KqlItem/1.0\",\"query\":\"SigninLogs | where TimeGenerated > ago(24h) | where ResultType != '0' | summarize count() by bin(TimeGenerated, 1h)\",\"size\":0,\"title\":\"Failed Sign-ins (24h)\",\"timeContext\":{\"durationMs\":86400000},\"queryType\":0},\"name\":\"failed-signins\"}]}"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Setup Checklist
|
||||
|
||||
```yaml
|
||||
azure_monitor_checklist:
|
||||
workspace_setup:
|
||||
- [ ] Log Analytics workspace created in appropriate region
|
||||
- [ ] Retention period configured (minimum per compliance framework)
|
||||
- [ ] Daily cap configured to prevent cost overruns
|
||||
- [ ] RBAC permissions set (Log Analytics Reader for auditors)
|
||||
|
||||
diagnostic_settings:
|
||||
- [ ] Subscription activity log exported to Log Analytics
|
||||
- [ ] Subscription activity log archived to storage account
|
||||
- [ ] Key Vault audit events enabled
|
||||
- [ ] Azure SQL audit logging enabled
|
||||
- [ ] NSG flow logs enabled
|
||||
- [ ] App Service audit logs enabled
|
||||
- [ ] Azure AD sign-in and audit logs connected
|
||||
|
||||
policy_enforcement:
|
||||
- [ ] Azure Policy assigned to enforce diagnostic settings
|
||||
- [ ] DeployIfNotExists policies for critical resource types
|
||||
- [ ] Compliance state monitored via Policy Insights
|
||||
|
||||
alerting:
|
||||
- [ ] Action groups configured for security and operations teams
|
||||
- [ ] Alert on brute force sign-in attempts
|
||||
- [ ] Alert on privileged role assignments
|
||||
- [ ] Alert on Key Vault sensitive operations
|
||||
- [ ] Alert on NSG rule changes
|
||||
- [ ] Alert on resource deletions in production
|
||||
|
||||
reporting:
|
||||
- [ ] Compliance workbook deployed
|
||||
- [ ] Weekly automated query reports exported
|
||||
- [ ] Quarterly access review queries prepared
|
||||
- [ ] Evidence collection queries documented for auditors
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Centralize to Log Analytics
|
||||
- Long-term archive to Storage
|
||||
- Configure alerts
|
||||
- Regular query reviews
|
||||
- Centralize all audit data into a single Log Analytics workspace per tenant
|
||||
- Archive logs to immutable storage for long-term retention and compliance
|
||||
- Use Azure Policy with DeployIfNotExists to enforce diagnostic settings on new resources
|
||||
- Create saved KQL queries for common investigation and compliance scenarios
|
||||
- Set up scheduled query alerts for security-critical events
|
||||
- Assign Log Analytics Reader role to auditors without granting broader access
|
||||
- Monitor the diagnostic settings pipeline itself for delivery failures
|
||||
- Use workbooks for visual compliance dashboards shared with stakeholders
|
||||
- Export query results on a schedule for compliance evidence packages
|
||||
- Separate operational and security alerting to avoid alert fatigue
|
||||
|
||||
@@ -9,63 +9,466 @@ metadata:
|
||||
|
||||
# GCP Audit Logs
|
||||
|
||||
Audit GCP activity with Cloud Audit Logs.
|
||||
Audit GCP activity with Cloud Audit Logs for compliance, security investigation, and operational monitoring.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Enabling organization-wide audit logging across GCP projects
|
||||
- Meeting compliance requirements for SOC 2, HIPAA, PCI DSS, or FedRAMP
|
||||
- Investigating unauthorized access or suspicious API activity
|
||||
- Setting up alerting on administrative and data access events
|
||||
- Exporting logs to BigQuery for long-term analysis and reporting
|
||||
|
||||
## Audit Log Types
|
||||
|
||||
```yaml
|
||||
log_types:
|
||||
admin_activity:
|
||||
- Always enabled
|
||||
- API calls that modify resources
|
||||
- No charge
|
||||
|
||||
description: API calls that modify resource configuration or metadata
|
||||
enabled: Always (cannot be disabled)
|
||||
retention: 400 days (default)
|
||||
cost: No charge
|
||||
examples:
|
||||
- Creating or deleting VM instances
|
||||
- Changing IAM policies
|
||||
- Modifying firewall rules
|
||||
|
||||
data_access:
|
||||
- Must be enabled
|
||||
- Read/write data operations
|
||||
- Can be high volume
|
||||
|
||||
description: API calls that read resource configuration, metadata, or user data
|
||||
enabled: Must be explicitly enabled (except BigQuery)
|
||||
retention: 30 days (default)
|
||||
cost: Can be significant at high volume
|
||||
subtypes:
|
||||
ADMIN_READ: Read resource configuration/metadata
|
||||
DATA_READ: Read user-provided data
|
||||
DATA_WRITE: Write user-provided data
|
||||
|
||||
system_event:
|
||||
- Always enabled
|
||||
- GCP system actions
|
||||
|
||||
description: Actions performed by GCP systems on behalf of resources
|
||||
enabled: Always (cannot be disabled)
|
||||
retention: 400 days (default)
|
||||
cost: No charge
|
||||
examples:
|
||||
- Live migration of VM instances
|
||||
- Automatic scaling events
|
||||
|
||||
policy_denied:
|
||||
- Always enabled
|
||||
- Access denials
|
||||
description: Actions denied by VPC Service Controls or organization policies
|
||||
enabled: Always (cannot be disabled)
|
||||
retention: 400 days (default)
|
||||
cost: No charge
|
||||
```
|
||||
|
||||
## Enable Data Access Logs
|
||||
## Enable Data Access Logs for an Organization
|
||||
|
||||
```bash
|
||||
# Enable for all services
|
||||
gcloud logging sinks create audit-sink \
|
||||
storage.googleapis.com/audit-logs-bucket \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com"'
|
||||
# Get current org IAM policy
|
||||
gcloud organizations get-iam-policy ORG_ID --format=json > org-policy.json
|
||||
|
||||
# IAM policy for data access logs
|
||||
gcloud projects get-iam-policy PROJECT_ID > policy.yaml
|
||||
# Add auditConfigs section
|
||||
gcloud projects set-iam-policy PROJECT_ID policy.yaml
|
||||
# Add audit config to org-policy.json:
|
||||
# {
|
||||
# "auditConfigs": [
|
||||
# {
|
||||
# "service": "allServices",
|
||||
# "auditLogConfigs": [
|
||||
# {"logType": "ADMIN_READ"},
|
||||
# {"logType": "DATA_READ"},
|
||||
# {"logType": "DATA_WRITE"}
|
||||
# ]
|
||||
# }
|
||||
# ],
|
||||
# ...existing bindings...
|
||||
# }
|
||||
|
||||
# Apply the updated policy
|
||||
gcloud organizations set-iam-policy ORG_ID org-policy.json
|
||||
|
||||
# Enable data access logs for specific services at project level
|
||||
gcloud projects get-iam-policy PROJECT_ID --format=json > project-policy.json
|
||||
|
||||
# Example: enable only for Cloud Storage and BigQuery
|
||||
# {
|
||||
# "auditConfigs": [
|
||||
# {
|
||||
# "service": "storage.googleapis.com",
|
||||
# "auditLogConfigs": [
|
||||
# {"logType": "DATA_READ"},
|
||||
# {"logType": "DATA_WRITE"}
|
||||
# ]
|
||||
# },
|
||||
# {
|
||||
# "service": "bigquery.googleapis.com",
|
||||
# "auditLogConfigs": [
|
||||
# {"logType": "DATA_READ"},
|
||||
# {"logType": "DATA_WRITE"}
|
||||
# ]
|
||||
# }
|
||||
# ]
|
||||
# }
|
||||
|
||||
gcloud projects set-iam-policy PROJECT_ID project-policy.json
|
||||
```
|
||||
|
||||
## BigQuery Analysis
|
||||
## Configure Log Sinks for Export
|
||||
|
||||
```bash
|
||||
# Create BigQuery dataset for audit log export
|
||||
bq mk --dataset \
|
||||
--description "Audit log export" \
|
||||
--default_table_expiration 0 \
|
||||
--location US \
|
||||
PROJECT_ID:audit_logs
|
||||
|
||||
# Create organization-level log sink to BigQuery
|
||||
gcloud logging sinks create org-audit-bigquery \
|
||||
bigquery.googleapis.com/projects/PROJECT_ID/datasets/audit_logs \
|
||||
--organization=ORG_ID \
|
||||
--include-children \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com"'
|
||||
|
||||
# Get the sink writer identity and grant BigQuery access
|
||||
SINK_SA=$(gcloud logging sinks describe org-audit-bigquery \
|
||||
--organization=ORG_ID --format='value(writerIdentity)')
|
||||
|
||||
bq add-iam-policy-binding \
|
||||
--member="$SINK_SA" \
|
||||
--role="roles/bigquery.dataEditor" \
|
||||
PROJECT_ID:audit_logs
|
||||
|
||||
# Create Cloud Storage sink for long-term archive
|
||||
gsutil mb -l US -b on gs://org-audit-logs-archive
|
||||
gsutil retention set 7y gs://org-audit-logs-archive
|
||||
|
||||
gcloud logging sinks create org-audit-storage \
|
||||
storage.googleapis.com/org-audit-logs-archive \
|
||||
--organization=ORG_ID \
|
||||
--include-children \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com"'
|
||||
|
||||
STORAGE_SA=$(gcloud logging sinks describe org-audit-storage \
|
||||
--organization=ORG_ID --format='value(writerIdentity)')
|
||||
|
||||
gsutil iam ch "$STORAGE_SA:objectCreator" gs://org-audit-logs-archive
|
||||
|
||||
# Create Pub/Sub sink for real-time streaming to SIEM
|
||||
gcloud pubsub topics create audit-log-stream
|
||||
|
||||
gcloud logging sinks create org-audit-pubsub \
|
||||
pubsub.googleapis.com/projects/PROJECT_ID/topics/audit-log-stream \
|
||||
--organization=ORG_ID \
|
||||
--include-children \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com" AND (protoPayload.methodName:"delete" OR protoPayload.methodName:"setIamPolicy" OR severity>=WARNING)'
|
||||
|
||||
PUBSUB_SA=$(gcloud logging sinks describe org-audit-pubsub \
|
||||
--organization=ORG_ID --format='value(writerIdentity)')
|
||||
|
||||
gcloud pubsub topics add-iam-policy-binding audit-log-stream \
|
||||
--member="$PUBSUB_SA" \
|
||||
--role="roles/pubsub.publisher"
|
||||
```
|
||||
|
||||
## Logging Queries (Cloud Logging Explorer)
|
||||
|
||||
```bash
|
||||
# View admin activity logs for the last 24 hours
|
||||
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
|
||||
AND timestamp>="2024-01-01T00:00:00Z"' \
|
||||
--project=PROJECT_ID \
|
||||
--format=json \
|
||||
--limit=100
|
||||
|
||||
# Find IAM policy changes
|
||||
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
|
||||
AND protoPayload.methodName="SetIamPolicy"' \
|
||||
--project=PROJECT_ID \
|
||||
--freshness=7d
|
||||
|
||||
# Find resource deletions
|
||||
gcloud logging read 'logName:"cloudaudit.googleapis.com/activity"
|
||||
AND protoPayload.methodName=~"delete"
|
||||
AND severity>=NOTICE' \
|
||||
--project=PROJECT_ID \
|
||||
--freshness=7d
|
||||
|
||||
# Data access audit log entries
|
||||
gcloud logging read 'logName:"cloudaudit.googleapis.com/data_access"
|
||||
AND protoPayload.serviceName="storage.googleapis.com"
|
||||
AND protoPayload.methodName="storage.objects.get"' \
|
||||
--project=PROJECT_ID \
|
||||
--freshness=24h
|
||||
|
||||
# Failed authorization attempts
|
||||
gcloud logging read 'logName:"cloudaudit.googleapis.com/policy"' \
|
||||
--project=PROJECT_ID \
|
||||
--freshness=7d
|
||||
```
|
||||
|
||||
## BigQuery Analysis Queries
|
||||
|
||||
```sql
|
||||
-- Query audit logs from BigQuery export
|
||||
-- All destructive operations in the last 30 days
|
||||
SELECT
|
||||
timestamp,
|
||||
protopayload_auditlog.authenticationInfo.principalEmail,
|
||||
protopayload_auditlog.methodName,
|
||||
resource.labels.project_id
|
||||
FROM `project.dataset.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 7 DAY)
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
|
||||
protopayload_auditlog.methodName AS method,
|
||||
protopayload_auditlog.resourceName AS resource,
|
||||
resource.labels.project_id AS project,
|
||||
protopayload_auditlog.status.code AS status_code,
|
||||
protopayload_auditlog.status.message AS status_message
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
|
||||
AND protopayload_auditlog.methodName LIKE '%delete%'
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 500;
|
||||
|
||||
-- IAM policy changes across the organization
|
||||
SELECT
|
||||
timestamp,
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS changed_by,
|
||||
resource.labels.project_id AS project,
|
||||
protopayload_auditlog.resourceName AS resource,
|
||||
protopayload_auditlog.servicedata_v1_iam.policyDelta.bindingDeltas
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 30 DAY))
|
||||
AND protopayload_auditlog.methodName = 'SetIamPolicy'
|
||||
ORDER BY timestamp DESC;
|
||||
|
||||
-- Activity per principal (detect anomalous usage)
|
||||
SELECT
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
|
||||
COUNT(*) AS action_count,
|
||||
COUNT(DISTINCT protopayload_auditlog.methodName) AS unique_methods,
|
||||
COUNT(DISTINCT protopayload_auditlog.requestMetadata.callerIp) AS unique_ips,
|
||||
MIN(timestamp) AS first_activity,
|
||||
MAX(timestamp) AS last_activity
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
|
||||
GROUP BY principal
|
||||
ORDER BY action_count DESC
|
||||
LIMIT 50;
|
||||
|
||||
-- Service account key creation events (security risk indicator)
|
||||
SELECT
|
||||
timestamp,
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS created_by,
|
||||
protopayload_auditlog.resourceName AS service_account,
|
||||
protopayload_auditlog.requestMetadata.callerIp AS source_ip
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 90 DAY))
|
||||
AND protopayload_auditlog.methodName = 'google.iam.admin.v1.CreateServiceAccountKey'
|
||||
ORDER BY timestamp DESC;
|
||||
|
||||
-- Data access patterns for sensitive Cloud Storage buckets
|
||||
SELECT
|
||||
timestamp,
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS accessor,
|
||||
protopayload_auditlog.resourceName AS object_path,
|
||||
protopayload_auditlog.methodName AS access_type,
|
||||
protopayload_auditlog.requestMetadata.callerIp AS source_ip
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_data_access_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
|
||||
AND protopayload_auditlog.resourceName LIKE '%sensitive-bucket%'
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 1000;
|
||||
|
||||
-- Failed operations indicating permission issues
|
||||
SELECT
|
||||
timestamp,
|
||||
protopayload_auditlog.authenticationInfo.principalEmail AS principal,
|
||||
protopayload_auditlog.methodName AS method,
|
||||
protopayload_auditlog.status.code AS error_code,
|
||||
protopayload_auditlog.status.message AS error_message,
|
||||
protopayload_auditlog.requestMetadata.callerIp AS source_ip
|
||||
FROM `project.audit_logs.cloudaudit_googleapis_com_activity_*`
|
||||
WHERE _TABLE_SUFFIX >= FORMAT_DATE('%Y%m%d', DATE_SUB(CURRENT_DATE(), INTERVAL 7 DAY))
|
||||
AND protopayload_auditlog.status.code != 0
|
||||
ORDER BY timestamp DESC
|
||||
LIMIT 500;
|
||||
```
|
||||
|
||||
## Alerting Policies
|
||||
|
||||
```bash
|
||||
# Alert on service account key creation
|
||||
gcloud alpha monitoring policies create \
|
||||
--display-name="SA Key Created" \
|
||||
--condition-display-name="Service Account Key Creation" \
|
||||
--condition-filter='resource.type="audited_resource" AND protoPayload.methodName="google.iam.admin.v1.CreateServiceAccountKey"' \
|
||||
--condition-threshold-value=0 \
|
||||
--condition-threshold-comparison=COMPARISON_GT \
|
||||
--condition-threshold-duration=0s \
|
||||
--notification-channels=projects/PROJECT_ID/notificationChannels/CHANNEL_ID \
|
||||
--combiner=OR
|
||||
|
||||
# Create a log-based metric for IAM changes
|
||||
gcloud logging metrics create iam-policy-changes \
|
||||
--description="Count of IAM policy changes" \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com/activity" AND protoPayload.methodName="SetIamPolicy"'
|
||||
|
||||
# Create alerting policy using the log-based metric
|
||||
gcloud alpha monitoring policies create \
|
||||
--display-name="IAM Policy Changes" \
|
||||
--condition-display-name="IAM Changes Detected" \
|
||||
--condition-filter='metric.type="logging.googleapis.com/user/iam-policy-changes"' \
|
||||
--condition-threshold-value=0 \
|
||||
--condition-threshold-comparison=COMPARISON_GT \
|
||||
--condition-threshold-duration=0s \
|
||||
--notification-channels=projects/PROJECT_ID/notificationChannels/CHANNEL_ID
|
||||
|
||||
# Create log-based metric for firewall changes
|
||||
gcloud logging metrics create firewall-rule-changes \
|
||||
--description="Count of firewall rule changes" \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com/activity"
|
||||
AND (protoPayload.methodName="v1.compute.firewalls.insert"
|
||||
OR protoPayload.methodName="v1.compute.firewalls.delete"
|
||||
OR protoPayload.methodName="v1.compute.firewalls.patch")'
|
||||
|
||||
# Create log-based metric for VPC network changes
|
||||
gcloud logging metrics create vpc-network-changes \
|
||||
--description="Count of VPC network changes" \
|
||||
--log-filter='logName:"cloudaudit.googleapis.com/activity"
|
||||
AND resource.type="gce_network"
|
||||
AND (protoPayload.methodName=~"insert$" OR protoPayload.methodName=~"delete$")'
|
||||
```
|
||||
|
||||
## Terraform Configuration
|
||||
|
||||
```hcl
|
||||
# Organization-level audit log sink to BigQuery
|
||||
resource "google_logging_organization_sink" "audit_bigquery" {
|
||||
name = "org-audit-bigquery"
|
||||
org_id = var.org_id
|
||||
destination = "bigquery.googleapis.com/projects/${var.project_id}/datasets/${google_bigquery_dataset.audit_logs.dataset_id}"
|
||||
filter = "logName:\"cloudaudit.googleapis.com\""
|
||||
include_children = true
|
||||
|
||||
bigquery_options {
|
||||
use_partitioned_tables = true
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_bigquery_dataset" "audit_logs" {
|
||||
dataset_id = "audit_logs"
|
||||
project = var.project_id
|
||||
location = "US"
|
||||
description = "Organization audit log export"
|
||||
|
||||
default_table_expiration_ms = null # No auto-expiry
|
||||
|
||||
access {
|
||||
role = "WRITER"
|
||||
user_by_email = google_logging_organization_sink.audit_bigquery.writer_identity
|
||||
}
|
||||
|
||||
access {
|
||||
role = "READER"
|
||||
group_by_email = "security-auditors@example.com"
|
||||
}
|
||||
}
|
||||
|
||||
# Retention bucket with bucket lock
|
||||
resource "google_storage_bucket" "audit_archive" {
|
||||
name = "org-audit-logs-archive"
|
||||
location = "US"
|
||||
force_destroy = false
|
||||
project = var.project_id
|
||||
|
||||
uniform_bucket_level_access = true
|
||||
|
||||
retention_policy {
|
||||
is_locked = true
|
||||
retention_period = 220752000 # 7 years in seconds
|
||||
}
|
||||
|
||||
lifecycle_rule {
|
||||
condition {
|
||||
age = 90
|
||||
}
|
||||
action {
|
||||
type = "SetStorageClass"
|
||||
storage_class = "COLDLINE"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
# Log-based alerting
|
||||
resource "google_logging_metric" "iam_changes" {
|
||||
name = "iam-policy-changes"
|
||||
project = var.project_id
|
||||
filter = "logName:\"cloudaudit.googleapis.com/activity\" AND protoPayload.methodName=\"SetIamPolicy\""
|
||||
|
||||
metric_descriptor {
|
||||
metric_kind = "DELTA"
|
||||
value_type = "INT64"
|
||||
}
|
||||
}
|
||||
|
||||
resource "google_monitoring_alert_policy" "iam_changes" {
|
||||
display_name = "IAM Policy Changes Detected"
|
||||
project = var.project_id
|
||||
combiner = "OR"
|
||||
|
||||
conditions {
|
||||
display_name = "IAM policy change count"
|
||||
condition_threshold {
|
||||
filter = "metric.type=\"logging.googleapis.com/user/iam-policy-changes\" AND resource.type=\"global\""
|
||||
comparison = "COMPARISON_GT"
|
||||
threshold_value = 0
|
||||
duration = "0s"
|
||||
}
|
||||
}
|
||||
|
||||
notification_channels = [var.notification_channel_id]
|
||||
}
|
||||
```
|
||||
|
||||
## Setup Checklist
|
||||
|
||||
```yaml
|
||||
gcp_audit_logs_checklist:
|
||||
log_enablement:
|
||||
- [ ] Admin activity logs verified active (always on)
|
||||
- [ ] Data access logs enabled for sensitive services
|
||||
- [ ] Data access exemptions configured to exclude high-volume, low-risk operations
|
||||
- [ ] System event logs verified active (always on)
|
||||
|
||||
log_routing:
|
||||
- [ ] Organization-level sink to BigQuery for analysis
|
||||
- [ ] Organization-level sink to Cloud Storage for long-term archive
|
||||
- [ ] Pub/Sub sink for real-time SIEM streaming (high severity events)
|
||||
- [ ] Sink writer identities granted appropriate destination permissions
|
||||
- [ ] Inclusion filters verified to capture all audit log types
|
||||
|
||||
storage_and_retention:
|
||||
- [ ] BigQuery dataset created with appropriate access controls
|
||||
- [ ] Cloud Storage bucket with retention policy and bucket lock
|
||||
- [ ] Storage class lifecycle rules configured (Standard to Coldline)
|
||||
- [ ] Default log retention in Cloud Logging extended if needed
|
||||
|
||||
alerting:
|
||||
- [ ] Notification channels configured (email, PagerDuty, Slack)
|
||||
- [ ] Log-based metric for IAM policy changes
|
||||
- [ ] Log-based metric for firewall rule changes
|
||||
- [ ] Log-based metric for service account key creation
|
||||
- [ ] Alert policy for each critical metric
|
||||
- [ ] Alert notification tested end-to-end
|
||||
|
||||
access_control:
|
||||
- [ ] Logging Admin role restricted to security team
|
||||
- [ ] BigQuery dataset read access granted to auditors only
|
||||
- [ ] Storage bucket access restricted with IAM
|
||||
- [ ] Sink configuration changes monitored via admin activity logs
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Export to BigQuery for analysis
|
||||
- Configure log retention
|
||||
- Enable data access logs for sensitive resources
|
||||
- Set up alerting policies
|
||||
- Enable data access logs selectively on sensitive services to control cost and volume
|
||||
- Use organization-level sinks with include-children to capture all projects automatically
|
||||
- Export to BigQuery with partitioned tables for efficient querying over large time ranges
|
||||
- Archive to Cloud Storage with bucket lock and retention policies for immutable long-term storage
|
||||
- Create log-based metrics and alerting policies for high-severity events
|
||||
- Stream critical audit events via Pub/Sub to SIEM for real-time correlation
|
||||
- Apply exemptions to exclude high-volume read-only service accounts from data access logs
|
||||
- Restrict access to audit log sinks and destinations with least-privilege IAM bindings
|
||||
- Regularly run BigQuery analysis queries to detect anomalous patterns and generate compliance reports
|
||||
- Monitor log sink health and delivery latency to ensure continuous audit coverage
|
||||
|
||||
@@ -9,78 +9,432 @@ metadata:
|
||||
|
||||
# Business Continuity Planning
|
||||
|
||||
Develop and maintain business continuity capabilities.
|
||||
Develop and maintain business continuity capabilities including Business Impact Analysis, communication plans, recovery procedures, and testing schedules for organizational resilience.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Developing a formal Business Continuity Plan (BCP) for the organization
|
||||
- Conducting a Business Impact Analysis (BIA) to prioritize recovery efforts
|
||||
- Establishing communication plans for crisis scenarios
|
||||
- Defining recovery procedures for critical business processes
|
||||
- Scheduling and conducting BCP exercises and tests
|
||||
- Meeting compliance requirements for continuity planning (SOC 2, ISO 27001, HIPAA, FedRAMP)
|
||||
|
||||
## BCP Framework
|
||||
|
||||
```yaml
|
||||
bcp_phases:
|
||||
1_analysis:
|
||||
- Business Impact Analysis (BIA)
|
||||
- Risk assessment
|
||||
- Critical process identification
|
||||
|
||||
2_planning:
|
||||
- Recovery strategies
|
||||
- Resource requirements
|
||||
- Communication plans
|
||||
|
||||
3_implementation:
|
||||
- Procedure documentation
|
||||
- Training
|
||||
- Technology setup
|
||||
|
||||
4_testing:
|
||||
- Plan exercises
|
||||
- Gap identification
|
||||
- Continuous improvement
|
||||
1_governance:
|
||||
actions:
|
||||
- Obtain executive sponsorship and funding
|
||||
- Assign BCP coordinator and team
|
||||
- Define BCP scope and policy
|
||||
- Establish BCP committee with cross-functional representation
|
||||
deliverables:
|
||||
- BCP policy statement
|
||||
- BCP team charter and roster
|
||||
- Scope document
|
||||
|
||||
2_analysis:
|
||||
actions:
|
||||
- Conduct Business Impact Analysis (BIA)
|
||||
- Perform risk assessment for continuity threats
|
||||
- Identify critical business processes and dependencies
|
||||
- Determine recovery priorities and resource requirements
|
||||
deliverables:
|
||||
- BIA report
|
||||
- Risk assessment report
|
||||
- Critical process inventory
|
||||
|
||||
3_strategy:
|
||||
actions:
|
||||
- Select recovery strategies for each critical process
|
||||
- Identify alternate work arrangements (remote, alternate site)
|
||||
- Define technology recovery strategies (DR plan)
|
||||
- Establish vendor and supply chain contingencies
|
||||
deliverables:
|
||||
- Recovery strategy document
|
||||
- Technology recovery plan
|
||||
- Alternate site arrangements
|
||||
|
||||
4_plan_development:
|
||||
actions:
|
||||
- Write detailed recovery procedures
|
||||
- Develop communication plans (internal and external)
|
||||
- Create emergency response procedures
|
||||
- Document roles, responsibilities, and contact information
|
||||
deliverables:
|
||||
- Business Continuity Plan document
|
||||
- Communication plan
|
||||
- Emergency response procedures
|
||||
- Contact lists and call trees
|
||||
|
||||
5_testing:
|
||||
actions:
|
||||
- Develop test plan and schedule
|
||||
- Conduct exercises (tabletop, functional, full-scale)
|
||||
- Evaluate results and identify gaps
|
||||
- Update plans based on lessons learned
|
||||
deliverables:
|
||||
- Test plan
|
||||
- Exercise reports
|
||||
- Updated BCP based on findings
|
||||
|
||||
6_maintenance:
|
||||
actions:
|
||||
- Review and update BCP annually (minimum)
|
||||
- Update after significant organizational changes
|
||||
- Refresh BIA when business processes change
|
||||
- Maintain training and awareness program
|
||||
deliverables:
|
||||
- Annual BCP review record
|
||||
- Updated BIA (if changes occurred)
|
||||
- Training completion records
|
||||
```
|
||||
|
||||
## Business Impact Analysis
|
||||
## Business Impact Analysis Template
|
||||
|
||||
```yaml
|
||||
process_classification:
|
||||
critical:
|
||||
max_downtime: 4 hours
|
||||
examples: Payment processing, authentication
|
||||
|
||||
essential:
|
||||
max_downtime: 24 hours
|
||||
examples: Customer support, reporting
|
||||
|
||||
necessary:
|
||||
max_downtime: 72 hours
|
||||
examples: Internal tools, analytics
|
||||
|
||||
desirable:
|
||||
max_downtime: 7 days
|
||||
examples: Development environments
|
||||
bia_template:
|
||||
process_assessment:
|
||||
process_name: ""
|
||||
process_owner: ""
|
||||
department: ""
|
||||
description: ""
|
||||
|
||||
criticality_classification:
|
||||
mission_critical:
|
||||
max_tolerable_downtime: "0-4 hours"
|
||||
description: "Failure causes immediate, severe impact to customers or revenue"
|
||||
examples:
|
||||
- Payment processing
|
||||
- Authentication and authorization
|
||||
- Core API serving customer requests
|
||||
- Order fulfillment
|
||||
|
||||
essential:
|
||||
max_tolerable_downtime: "4-24 hours"
|
||||
description: "Failure causes significant degradation but not complete loss"
|
||||
examples:
|
||||
- Customer support systems
|
||||
- Reporting and dashboards
|
||||
- Email and notifications
|
||||
- Billing and invoicing
|
||||
|
||||
important:
|
||||
max_tolerable_downtime: "1-3 days"
|
||||
description: "Failure causes inconvenience and workarounds are available"
|
||||
examples:
|
||||
- Internal collaboration tools
|
||||
- Analytics and BI platforms
|
||||
- HR self-service systems
|
||||
- Knowledge base
|
||||
|
||||
non_essential:
|
||||
max_tolerable_downtime: "3-7 days"
|
||||
description: "Failure has minimal operational impact"
|
||||
examples:
|
||||
- Development and test environments
|
||||
- Training platforms
|
||||
- Archive systems
|
||||
|
||||
impact_categories:
|
||||
financial:
|
||||
revenue_loss_per_hour: ""
|
||||
penalty_or_fine_risk: ""
|
||||
recovery_cost_estimate: ""
|
||||
|
||||
operational:
|
||||
affected_employees: ""
|
||||
affected_customers: ""
|
||||
workaround_available: "yes/no"
|
||||
workaround_description: ""
|
||||
|
||||
reputational:
|
||||
customer_visibility: "high/medium/low"
|
||||
media_attention_risk: "high/medium/low"
|
||||
regulatory_reporting_required: "yes/no"
|
||||
|
||||
legal_regulatory:
|
||||
compliance_impact: ""
|
||||
contractual_sla_breach: "yes/no"
|
||||
sla_penalty_details: ""
|
||||
|
||||
dependencies:
|
||||
technology:
|
||||
- system: ""
|
||||
rto: ""
|
||||
rpo: ""
|
||||
dr_strategy: ""
|
||||
people:
|
||||
- role: ""
|
||||
minimum_staff: ""
|
||||
remote_capable: "yes/no"
|
||||
vendors:
|
||||
- vendor: ""
|
||||
service: ""
|
||||
sla: ""
|
||||
alternative: ""
|
||||
facilities:
|
||||
- location: ""
|
||||
alternative: ""
|
||||
|
||||
recovery_requirements:
|
||||
rto: ""
|
||||
rpo: ""
|
||||
minimum_recovery_level: "Description of minimum acceptable service"
|
||||
full_recovery_target: "Time to full normal operations"
|
||||
```
|
||||
|
||||
## Communication Plan
|
||||
|
||||
```yaml
|
||||
communication:
|
||||
internal:
|
||||
- Executive notification
|
||||
- Team communication
|
||||
- Status updates
|
||||
|
||||
external:
|
||||
- Customer notification
|
||||
- Regulatory reporting
|
||||
- Media relations
|
||||
|
||||
channels:
|
||||
- Primary: Slack/Teams
|
||||
- Secondary: Email
|
||||
- Emergency: Phone tree
|
||||
communication_plan:
|
||||
activation_criteria:
|
||||
- Event affecting multiple critical systems
|
||||
- Physical facility unavailable
|
||||
- Pandemic or workforce availability crisis
|
||||
- Major vendor/partner outage
|
||||
- Cybersecurity incident with operational impact
|
||||
|
||||
internal_communication:
|
||||
executive_notification:
|
||||
who: "CEO, CTO, CFO, VP Engineering, VP Operations"
|
||||
when: "Within 15 minutes of BCP activation"
|
||||
method: "Phone call (primary), SMS (secondary)"
|
||||
message_template: |
|
||||
BUSINESS CONTINUITY EVENT ACTIVATED
|
||||
Incident: [Brief description]
|
||||
Impact: [Systems/processes affected]
|
||||
Status: [Current state]
|
||||
Next update: [Time]
|
||||
Bridge call: [Number/link]
|
||||
|
||||
team_notification:
|
||||
who: "All affected department leads and their teams"
|
||||
when: "Within 30 minutes of BCP activation"
|
||||
method: "Slack/Teams (primary), Email (secondary), SMS (tertiary)"
|
||||
message_template: |
|
||||
BCP ACTIVATED - [Event Type]
|
||||
What happened: [Description]
|
||||
What is affected: [Systems/services]
|
||||
What to do: [Immediate actions for your team]
|
||||
Status updates: [Channel/frequency]
|
||||
Questions: Contact [BCP coordinator]
|
||||
|
||||
all_staff_notification:
|
||||
who: "All employees"
|
||||
when: "Within 1 hour of BCP activation"
|
||||
method: "Email, Slack/Teams announcement, intranet"
|
||||
content: "Situation summary, impact on work, expectations"
|
||||
|
||||
status_updates:
|
||||
frequency: "Every 2 hours during active event, daily after stabilization"
|
||||
channel: "Dedicated Slack channel, email distribution list"
|
||||
content: "Current status, actions taken, next steps, timeline"
|
||||
|
||||
external_communication:
|
||||
customers:
|
||||
who: "Affected customers"
|
||||
when: "Within 2 hours of BCP activation (if customer-facing impact)"
|
||||
method: "Status page update, email, in-app notification"
|
||||
message_template: |
|
||||
We are currently experiencing [issue description].
|
||||
Impact: [What customers may notice]
|
||||
Status: We are actively working to resolve this.
|
||||
Updates: Follow our status page at status.example.com
|
||||
ETA: [Estimated resolution time or "investigating"]
|
||||
|
||||
regulatory:
|
||||
who: "Applicable regulatory bodies"
|
||||
when: "Per regulatory requirements (e.g., 72 hours for GDPR breach)"
|
||||
method: "Formal notification per regulatory procedure"
|
||||
|
||||
media:
|
||||
who: "Press inquiries"
|
||||
when: "Only if media attention occurs"
|
||||
method: "Prepared statement through communications team"
|
||||
rule: "All media inquiries routed to designated spokesperson"
|
||||
|
||||
vendors_partners:
|
||||
who: "Critical vendors and business partners"
|
||||
when: "Within 4 hours if partner services affected"
|
||||
method: "Direct contact via relationship manager"
|
||||
|
||||
contact_lists:
|
||||
maintenance: "Updated quarterly"
|
||||
storage: "Accessible offline (printed, mobile app, cloud-independent)"
|
||||
includes:
|
||||
- BCP team members (name, role, phone, email, alternate phone)
|
||||
- Executive team
|
||||
- Department leads
|
||||
- Key vendor contacts
|
||||
- Regulatory contacts
|
||||
- Legal counsel
|
||||
- Insurance broker
|
||||
- PR/communications firm
|
||||
```
|
||||
|
||||
## Recovery Procedures
|
||||
|
||||
```yaml
|
||||
recovery_procedures:
|
||||
immediate_response:
|
||||
step_1: "Incident commander assesses situation and declares BCP activation"
|
||||
step_2: "Notify BCP team and establish command structure"
|
||||
step_3: "Activate communication plan"
|
||||
step_4: "Assess damage and determine scope of disruption"
|
||||
step_5: "Initiate appropriate recovery procedures based on scenario"
|
||||
|
||||
scenario_specific:
|
||||
data_center_or_region_outage:
|
||||
- Activate DR failover procedures
|
||||
- Redirect traffic to DR region
|
||||
- Verify service restoration
|
||||
- Communicate status to stakeholders
|
||||
- Plan return to primary when available
|
||||
|
||||
cybersecurity_incident:
|
||||
- Engage incident response team
|
||||
- Contain the threat (isolate affected systems)
|
||||
- Assess data impact and potential breach
|
||||
- Activate forensic investigation
|
||||
- Restore from known-good backups if needed
|
||||
- Notify legal and regulatory as required
|
||||
|
||||
pandemic_workforce_disruption:
|
||||
- Activate remote work procedures
|
||||
- Verify VPN and remote access capacity
|
||||
- Redistribute critical functions if staff unavailable
|
||||
- Implement shift rotations to maintain coverage
|
||||
- Assess vendor ability to maintain service levels
|
||||
|
||||
key_vendor_failure:
|
||||
- Assess impact on dependent business processes
|
||||
- Activate vendor contingency plan
|
||||
- Engage alternate vendor if available
|
||||
- Implement manual workarounds as needed
|
||||
- Communicate impact to affected stakeholders
|
||||
|
||||
facility_unavailable:
|
||||
- Account for all personnel safety
|
||||
- Activate alternate work site arrangements
|
||||
- Redirect mail and deliveries
|
||||
- Set up temporary communication channels
|
||||
- Assess timeline for facility restoration
|
||||
|
||||
stabilization:
|
||||
- Monitor recovered services continuously
|
||||
- Address any residual issues
|
||||
- Begin planning return to normal operations
|
||||
- Continue stakeholder communication
|
||||
- Document all actions and decisions
|
||||
|
||||
return_to_normal:
|
||||
- Develop return-to-normal plan
|
||||
- Execute failback procedures (if DR was activated)
|
||||
- Verify data consistency and integrity
|
||||
- Restore standard operating procedures
|
||||
- Conduct post-event review
|
||||
- Update BCP based on lessons learned
|
||||
```
|
||||
|
||||
## Testing Schedule and Types
|
||||
|
||||
```yaml
|
||||
testing_schedule:
|
||||
tabletop_exercise:
|
||||
frequency: "Quarterly"
|
||||
duration: "2-3 hours"
|
||||
participants: "BCP team, department leads, executive sponsor"
|
||||
format: "Facilitated discussion of a scenario"
|
||||
scenarios_to_rotate:
|
||||
- Major cloud provider region outage
|
||||
- Ransomware attack on production systems
|
||||
- Key employee unavailability (bus factor scenario)
|
||||
- Critical vendor goes out of business
|
||||
- Office building inaccessible
|
||||
output: "Exercise report with findings and action items"
|
||||
|
||||
functional_exercise:
|
||||
frequency: "Semi-annually"
|
||||
duration: "4-8 hours"
|
||||
participants: "BCP team, IT operations, affected departments"
|
||||
format: "Execute specific recovery procedures without full disruption"
|
||||
examples:
|
||||
- "Activate remote work for one department for a day"
|
||||
- "Failover a non-production database and verify application connectivity"
|
||||
- "Execute communication plan and verify contact list accuracy"
|
||||
- "Restore a critical system from backup in an isolated environment"
|
||||
output: "Functional test report with measured recovery times"
|
||||
|
||||
full_scale_exercise:
|
||||
frequency: "Annually"
|
||||
duration: "1-2 days"
|
||||
participants: "All BCP team members, IT, communications, management"
|
||||
format: "Simulate a major disruption and execute full recovery"
|
||||
includes:
|
||||
- "Activate BCP command structure"
|
||||
- "Execute DR failover for production systems"
|
||||
- "Activate communication plan"
|
||||
- "Operate from alternate arrangements for set period"
|
||||
- "Execute failback and return to normal"
|
||||
output: "Full exercise report with comprehensive metrics and lessons learned"
|
||||
|
||||
testing_metrics:
|
||||
- "Time to activate BCP command structure"
|
||||
- "Time to complete communication notifications"
|
||||
- "Contact list accuracy (% reachable)"
|
||||
- "Actual RTO vs. target RTO per system"
|
||||
- "Actual RPO vs. target RPO per system"
|
||||
- "Number of issues identified"
|
||||
- "Number of runbook corrections needed"
|
||||
```
|
||||
|
||||
## BCP Maintenance Checklist
|
||||
|
||||
```yaml
|
||||
bcp_maintenance_checklist:
|
||||
quarterly:
|
||||
- [ ] Contact lists verified and updated
|
||||
- [ ] Tabletop exercise conducted
|
||||
- [ ] BCP team roster reviewed
|
||||
- [ ] Vendor contact information verified
|
||||
- [ ] Communication channels tested
|
||||
|
||||
semi_annually:
|
||||
- [ ] Functional exercise conducted
|
||||
- [ ] Recovery procedures reviewed for accuracy
|
||||
- [ ] Technology dependencies verified
|
||||
- [ ] Vendor continuity capabilities confirmed
|
||||
|
||||
annually:
|
||||
- [ ] Full-scale exercise conducted
|
||||
- [ ] Business Impact Analysis refreshed
|
||||
- [ ] Risk assessment updated
|
||||
- [ ] BCP document fully reviewed and updated
|
||||
- [ ] Executive review and sign-off obtained
|
||||
- [ ] Training completed for all BCP team members
|
||||
- [ ] Lessons learned from all exercises incorporated
|
||||
|
||||
triggered_by_change:
|
||||
- [ ] New critical business process added
|
||||
- [ ] Major organizational restructuring
|
||||
- [ ] Technology platform migration
|
||||
- [ ] New regulatory requirement
|
||||
- [ ] Significant vendor change
|
||||
- [ ] Actual disruption event (post-event update)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Annual BIA updates
|
||||
- Regular plan testing
|
||||
- Clear roles and responsibilities
|
||||
- Multiple communication channels
|
||||
- Executive sponsorship
|
||||
- Secure executive sponsorship: BCP without leadership commitment will not be taken seriously
|
||||
- Base recovery priorities on Business Impact Analysis, not assumptions or technical preferences
|
||||
- Test the communication plan independently: it fails more often than the technology recovery
|
||||
- Maintain contact lists as if your primary systems are unavailable (offline copies, mobile access)
|
||||
- Conduct tabletop exercises quarterly at minimum: they are low-cost and high-value for identifying gaps
|
||||
- Include non-IT scenarios in planning (pandemic, facility loss, key personnel unavailability)
|
||||
- Define clear activation criteria so there is no ambiguity about when to invoke the BCP
|
||||
- Keep the BCP document practical and actionable, not a shelf document written for auditors
|
||||
- Update the BCP after every significant organizational or technology change
|
||||
- Review and incorporate lessons from every exercise and every real event into the plan
|
||||
|
||||
@@ -9,65 +9,547 @@ metadata:
|
||||
|
||||
# Disaster Recovery
|
||||
|
||||
Implement disaster recovery strategies and procedures.
|
||||
Implement disaster recovery strategies including RTO/RPO planning, AWS cross-region failover patterns, DR testing procedures, and automated failover scripts.
|
||||
|
||||
## DR Metrics
|
||||
## When to Use
|
||||
|
||||
- Defining RTO and RPO targets for critical systems
|
||||
- Designing multi-region or multi-cloud disaster recovery architectures
|
||||
- Implementing automated failover and failback procedures
|
||||
- Conducting DR tests (tabletop, component, full failover)
|
||||
- Meeting compliance requirements for contingency planning (SOC 2, HIPAA, FedRAMP, ISO 27001)
|
||||
|
||||
## RTO/RPO Planning
|
||||
|
||||
```yaml
|
||||
recovery_metrics:
|
||||
RTO: Recovery Time Objective
|
||||
- Maximum acceptable downtime
|
||||
- How long to restore service
|
||||
|
||||
RPO: Recovery Point Objective
|
||||
- Maximum acceptable data loss
|
||||
- How much data can be lost
|
||||
RTO:
|
||||
definition: "Recovery Time Objective - maximum acceptable downtime"
|
||||
measurement: "From incident declaration to service restoration"
|
||||
factors:
|
||||
- Failover automation maturity
|
||||
- Data replication lag
|
||||
- DNS propagation time
|
||||
- Application warm-up time
|
||||
- Verification procedures
|
||||
|
||||
RPO:
|
||||
definition: "Recovery Point Objective - maximum acceptable data loss"
|
||||
measurement: "Time gap between last good backup and the incident"
|
||||
factors:
|
||||
- Backup frequency
|
||||
- Replication method (sync vs. async)
|
||||
- Transaction log shipping interval
|
||||
- Cross-region replication lag
|
||||
|
||||
service_tier_targets:
|
||||
tier_1_critical:
|
||||
examples: "Authentication, payment processing, core API"
|
||||
rto: "< 15 minutes"
|
||||
rpo: "< 1 minute (near-zero)"
|
||||
strategy: "Multi-site active-active or warm standby"
|
||||
replication: "Synchronous or near-synchronous"
|
||||
testing: "Quarterly failover test"
|
||||
|
||||
tier_2_essential:
|
||||
examples: "Customer dashboards, reporting, notifications"
|
||||
rto: "< 1 hour"
|
||||
rpo: "< 15 minutes"
|
||||
strategy: "Warm standby or pilot light"
|
||||
replication: "Asynchronous with short interval"
|
||||
testing: "Semi-annual failover test"
|
||||
|
||||
tier_3_standard:
|
||||
examples: "Internal tools, analytics, batch processing"
|
||||
rto: "< 4 hours"
|
||||
rpo: "< 1 hour"
|
||||
strategy: "Pilot light or backup and restore"
|
||||
replication: "Periodic snapshots"
|
||||
testing: "Annual failover test"
|
||||
|
||||
tier_4_non_essential:
|
||||
examples: "Development environments, documentation sites"
|
||||
rto: "< 24 hours"
|
||||
rpo: "< 24 hours"
|
||||
strategy: "Backup and restore"
|
||||
replication: "Daily backups"
|
||||
testing: "Annual backup restore verification"
|
||||
```
|
||||
|
||||
## DR Strategies
|
||||
|
||||
| Strategy | RTO | RPO | Cost |
|
||||
|----------|-----|-----|------|
|
||||
| Backup & Restore | Hours | Hours | $ |
|
||||
| Pilot Light | Minutes-Hours | Minutes | $$ |
|
||||
| Warm Standby | Minutes | Seconds | $$$ |
|
||||
| Multi-Site Active | Near-zero | Near-zero | $$$$ |
|
||||
|
||||
## AWS Multi-Region
|
||||
|
||||
```bash
|
||||
# Cross-region RDS replica
|
||||
aws rds create-db-instance-read-replica \
|
||||
--db-instance-identifier dr-replica \
|
||||
--source-db-instance-identifier prod-db \
|
||||
--source-region us-east-1 \
|
||||
--region us-west-2
|
||||
|
||||
# S3 cross-region replication
|
||||
aws s3api put-bucket-replication \
|
||||
--bucket source-bucket \
|
||||
--replication-configuration file://replication.json
|
||||
```
|
||||
|
||||
## DR Testing
|
||||
## DR Strategies Comparison
|
||||
|
||||
```yaml
|
||||
dr_test_schedule:
|
||||
tabletop: Quarterly
|
||||
component_failover: Monthly
|
||||
full_failover: Annually
|
||||
|
||||
test_checklist:
|
||||
- [ ] Verify backup integrity
|
||||
- [ ] Test failover procedures
|
||||
- [ ] Validate data consistency
|
||||
- [ ] Measure actual RTO/RPO
|
||||
- [ ] Document lessons learned
|
||||
strategies:
|
||||
backup_and_restore:
|
||||
rto: "Hours"
|
||||
rpo: "Hours (depends on backup frequency)"
|
||||
cost: "$"
|
||||
description: "Regular backups stored in DR region. Restore from backup when needed."
|
||||
aws_services:
|
||||
- "S3 cross-region replication for backups"
|
||||
- "RDS automated snapshots copied to DR region"
|
||||
- "AMI copies in DR region"
|
||||
- "Terraform/CloudFormation for infrastructure rebuild"
|
||||
pros: "Lowest cost, simplest to maintain"
|
||||
cons: "Longest recovery time, highest data loss potential"
|
||||
|
||||
pilot_light:
|
||||
rto: "Minutes to hours"
|
||||
rpo: "Minutes"
|
||||
cost: "$$"
|
||||
description: "Core infrastructure running in DR region (databases replicated). Scale up compute on failover."
|
||||
aws_services:
|
||||
- "RDS cross-region read replica (always running)"
|
||||
- "S3 cross-region replication"
|
||||
- "AMIs pre-built in DR region"
|
||||
- "Auto Scaling groups at zero/minimal capacity"
|
||||
pros: "Fast database recovery, moderate cost"
|
||||
cons: "Compute scale-up adds to recovery time"
|
||||
|
||||
warm_standby:
|
||||
rto: "Minutes"
|
||||
rpo: "Seconds to minutes"
|
||||
cost: "$$$"
|
||||
description: "Scaled-down but functional environment in DR region. Scale up on failover."
|
||||
aws_services:
|
||||
- "RDS cross-region read replica"
|
||||
- "ECS/EKS running at reduced capacity"
|
||||
- "Route53 health checks for automated DNS failover"
|
||||
- "Global Accelerator for traffic management"
|
||||
pros: "Fast failover, reduced risk"
|
||||
cons: "Higher baseline cost for idle resources"
|
||||
|
||||
multi_site_active:
|
||||
rto: "Near-zero"
|
||||
rpo: "Near-zero"
|
||||
cost: "$$$$"
|
||||
description: "Active-active across regions. Traffic served from both regions simultaneously."
|
||||
aws_services:
|
||||
- "DynamoDB Global Tables or Aurora Global Database"
|
||||
- "Route53 latency/weighted routing"
|
||||
- "CloudFront with multi-origin"
|
||||
- "Global Accelerator"
|
||||
- "ECS/EKS in both regions"
|
||||
pros: "Minimal downtime and data loss"
|
||||
cons: "Highest cost, most complex to operate"
|
||||
```
|
||||
|
||||
## AWS Cross-Region DR Implementation
|
||||
|
||||
```bash
|
||||
# === Database Replication ===
|
||||
|
||||
# Create cross-region RDS read replica
|
||||
aws rds create-db-instance-read-replica \
|
||||
--db-instance-identifier prod-db-dr-replica \
|
||||
--source-db-instance-identifier arn:aws:rds:us-east-1:123456789012:db:prod-db \
|
||||
--db-instance-class db.r6g.large \
|
||||
--region us-west-2 \
|
||||
--kms-key-id arn:aws:kms:us-west-2:123456789012:alias/rds-dr-key \
|
||||
--multi-az \
|
||||
--tags Key=Purpose,Value=DR Key=Environment,Value=production
|
||||
|
||||
# Create Aurora Global Database for near-zero RPO
|
||||
aws rds create-global-cluster \
|
||||
--global-cluster-identifier prod-global-db \
|
||||
--source-db-cluster-identifier arn:aws:rds:us-east-1:123456789012:cluster:prod-aurora-cluster \
|
||||
--region us-east-1
|
||||
|
||||
# Add secondary region to Aurora Global Database
|
||||
aws rds create-db-cluster \
|
||||
--db-cluster-identifier prod-aurora-dr \
|
||||
--global-cluster-identifier prod-global-db \
|
||||
--engine aurora-postgresql \
|
||||
--region us-west-2 \
|
||||
--kms-key-id arn:aws:kms:us-west-2:123456789012:alias/aurora-dr-key
|
||||
|
||||
# === Storage Replication ===
|
||||
|
||||
# S3 cross-region replication
|
||||
cat > /tmp/replication-config.json << 'EOF'
|
||||
{
|
||||
"Role": "arn:aws:iam::123456789012:role/s3-replication-role",
|
||||
"Rules": [
|
||||
{
|
||||
"ID": "ReplicateAll",
|
||||
"Status": "Enabled",
|
||||
"Filter": {"Prefix": ""},
|
||||
"Destination": {
|
||||
"Bucket": "arn:aws:s3:::prod-data-dr-usw2",
|
||||
"StorageClass": "STANDARD",
|
||||
"EncryptionConfiguration": {
|
||||
"ReplicaKmsKeyID": "arn:aws:kms:us-west-2:123456789012:alias/s3-dr-key"
|
||||
}
|
||||
},
|
||||
"DeleteMarkerReplication": {"Status": "Enabled"}
|
||||
}
|
||||
]
|
||||
}
|
||||
EOF
|
||||
|
||||
aws s3api put-bucket-replication \
|
||||
--bucket prod-data-use1 \
|
||||
--replication-configuration file:///tmp/replication-config.json
|
||||
|
||||
# === DNS Failover ===
|
||||
|
||||
# Route53 health check for primary region
|
||||
aws route53 create-health-check --caller-reference "prod-health-$(date +%s)" \
|
||||
--health-check-config '{
|
||||
"Type": "HTTPS",
|
||||
"FullyQualifiedDomainName": "api.example.com",
|
||||
"Port": 443,
|
||||
"ResourcePath": "/health",
|
||||
"RequestInterval": 10,
|
||||
"FailureThreshold": 3,
|
||||
"EnableSNI": true
|
||||
}'
|
||||
|
||||
# Configure failover routing
|
||||
aws route53 change-resource-record-sets --hosted-zone-id Z123456 \
|
||||
--change-batch '{
|
||||
"Changes": [
|
||||
{
|
||||
"Action": "UPSERT",
|
||||
"ResourceRecordSet": {
|
||||
"Name": "api.example.com",
|
||||
"Type": "A",
|
||||
"SetIdentifier": "primary",
|
||||
"Failover": "PRIMARY",
|
||||
"AliasTarget": {
|
||||
"HostedZoneId": "Z1234PRIMARY",
|
||||
"DNSName": "primary-alb.us-east-1.elb.amazonaws.com",
|
||||
"EvaluateTargetHealth": true
|
||||
},
|
||||
"HealthCheckId": "health-check-id-primary"
|
||||
}
|
||||
},
|
||||
{
|
||||
"Action": "UPSERT",
|
||||
"ResourceRecordSet": {
|
||||
"Name": "api.example.com",
|
||||
"Type": "A",
|
||||
"SetIdentifier": "secondary",
|
||||
"Failover": "SECONDARY",
|
||||
"AliasTarget": {
|
||||
"HostedZoneId": "Z5678SECONDARY",
|
||||
"DNSName": "dr-alb.us-west-2.elb.amazonaws.com",
|
||||
"EvaluateTargetHealth": true
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}'
|
||||
```
|
||||
|
||||
## Failover Script
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# dr-failover.sh - Execute disaster recovery failover to DR region
|
||||
set -euo pipefail
|
||||
|
||||
DR_REGION="us-west-2"
|
||||
PRIMARY_REGION="us-east-1"
|
||||
SLACK_WEBHOOK="${DR_SLACK_WEBHOOK}"
|
||||
LOG_FILE="/var/log/dr-failover-$(date +%Y%m%d-%H%M%S).log"
|
||||
|
||||
log() {
|
||||
echo "[$(date -u +%Y-%m-%dT%H:%M:%SZ)] $1" | tee -a "$LOG_FILE"
|
||||
}
|
||||
|
||||
notify() {
|
||||
curl -s -X POST "$SLACK_WEBHOOK" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "{\"text\":\"DR FAILOVER: $1\"}" > /dev/null
|
||||
}
|
||||
|
||||
log "=== DR Failover Initiated ==="
|
||||
notify "DR failover initiated to $DR_REGION"
|
||||
|
||||
# Step 1: Promote RDS read replica
|
||||
log "Step 1: Promoting RDS read replica in $DR_REGION"
|
||||
aws rds promote-read-replica \
|
||||
--db-instance-identifier prod-db-dr-replica \
|
||||
--region "$DR_REGION"
|
||||
log "Waiting for RDS promotion to complete..."
|
||||
aws rds wait db-instance-available \
|
||||
--db-instance-identifier prod-db-dr-replica \
|
||||
--region "$DR_REGION"
|
||||
log "RDS promotion complete"
|
||||
notify "RDS read replica promoted to primary in $DR_REGION"
|
||||
|
||||
# Step 2: Scale up application in DR region
|
||||
log "Step 2: Scaling up application in $DR_REGION"
|
||||
aws ecs update-service \
|
||||
--cluster prod-cluster-dr \
|
||||
--service api-service \
|
||||
--desired-count 4 \
|
||||
--region "$DR_REGION"
|
||||
log "Waiting for ECS service to stabilize..."
|
||||
aws ecs wait services-stable \
|
||||
--cluster prod-cluster-dr \
|
||||
--services api-service \
|
||||
--region "$DR_REGION"
|
||||
log "ECS service scaled up and stable"
|
||||
notify "Application scaled up in $DR_REGION"
|
||||
|
||||
# Step 3: Verify health
|
||||
log "Step 3: Verifying health in $DR_REGION"
|
||||
for i in $(seq 1 10); do
|
||||
STATUS=$(curl -s -o /dev/null -w "%{http_code}" "https://dr-alb.us-west-2.elb.amazonaws.com/health")
|
||||
if [ "$STATUS" = "200" ]; then
|
||||
log "Health check passed (attempt $i)"
|
||||
break
|
||||
fi
|
||||
log "Health check failed (attempt $i, status $STATUS), retrying..."
|
||||
sleep 10
|
||||
done
|
||||
|
||||
if [ "$STATUS" != "200" ]; then
|
||||
log "ERROR: Health check failed after 10 attempts"
|
||||
notify "ALERT: DR health check failing - manual intervention required"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Step 4: Update DNS (if not using automatic Route53 failover)
|
||||
log "Step 4: DNS failover (Route53 automatic failover should handle this)"
|
||||
log "Verifying DNS resolution..."
|
||||
DR_IP=$(dig +short api.example.com)
|
||||
log "api.example.com resolves to: $DR_IP"
|
||||
|
||||
# Step 5: Verify end-to-end
|
||||
log "Step 5: End-to-end verification"
|
||||
RESPONSE=$(curl -s "https://api.example.com/health")
|
||||
log "Health response: $RESPONSE"
|
||||
|
||||
log "=== DR Failover Complete ==="
|
||||
notify "DR failover to $DR_REGION complete. Service restored."
|
||||
|
||||
# Generate failover report
|
||||
cat > "/var/log/dr-failover-report-$(date +%Y%m%d).md" << EOF
|
||||
# DR Failover Report
|
||||
- **Date:** $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
- **Primary Region:** $PRIMARY_REGION
|
||||
- **DR Region:** $DR_REGION
|
||||
- **RTO Actual:** Calculate from incident declaration
|
||||
- **RPO Actual:** Check replication lag at time of incident
|
||||
- **Status:** Operational in DR region
|
||||
- **Actions Required:**
|
||||
- [ ] Monitor error rates and latency
|
||||
- [ ] Plan failback when primary region is restored
|
||||
- [ ] Conduct post-incident review
|
||||
EOF
|
||||
```
|
||||
|
||||
## DR Testing Procedures
|
||||
|
||||
```yaml
|
||||
dr_test_types:
|
||||
tabletop_exercise:
|
||||
frequency: Quarterly
|
||||
duration: "1-2 hours"
|
||||
participants: "Engineering, SRE, management, communications"
|
||||
process:
|
||||
- Present a disaster scenario (region outage, data corruption, etc.)
|
||||
- Walk through the response step by step
|
||||
- Identify gaps in runbooks and communication plans
|
||||
- Document action items
|
||||
output: "Tabletop exercise report with findings and action items"
|
||||
|
||||
component_failover:
|
||||
frequency: Monthly
|
||||
duration: "1-4 hours"
|
||||
scope: "Individual component failover (database, single service)"
|
||||
process:
|
||||
- Select component for testing
|
||||
- Execute failover procedure from runbook
|
||||
- Measure actual RTO and RPO
|
||||
- Execute failback procedure
|
||||
- Document results
|
||||
output: "Component test report with measured RTO/RPO"
|
||||
|
||||
full_failover:
|
||||
frequency: Annually
|
||||
duration: "4-8 hours (scheduled maintenance window)"
|
||||
scope: "Complete regional failover of all tier 1 and tier 2 services"
|
||||
process:
|
||||
1_preparation:
|
||||
- Schedule maintenance window and notify stakeholders
|
||||
- Verify DR environment is healthy
|
||||
- Brief all participating teams
|
||||
- Set up war room communication channel
|
||||
2_execute:
|
||||
- Simulate primary region failure
|
||||
- Execute failover runbooks for all services
|
||||
- Record timestamps at each milestone
|
||||
3_verify:
|
||||
- Run end-to-end test suite against DR environment
|
||||
- Verify data consistency
|
||||
- Check monitoring and alerting in DR region
|
||||
- Confirm external integrations work
|
||||
4_failback:
|
||||
- Restore primary region
|
||||
- Re-establish replication
|
||||
- Execute failback to primary
|
||||
- Verify data consistency post-failback
|
||||
5_report:
|
||||
- Document actual RTO and RPO for each service
|
||||
- Compare against targets
|
||||
- List all issues encountered
|
||||
- Create action items for improvements
|
||||
output: "Full DR test report with measured vs. target metrics"
|
||||
|
||||
dr_test_checklist:
|
||||
before_test:
|
||||
- [ ] Test plan documented and approved
|
||||
- [ ] Maintenance window scheduled and communicated
|
||||
- [ ] All DR runbooks reviewed and updated
|
||||
- [ ] DR environment health verified
|
||||
- [ ] Monitoring configured in DR region
|
||||
- [ ] Communication channel established
|
||||
- [ ] Rollback plan confirmed
|
||||
|
||||
during_test:
|
||||
- [ ] Timestamps recorded for each step
|
||||
- [ ] Screenshots captured for evidence
|
||||
- [ ] Issues logged in real-time
|
||||
- [ ] Data consistency verified
|
||||
- [ ] External integrations tested
|
||||
- [ ] Health checks passing in DR
|
||||
|
||||
after_test:
|
||||
- [ ] Failback completed successfully
|
||||
- [ ] Primary region replication re-established
|
||||
- [ ] Data consistency verified post-failback
|
||||
- [ ] Test report written with metrics
|
||||
- [ ] Action items created and assigned
|
||||
- [ ] Runbooks updated based on findings
|
||||
- [ ] Results presented to management
|
||||
```
|
||||
|
||||
## Terraform DR Infrastructure
|
||||
|
||||
```hcl
|
||||
# DR region infrastructure
|
||||
provider "aws" {
|
||||
alias = "dr"
|
||||
region = "us-west-2"
|
||||
}
|
||||
|
||||
resource "aws_db_instance" "dr_replica" {
|
||||
provider = aws.dr
|
||||
identifier = "prod-db-dr-replica"
|
||||
replicate_source_db = aws_db_instance.primary.arn
|
||||
instance_class = "db.r6g.large"
|
||||
storage_encrypted = true
|
||||
kms_key_id = aws_kms_key.dr_rds.arn
|
||||
multi_az = true
|
||||
deletion_protection = true
|
||||
skip_final_snapshot = false
|
||||
|
||||
tags = {
|
||||
Purpose = "DR"
|
||||
Environment = "production"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route53_health_check" "primary" {
|
||||
fqdn = "primary-alb.us-east-1.elb.amazonaws.com"
|
||||
port = 443
|
||||
type = "HTTPS"
|
||||
resource_path = "/health"
|
||||
failure_threshold = 3
|
||||
request_interval = 10
|
||||
enable_sni = true
|
||||
|
||||
tags = {
|
||||
Name = "primary-health-check"
|
||||
}
|
||||
}
|
||||
|
||||
resource "aws_route53_record" "failover_primary" {
|
||||
zone_id = aws_route53_zone.main.zone_id
|
||||
name = "api.example.com"
|
||||
type = "A"
|
||||
set_identifier = "primary"
|
||||
|
||||
failover_routing_policy {
|
||||
type = "PRIMARY"
|
||||
}
|
||||
|
||||
alias {
|
||||
name = aws_lb.primary.dns_name
|
||||
zone_id = aws_lb.primary.zone_id
|
||||
evaluate_target_health = true
|
||||
}
|
||||
|
||||
health_check_id = aws_route53_health_check.primary.id
|
||||
}
|
||||
|
||||
resource "aws_route53_record" "failover_secondary" {
|
||||
zone_id = aws_route53_zone.main.zone_id
|
||||
name = "api.example.com"
|
||||
type = "A"
|
||||
set_identifier = "secondary"
|
||||
|
||||
failover_routing_policy {
|
||||
type = "SECONDARY"
|
||||
}
|
||||
|
||||
alias {
|
||||
name = aws_lb.dr.dns_name
|
||||
zone_id = aws_lb.dr.zone_id
|
||||
evaluate_target_health = true
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## DR Compliance Checklist
|
||||
|
||||
```yaml
|
||||
dr_compliance_checklist:
|
||||
planning:
|
||||
- [ ] RTO and RPO targets defined per service tier
|
||||
- [ ] DR strategy selected based on targets and budget
|
||||
- [ ] DR architecture documented with diagrams
|
||||
- [ ] Failover and failback runbooks written
|
||||
- [ ] Communication plan for DR events documented
|
||||
- [ ] DR roles and responsibilities assigned
|
||||
|
||||
implementation:
|
||||
- [ ] Cross-region database replication configured
|
||||
- [ ] Storage replication configured (S3, EBS snapshots)
|
||||
- [ ] DNS failover routing configured
|
||||
- [ ] DR region infrastructure provisioned (IaC)
|
||||
- [ ] Monitoring and alerting configured in DR region
|
||||
- [ ] Secrets and credentials available in DR region
|
||||
|
||||
testing:
|
||||
- [ ] Tabletop exercises conducted quarterly
|
||||
- [ ] Component failover tests conducted monthly
|
||||
- [ ] Full failover test conducted annually
|
||||
- [ ] Actual RTO/RPO measured and compared to targets
|
||||
- [ ] Test results documented and reviewed
|
||||
- [ ] Runbooks updated based on test findings
|
||||
|
||||
operational:
|
||||
- [ ] Replication lag monitored with alerting
|
||||
- [ ] DR environment health checked regularly
|
||||
- [ ] Backup integrity verified monthly
|
||||
- [ ] DR runbooks reviewed and updated quarterly
|
||||
- [ ] DR test evidence archived for compliance audits
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Regular DR testing
|
||||
- Automate failover where possible
|
||||
- Document all procedures
|
||||
- Update runbooks after tests
|
||||
- Define RTO and RPO targets based on business impact analysis, not technical convenience
|
||||
- Choose the DR strategy that matches your targets and budget: do not over-engineer or under-invest
|
||||
- Automate failover as much as possible to reduce human error and recovery time
|
||||
- Test DR procedures regularly at increasing levels of complexity (tabletop, component, full)
|
||||
- Measure actual RTO and RPO during tests and compare against targets every time
|
||||
- Include failback procedures in your DR plan: getting back to normal is as important as failing over
|
||||
- Monitor replication lag continuously and alert when it exceeds RPO thresholds
|
||||
- Keep DR infrastructure managed by the same IaC as production to prevent configuration drift
|
||||
- Practice DR in non-emergency conditions so the team is prepared when a real disaster occurs
|
||||
- Archive DR test results as compliance evidence for SOC 2, HIPAA, and other frameworks
|
||||
|
||||
@@ -9,82 +9,453 @@ metadata:
|
||||
|
||||
# Incident Management
|
||||
|
||||
Implement effective incident management processes.
|
||||
Implement effective incident management processes including severity definitions, escalation matrices, war room procedures, and blameless post-mortem templates.
|
||||
|
||||
## Incident Severity
|
||||
## When to Use
|
||||
|
||||
| Severity | Impact | Response | Example |
|
||||
|----------|--------|----------|---------|
|
||||
| SEV1 | Total outage | Immediate, all-hands | Site down |
|
||||
| SEV2 | Major degradation | Urgent, on-call | Feature broken |
|
||||
| SEV3 | Minor impact | Standard | Slow performance |
|
||||
| SEV4 | Minimal | Next business day | Cosmetic issue |
|
||||
- Establishing incident management processes for production systems
|
||||
- Defining severity levels and escalation procedures
|
||||
- Running war rooms and coordinating incident response
|
||||
- Conducting blameless post-incident reviews
|
||||
- Building on-call schedules and notification workflows
|
||||
- Meeting compliance requirements for incident response (SOC 2, HIPAA, PCI DSS)
|
||||
|
||||
## Incident Process
|
||||
## Severity Levels
|
||||
|
||||
```yaml
|
||||
incident_workflow:
|
||||
1_detect:
|
||||
- Alerting triggers
|
||||
- Customer reports
|
||||
- Monitoring anomalies
|
||||
|
||||
2_triage:
|
||||
- Severity assessment
|
||||
- Impact determination
|
||||
- Team notification
|
||||
|
||||
3_respond:
|
||||
- Incident commander assigned
|
||||
- Communication established
|
||||
- Mitigation started
|
||||
|
||||
4_resolve:
|
||||
- Root cause addressed
|
||||
- Service restored
|
||||
- Customer notified
|
||||
|
||||
5_review:
|
||||
- Timeline documented
|
||||
- Root cause analysis
|
||||
- Action items created
|
||||
severity_definitions:
|
||||
SEV1_critical:
|
||||
impact: "Complete service outage or data breach affecting all/most customers"
|
||||
examples:
|
||||
- Production site completely down
|
||||
- Data breach confirmed or suspected
|
||||
- Complete loss of a critical business function
|
||||
- Security incident with active exploitation
|
||||
response_time: "Immediate (within 5 minutes)"
|
||||
update_frequency: "Every 15-30 minutes"
|
||||
who_is_paged: "On-call engineer, engineering manager, incident commander, executive on-call"
|
||||
communication: "Status page update, customer email, executive notification"
|
||||
resolution_target: "< 1 hour to mitigate"
|
||||
|
||||
SEV2_major:
|
||||
impact: "Major feature broken or severe degradation affecting many customers"
|
||||
examples:
|
||||
- Key feature completely non-functional
|
||||
- Significant performance degradation (>5x latency)
|
||||
- Data processing pipeline completely stalled
|
||||
- Partial outage affecting a region or segment
|
||||
response_time: "Within 15 minutes"
|
||||
update_frequency: "Every 30-60 minutes"
|
||||
who_is_paged: "On-call engineer, engineering manager"
|
||||
communication: "Status page update if customer-facing"
|
||||
resolution_target: "< 4 hours to mitigate"
|
||||
|
||||
SEV3_moderate:
|
||||
impact: "Minor feature impaired or degradation affecting some customers"
|
||||
examples:
|
||||
- Non-critical feature broken
|
||||
- Moderate performance degradation
|
||||
- Elevated error rate (below threshold for SEV2)
|
||||
- Single-customer impact on non-critical function
|
||||
response_time: "Within 1 hour during business hours"
|
||||
update_frequency: "Every 2-4 hours"
|
||||
who_is_paged: "On-call engineer"
|
||||
communication: "Internal only unless customer inquires"
|
||||
resolution_target: "< 1 business day"
|
||||
|
||||
SEV4_low:
|
||||
impact: "Cosmetic issue, minor inconvenience, or non-customer-facing problem"
|
||||
examples:
|
||||
- UI cosmetic bug
|
||||
- Non-critical monitoring gap
|
||||
- Internal tool degradation
|
||||
- Documentation inaccuracy in production
|
||||
response_time: "Next business day"
|
||||
update_frequency: "As needed"
|
||||
who_is_paged: "None (ticket created)"
|
||||
communication: "None"
|
||||
resolution_target: "Within sprint planning cycle"
|
||||
```
|
||||
|
||||
## Incident Commander
|
||||
## Escalation Matrix
|
||||
|
||||
```yaml
|
||||
ic_responsibilities:
|
||||
- Own incident resolution
|
||||
- Coordinate response teams
|
||||
- Manage communication
|
||||
- Make escalation decisions
|
||||
- Schedule post-mortem
|
||||
escalation_matrix:
|
||||
tier_1_on_call_engineer:
|
||||
reached_via: "PagerDuty / OpsGenie alert"
|
||||
responsibilities:
|
||||
- Acknowledge alert within 5 minutes
|
||||
- Assess severity and impact
|
||||
- Begin troubleshooting
|
||||
- Escalate to Tier 2 if unable to resolve within 30 minutes (SEV1/2)
|
||||
escalation_trigger: "Cannot resolve, needs additional expertise, or severity upgrade"
|
||||
|
||||
tier_2_team_lead_or_sme:
|
||||
reached_via: "PagerDuty escalation or direct page"
|
||||
responsibilities:
|
||||
- Provide subject matter expertise
|
||||
- Assist with diagnosis and resolution
|
||||
- Coordinate with other teams if cross-service issue
|
||||
- Escalate to Tier 3 if broader coordination needed
|
||||
escalation_trigger: "Multi-service issue, needs executive decision, or customer-facing SEV1"
|
||||
|
||||
tier_3_engineering_management:
|
||||
reached_via: "PagerDuty escalation or direct call"
|
||||
responsibilities:
|
||||
- Assign incident commander (if not already)
|
||||
- Allocate additional resources
|
||||
- Make business decisions (feature disable, rollback, etc.)
|
||||
- Coordinate external communication
|
||||
escalation_trigger: "Business impact decision, extended outage, or PR/legal concern"
|
||||
|
||||
tier_4_executive:
|
||||
reached_via: "Direct phone call"
|
||||
responsibilities:
|
||||
- Authorize extraordinary measures
|
||||
- Manage board/investor communication
|
||||
- Approve public statements
|
||||
- Engage external resources (vendors, consultants)
|
||||
escalation_trigger: "Major breach, extended SEV1, regulatory or legal implication"
|
||||
|
||||
time_based_escalation:
|
||||
sev1:
|
||||
"15 min no ack": "Re-page on-call + backup on-call"
|
||||
"30 min unresolved": "Page team lead"
|
||||
"1 hour unresolved": "Page engineering manager + executive on-call"
|
||||
"2 hours unresolved": "All-hands engineering involvement"
|
||||
sev2:
|
||||
"30 min no ack": "Re-page on-call + backup on-call"
|
||||
"1 hour unresolved": "Page team lead"
|
||||
"4 hours unresolved": "Page engineering manager"
|
||||
```
|
||||
|
||||
## Post-Incident Review
|
||||
## War Room Procedures
|
||||
|
||||
```yaml
|
||||
war_room:
|
||||
activation: "Automatically for SEV1, on-demand for SEV2"
|
||||
|
||||
setup:
|
||||
communication_channel:
|
||||
primary: "Dedicated Slack channel (#incident-YYYY-MM-DD-brief-name)"
|
||||
voice: "Zoom/Google Meet bridge (persistent link)"
|
||||
backup: "Phone conference bridge"
|
||||
channel_rules:
|
||||
- "Only incident-related communication in the channel"
|
||||
- "Use threads for side discussions"
|
||||
- "Prefix messages with role (IC:, COMMS:, ENG:)"
|
||||
|
||||
roles:
|
||||
incident_commander:
|
||||
responsibilities:
|
||||
- Own the incident from declaration to resolution
|
||||
- Coordinate all response activities
|
||||
- Make decisions on response actions
|
||||
- Assign tasks to responders
|
||||
- Determine when incident is resolved
|
||||
- Schedule post-mortem
|
||||
selection: "On-call IC roster, or senior engineer who declares the incident"
|
||||
|
||||
communications_lead:
|
||||
responsibilities:
|
||||
- Draft and publish status page updates
|
||||
- Coordinate customer notifications
|
||||
- Handle internal stakeholder updates
|
||||
- Manage executive communication
|
||||
- Document timeline in real-time
|
||||
selection: "Designated from on-call comms roster or engineering manager"
|
||||
|
||||
technical_lead:
|
||||
responsibilities:
|
||||
- Lead technical diagnosis and troubleshooting
|
||||
- Coordinate technical responders
|
||||
- Recommend mitigation and resolution actions
|
||||
- Verify fix effectiveness
|
||||
selection: "Senior engineer with relevant system expertise"
|
||||
|
||||
scribe:
|
||||
responsibilities:
|
||||
- Document all actions, decisions, and findings
|
||||
- Maintain real-time timeline
|
||||
- Record who did what and when
|
||||
- Capture screenshots and log excerpts
|
||||
selection: "Any available team member (can be rotated)"
|
||||
|
||||
workflow:
|
||||
1_declare:
|
||||
- "IC declares incident with severity level"
|
||||
- "War room channel and bridge created"
|
||||
- "Roles assigned"
|
||||
- "First status update posted"
|
||||
|
||||
2_assess:
|
||||
- "Determine scope and customer impact"
|
||||
- "Identify affected systems and services"
|
||||
- "Establish working hypothesis"
|
||||
|
||||
3_mitigate:
|
||||
- "Focus on restoring service first, root cause second"
|
||||
- "IC approves all changes to production"
|
||||
- "Changes documented in real-time"
|
||||
- "Rollback if mitigation makes things worse"
|
||||
|
||||
4_resolve:
|
||||
- "Confirm service restored to normal"
|
||||
- "Verify monitoring shows healthy metrics"
|
||||
- "IC declares incident resolved"
|
||||
- "Final status page update"
|
||||
|
||||
5_follow_up:
|
||||
- "Schedule post-mortem within 48 hours"
|
||||
- "Assign action items from immediate findings"
|
||||
- "Send internal summary"
|
||||
```
|
||||
|
||||
## On-Call Configuration
|
||||
|
||||
```yaml
|
||||
on_call_schedule:
|
||||
rotation_structure:
|
||||
primary:
|
||||
rotation: "Weekly"
|
||||
handoff: "Monday 10:00 AM local time"
|
||||
team_size: "Minimum 5 engineers in rotation"
|
||||
secondary:
|
||||
rotation: "Weekly (offset from primary)"
|
||||
activation: "If primary does not acknowledge within 10 minutes"
|
||||
|
||||
expectations:
|
||||
response_time: "Acknowledge alert within 5 minutes"
|
||||
availability: "Reachable by phone and laptop within 15 minutes"
|
||||
handoff: "Document any ongoing issues during handoff"
|
||||
compensation: "Per company on-call compensation policy"
|
||||
|
||||
health:
|
||||
max_consecutive_weeks: 2
|
||||
minimum_gap_between_rotations: "2 weeks"
|
||||
post_incident_rest: "If engaged for 4+ hours overnight, late start next day"
|
||||
burnout_monitoring: "Track pages per person per week, rebalance if needed"
|
||||
|
||||
pagerduty_configuration:
|
||||
escalation_policy:
|
||||
- level_1:
|
||||
target: "Primary on-call"
|
||||
timeout: "5 minutes"
|
||||
- level_2:
|
||||
target: "Secondary on-call"
|
||||
timeout: "10 minutes"
|
||||
- level_3:
|
||||
target: "Engineering manager"
|
||||
timeout: "15 minutes"
|
||||
|
||||
notification_rules:
|
||||
high_urgency:
|
||||
- "Push notification immediately"
|
||||
- "Phone call after 1 minute"
|
||||
- "SMS after 2 minutes"
|
||||
low_urgency:
|
||||
- "Push notification"
|
||||
- "Email after 5 minutes"
|
||||
```
|
||||
|
||||
## Post-Mortem Template
|
||||
|
||||
```markdown
|
||||
## Incident Summary
|
||||
- Duration:
|
||||
- Impact:
|
||||
- Severity:
|
||||
# Post-Incident Review: [Incident Title]
|
||||
|
||||
## Timeline
|
||||
**Date:** YYYY-MM-DD
|
||||
**Severity:** SEV[1-4]
|
||||
**Duration:** [Start time] to [End time] ([X hours Y minutes])
|
||||
**Incident Commander:** [Name]
|
||||
**Author:** [Name]
|
||||
**Status:** Draft / In Review / Final
|
||||
|
||||
## Executive Summary
|
||||
[2-3 sentence summary of what happened, the impact, and the resolution]
|
||||
|
||||
## Impact
|
||||
- **Customer impact:** [Number/percentage of customers affected, what they experienced]
|
||||
- **Duration of impact:** [How long customers were affected]
|
||||
- **Revenue impact:** [Estimated financial impact, if applicable]
|
||||
- **Data impact:** [Any data loss or corruption]
|
||||
- **SLA impact:** [Any SLA breaches]
|
||||
|
||||
## Timeline (all times UTC)
|
||||
| Time | Event |
|
||||
|------|-------|
|
||||
| HH:MM | [First anomaly detected by monitoring] |
|
||||
| HH:MM | [Alert fired / customer report received] |
|
||||
| HH:MM | [On-call engineer acknowledged] |
|
||||
| HH:MM | [Incident declared at SEV level] |
|
||||
| HH:MM | [War room established] |
|
||||
| HH:MM | [Root cause identified] |
|
||||
| HH:MM | [Mitigation applied] |
|
||||
| HH:MM | [Service restored] |
|
||||
| HH:MM | [Incident resolved] |
|
||||
|
||||
## Root Cause
|
||||
[Detailed technical explanation of what caused the incident]
|
||||
|
||||
## What Went Well
|
||||
## Detection
|
||||
- **How was the incident detected?** [Monitoring alert / customer report / manual observation]
|
||||
- **Time to detect:** [Time from first anomaly to detection]
|
||||
- **Could we have detected sooner?** [Yes/No, with explanation]
|
||||
|
||||
## What Could Be Improved
|
||||
## Response
|
||||
- **What went well:**
|
||||
- [List things that worked effectively during response]
|
||||
- [E.g., "Runbook for database failover was accurate and followed successfully"]
|
||||
- [E.g., "Communication to customers was timely and clear"]
|
||||
|
||||
- **What could be improved:**
|
||||
- [List things that slowed or hindered response]
|
||||
- [E.g., "Took 20 minutes to identify the correct service owner"]
|
||||
- [E.g., "Monitoring did not alert on the specific failure mode"]
|
||||
|
||||
## Contributing Factors
|
||||
[List all factors that contributed to the incident occurring or being worse than it could have been. This is not about blame - it is about understanding the system.]
|
||||
|
||||
1. [Factor 1: e.g., "Configuration change was not tested in staging"]
|
||||
2. [Factor 2: e.g., "Alert threshold was too high to catch gradual degradation"]
|
||||
3. [Factor 3: e.g., "No circuit breaker between Service A and Service B"]
|
||||
|
||||
## Action Items
|
||||
| Item | Owner | Due Date |
|
||||
| ID | Action | Owner | Priority | Due Date | Status |
|
||||
|----|--------|-------|----------|----------|--------|
|
||||
| 1 | [Preventive action] | [Name] | P1 | YYYY-MM-DD | Open |
|
||||
| 2 | [Detection improvement] | [Name] | P2 | YYYY-MM-DD | Open |
|
||||
| 3 | [Process improvement] | [Name] | P2 | YYYY-MM-DD | Open |
|
||||
| 4 | [Runbook update] | [Name] | P3 | YYYY-MM-DD | Open |
|
||||
|
||||
## Lessons Learned
|
||||
[Key takeaways that should be shared broadly]
|
||||
|
||||
## Appendix
|
||||
- [Link to monitoring dashboards during incident]
|
||||
- [Link to relevant log queries]
|
||||
- [Link to war room channel archive]
|
||||
```
|
||||
|
||||
## Post-Mortem Process
|
||||
|
||||
```yaml
|
||||
post_mortem_process:
|
||||
scheduling:
|
||||
sev1: "Within 48 hours of resolution"
|
||||
sev2: "Within 1 week of resolution"
|
||||
sev3: "Within 2 weeks (optional, based on learning potential)"
|
||||
sev4: "Not required"
|
||||
|
||||
meeting_format:
|
||||
duration: "60-90 minutes"
|
||||
attendees:
|
||||
required: "IC, technical lead, scribe, involved engineers"
|
||||
optional: "Engineering manager, product manager, affected team leads"
|
||||
agenda:
|
||||
- "5 min: Review timeline and facts"
|
||||
- "15 min: Walk through root cause and contributing factors"
|
||||
- "15 min: Discuss what went well"
|
||||
- "15 min: Discuss what could be improved"
|
||||
- "15 min: Define and assign action items"
|
||||
- "5 min: Identify lessons learned and sharing plan"
|
||||
|
||||
principles:
|
||||
- "Blameless: Focus on systems and processes, not individuals"
|
||||
- "Factual: Base discussion on data, logs, and observations"
|
||||
- "Forward-looking: Prioritize preventive actions over assigning fault"
|
||||
- "Complete: Address detection, response, and prevention"
|
||||
- "Actionable: Every finding should produce a tracked action item"
|
||||
|
||||
action_item_tracking:
|
||||
- "All action items entered into issue tracker (Jira, GitHub Issues)"
|
||||
- "Priority assigned based on risk reduction potential"
|
||||
- "Owner assigned with due date"
|
||||
- "Reviewed in team standups and sprint planning"
|
||||
- "Tracked to completion"
|
||||
- "Monthly review of open post-mortem action items"
|
||||
```
|
||||
|
||||
## Incident Metrics
|
||||
|
||||
```yaml
|
||||
incident_metrics:
|
||||
mttr:
|
||||
name: "Mean Time to Resolve"
|
||||
definition: "Average time from incident detection to resolution"
|
||||
target: "SEV1: <1h, SEV2: <4h"
|
||||
trending: "Track monthly, aim for improvement"
|
||||
|
||||
mttd:
|
||||
name: "Mean Time to Detect"
|
||||
definition: "Average time from incident start to detection"
|
||||
target: "< 5 minutes for SEV1/2"
|
||||
trending: "Monitors effectiveness of alerting"
|
||||
|
||||
mtta:
|
||||
name: "Mean Time to Acknowledge"
|
||||
definition: "Average time from alert to engineer acknowledgment"
|
||||
target: "< 5 minutes"
|
||||
trending: "Monitors on-call responsiveness"
|
||||
|
||||
incident_frequency:
|
||||
name: "Incidents per week/month by severity"
|
||||
target: "Trending downward"
|
||||
trending: "Monitors system reliability improvement"
|
||||
|
||||
action_item_completion:
|
||||
name: "Post-mortem action item completion rate"
|
||||
target: "> 90% completed on time"
|
||||
trending: "Monitors follow-through on improvements"
|
||||
|
||||
recurring_incidents:
|
||||
name: "Percentage of incidents with same root cause as previous incident"
|
||||
target: "< 10%"
|
||||
trending: "Monitors effectiveness of preventive actions"
|
||||
```
|
||||
|
||||
## Incident Management Checklist
|
||||
|
||||
```yaml
|
||||
incident_management_checklist:
|
||||
process_setup:
|
||||
- [ ] Severity levels defined with clear criteria
|
||||
- [ ] Escalation matrix documented
|
||||
- [ ] On-call schedule established and staffed
|
||||
- [ ] War room procedures documented
|
||||
- [ ] Post-mortem template created
|
||||
- [ ] Communication templates prepared (status page, email)
|
||||
- [ ] Incident management tool configured (PagerDuty, OpsGenie)
|
||||
|
||||
per_incident:
|
||||
- [ ] Incident declared with severity level
|
||||
- [ ] War room established (SEV1/2)
|
||||
- [ ] Roles assigned (IC, comms, technical lead, scribe)
|
||||
- [ ] Timeline maintained in real-time
|
||||
- [ ] Status page updated (customer-facing impact)
|
||||
- [ ] Stakeholders notified per communication plan
|
||||
- [ ] Resolution verified with monitoring
|
||||
- [ ] Post-mortem scheduled
|
||||
- [ ] Post-mortem conducted and published
|
||||
- [ ] Action items tracked to completion
|
||||
|
||||
compliance:
|
||||
- [ ] All SEV1/2 incidents have post-mortems
|
||||
- [ ] Incident log maintained for audit evidence
|
||||
- [ ] Metrics reported monthly
|
||||
- [ ] On-call health monitored (pages per person)
|
||||
- [ ] Annual incident response training conducted
|
||||
- [ ] Annual incident response plan test completed
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Clear severity definitions
|
||||
- Defined escalation paths
|
||||
- Blameless post-mortems
|
||||
- Action item tracking
|
||||
- Regular training
|
||||
- Define severity levels with concrete examples so there is no ambiguity during an active incident
|
||||
- Implement time-based escalation: if the on-call does not acknowledge, automatically escalate
|
||||
- Focus on mitigation first, root cause second: restore service before investigating why it failed
|
||||
- Run blameless post-mortems: the goal is to improve systems, not to assign fault to individuals
|
||||
- Track post-mortem action items to completion: an unfinished action item means the same incident can recur
|
||||
- Monitor incident metrics (MTTR, MTTD, frequency) as leading indicators of system reliability
|
||||
- Protect on-call health: track page volume per person and redistribute if someone is overburdened
|
||||
- Separate the incident commander role from the technical lead role in SEV1/2 incidents
|
||||
- Practice incident response regularly with game days or chaos engineering exercises
|
||||
- Archive incident records and post-mortems for compliance evidence and organizational learning
|
||||
|
||||
@@ -9,88 +9,477 @@ metadata:
|
||||
|
||||
# Runbook Creation
|
||||
|
||||
Create effective operational runbooks and procedures.
|
||||
Create effective operational runbooks, standard operating procedures, and
|
||||
troubleshooting guides that any on-call engineer can follow under pressure.
|
||||
|
||||
## Runbook Structure
|
||||
## Runbook Template — Full Structure
|
||||
|
||||
```markdown
|
||||
# Runbook: [Service/Process Name]
|
||||
````markdown
|
||||
# Runbook: [Service / Process Name]
|
||||
|
||||
**Owner:** [Team or individual]
|
||||
**Last Reviewed:** YYYY-MM-DD
|
||||
**Version:** X.Y
|
||||
**Severity if unavailable:** SEV[1-4]
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
Brief description of the service and runbook purpose.
|
||||
|
||||
Brief description of the service, why this runbook exists, and when to
|
||||
use it.
|
||||
|
||||
## Prerequisites
|
||||
- Required access
|
||||
- Tools needed
|
||||
- Knowledge required
|
||||
|
||||
- [ ] Required access / IAM role: [details]
|
||||
- [ ] Tools installed: [kubectl, aws-cli, psql, etc.]
|
||||
- [ ] VPN connected to [environment]
|
||||
- [ ] Communication channel open: [Slack #channel]
|
||||
|
||||
## Procedure
|
||||
Step-by-step instructions with commands.
|
||||
|
||||
## Verification
|
||||
How to confirm success.
|
||||
### Step 1 — [Action Name]
|
||||
|
||||
## Rollback
|
||||
Steps to undo if needed.
|
||||
[Explanation of what this step does and why.]
|
||||
|
||||
## Escalation
|
||||
When and how to escalate.
|
||||
|
||||
## Related Runbooks
|
||||
Links to related procedures.
|
||||
```bash
|
||||
# command here
|
||||
```
|
||||
|
||||
## Example Runbook
|
||||
**Expected output:** [describe what success looks like]
|
||||
|
||||
```markdown
|
||||
# Runbook: Database Failover
|
||||
### Step 2 — [Action Name]
|
||||
|
||||
```bash
|
||||
# command here
|
||||
```
|
||||
|
||||
**Expected output:** [description]
|
||||
|
||||
*(Continue with numbered steps...)*
|
||||
|
||||
## Verification
|
||||
|
||||
How to confirm the procedure succeeded:
|
||||
|
||||
- [ ] [Check 1 — e.g., health endpoint returns 200]
|
||||
- [ ] [Check 2 — e.g., no errors in logs for 5 minutes]
|
||||
- [ ] [Check 3 — e.g., metrics return to baseline]
|
||||
|
||||
## Rollback
|
||||
|
||||
If the procedure fails or causes unexpected issues:
|
||||
|
||||
### Rollback Step 1
|
||||
```bash
|
||||
# rollback command
|
||||
```
|
||||
|
||||
### Rollback Step 2
|
||||
```bash
|
||||
# rollback command
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
| Symptom | Likely Cause | Resolution |
|
||||
|---------|-------------|------------|
|
||||
| [symptom 1] | [cause] | [fix] |
|
||||
| [symptom 2] | [cause] | [fix] |
|
||||
|
||||
## Escalation
|
||||
|
||||
If unresolved after [X] minutes:
|
||||
- **Primary:** @[team-lead] — [phone/Slack]
|
||||
- **Secondary:** @[manager] — [phone/Slack]
|
||||
|
||||
## Related Runbooks
|
||||
|
||||
- [Link to related runbook 1]
|
||||
- [Link to related runbook 2]
|
||||
|
||||
## Change Log
|
||||
|
||||
| Date | Author | Change |
|
||||
|------|--------|--------|
|
||||
| YYYY-MM-DD | [Name] | Initial version |
|
||||
````
|
||||
|
||||
## Example Runbook — Database Failover
|
||||
|
||||
````markdown
|
||||
# Runbook: PostgreSQL Database Failover
|
||||
|
||||
**Owner:** Platform / DBA team
|
||||
**Last Reviewed:** 2025-06-15
|
||||
**Version:** 2.1
|
||||
**Severity if unavailable:** SEV1
|
||||
|
||||
---
|
||||
|
||||
## Overview
|
||||
Procedure to failover PostgreSQL to replica.
|
||||
|
||||
Failover the primary PostgreSQL instance to the synchronous replica when
|
||||
the primary is unreachable or degraded. This runbook covers both planned
|
||||
(maintenance) and unplanned (emergency) failover.
|
||||
|
||||
## Prerequisites
|
||||
- [ ] DBA access to primary and replica
|
||||
- [ ] VPN connected
|
||||
|
||||
- [ ] DBA or SRE-level access to primary and replica hosts
|
||||
- [ ] `psql` client installed (v14+)
|
||||
- [ ] VPN connected to production network
|
||||
- [ ] Slack channel #db-ops open
|
||||
- [ ] Confirm replica is in sync: replication lag < 1 MB
|
||||
|
||||
## Procedure
|
||||
|
||||
### 1. Verify Replica Status
|
||||
\`\`\`bash
|
||||
psql -h replica -c "SELECT pg_is_in_recovery();"
|
||||
# Should return 't'
|
||||
\`\`\`
|
||||
### Step 1 — Verify Replica Health
|
||||
|
||||
### 2. Stop Application Writes
|
||||
\`\`\`bash
|
||||
kubectl scale deployment app --replicas=0
|
||||
\`\`\`
|
||||
```bash
|
||||
psql -h replica.db.internal -U dba -d postgres -c \
|
||||
"SELECT pg_is_in_recovery(), pg_last_wal_replay_lsn();"
|
||||
```
|
||||
|
||||
### 3. Promote Replica
|
||||
\`\`\`bash
|
||||
psql -h replica -c "SELECT pg_promote();"
|
||||
\`\`\`
|
||||
**Expected output:** `pg_is_in_recovery = t`, LSN advancing.
|
||||
|
||||
### 4. Update DNS
|
||||
\`\`\`bash
|
||||
aws route53 change-resource-record-sets ...
|
||||
\`\`\`
|
||||
### Step 2 — Stop Application Writes
|
||||
|
||||
```bash
|
||||
kubectl scale deployment api-server --replicas=0 -n production
|
||||
kubectl scale deployment worker --replicas=0 -n production
|
||||
```
|
||||
|
||||
**Expected output:** Deployments scaled to 0 pods.
|
||||
|
||||
### Step 3 — Confirm Write Quiesce
|
||||
|
||||
```bash
|
||||
psql -h primary.db.internal -U dba -d postgres -c \
|
||||
"SELECT count(*) FROM pg_stat_activity WHERE state = 'active' AND query !~ 'pg_stat';"
|
||||
```
|
||||
|
||||
**Expected output:** Count = 0 (no active queries).
|
||||
|
||||
### Step 4 — Promote Replica
|
||||
|
||||
```bash
|
||||
psql -h replica.db.internal -U dba -d postgres -c "SELECT pg_promote();"
|
||||
```
|
||||
|
||||
Wait up to 30 seconds, then confirm:
|
||||
|
||||
```bash
|
||||
psql -h replica.db.internal -U dba -d postgres -c "SELECT pg_is_in_recovery();"
|
||||
```
|
||||
|
||||
**Expected output:** `pg_is_in_recovery = f` (no longer a replica).
|
||||
|
||||
### Step 5 — Update DNS
|
||||
|
||||
```bash
|
||||
aws route53 change-resource-record-sets \
|
||||
--hosted-zone-id Z1234567890 \
|
||||
--change-batch '{
|
||||
"Changes": [{
|
||||
"Action": "UPSERT",
|
||||
"ResourceRecordSet": {
|
||||
"Name": "db.internal.example.com",
|
||||
"Type": "CNAME",
|
||||
"TTL": 60,
|
||||
"ResourceRecords": [{"Value": "replica.db.internal"}]
|
||||
}
|
||||
}]
|
||||
}'
|
||||
```
|
||||
|
||||
### Step 6 — Restart Application
|
||||
|
||||
```bash
|
||||
kubectl scale deployment api-server --replicas=6 -n production
|
||||
kubectl scale deployment worker --replicas=4 -n production
|
||||
```
|
||||
|
||||
## Verification
|
||||
- [ ] Application connects to new primary
|
||||
- [ ] No replication lag errors
|
||||
- [ ] Transactions completing
|
||||
|
||||
- [ ] `psql -h db.internal.example.com -c "SELECT 1;"` returns successfully
|
||||
- [ ] Application logs show successful DB connections (no errors for 5 min)
|
||||
- [ ] Transaction throughput returns to baseline on Grafana dashboard
|
||||
- [ ] No replication-lag alerts firing
|
||||
|
||||
## Rollback
|
||||
|
||||
If the promoted replica has issues, restore from the most recent backup:
|
||||
|
||||
```bash
|
||||
# Restore latest automated snapshot (RDS example)
|
||||
aws rds restore-db-instance-from-db-snapshot \
|
||||
--db-instance-identifier prod-db-restored \
|
||||
--db-snapshot-identifier prod-db-latest-snapshot
|
||||
```
|
||||
|
||||
## Escalation
|
||||
If issues persist after 15 minutes, escalate to:
|
||||
- Primary: @dba-lead
|
||||
- Secondary: @platform-oncall
|
||||
|
||||
If unresolved after 15 minutes:
|
||||
- **Primary:** @dba-lead — +1-555-0101
|
||||
- **Secondary:** @platform-oncall — +1-555-0102
|
||||
````
|
||||
|
||||
## Automation Scripts for Common Operations
|
||||
|
||||
### Service Health Check
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# health-check.sh — Check health of critical services
|
||||
set -euo pipefail
|
||||
|
||||
SERVICES=(
|
||||
"https://api.example.com/healthz"
|
||||
"https://app.example.com/healthz"
|
||||
"https://admin.example.com/healthz"
|
||||
)
|
||||
|
||||
EXIT_CODE=0
|
||||
|
||||
for url in "${SERVICES[@]}"; do
|
||||
HTTP_CODE=$(curl -so /dev/null -w '%{http_code}' --max-time 5 "$url" 2>/dev/null || echo "000")
|
||||
if [ "$HTTP_CODE" -eq 200 ]; then
|
||||
printf " OK %s\n" "$url"
|
||||
else
|
||||
printf " FAIL %s (HTTP %s)\n" "$url" "$HTTP_CODE"
|
||||
EXIT_CODE=1
|
||||
fi
|
||||
done
|
||||
|
||||
exit $EXIT_CODE
|
||||
```
|
||||
|
||||
### Log Collection for Incident Investigation
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# collect-logs.sh — Gather logs from multiple sources for incident review
|
||||
set -euo pipefail
|
||||
|
||||
INCIDENT_ID="${1:?Usage: collect-logs.sh <incident-id>}"
|
||||
OUTDIR="/tmp/incident-${INCIDENT_ID}"
|
||||
mkdir -p "$OUTDIR"
|
||||
|
||||
echo "Collecting logs for incident $INCIDENT_ID..."
|
||||
|
||||
# Kubernetes pod logs (last 30 min)
|
||||
kubectl logs -l app=api-server -n production --since=30m \
|
||||
> "${OUTDIR}/api-server-pods.log" 2>&1
|
||||
|
||||
# CloudWatch Logs (last 30 min)
|
||||
aws logs filter-log-events \
|
||||
--log-group-name /ecs/production/api \
|
||||
--start-time "$(date -d '30 minutes ago' +%s)000" \
|
||||
--output text > "${OUTDIR}/cloudwatch-api.log" 2>&1
|
||||
|
||||
# Database slow query log
|
||||
psql -h db.internal -U dba -d postgres -c \
|
||||
"SELECT * FROM pg_stat_activity WHERE state != 'idle' ORDER BY query_start;" \
|
||||
> "${OUTDIR}/db-active-queries.log" 2>&1
|
||||
|
||||
# System resource snapshot
|
||||
kubectl top pods -n production > "${OUTDIR}/pod-resources.log" 2>&1
|
||||
|
||||
echo "Logs saved to $OUTDIR"
|
||||
tar czf "${OUTDIR}.tar.gz" -C /tmp "incident-${INCIDENT_ID}"
|
||||
echo "Archive: ${OUTDIR}.tar.gz"
|
||||
```
|
||||
|
||||
### Certificate Expiry Check
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# cert-check.sh — Warn if TLS certificates expire within 30 days
|
||||
set -euo pipefail
|
||||
|
||||
DOMAINS=(
|
||||
"api.example.com"
|
||||
"app.example.com"
|
||||
"admin.example.com"
|
||||
)
|
||||
|
||||
WARN_DAYS=30
|
||||
TODAY=$(date +%s)
|
||||
EXIT_CODE=0
|
||||
|
||||
for domain in "${DOMAINS[@]}"; do
|
||||
EXPIRY=$(echo | openssl s_client -servername "$domain" -connect "${domain}:443" 2>/dev/null \
|
||||
| openssl x509 -noout -enddate 2>/dev/null | cut -d= -f2)
|
||||
EXPIRY_EPOCH=$(date -d "$EXPIRY" +%s 2>/dev/null || echo 0)
|
||||
DAYS_LEFT=$(( (EXPIRY_EPOCH - TODAY) / 86400 ))
|
||||
|
||||
if [ "$DAYS_LEFT" -lt "$WARN_DAYS" ]; then
|
||||
printf " WARN %s expires in %d days (%s)\n" "$domain" "$DAYS_LEFT" "$EXPIRY"
|
||||
EXIT_CODE=1
|
||||
else
|
||||
printf " OK %s — %d days remaining\n" "$domain" "$DAYS_LEFT"
|
||||
fi
|
||||
done
|
||||
|
||||
exit $EXIT_CODE
|
||||
```
|
||||
|
||||
### Disk Space Cleanup
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# disk-cleanup.sh — Free disk space on a host
|
||||
set -euo pipefail
|
||||
|
||||
echo "=== Disk Usage Before ==="
|
||||
df -h /
|
||||
|
||||
# Remove old journal logs (> 7 days)
|
||||
journalctl --vacuum-time=7d 2>/dev/null || true
|
||||
|
||||
# Clean Docker artifacts
|
||||
docker system prune -f --volumes 2>/dev/null || true
|
||||
|
||||
# Remove old log files
|
||||
find /var/log -name "*.gz" -mtime +7 -delete 2>/dev/null || true
|
||||
find /tmp -type f -mtime +3 -delete 2>/dev/null || true
|
||||
|
||||
echo "=== Disk Usage After ==="
|
||||
df -h /
|
||||
```
|
||||
|
||||
## Runbook Review Checklist
|
||||
|
||||
Use this checklist every time a runbook is created or updated.
|
||||
|
||||
```yaml
|
||||
content_review:
|
||||
- [ ] Title clearly identifies the service and operation
|
||||
- [ ] Overview explains WHEN and WHY to use this runbook
|
||||
- [ ] Prerequisites list all required access, tools, and setup
|
||||
- [ ] Every step has a concrete command (no vague instructions)
|
||||
- [ ] Expected output is documented for each step
|
||||
- [ ] Verification section confirms success with specific checks
|
||||
- [ ] Rollback section exists and has been tested
|
||||
- [ ] Escalation contacts are current (names, phones, Slack handles)
|
||||
- [ ] Troubleshooting table covers the top 3-5 known failure modes
|
||||
|
||||
usability_review:
|
||||
- [ ] A new team member can follow the runbook without tribal knowledge
|
||||
- [ ] Steps are numbered and sequential (no branching without clear labels)
|
||||
- [ ] Commands can be copy-pasted (no placeholder values without explanation)
|
||||
- [ ] Time estimates included for long-running steps
|
||||
- [ ] No jargon or acronyms used without definition
|
||||
|
||||
maintenance_review:
|
||||
- [ ] Owner and last-reviewed date are set
|
||||
- [ ] Version number incremented
|
||||
- [ ] Change log entry added
|
||||
- [ ] Related runbooks section is up to date
|
||||
- [ ] Links to dashboards and docs are valid (not broken)
|
||||
```
|
||||
|
||||
## Runbook Testing Procedures
|
||||
|
||||
```yaml
|
||||
testing_strategy:
|
||||
dry_run:
|
||||
frequency: "Every time a runbook is created or substantially edited"
|
||||
method: "Walk through each step in a staging environment"
|
||||
goal: "Verify commands work and output matches documentation"
|
||||
|
||||
peer_review:
|
||||
frequency: "Every edit"
|
||||
method: "Another engineer follows the runbook in staging without help"
|
||||
goal: "Confirm the runbook is self-contained and unambiguous"
|
||||
|
||||
scheduled_validation:
|
||||
frequency: "Quarterly"
|
||||
method: "SRE team picks 5 runbooks at random, executes in staging"
|
||||
goal: "Catch runbooks that have drifted from production reality"
|
||||
|
||||
incident_triggered:
|
||||
trigger: "Any time a runbook is used in a real incident"
|
||||
method: "Post-mortem includes runbook accuracy assessment"
|
||||
goal: "Capture improvements while the experience is fresh"
|
||||
|
||||
automation_testing:
|
||||
method: "CI pipeline validates bash scripts with shellcheck and dry-run"
|
||||
example: |
|
||||
# .github/workflows/runbook-lint.yml
|
||||
name: Lint Runbook Scripts
|
||||
on: [pull_request]
|
||||
jobs:
|
||||
shellcheck:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: ShellCheck
|
||||
run: |
|
||||
find runbooks/ -name "*.sh" -exec shellcheck {} +
|
||||
```
|
||||
|
||||
## Versioning Strategy
|
||||
|
||||
```yaml
|
||||
versioning:
|
||||
storage: "Git repository — one directory per service, one file per runbook"
|
||||
naming: "runbooks/<service>/<operation>.md"
|
||||
branching: "PRs required for all changes; reviewed by service owner"
|
||||
|
||||
version_scheme:
|
||||
format: "MAJOR.MINOR"
|
||||
major_bump: "Procedure changes that alter the steps or their order"
|
||||
minor_bump: "Clarifications, typo fixes, updated contact info"
|
||||
|
||||
directory_layout: |
|
||||
runbooks/
|
||||
api-server/
|
||||
deploy.md
|
||||
rollback.md
|
||||
scale-up.md
|
||||
database/
|
||||
failover.md
|
||||
backup-restore.md
|
||||
vacuum-maintenance.md
|
||||
infrastructure/
|
||||
dns-update.md
|
||||
certificate-renewal.md
|
||||
disk-cleanup.md
|
||||
|
||||
review_requirements:
|
||||
- PR must be approved by the service owner
|
||||
- CI must pass (shellcheck for scripts, markdown lint)
|
||||
- Reviewer confirms they can follow the steps independently
|
||||
|
||||
retention: "Git history serves as full audit trail — never delete old versions"
|
||||
```
|
||||
|
||||
## Runbook Index Template
|
||||
|
||||
Keep a top-level index so engineers can find the right runbook quickly.
|
||||
|
||||
```markdown
|
||||
# Runbook Index
|
||||
|
||||
| Service | Runbook | Severity | Owner | Last Tested |
|
||||
|---------|---------|----------|-------|-------------|
|
||||
| API Server | [Deploy](api-server/deploy.md) | — | @platform | 2025-05-01 |
|
||||
| API Server | [Rollback](api-server/rollback.md) | SEV1 | @platform | 2025-05-01 |
|
||||
| Database | [Failover](database/failover.md) | SEV1 | @dba | 2025-04-15 |
|
||||
| Database | [Backup Restore](database/backup-restore.md) | SEV2 | @dba | 2025-04-15 |
|
||||
| Infra | [DNS Update](infrastructure/dns-update.md) | SEV2 | @sre | 2025-06-01 |
|
||||
| Infra | [Cert Renewal](infrastructure/certificate-renewal.md) | SEV3 | @sre | 2025-06-01 |
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Keep procedures simple and clear
|
||||
- Include verification steps
|
||||
- Test runbooks regularly
|
||||
- Version control runbooks
|
||||
- Include troubleshooting tips
|
||||
- Write runbooks for the engineer at 3 AM — clear, sequential, copy-pasteable
|
||||
- Include expected output so the operator knows if a step succeeded
|
||||
- Always provide a rollback path; every action should be reversible
|
||||
- Test runbooks in staging before they are needed in production
|
||||
- Keep runbooks in version control alongside the code they support
|
||||
- Assign an owner to every runbook; ownerless runbooks rot fast
|
||||
- After every incident, update the relevant runbook with lessons learned
|
||||
- Automate repetitive runbook steps into scripts, but keep the runbook as
|
||||
the orchestration guide so operators understand the "why"
|
||||
|
||||
@@ -9,63 +9,421 @@ metadata:
|
||||
|
||||
# FedRAMP Compliance
|
||||
|
||||
Implement FedRAMP requirements for federal cloud services.
|
||||
Implement FedRAMP (Federal Risk and Authorization Management Program) requirements for cloud service providers serving US federal agencies.
|
||||
|
||||
## When to Use
|
||||
|
||||
- Pursuing FedRAMP authorization for a cloud service offering
|
||||
- Implementing NIST 800-53 security controls for federal workloads
|
||||
- Establishing continuous monitoring (ConMon) processes
|
||||
- Managing Plan of Action and Milestones (POA&M) tracking
|
||||
- Preparing for a Third-Party Assessment Organization (3PAO) audit
|
||||
- Operating a FedRAMP-authorized system and maintaining authorization
|
||||
|
||||
## Impact Levels
|
||||
|
||||
```yaml
|
||||
levels:
|
||||
impact_levels:
|
||||
low:
|
||||
controls: ~125
|
||||
use_case: Public data
|
||||
|
||||
control_count: ~125
|
||||
use_case: "Publicly available federal information"
|
||||
examples:
|
||||
- Public-facing websites with no sensitive data
|
||||
- Open data portals
|
||||
- Marketing and informational systems
|
||||
data_types: "No PII, no CUI, publicly releasable only"
|
||||
authorization_path: "FedRAMP Tailored (Li-SaaS) or standard Low"
|
||||
|
||||
moderate:
|
||||
controls: ~325
|
||||
use_case: CUI, most federal systems
|
||||
|
||||
control_count: ~325
|
||||
use_case: "Most federal systems, including CUI"
|
||||
examples:
|
||||
- Email and collaboration platforms
|
||||
- Case management systems
|
||||
- Financial management systems
|
||||
- HR and personnel systems
|
||||
data_types: "CUI, PII, law enforcement sensitive (LES)"
|
||||
authorization_path: "Agency or JAB P-ATO"
|
||||
note: "~80% of FedRAMP authorizations are at Moderate"
|
||||
|
||||
high:
|
||||
controls: ~425
|
||||
use_case: Law enforcement, emergency services
|
||||
control_count: ~425
|
||||
use_case: "High-impact federal systems"
|
||||
examples:
|
||||
- Law enforcement and criminal justice systems
|
||||
- Emergency services and public safety
|
||||
- Financial systems with significant impact
|
||||
- Healthcare systems with PHI
|
||||
data_types: "Classified-adjacent, life-safety, critical infrastructure"
|
||||
authorization_path: "JAB P-ATO required"
|
||||
```
|
||||
|
||||
## NIST 800-53 Families
|
||||
## NIST 800-53 Control Families
|
||||
|
||||
```yaml
|
||||
control_families:
|
||||
AC: Access Control
|
||||
AU: Audit and Accountability
|
||||
AT: Awareness and Training
|
||||
CM: Configuration Management
|
||||
CP: Contingency Planning
|
||||
IA: Identification and Authentication
|
||||
IR: Incident Response
|
||||
MA: Maintenance
|
||||
MP: Media Protection
|
||||
PE: Physical Protection
|
||||
PL: Planning
|
||||
PS: Personnel Security
|
||||
RA: Risk Assessment
|
||||
CA: Assessment and Authorization
|
||||
SC: System and Communications Protection
|
||||
SI: System and Information Integrity
|
||||
SA: System and Services Acquisition
|
||||
PM: Program Management
|
||||
AC:
|
||||
name: "Access Control"
|
||||
key_controls:
|
||||
AC-2: "Account Management - manage system accounts lifecycle"
|
||||
AC-3: "Access Enforcement - enforce approved authorizations"
|
||||
AC-6: "Least Privilege - employ principle of least privilege"
|
||||
AC-17: "Remote Access - establish usage restrictions for remote access"
|
||||
implementation_notes: "Map to IAM policies, RBAC, MFA enforcement"
|
||||
|
||||
AU:
|
||||
name: "Audit and Accountability"
|
||||
key_controls:
|
||||
AU-2: "Audit Events - define auditable events"
|
||||
AU-3: "Content of Audit Records - ensure records contain required info"
|
||||
AU-6: "Audit Review, Analysis, and Reporting"
|
||||
AU-12: "Audit Generation - generate audit records"
|
||||
implementation_notes: "Map to CloudTrail, CloudWatch Logs, SIEM"
|
||||
|
||||
AT:
|
||||
name: "Awareness and Training"
|
||||
key_controls:
|
||||
AT-2: "Security Awareness Training - provide training to users"
|
||||
AT-3: "Role-Based Security Training - for personnel with security roles"
|
||||
implementation_notes: "Annual security training, role-specific training"
|
||||
|
||||
CM:
|
||||
name: "Configuration Management"
|
||||
key_controls:
|
||||
CM-2: "Baseline Configuration - develop and maintain baseline"
|
||||
CM-6: "Configuration Settings - establish mandatory settings"
|
||||
CM-7: "Least Functionality - restrict to essential capabilities"
|
||||
CM-8: "Information System Component Inventory"
|
||||
implementation_notes: "Map to AWS Config, SSM, hardened AMIs"
|
||||
|
||||
CP:
|
||||
name: "Contingency Planning"
|
||||
key_controls:
|
||||
CP-2: "Contingency Plan - develop and maintain plan"
|
||||
CP-4: "Contingency Plan Testing - test plan annually"
|
||||
CP-9: "Information System Backup"
|
||||
CP-10: "Information System Recovery and Reconstitution"
|
||||
implementation_notes: "Map to DR plan, backup strategy, failover testing"
|
||||
|
||||
IA:
|
||||
name: "Identification and Authentication"
|
||||
key_controls:
|
||||
IA-2: "Identification and Authentication (Org Users)"
|
||||
IA-5: "Authenticator Management"
|
||||
IA-8: "Identification and Authentication (Non-Org Users)"
|
||||
implementation_notes: "Map to SSO, MFA, certificate-based auth, PIV/CAC"
|
||||
|
||||
IR:
|
||||
name: "Incident Response"
|
||||
key_controls:
|
||||
IR-2: "Incident Response Training"
|
||||
IR-4: "Incident Handling - implement incident handling capability"
|
||||
IR-6: "Incident Reporting - report incidents to US-CERT"
|
||||
IR-8: "Incident Response Plan"
|
||||
implementation_notes: "US-CERT reporting within 1 hour for federal incidents"
|
||||
|
||||
MA:
|
||||
name: "Maintenance"
|
||||
key_controls:
|
||||
MA-2: "Controlled Maintenance"
|
||||
MA-4: "Nonlocal Maintenance - authorize nonlocal maintenance"
|
||||
implementation_notes: "Patching procedures, remote maintenance controls"
|
||||
|
||||
MP:
|
||||
name: "Media Protection"
|
||||
key_controls:
|
||||
MP-2: "Media Access - restrict access to media"
|
||||
MP-6: "Media Sanitization - sanitize media prior to disposal"
|
||||
implementation_notes: "Encryption at rest, secure disposal procedures"
|
||||
|
||||
PE:
|
||||
name: "Physical and Environmental Protection"
|
||||
key_controls:
|
||||
PE-2: "Physical Access Authorizations"
|
||||
PE-3: "Physical Access Control"
|
||||
PE-6: "Monitoring Physical Access"
|
||||
implementation_notes: "Inherit from CSP for IaaS/PaaS, document inheritance"
|
||||
|
||||
PL:
|
||||
name: "Planning"
|
||||
key_controls:
|
||||
PL-2: "System Security Plan (SSP)"
|
||||
implementation_notes: "SSP is the core FedRAMP deliverable"
|
||||
|
||||
PS:
|
||||
name: "Personnel Security"
|
||||
key_controls:
|
||||
PS-3: "Personnel Screening"
|
||||
PS-4: "Personnel Termination"
|
||||
PS-5: "Personnel Transfer"
|
||||
implementation_notes: "Background checks, access revocation on termination"
|
||||
|
||||
RA:
|
||||
name: "Risk Assessment"
|
||||
key_controls:
|
||||
RA-3: "Risk Assessment - conduct risk assessment"
|
||||
RA-5: "Vulnerability Scanning"
|
||||
implementation_notes: "Annual risk assessment, monthly vulnerability scans"
|
||||
|
||||
CA:
|
||||
name: "Security Assessment and Authorization"
|
||||
key_controls:
|
||||
CA-2: "Security Assessments"
|
||||
CA-6: "Security Authorization"
|
||||
CA-7: "Continuous Monitoring"
|
||||
implementation_notes: "Annual assessment by 3PAO, ConMon program"
|
||||
|
||||
SC:
|
||||
name: "System and Communications Protection"
|
||||
key_controls:
|
||||
SC-7: "Boundary Protection"
|
||||
SC-8: "Transmission Confidentiality and Integrity"
|
||||
SC-12: "Cryptographic Key Establishment and Management"
|
||||
SC-13: "Cryptographic Protection - FIPS 140-2 validated"
|
||||
SC-28: "Protection of Information at Rest"
|
||||
implementation_notes: "FIPS 140-2 validated modules required"
|
||||
|
||||
SI:
|
||||
name: "System and Information Integrity"
|
||||
key_controls:
|
||||
SI-2: "Flaw Remediation"
|
||||
SI-3: "Malicious Code Protection"
|
||||
SI-4: "Information System Monitoring"
|
||||
SI-5: "Security Alerts, Advisories, and Directives"
|
||||
implementation_notes: "Patching SLAs, antimalware, IDS/IPS, SIEM"
|
||||
|
||||
SA:
|
||||
name: "System and Services Acquisition"
|
||||
key_controls:
|
||||
SA-4: "Acquisition Process - security requirements in contracts"
|
||||
SA-9: "External Information System Services"
|
||||
SA-11: "Developer Security Testing"
|
||||
implementation_notes: "Supply chain risk management, SBOM"
|
||||
|
||||
PM:
|
||||
name: "Program Management"
|
||||
key_controls:
|
||||
PM-1: "Information Security Program Plan"
|
||||
PM-9: "Risk Management Strategy"
|
||||
implementation_notes: "Organization-wide security program"
|
||||
```
|
||||
|
||||
## Continuous Monitoring
|
||||
## System Security Plan (SSP) Outline
|
||||
|
||||
```yaml
|
||||
conmon:
|
||||
vulnerability_scans: Monthly
|
||||
penetration_tests: Annual
|
||||
poa_m_updates: Monthly
|
||||
security_assessment: Annual
|
||||
ssp_sections:
|
||||
section_1: "Information System Name and Title"
|
||||
section_2: "Information System Categorization (FIPS 199)"
|
||||
section_3: "Information System Owner"
|
||||
section_4: "Authorizing Official"
|
||||
section_5: "Other Designated Contacts"
|
||||
section_6: "Assignment of Security Responsibility"
|
||||
section_7: "Information System Operational Status"
|
||||
section_8: "Information System Type (cloud service model)"
|
||||
section_9: "General System Description"
|
||||
section_10: "System Environment and Special Considerations"
|
||||
section_11: "System Interconnections"
|
||||
section_12: "Laws, Regulations, Policies Applicable"
|
||||
section_13: "Minimum Security Controls"
|
||||
|
||||
key_attachments:
|
||||
- "Control Implementation Summary (CIS) workbook"
|
||||
- "Network architecture diagrams"
|
||||
- "Data flow diagrams"
|
||||
- "Interconnection security agreements (ISAs)"
|
||||
- "Incident response plan"
|
||||
- "Contingency plan"
|
||||
- "Configuration management plan"
|
||||
```
|
||||
|
||||
## POA&M (Plan of Action and Milestones) Tracking
|
||||
|
||||
```yaml
|
||||
# poam_template.yaml
|
||||
poam_entry:
|
||||
- id: "POAM-2025-001"
|
||||
weakness: "AC-2(3) - Automated account disable after 90 days inactivity not implemented"
|
||||
control: "AC-2"
|
||||
risk_level: "moderate"
|
||||
finding_source: "3PAO Annual Assessment - 2025"
|
||||
date_identified: "2025-03-15"
|
||||
scheduled_completion: "2025-06-15"
|
||||
milestone_1:
|
||||
description: "Configure IdP inactivity policy"
|
||||
target_date: "2025-04-15"
|
||||
status: "complete"
|
||||
milestone_2:
|
||||
description: "Test automated disable in staging"
|
||||
target_date: "2025-05-01"
|
||||
status: "in_progress"
|
||||
milestone_3:
|
||||
description: "Deploy to production and validate"
|
||||
target_date: "2025-06-15"
|
||||
status: "not_started"
|
||||
responsible_party: "IAM Team"
|
||||
status: "open"
|
||||
vendor_dependency: false
|
||||
|
||||
- id: "POAM-2025-002"
|
||||
weakness: "RA-5 - Vulnerability scan coverage does not include container images"
|
||||
control: "RA-5"
|
||||
risk_level: "high"
|
||||
finding_source: "3PAO Annual Assessment - 2025"
|
||||
date_identified: "2025-03-15"
|
||||
scheduled_completion: "2025-05-15"
|
||||
milestone_1:
|
||||
description: "Evaluate and select container scanning tool"
|
||||
target_date: "2025-04-01"
|
||||
status: "complete"
|
||||
milestone_2:
|
||||
description: "Integrate scanning into CI/CD pipeline"
|
||||
target_date: "2025-04-30"
|
||||
status: "in_progress"
|
||||
milestone_3:
|
||||
description: "Demonstrate full coverage to 3PAO"
|
||||
target_date: "2025-05-15"
|
||||
status: "not_started"
|
||||
responsible_party: "Security Engineering"
|
||||
status: "open"
|
||||
vendor_dependency: false
|
||||
|
||||
poam_aging_thresholds:
|
||||
high: "Must be resolved within 30 days"
|
||||
moderate: "Must be resolved within 90 days"
|
||||
low: "Must be resolved within 180 days"
|
||||
overdue_escalation: "Reported to authorizing official monthly"
|
||||
```
|
||||
|
||||
## Continuous Monitoring (ConMon) Procedures
|
||||
|
||||
```yaml
|
||||
continuous_monitoring:
|
||||
monthly:
|
||||
vulnerability_scanning:
|
||||
scope: "All operating systems, databases, web applications, and containers"
|
||||
tool: "Tenable.io, Qualys, or equivalent"
|
||||
deliverable: "Monthly scan report with remediation status"
|
||||
sla:
|
||||
critical_cvss_9_plus: "Remediate within 30 days"
|
||||
high_cvss_7_to_9: "Remediate within 30 days"
|
||||
moderate_cvss_4_to_7: "Remediate within 90 days"
|
||||
low_cvss_below_4: "Remediate within 180 days"
|
||||
|
||||
poam_updates:
|
||||
action: "Update all open POA&M items with current status"
|
||||
deliverable: "Updated POA&M spreadsheet submitted to agency"
|
||||
content:
|
||||
- "Milestone completion updates"
|
||||
- "New POA&M items from scans"
|
||||
- "Closed POA&M items with evidence"
|
||||
|
||||
inventory_updates:
|
||||
action: "Review and update system component inventory"
|
||||
deliverable: "Updated hardware and software inventory"
|
||||
|
||||
quarterly:
|
||||
- "Review and update SSP with any system changes"
|
||||
- "Submit ConMon deliverables package to agency"
|
||||
- "Review access control lists and user accounts"
|
||||
- "Update network diagrams if changes occurred"
|
||||
|
||||
annual:
|
||||
security_assessment:
|
||||
performed_by: "3PAO"
|
||||
scope: "Subset of controls (~1/3 each year, full coverage in 3 years)"
|
||||
deliverable: "Security Assessment Report (SAR)"
|
||||
|
||||
penetration_testing:
|
||||
performed_by: "3PAO or qualified third party"
|
||||
scope: "External and internal network, web applications"
|
||||
deliverable: "Penetration test report with findings"
|
||||
|
||||
contingency_plan_test:
|
||||
scope: "Full DR/BCP test including failover"
|
||||
deliverable: "Contingency plan test report"
|
||||
|
||||
incident_response_test:
|
||||
scope: "Tabletop exercise or functional exercise"
|
||||
deliverable: "IR test report with lessons learned"
|
||||
```
|
||||
|
||||
## FedRAMP FIPS 140-2 Cryptography Requirements
|
||||
|
||||
```bash
|
||||
# Verify FIPS mode is enabled on Linux systems
|
||||
cat /proc/sys/crypto/fips_enabled
|
||||
# Output should be: 1
|
||||
|
||||
# Check OpenSSL FIPS module
|
||||
openssl version
|
||||
openssl list -providers # Should show FIPS provider
|
||||
|
||||
# AWS: Use FIPS endpoints
|
||||
# Example: Use FIPS endpoint for S3
|
||||
aws s3 ls --endpoint-url https://s3-fips.us-east-1.amazonaws.com
|
||||
|
||||
# Configure AWS CLI for FIPS
|
||||
# ~/.aws/config
|
||||
# [default]
|
||||
# use_fips_endpoint = true
|
||||
|
||||
# Verify TLS configuration meets FedRAMP requirements
|
||||
openssl s_client -connect your-service.example.com:443 -tls1_2 < /dev/null 2>/dev/null | \
|
||||
grep -E "Protocol|Cipher"
|
||||
# Must be TLS 1.2 or higher with FIPS-approved cipher suites
|
||||
```
|
||||
|
||||
## FedRAMP Authorization Checklist
|
||||
|
||||
```yaml
|
||||
authorization_checklist:
|
||||
pre_authorization:
|
||||
- [ ] Determine impact level (Low, Moderate, High)
|
||||
- [ ] Choose authorization path (Agency ATO or JAB P-ATO)
|
||||
- [ ] Engage FedRAMP PMO for readiness assessment
|
||||
- [ ] Select 3PAO from FedRAMP marketplace
|
||||
- [ ] Complete SSP with all control implementations documented
|
||||
- [ ] Develop required policies and procedures
|
||||
- [ ] Implement all applicable NIST 800-53 controls
|
||||
- [ ] Ensure FIPS 140-2 validated cryptographic modules in use
|
||||
|
||||
assessment:
|
||||
- [ ] 3PAO conducts readiness assessment (optional but recommended)
|
||||
- [ ] 3PAO conducts full security assessment
|
||||
- [ ] 3PAO delivers Security Assessment Report (SAR)
|
||||
- [ ] Develop POA&M for all findings
|
||||
- [ ] Remediate critical and high findings before authorization
|
||||
|
||||
authorization_package:
|
||||
- [ ] System Security Plan (SSP)
|
||||
- [ ] Security Assessment Report (SAR)
|
||||
- [ ] Plan of Action and Milestones (POA&M)
|
||||
- [ ] Continuous Monitoring Plan
|
||||
- [ ] Incident Response Plan
|
||||
- [ ] Contingency Plan
|
||||
- [ ] Configuration Management Plan
|
||||
- [ ] Control Implementation Summary (CIS)
|
||||
- [ ] Interconnection Security Agreements
|
||||
|
||||
post_authorization:
|
||||
- [ ] Establish ConMon program with monthly deliverables
|
||||
- [ ] Monthly vulnerability scanning and POA&M updates
|
||||
- [ ] Annual 3PAO assessment of control subset
|
||||
- [ ] Annual penetration testing
|
||||
- [ ] Report significant changes to authorizing official
|
||||
- [ ] Report security incidents to US-CERT within 1 hour
|
||||
- [ ] Maintain authorization by meeting ConMon requirements
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- 3PAO assessment
|
||||
- SSP documentation
|
||||
- POA&M tracking
|
||||
- Continuous monitoring
|
||||
- Annual authorization
|
||||
- Start with a FedRAMP Readiness Assessment to identify gaps before the formal 3PAO assessment
|
||||
- Use the FedRAMP SSP template exactly as provided to avoid review delays
|
||||
- Inherit controls from your IaaS provider (AWS GovCloud, Azure Government) and document the inheritance clearly
|
||||
- Implement FIPS 140-2 validated cryptographic modules for all encryption (TLS, at-rest, key management)
|
||||
- Automate continuous monitoring deliverables to reduce manual effort and human error
|
||||
- Maintain POA&M items within aging thresholds; overdue items risk losing authorization
|
||||
- Report significant system changes to the authorizing official before implementation
|
||||
- Treat the SSP as a living document and update it with every change to the system boundary
|
||||
- Use US-CERT reporting procedures and maintain the 1-hour incident notification requirement
|
||||
- Engage the FedRAMP PMO early and often for guidance on the authorization process
|
||||
|
||||
@@ -9,56 +9,552 @@ metadata:
|
||||
|
||||
# GDPR Compliance
|
||||
|
||||
Implement GDPR requirements for EU data protection.
|
||||
Implement General Data Protection Regulation requirements for organizations that process personal data of EU/EEA residents, covering lawful processing, data subject rights, and technical safeguards.
|
||||
|
||||
## Key Principles
|
||||
## When to Use
|
||||
|
||||
- Processing personal data of EU/EEA residents in any capacity
|
||||
- Building consent management and preference centers
|
||||
- Implementing Data Subject Access Request (DSAR) workflows
|
||||
- Conducting Data Protection Impact Assessments (DPIAs)
|
||||
- Setting up data processing agreements with third-party processors
|
||||
- Designing systems with privacy by design and by default principles
|
||||
|
||||
## Key Principles and Legal Bases
|
||||
|
||||
```yaml
|
||||
principles:
|
||||
lawfulness: Legal basis for processing
|
||||
purpose_limitation: Specific, explicit purposes
|
||||
data_minimization: Adequate, relevant, limited
|
||||
accuracy: Accurate and up to date
|
||||
storage_limitation: No longer than necessary
|
||||
integrity: Secure processing
|
||||
accountability: Demonstrate compliance
|
||||
gdpr_principles:
|
||||
article_5:
|
||||
lawfulness_fairness_transparency:
|
||||
description: "Process data lawfully, fairly, and transparently"
|
||||
implementation:
|
||||
- Document legal basis for every processing activity
|
||||
- Provide clear privacy notices
|
||||
- No hidden or deceptive data collection
|
||||
|
||||
purpose_limitation:
|
||||
description: "Collect for specified, explicit, and legitimate purposes"
|
||||
implementation:
|
||||
- Define purpose before collection
|
||||
- Do not repurpose data without new legal basis
|
||||
- Document all processing purposes in ROPA
|
||||
|
||||
data_minimization:
|
||||
description: "Adequate, relevant, and limited to what is necessary"
|
||||
implementation:
|
||||
- Collect only required fields
|
||||
- Review data models for unnecessary fields
|
||||
- Remove optional fields that are not used
|
||||
|
||||
accuracy:
|
||||
description: "Accurate and kept up to date"
|
||||
implementation:
|
||||
- Provide self-service profile editing
|
||||
- Implement data validation at point of entry
|
||||
- Schedule regular data quality reviews
|
||||
|
||||
storage_limitation:
|
||||
description: "Kept no longer than necessary"
|
||||
implementation:
|
||||
- Define retention periods per data category
|
||||
- Automate deletion when retention expires
|
||||
- Document retention schedule
|
||||
|
||||
integrity_and_confidentiality:
|
||||
description: "Appropriate security measures"
|
||||
implementation:
|
||||
- Encryption at rest and in transit
|
||||
- Access controls and audit logging
|
||||
- Pseudonymization where appropriate
|
||||
|
||||
accountability:
|
||||
description: "Demonstrate compliance"
|
||||
implementation:
|
||||
- Maintain Records of Processing Activities
|
||||
- Conduct DPIAs for high-risk processing
|
||||
- Appoint DPO if required
|
||||
|
||||
legal_bases:
|
||||
article_6:
|
||||
consent: "Freely given, specific, informed, unambiguous"
|
||||
contract: "Necessary for performance of a contract"
|
||||
legal_obligation: "Required by EU or member state law"
|
||||
vital_interests: "Protect life of data subject or another person"
|
||||
public_interest: "Task carried out in public interest"
|
||||
legitimate_interest: "Legitimate interest not overridden by data subject rights"
|
||||
```
|
||||
|
||||
## Data Subject Rights
|
||||
## Data Mapping Template (Records of Processing Activities)
|
||||
|
||||
```yaml
|
||||
rights:
|
||||
- Right to access
|
||||
- Right to rectification
|
||||
- Right to erasure
|
||||
- Right to restrict processing
|
||||
- Right to data portability
|
||||
- Right to object
|
||||
- Rights related to automated decisions
|
||||
# Record of Processing Activities (ROPA) - Article 30
|
||||
processing_activity:
|
||||
name: "Customer Account Management"
|
||||
controller: "Example Corp, 123 Main St, Dublin, Ireland"
|
||||
dpo_contact: "dpo@example.com"
|
||||
purpose: "Manage customer accounts, provide services, handle billing"
|
||||
legal_basis: "Contract (Art. 6(1)(b))"
|
||||
categories_of_data_subjects:
|
||||
- Customers
|
||||
- Prospective customers
|
||||
categories_of_personal_data:
|
||||
- Name, email, phone number
|
||||
- Billing address
|
||||
- Payment information (tokenized)
|
||||
- Service usage data
|
||||
- Support ticket history
|
||||
special_categories: "None"
|
||||
recipients:
|
||||
- Payment processor (Stripe) - processor
|
||||
- Email service (SendGrid) - processor
|
||||
- Cloud hosting (AWS) - processor
|
||||
international_transfers:
|
||||
- Destination: United States
|
||||
Safeguard: "Standard Contractual Clauses (SCCs)"
|
||||
TIA_completed: true
|
||||
retention_period: "Account data retained for duration of contract + 7 years for legal obligations"
|
||||
security_measures:
|
||||
- AES-256 encryption at rest
|
||||
- TLS 1.3 in transit
|
||||
- Role-based access control
|
||||
- Audit logging of all access
|
||||
dpia_required: false
|
||||
last_reviewed: "2024-06-01"
|
||||
|
||||
# Template for each processing activity
|
||||
processing_activity_template:
|
||||
name: ""
|
||||
controller: ""
|
||||
joint_controller: "" # if applicable
|
||||
processor: "" # if acting as processor
|
||||
dpo_contact: ""
|
||||
purpose: ""
|
||||
legal_basis: "" # consent | contract | legal_obligation | vital_interests | public_interest | legitimate_interest
|
||||
legitimate_interest_assessment: "" # if legitimate interest
|
||||
categories_of_data_subjects: []
|
||||
categories_of_personal_data: []
|
||||
special_categories: "" # Art. 9 data
|
||||
recipients: []
|
||||
international_transfers: []
|
||||
retention_period: ""
|
||||
security_measures: []
|
||||
dpia_required: false
|
||||
date_added: ""
|
||||
last_reviewed: ""
|
||||
```
|
||||
|
||||
## Technical Implementation
|
||||
## Consent Management Implementation
|
||||
|
||||
```python
|
||||
# Data export for portability
|
||||
def export_user_data(user_id):
|
||||
return {
|
||||
"profile": get_profile(user_id),
|
||||
"activity": get_activity_log(user_id),
|
||||
"preferences": get_preferences(user_id)
|
||||
}
|
||||
"""
|
||||
Consent management system implementing GDPR Article 7 requirements.
|
||||
Consent must be freely given, specific, informed, and unambiguous.
|
||||
"""
|
||||
from datetime import datetime, timezone
|
||||
from enum import Enum
|
||||
import json
|
||||
import hashlib
|
||||
|
||||
# Right to erasure
|
||||
def delete_user_data(user_id):
|
||||
anonymize_profile(user_id)
|
||||
delete_activity_log(user_id)
|
||||
log_deletion(user_id)
|
||||
|
||||
class ConsentPurpose(Enum):
|
||||
MARKETING_EMAIL = "marketing_email"
|
||||
MARKETING_SMS = "marketing_sms"
|
||||
ANALYTICS = "analytics"
|
||||
PERSONALIZATION = "personalization"
|
||||
THIRD_PARTY_SHARING = "third_party_sharing"
|
||||
PROFILING = "profiling"
|
||||
|
||||
|
||||
class ConsentManager:
|
||||
def __init__(self, db):
|
||||
self.db = db
|
||||
|
||||
def record_consent(self, user_id, purpose, granted, source,
|
||||
privacy_policy_version, ip_address=None):
|
||||
"""Record a consent decision with full audit trail."""
|
||||
consent_record = {
|
||||
"user_id": user_id,
|
||||
"purpose": purpose.value,
|
||||
"granted": granted,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
"source": source, # e.g., "web_signup", "preference_center", "cookie_banner"
|
||||
"privacy_policy_version": privacy_policy_version,
|
||||
"ip_address": ip_address,
|
||||
"withdrawal_timestamp": None,
|
||||
}
|
||||
# Store with immutable audit trail
|
||||
consent_record["record_hash"] = hashlib.sha256(
|
||||
json.dumps(consent_record, sort_keys=True).encode()
|
||||
).hexdigest()
|
||||
self.db.consent_records.insert(consent_record)
|
||||
return consent_record
|
||||
|
||||
def withdraw_consent(self, user_id, purpose):
|
||||
"""Process consent withdrawal - must be as easy as giving consent."""
|
||||
record = self.record_consent(
|
||||
user_id=user_id,
|
||||
purpose=purpose,
|
||||
granted=False,
|
||||
source="withdrawal",
|
||||
privacy_policy_version="N/A",
|
||||
)
|
||||
# Trigger downstream actions
|
||||
self._notify_processors(user_id, purpose, "withdrawn")
|
||||
self._stop_processing(user_id, purpose)
|
||||
return record
|
||||
|
||||
def get_consent_status(self, user_id, purpose):
|
||||
"""Get current consent status for a specific purpose."""
|
||||
latest = self.db.consent_records.find_one(
|
||||
{"user_id": user_id, "purpose": purpose.value},
|
||||
sort=[("timestamp", -1)]
|
||||
)
|
||||
return latest["granted"] if latest else False
|
||||
|
||||
def get_all_consents(self, user_id):
|
||||
"""Get all consent records for a user (for DSAR response)."""
|
||||
return list(self.db.consent_records.find(
|
||||
{"user_id": user_id},
|
||||
sort=[("timestamp", -1)]
|
||||
))
|
||||
|
||||
def export_consent_proof(self, user_id, purpose):
|
||||
"""Export verifiable consent proof for accountability."""
|
||||
records = list(self.db.consent_records.find(
|
||||
{"user_id": user_id, "purpose": purpose.value},
|
||||
sort=[("timestamp", 1)]
|
||||
))
|
||||
return {
|
||||
"user_id": user_id,
|
||||
"purpose": purpose.value,
|
||||
"consent_history": records,
|
||||
"current_status": self.get_consent_status(user_id, purpose),
|
||||
"exported_at": datetime.now(timezone.utc).isoformat(),
|
||||
}
|
||||
|
||||
def _notify_processors(self, user_id, purpose, action):
|
||||
"""Notify downstream processors of consent change."""
|
||||
pass # Implement webhook/API calls to processors
|
||||
|
||||
def _stop_processing(self, user_id, purpose):
|
||||
"""Immediately stop processing for withdrawn consent."""
|
||||
pass # Implement processing halt logic
|
||||
```
|
||||
|
||||
## Data Subject Access Request (DSAR) Procedures
|
||||
|
||||
```yaml
|
||||
dsar_workflow:
|
||||
step_1_receive:
|
||||
actions:
|
||||
- Log the request with timestamp and channel received
|
||||
- Assign unique tracking ID
|
||||
- Acknowledge receipt within 3 business days
|
||||
identity_verification:
|
||||
- Verify identity before providing any data
|
||||
- Use existing authentication where possible
|
||||
- Request additional proof if necessary (but not excessive)
|
||||
sla: "Must respond within 30 days (extendable to 90 days for complex requests)"
|
||||
|
||||
step_2_assess:
|
||||
actions:
|
||||
- Determine request type (access, rectification, erasure, portability, etc.)
|
||||
- Identify all systems containing the individual's data
|
||||
- Check for lawful grounds to refuse (legal obligations, etc.)
|
||||
- Assess if extension is needed (complex or numerous requests)
|
||||
|
||||
step_3_collect:
|
||||
systems_to_search:
|
||||
- Primary application database
|
||||
- CRM system
|
||||
- Email marketing platform
|
||||
- Analytics systems
|
||||
- Customer support tickets
|
||||
- Backup systems (if practically retrievable)
|
||||
- Log files containing PII
|
||||
- Third-party processors (request from each)
|
||||
|
||||
step_4_respond:
|
||||
access_request:
|
||||
- Provide copy of all personal data in commonly used electronic format
|
||||
- Include processing purposes, categories, recipients, retention periods
|
||||
- Include source of data if not collected from the individual
|
||||
- Include information about automated decision-making
|
||||
rectification_request:
|
||||
- Update data in all systems
|
||||
- Notify all recipients of the correction
|
||||
erasure_request:
|
||||
- Delete data from all active systems
|
||||
- Remove from backups where technically feasible
|
||||
- Notify all processors and recipients
|
||||
- Document what was deleted and any retained data with legal basis
|
||||
portability_request:
|
||||
- Provide data in structured, machine-readable format (JSON/CSV)
|
||||
- Include only data provided by the data subject
|
||||
- Transfer directly to another controller if requested and feasible
|
||||
|
||||
step_5_close:
|
||||
actions:
|
||||
- Send response to data subject
|
||||
- Document the entire handling process
|
||||
- Archive DSAR record for accountability
|
||||
- Update data mapping if new data stores discovered
|
||||
```
|
||||
|
||||
```python
|
||||
"""DSAR automation - data collection across systems."""
|
||||
import json
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
class DSARProcessor:
|
||||
def __init__(self, data_sources):
|
||||
self.data_sources = data_sources # Dict of system_name: DataSource
|
||||
|
||||
def process_access_request(self, user_identifier):
|
||||
"""Collect all personal data across registered systems."""
|
||||
collected_data = {
|
||||
"request_id": f"DSAR-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}",
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"data_subject": user_identifier,
|
||||
"systems": {},
|
||||
}
|
||||
|
||||
for system_name, source in self.data_sources.items():
|
||||
try:
|
||||
data = source.extract_user_data(user_identifier)
|
||||
collected_data["systems"][system_name] = {
|
||||
"status": "collected",
|
||||
"record_count": len(data) if isinstance(data, list) else 1,
|
||||
"data": data,
|
||||
}
|
||||
except Exception as e:
|
||||
collected_data["systems"][system_name] = {
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
}
|
||||
|
||||
return collected_data
|
||||
|
||||
def process_erasure_request(self, user_identifier):
|
||||
"""Delete personal data across all systems (right to erasure)."""
|
||||
results = {
|
||||
"request_id": f"ERASE-{datetime.now(timezone.utc).strftime('%Y%m%d%H%M%S')}",
|
||||
"data_subject": user_identifier,
|
||||
"systems": {},
|
||||
}
|
||||
|
||||
for system_name, source in self.data_sources.items():
|
||||
try:
|
||||
deleted = source.delete_user_data(user_identifier)
|
||||
retained = source.get_retained_data(user_identifier)
|
||||
results["systems"][system_name] = {
|
||||
"status": "deleted",
|
||||
"records_deleted": deleted,
|
||||
"retained_data": retained, # Data kept for legal obligations
|
||||
"retention_basis": source.retention_legal_basis,
|
||||
}
|
||||
except Exception as e:
|
||||
results["systems"][system_name] = {
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
}
|
||||
|
||||
return results
|
||||
|
||||
def export_portable_data(self, user_identifier, format="json"):
|
||||
"""Export data in machine-readable format for portability."""
|
||||
data = self.process_access_request(user_identifier)
|
||||
if format == "json":
|
||||
return json.dumps(data, indent=2, default=str)
|
||||
elif format == "csv":
|
||||
return self._convert_to_csv(data)
|
||||
raise ValueError(f"Unsupported format: {format}")
|
||||
```
|
||||
|
||||
## Data Processing Agreement (DPA) Requirements
|
||||
|
||||
```yaml
|
||||
dpa_requirements:
|
||||
mandatory_clauses:
|
||||
article_28:
|
||||
- Subject matter, duration, nature, and purpose of processing
|
||||
- Type of personal data and categories of data subjects
|
||||
- Obligations and rights of the controller
|
||||
- Processing only on documented instructions from controller
|
||||
- Confidentiality obligations on processor personnel
|
||||
- Appropriate technical and organizational security measures
|
||||
- Conditions for engaging sub-processors (prior authorization)
|
||||
- Assistance with data subject rights requests
|
||||
- Assistance with security obligations (Art. 32-36)
|
||||
- Deletion or return of data after service ends
|
||||
- Audit and inspection rights for the controller
|
||||
|
||||
sub_processor_management:
|
||||
- [ ] List of current sub-processors provided by processor
|
||||
- [ ] Notification mechanism for new sub-processors (30-day notice)
|
||||
- [ ] Right to object to new sub-processors
|
||||
- [ ] Sub-processors bound by same data protection obligations
|
||||
- [ ] Processor remains liable for sub-processor compliance
|
||||
|
||||
international_transfers:
|
||||
mechanisms:
|
||||
- Standard Contractual Clauses (SCCs) - most common
|
||||
- Binding Corporate Rules (BCRs) - intra-group transfers
|
||||
- Adequacy decision (countries deemed adequate by EC)
|
||||
- Derogations for specific situations (explicit consent, contract necessity)
|
||||
transfer_impact_assessment:
|
||||
- [ ] Assess laws of the destination country
|
||||
- [ ] Evaluate effectiveness of safeguards
|
||||
- [ ] Document supplementary measures if needed
|
||||
- [ ] Review periodically for legal changes
|
||||
|
||||
dpa_registry:
|
||||
track_per_processor:
|
||||
- Processor name and contact details
|
||||
- DPA execution date
|
||||
- Data types processed
|
||||
- Sub-processors and their locations
|
||||
- SCC version used for international transfers
|
||||
- TIA completion date
|
||||
- Next review date
|
||||
```
|
||||
|
||||
## Data Protection Impact Assessment (DPIA) Template
|
||||
|
||||
```yaml
|
||||
dpia_template:
|
||||
when_required:
|
||||
- Systematic and extensive profiling with significant effects
|
||||
- Large-scale processing of special category data
|
||||
- Systematic monitoring of publicly accessible areas
|
||||
- Any processing on national supervisory authority's list
|
||||
- New technologies with likely high risk to rights and freedoms
|
||||
|
||||
assessment:
|
||||
section_1_description:
|
||||
processing_activity: ""
|
||||
purpose: ""
|
||||
legal_basis: ""
|
||||
data_categories: []
|
||||
data_subjects: []
|
||||
recipients: []
|
||||
retention: ""
|
||||
data_flows: "Describe how data moves through systems"
|
||||
|
||||
section_2_necessity:
|
||||
is_processing_necessary: ""
|
||||
is_processing_proportionate: ""
|
||||
alternatives_considered: ""
|
||||
data_minimization_applied: ""
|
||||
|
||||
section_3_risks:
|
||||
risk_assessment:
|
||||
- risk: "Unauthorized access to personal data"
|
||||
likelihood: "medium"
|
||||
severity: "high"
|
||||
risk_level: "high"
|
||||
existing_controls: "Encryption, access controls, audit logs"
|
||||
residual_risk: "medium"
|
||||
|
||||
- risk: "Accidental data loss or destruction"
|
||||
likelihood: "low"
|
||||
severity: "high"
|
||||
risk_level: "medium"
|
||||
existing_controls: "Backups, replication, DR procedures"
|
||||
residual_risk: "low"
|
||||
|
||||
- risk: "Excessive data collection beyond purpose"
|
||||
likelihood: "medium"
|
||||
severity: "medium"
|
||||
risk_level: "medium"
|
||||
existing_controls: "Data minimization review, schema validation"
|
||||
residual_risk: "low"
|
||||
|
||||
section_4_measures:
|
||||
technical_measures:
|
||||
- Pseudonymization of personal data
|
||||
- Encryption at rest (AES-256) and in transit (TLS 1.3)
|
||||
- Access controls with least privilege
|
||||
- Automated data retention enforcement
|
||||
organizational_measures:
|
||||
- Staff training on data protection
|
||||
- Data protection policies and procedures
|
||||
- Incident response procedures
|
||||
- Regular access reviews
|
||||
monitoring:
|
||||
- Audit logging of all data access
|
||||
- Anomaly detection for unusual access patterns
|
||||
- Regular compliance testing
|
||||
|
||||
section_5_sign_off:
|
||||
dpo_consultation: "Required if high residual risk"
|
||||
dpo_opinion: ""
|
||||
supervisory_authority_consultation: "Required if risk cannot be mitigated"
|
||||
approval_date: ""
|
||||
next_review_date: ""
|
||||
```
|
||||
|
||||
## GDPR Compliance Checklist
|
||||
|
||||
```yaml
|
||||
gdpr_compliance_checklist:
|
||||
governance:
|
||||
- [ ] Data Protection Officer appointed (if required under Art. 37)
|
||||
- [ ] Records of Processing Activities (ROPA) maintained
|
||||
- [ ] Privacy policies published and up to date
|
||||
- [ ] Data protection training conducted for all staff
|
||||
- [ ] Data breach response plan documented and tested
|
||||
|
||||
lawful_processing:
|
||||
- [ ] Legal basis identified and documented for each processing activity
|
||||
- [ ] Consent mechanisms comply with Art. 7 (freely given, specific, informed)
|
||||
- [ ] Consent withdrawal is as easy as giving consent
|
||||
- [ ] Legitimate interest assessments completed where applicable
|
||||
- [ ] Special category data has Art. 9 legal basis documented
|
||||
|
||||
data_subject_rights:
|
||||
- [ ] DSAR intake process established (multiple channels)
|
||||
- [ ] Identity verification procedure defined
|
||||
- [ ] Response within 30 days (or extension communicated)
|
||||
- [ ] Right to access implemented and tested
|
||||
- [ ] Right to rectification implemented
|
||||
- [ ] Right to erasure implemented with legal retention exceptions
|
||||
- [ ] Right to portability implemented (structured, machine-readable export)
|
||||
- [ ] Right to object implemented (especially for direct marketing)
|
||||
|
||||
technical_measures:
|
||||
- [ ] Encryption at rest and in transit for all personal data
|
||||
- [ ] Pseudonymization applied where feasible
|
||||
- [ ] Access controls enforce least privilege
|
||||
- [ ] Audit logging of personal data access
|
||||
- [ ] Data retention automated with defined schedules
|
||||
- [ ] Secure deletion procedures verified
|
||||
|
||||
third_parties:
|
||||
- [ ] Data Processing Agreements signed with all processors
|
||||
- [ ] Sub-processor notification mechanism in place
|
||||
- [ ] International transfer safeguards implemented (SCCs, etc.)
|
||||
- [ ] Transfer Impact Assessments completed
|
||||
- [ ] Processor compliance verified periodically
|
||||
|
||||
breach_management:
|
||||
- [ ] Breach detection and assessment procedures documented
|
||||
- [ ] 72-hour supervisory authority notification process ready
|
||||
- [ ] Individual notification procedures for high-risk breaches
|
||||
- [ ] Breach register maintained
|
||||
- [ ] Post-breach review and improvement process
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Privacy impact assessments
|
||||
- Data processing agreements
|
||||
- Consent management
|
||||
- Breach notification (72 hours)
|
||||
- Data Protection Officer (if required)
|
||||
- Maintain a comprehensive Records of Processing Activities as the foundation of GDPR compliance
|
||||
- Implement privacy by design: build data protection into systems from the start, not retrofitted
|
||||
- Apply data minimization rigorously: do not collect personal data "just in case"
|
||||
- Automate DSAR processing to meet the 30-day response deadline consistently
|
||||
- Keep consent granular and purpose-specific; avoid bundled consent for multiple purposes
|
||||
- Conduct DPIAs before launching high-risk processing activities
|
||||
- Ensure data processing agreements are signed with every processor before sharing personal data
|
||||
- Implement automated retention enforcement to prevent storage beyond defined periods
|
||||
- Train all staff who handle personal data, not just the IT and legal teams
|
||||
- Regularly audit data flows to discover shadow processing or undocumented data stores
|
||||
|
||||
@@ -9,66 +9,420 @@ metadata:
|
||||
|
||||
# HIPAA Compliance
|
||||
|
||||
Implement HIPAA requirements for healthcare data protection.
|
||||
Implement HIPAA Security Rule, Privacy Rule, and Breach Notification Rule requirements for systems that create, receive, maintain, or transmit electronic Protected Health Information (ePHI).
|
||||
|
||||
## HIPAA Rules
|
||||
## When to Use
|
||||
|
||||
- Building or operating systems that handle electronic Protected Health Information
|
||||
- Configuring cloud infrastructure for HIPAA-eligible workloads
|
||||
- Establishing Business Associate Agreements with vendors
|
||||
- Implementing technical safeguards for PHI protection
|
||||
- Preparing for HIPAA compliance audits or OCR investigations
|
||||
|
||||
## HIPAA Rules and Safeguards
|
||||
|
||||
```yaml
|
||||
security_rule:
|
||||
administrative:
|
||||
- Risk analysis
|
||||
- Security management
|
||||
- Workforce training
|
||||
- Contingency planning
|
||||
|
||||
physical:
|
||||
- Facility access
|
||||
- Workstation security
|
||||
- Device controls
|
||||
|
||||
technical:
|
||||
- Access control
|
||||
- Audit controls
|
||||
- Integrity controls
|
||||
- Transmission security
|
||||
administrative_safeguards:
|
||||
164.308_a_1: "Security Management Process"
|
||||
actions:
|
||||
- Conduct risk analysis (required)
|
||||
- Implement risk management program (required)
|
||||
- Apply sanction policy for violations (required)
|
||||
- Review information system activity (required)
|
||||
|
||||
164.308_a_3: "Workforce Security"
|
||||
actions:
|
||||
- Authorization/supervision procedures (addressable)
|
||||
- Workforce clearance procedure (addressable)
|
||||
- Termination procedures (addressable)
|
||||
|
||||
164.308_a_4: "Information Access Management"
|
||||
actions:
|
||||
- Access authorization policies (addressable)
|
||||
- Access establishment and modification (addressable)
|
||||
- Isolate healthcare clearinghouse functions (required)
|
||||
|
||||
164.308_a_5: "Security Awareness and Training"
|
||||
actions:
|
||||
- Security reminders (addressable)
|
||||
- Protection from malicious software (addressable)
|
||||
- Log-in monitoring (addressable)
|
||||
- Password management (addressable)
|
||||
|
||||
164.308_a_6: "Security Incident Procedures"
|
||||
actions:
|
||||
- Response and reporting procedures (required)
|
||||
|
||||
164.308_a_7: "Contingency Plan"
|
||||
actions:
|
||||
- Data backup plan (required)
|
||||
- Disaster recovery plan (required)
|
||||
- Emergency mode operation plan (required)
|
||||
- Testing and revision procedures (addressable)
|
||||
- Applications and data criticality analysis (addressable)
|
||||
|
||||
164.308_a_8: "Evaluation"
|
||||
actions:
|
||||
- Periodic technical and nontechnical evaluation (required)
|
||||
|
||||
physical_safeguards:
|
||||
164.310_a: "Facility Access Controls"
|
||||
164.310_b: "Workstation Use"
|
||||
164.310_c: "Workstation Security"
|
||||
164.310_d: "Device and Media Controls"
|
||||
|
||||
technical_safeguards:
|
||||
164.312_a: "Access Control"
|
||||
actions:
|
||||
- Unique user identification (required)
|
||||
- Emergency access procedure (required)
|
||||
- Automatic logoff (addressable)
|
||||
- Encryption and decryption (addressable)
|
||||
|
||||
164.312_b: "Audit Controls"
|
||||
actions:
|
||||
- Implement hardware/software/procedural mechanisms to record and examine access (required)
|
||||
|
||||
164.312_c: "Integrity"
|
||||
actions:
|
||||
- Mechanism to authenticate ePHI (addressable)
|
||||
|
||||
164.312_d: "Person or Entity Authentication"
|
||||
actions:
|
||||
- Verify identity of person/entity seeking access (required)
|
||||
|
||||
164.312_e: "Transmission Security"
|
||||
actions:
|
||||
- Integrity controls (addressable)
|
||||
- Encryption (addressable)
|
||||
|
||||
privacy_rule:
|
||||
minimum_necessary: "Limit PHI use, disclosure, and requests to minimum necessary"
|
||||
individual_rights: "Access, amendment, accounting of disclosures, restrictions"
|
||||
notice_of_practices: "Provide notice of privacy practices to individuals"
|
||||
|
||||
breach_notification_rule:
|
||||
individual_notification: "Within 60 days of discovery"
|
||||
hhs_notification: "Annual for <500 records; within 60 days for 500+"
|
||||
media_notification: "Required when 500+ individuals in a state/jurisdiction"
|
||||
```
|
||||
|
||||
## Technical Safeguards
|
||||
## Technical Safeguards Implementation Checklist
|
||||
|
||||
```yaml
|
||||
requirements:
|
||||
encryption:
|
||||
at_rest: AES-256
|
||||
in_transit: TLS 1.2+
|
||||
|
||||
access_control:
|
||||
- Unique user IDs
|
||||
- Emergency access procedure
|
||||
- Automatic logoff
|
||||
- Encryption/decryption
|
||||
|
||||
audit:
|
||||
- Access logging
|
||||
- Activity monitoring
|
||||
- Log retention (6 years)
|
||||
encryption_requirements:
|
||||
at_rest:
|
||||
standard: AES-256
|
||||
aws_services:
|
||||
- [ ] RDS encryption enabled (KMS managed key)
|
||||
- [ ] S3 bucket default encryption (SSE-KMS)
|
||||
- [ ] EBS volume encryption enabled
|
||||
- [ ] DynamoDB table encryption (KMS)
|
||||
- [ ] ElastiCache encryption at rest enabled
|
||||
- [ ] Redshift cluster encryption enabled
|
||||
- [ ] EFS encryption enabled
|
||||
azure_services:
|
||||
- [ ] Azure SQL TDE enabled (customer-managed key)
|
||||
- [ ] Storage Account encryption (CMK)
|
||||
- [ ] Managed Disk encryption (SSE with CMK)
|
||||
- [ ] Cosmos DB encryption at rest
|
||||
gcp_services:
|
||||
- [ ] Cloud SQL encryption (CMEK)
|
||||
- [ ] Cloud Storage encryption (CMEK)
|
||||
- [ ] BigQuery encryption (CMEK)
|
||||
- [ ] Persistent Disk encryption (CMEK)
|
||||
|
||||
in_transit:
|
||||
standard: TLS 1.2 or higher
|
||||
checks:
|
||||
- [ ] TLS 1.2+ enforced on all load balancers
|
||||
- [ ] HTTP-to-HTTPS redirect enabled
|
||||
- [ ] Internal service-to-service mTLS configured
|
||||
- [ ] Database connections use SSL/TLS
|
||||
- [ ] API gateways enforce TLS minimum version
|
||||
- [ ] Email encryption for PHI (S/MIME or TLS)
|
||||
- [ ] VPN or private connectivity for admin access
|
||||
|
||||
key_management:
|
||||
- [ ] Customer-managed KMS keys for PHI data stores
|
||||
- [ ] Key rotation enabled (annual minimum)
|
||||
- [ ] Key access restricted to authorized roles only
|
||||
- [ ] Key usage audited via CloudTrail / audit logs
|
||||
- [ ] Key deletion protection enabled
|
||||
|
||||
access_control:
|
||||
unique_user_identification:
|
||||
- [ ] Individual user accounts (no shared credentials)
|
||||
- [ ] MFA enforced for all users accessing PHI systems
|
||||
- [ ] Service accounts with unique identities and audited usage
|
||||
- [ ] Federated identity with SSO (SAML/OIDC)
|
||||
|
||||
role_based_access:
|
||||
- [ ] Least privilege roles defined per job function
|
||||
- [ ] PHI access restricted to need-to-know
|
||||
- [ ] Separate roles for data access vs. administration
|
||||
- [ ] Privileged access requires just-in-time approval
|
||||
|
||||
session_management:
|
||||
- [ ] Automatic session timeout (15 minutes idle for workstations)
|
||||
- [ ] Re-authentication for sensitive operations
|
||||
- [ ] Concurrent session limits
|
||||
- [ ] Session tokens secured (HttpOnly, Secure, SameSite)
|
||||
|
||||
emergency_access:
|
||||
- [ ] Break-glass procedure documented and tested
|
||||
- [ ] Emergency access credentials stored securely
|
||||
- [ ] All emergency access usage audited and reviewed
|
||||
- [ ] Emergency access automatically expires
|
||||
|
||||
audit_controls:
|
||||
logging_requirements:
|
||||
- [ ] All PHI access logged (read, write, delete)
|
||||
- [ ] User authentication events logged
|
||||
- [ ] Administrative actions logged
|
||||
- [ ] Failed access attempts logged
|
||||
- [ ] Log integrity protection (hash chaining or WORM storage)
|
||||
- [ ] Logs retained for minimum 6 years
|
||||
- [ ] Regular log review process documented
|
||||
|
||||
monitoring:
|
||||
- [ ] Real-time alerting on unauthorized PHI access attempts
|
||||
- [ ] Anomaly detection for unusual data access patterns
|
||||
- [ ] Privileged action monitoring
|
||||
- [ ] Data export/download alerting
|
||||
```
|
||||
|
||||
## AWS HIPAA Setup
|
||||
## AWS HIPAA-Eligible Architecture
|
||||
|
||||
```bash
|
||||
# Enable CloudTrail for HIPAA auditing
|
||||
aws cloudtrail create-trail \
|
||||
--name hipaa-audit-trail \
|
||||
--s3-bucket-name hipaa-logs \
|
||||
--is-multi-region-trail \
|
||||
--enable-log-file-validation
|
||||
# Verify you are using only HIPAA-eligible AWS services
|
||||
# Reference: https://aws.amazon.com/compliance/hipaa-eligible-services-reference/
|
||||
|
||||
# Use HIPAA-eligible services only
|
||||
# Create a dedicated VPC for PHI workloads
|
||||
aws ec2 create-vpc --cidr-block 10.100.0.0/16 \
|
||||
--tag-specifications 'ResourceType=vpc,Tags=[{Key=Name,Value=phi-vpc},{Key=Compliance,Value=HIPAA}]'
|
||||
|
||||
# Enable VPC flow logs for network auditing
|
||||
aws ec2 create-flow-log \
|
||||
--resource-type VPC \
|
||||
--resource-ids vpc-XXXXXXXX \
|
||||
--traffic-type ALL \
|
||||
--log-destination-type cloud-watch-logs \
|
||||
--log-group-name /vpc/phi-flow-logs \
|
||||
--deliver-logs-permission-arn arn:aws:iam::123456789012:role/VPCFlowLogsRole
|
||||
|
||||
# Create encrypted RDS instance for PHI
|
||||
aws rds create-db-instance \
|
||||
--db-instance-identifier phi-database \
|
||||
--db-instance-class db.r6g.large \
|
||||
--engine postgres \
|
||||
--master-username admin \
|
||||
--master-user-password "USE_SECRETS_MANAGER" \
|
||||
--storage-encrypted \
|
||||
--kms-key-id arn:aws:kms:us-east-1:123456789012:alias/phi-rds-key \
|
||||
--vpc-security-group-ids sg-XXXXXXXX \
|
||||
--db-subnet-group-name phi-subnet-group \
|
||||
--backup-retention-period 35 \
|
||||
--multi-az \
|
||||
--deletion-protection \
|
||||
--enable-cloudwatch-logs-exports '["postgresql","upgrade"]' \
|
||||
--tags Key=Compliance,Value=HIPAA Key=DataClassification,Value=PHI
|
||||
|
||||
# Create S3 bucket with HIPAA controls
|
||||
aws s3api create-bucket --bucket phi-data-bucket --region us-east-1
|
||||
|
||||
aws s3api put-bucket-encryption --bucket phi-data-bucket \
|
||||
--server-side-encryption-configuration '{
|
||||
"Rules": [{"ApplyServerSideEncryptionByDefault": {"SSEAlgorithm": "aws:kms", "KMSMasterKeyID": "alias/phi-s3-key"}, "BucketKeyEnabled": true}]
|
||||
}'
|
||||
|
||||
aws s3api put-public-access-block --bucket phi-data-bucket \
|
||||
--public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
|
||||
|
||||
aws s3api put-bucket-versioning --bucket phi-data-bucket \
|
||||
--versioning-configuration Status=Enabled
|
||||
|
||||
aws s3api put-bucket-logging --bucket phi-data-bucket \
|
||||
--bucket-logging-status '{"LoggingEnabled": {"TargetBucket": "phi-access-logs", "TargetPrefix": "phi-data-bucket/"}}'
|
||||
|
||||
# Enable CloudTrail data events for PHI buckets
|
||||
aws cloudtrail put-event-selectors --trail-name hipaa-audit-trail \
|
||||
--advanced-event-selectors '[{
|
||||
"Name": "PHI-S3-DataEvents",
|
||||
"FieldSelectors": [
|
||||
{"Field": "eventCategory", "Equals": ["Data"]},
|
||||
{"Field": "resources.type", "Equals": ["AWS::S3::Object"]},
|
||||
{"Field": "resources.ARN", "StartsWith": ["arn:aws:s3:::phi-data-bucket/"]}
|
||||
]
|
||||
}]'
|
||||
```
|
||||
|
||||
## Business Associate Agreement Tracking
|
||||
|
||||
```yaml
|
||||
baa_tracking:
|
||||
required_when:
|
||||
- Vendor creates, receives, maintains, or transmits PHI on your behalf
|
||||
- Subcontractor of a business associate handles PHI
|
||||
- Cloud service provider stores or processes PHI
|
||||
|
||||
not_required_for:
|
||||
- Conduit exception (postal service, ISP carrying encrypted data)
|
||||
- Treatment providers sharing PHI for treatment purposes
|
||||
- Plan sponsor receiving summary health information
|
||||
|
||||
baa_registry:
|
||||
format:
|
||||
vendor_name: ""
|
||||
baa_execution_date: ""
|
||||
baa_expiration_date: ""
|
||||
phi_types_shared: []
|
||||
services_provided: ""
|
||||
subcontractors_identified: []
|
||||
breach_notification_sla: "hours"
|
||||
last_risk_assessment: ""
|
||||
next_review_date: ""
|
||||
status: "active | pending | expired"
|
||||
|
||||
cloud_provider_baas:
|
||||
aws:
|
||||
- Sign AWS BAA via AWS Artifact in the console
|
||||
- Applies to all HIPAA-eligible services in the account
|
||||
- Must restrict PHI to eligible services only
|
||||
azure:
|
||||
- Microsoft BAA is part of Online Services Terms
|
||||
- Automatically applies when using qualifying services
|
||||
gcp:
|
||||
- Sign Google Cloud BAA via Google Workspace Admin or Cloud console
|
||||
- Covers HIPAA-eligible GCP services
|
||||
|
||||
review_schedule:
|
||||
- [ ] Annual review of all active BAAs
|
||||
- [ ] Verify vendor compliance certifications are current
|
||||
- [ ] Confirm subcontractor BAAs are in place
|
||||
- [ ] Update BAA registry with any vendor changes
|
||||
- [ ] Assess vendor security posture annually
|
||||
```
|
||||
|
||||
## Risk Analysis Automation
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# hipaa-risk-scan.sh - Technical risk analysis checks for HIPAA
|
||||
|
||||
echo "=== HIPAA Technical Safeguard Checks ==="
|
||||
|
||||
echo "--- Encryption at Rest ---"
|
||||
# Check for unencrypted RDS instances
|
||||
UNENCRYPTED_RDS=$(aws rds describe-db-instances \
|
||||
--query 'DBInstances[?StorageEncrypted==`false`].DBInstanceIdentifier' --output text)
|
||||
[ -z "$UNENCRYPTED_RDS" ] && echo "PASS: All RDS instances encrypted" || \
|
||||
echo "FAIL: Unencrypted RDS: $UNENCRYPTED_RDS"
|
||||
|
||||
# Check for unencrypted S3 buckets
|
||||
for bucket in $(aws s3api list-buckets --query 'Buckets[*].Name' --output text); do
|
||||
enc=$(aws s3api get-bucket-encryption --bucket "$bucket" 2>/dev/null)
|
||||
[ -z "$enc" ] && echo "FAIL: S3 bucket $bucket has no default encryption"
|
||||
done
|
||||
|
||||
# Check for unencrypted EBS volumes
|
||||
UNENCRYPTED_EBS=$(aws ec2 describe-volumes \
|
||||
--query 'Volumes[?Encrypted==`false`].VolumeId' --output text)
|
||||
[ -z "$UNENCRYPTED_EBS" ] && echo "PASS: All EBS volumes encrypted" || \
|
||||
echo "FAIL: Unencrypted EBS: $UNENCRYPTED_EBS"
|
||||
|
||||
echo "--- Access Control ---"
|
||||
# Check for users without MFA
|
||||
aws iam generate-credential-report > /dev/null 2>&1 && sleep 5
|
||||
aws iam get-credential-report --output text --query Content | base64 -d | \
|
||||
awk -F, '$4=="true" && $8=="false" {print "FAIL: User without MFA: "$1}'
|
||||
|
||||
# Check for unused access keys (90+ days)
|
||||
THRESHOLD=$(date -d '90 days ago' +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -v-90d +%Y-%m-%dT%H:%M:%S)
|
||||
aws iam get-credential-report --output text --query Content | base64 -d | \
|
||||
awk -F, -v t="$THRESHOLD" 'NR>1 && $11!="N/A" && $11<t {print "WARN: Stale access key for "$1}'
|
||||
|
||||
echo "--- Audit Controls ---"
|
||||
# Verify CloudTrail is logging
|
||||
CT_STATUS=$(aws cloudtrail get-trail-status --name hipaa-audit-trail --query 'IsLogging' --output text)
|
||||
[ "$CT_STATUS" = "True" ] && echo "PASS: CloudTrail active" || echo "FAIL: CloudTrail not logging"
|
||||
|
||||
# Verify VPC flow logs
|
||||
for vpc in $(aws ec2 describe-vpcs --query 'Vpcs[*].VpcId' --output text); do
|
||||
fl=$(aws ec2 describe-flow-logs --filter "Name=resource-id,Values=$vpc" --query 'FlowLogs[0].FlowLogId' --output text)
|
||||
[ "$fl" = "None" ] && echo "FAIL: No flow logs for VPC $vpc"
|
||||
done
|
||||
|
||||
echo "--- Transmission Security ---"
|
||||
# Check for ALBs without HTTPS listener
|
||||
for alb in $(aws elbv2 describe-load-balancers --query 'LoadBalancers[*].LoadBalancerArn' --output text); do
|
||||
HTTPS=$(aws elbv2 describe-listeners --load-balancer-arn "$alb" \
|
||||
--query 'Listeners[?Protocol==`HTTPS`].ListenerArn' --output text)
|
||||
[ -z "$HTTPS" ] && echo "FAIL: ALB without HTTPS: $alb"
|
||||
done
|
||||
|
||||
echo "=== Scan complete ==="
|
||||
```
|
||||
|
||||
## HIPAA Compliance Checklist
|
||||
|
||||
```yaml
|
||||
hipaa_compliance_checklist:
|
||||
administrative:
|
||||
- [ ] Risk analysis conducted and documented
|
||||
- [ ] Risk management plan implemented
|
||||
- [ ] Security officer designated
|
||||
- [ ] Privacy officer designated
|
||||
- [ ] Workforce security awareness training completed
|
||||
- [ ] Sanction policy documented and communicated
|
||||
- [ ] Contingency plan (backup, DR, emergency mode) documented
|
||||
- [ ] Business associate agreements signed for all applicable vendors
|
||||
- [ ] Periodic evaluation/audit scheduled
|
||||
|
||||
technical:
|
||||
- [ ] Unique user identification enforced
|
||||
- [ ] MFA enabled for all PHI system access
|
||||
- [ ] Automatic logoff configured (15-minute timeout)
|
||||
- [ ] Encryption at rest (AES-256) for all PHI stores
|
||||
- [ ] Encryption in transit (TLS 1.2+) for all PHI transmission
|
||||
- [ ] Audit logging enabled for all PHI access
|
||||
- [ ] Log retention configured for 6+ years
|
||||
- [ ] Integrity controls on PHI (checksums, signatures)
|
||||
- [ ] Emergency access (break-glass) procedure tested
|
||||
|
||||
physical:
|
||||
- [ ] Facility access controls documented
|
||||
- [ ] Workstation use policy in place
|
||||
- [ ] Device and media disposal procedures documented
|
||||
- [ ] Media re-use procedures documented
|
||||
|
||||
breach_response:
|
||||
- [ ] Breach notification procedure documented
|
||||
- [ ] Breach risk assessment methodology defined
|
||||
- [ ] Individual notification template prepared
|
||||
- [ ] HHS notification process understood
|
||||
- [ ] Breach log maintained
|
||||
- [ ] Annual breach assessment reviewed
|
||||
|
||||
operational:
|
||||
- [ ] PHI data inventory maintained
|
||||
- [ ] Minimum necessary access enforced
|
||||
- [ ] Access reviews conducted quarterly
|
||||
- [ ] Vendor risk assessments current
|
||||
- [ ] Incident response plan tested annually
|
||||
- [ ] Policies reviewed and updated annually
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Business Associate Agreements (BAAs)
|
||||
- Minimum necessary access
|
||||
- Breach notification procedures
|
||||
- Regular risk assessments
|
||||
- Conduct a thorough risk analysis annually and after significant system changes
|
||||
- Use only HIPAA-eligible cloud services and sign BAAs before deploying PHI workloads
|
||||
- Encrypt all PHI at rest and in transit with no exceptions
|
||||
- Implement the minimum necessary standard: grant access only to the PHI needed for each role
|
||||
- Maintain audit logs of all PHI access for a minimum of 6 years
|
||||
- Train all workforce members on HIPAA policies at onboarding and annually
|
||||
- Test contingency plans (backup restore, DR failover, emergency access) at least annually
|
||||
- Track all Business Associate Agreements in a central registry with review dates
|
||||
- Document every addressable specification decision (implement, alternative, or not applicable with rationale)
|
||||
- Prepare breach notification templates and procedures before an incident occurs
|
||||
|
||||
@@ -9,77 +9,429 @@ metadata:
|
||||
|
||||
# ISO 27001 Compliance
|
||||
|
||||
Implement ISO 27001 Information Security Management System.
|
||||
Implement an Information Security Management System (ISMS) aligned with ISO/IEC 27001:2022.
|
||||
|
||||
## ISMS Framework
|
||||
## When to Use
|
||||
|
||||
- Establishing an ISMS for the first time in an organization
|
||||
- Preparing for ISO 27001 certification audit
|
||||
- Conducting risk assessments and developing risk treatment plans
|
||||
- Creating the Statement of Applicability (SoA)
|
||||
- Transitioning from ISO 27001:2013 to the 2022 revision
|
||||
- Meeting customer or regulatory requirements for ISO 27001 certification
|
||||
|
||||
## ISMS Plan-Do-Check-Act Cycle
|
||||
|
||||
```yaml
|
||||
plan_do_check_act:
|
||||
pdca_cycle:
|
||||
plan:
|
||||
- Define scope
|
||||
- Risk assessment
|
||||
- Risk treatment plan
|
||||
- Statement of Applicability
|
||||
|
||||
- Define ISMS scope and boundaries
|
||||
- Establish information security policy
|
||||
- Conduct risk assessment
|
||||
- Develop risk treatment plan
|
||||
- Produce Statement of Applicability
|
||||
- Obtain management approval and commitment
|
||||
- Define security objectives and metrics
|
||||
|
||||
do:
|
||||
- Implement controls
|
||||
- Security awareness
|
||||
- Document procedures
|
||||
|
||||
- Implement selected Annex A controls
|
||||
- Deploy technical security controls
|
||||
- Conduct security awareness training
|
||||
- Document all procedures and processes
|
||||
- Implement incident management process
|
||||
- Establish supplier security management
|
||||
|
||||
check:
|
||||
- Internal audits
|
||||
- Management review
|
||||
- Performance measurement
|
||||
|
||||
- Conduct internal audits (at least annual)
|
||||
- Perform management review meetings
|
||||
- Monitor and measure control effectiveness
|
||||
- Review incident trends and near misses
|
||||
- Assess compliance with legal requirements
|
||||
- Evaluate security metrics against objectives
|
||||
|
||||
act:
|
||||
- Corrective actions
|
||||
- Continual improvement
|
||||
- Address nonconformities with corrective actions
|
||||
- Implement continual improvement initiatives
|
||||
- Update risk assessment based on changes
|
||||
- Refine controls based on audit findings
|
||||
- Communicate improvements to stakeholders
|
||||
```
|
||||
|
||||
## Annex A Controls
|
||||
## ISMS Scope Definition
|
||||
|
||||
```yaml
|
||||
control_domains:
|
||||
A.5: Information security policies
|
||||
A.6: Organization of information security
|
||||
A.7: Human resource security
|
||||
A.8: Asset management
|
||||
A.9: Access control
|
||||
A.10: Cryptography
|
||||
A.11: Physical security
|
||||
A.12: Operations security
|
||||
A.13: Communications security
|
||||
A.14: System acquisition/development
|
||||
A.15: Supplier relationships
|
||||
A.16: Incident management
|
||||
A.17: Business continuity
|
||||
A.18: Compliance
|
||||
isms_scope:
|
||||
template:
|
||||
organization: "Company Name, Ltd."
|
||||
scope_statement: |
|
||||
The ISMS covers the design, development, operation, and support of
|
||||
the Company's cloud-based SaaS platform, including all supporting
|
||||
infrastructure, personnel, and processes at the following locations.
|
||||
|
||||
included:
|
||||
locations:
|
||||
- "Primary office: 123 Main Street, City, Country"
|
||||
- "AWS us-east-1 and eu-west-1 regions"
|
||||
- "Remote workers accessing corporate systems"
|
||||
business_processes:
|
||||
- "Software development and deployment"
|
||||
- "Cloud infrastructure management"
|
||||
- "Customer data processing and storage"
|
||||
- "Customer support operations"
|
||||
- "Corporate IT and internal systems"
|
||||
information_assets:
|
||||
- "Customer data (PII, business data)"
|
||||
- "Source code and intellectual property"
|
||||
- "Employee personal data"
|
||||
- "Financial records"
|
||||
- "Security configurations and credentials"
|
||||
technology:
|
||||
- "AWS cloud infrastructure"
|
||||
- "SaaS application stack"
|
||||
- "Corporate IT systems (Google Workspace, Okta, Jira)"
|
||||
- "Development tools (GitHub, CI/CD pipelines)"
|
||||
|
||||
excluded:
|
||||
- "Physical data center operations (inherited from AWS)"
|
||||
- "Third-party SaaS platforms beyond integration points"
|
||||
exclusion_justification: "Physical data center controls are inherited from AWS, which maintains its own ISO 27001 certification."
|
||||
|
||||
interfaces:
|
||||
- "Customer API endpoints"
|
||||
- "Third-party integrations (payment processor, email provider)"
|
||||
- "AWS management plane"
|
||||
```
|
||||
|
||||
## Risk Assessment
|
||||
## Risk Assessment Process
|
||||
|
||||
```yaml
|
||||
risk_assessment:
|
||||
identify:
|
||||
- Asset inventory
|
||||
- Threat identification
|
||||
- Vulnerability assessment
|
||||
|
||||
analyze:
|
||||
- Likelihood rating
|
||||
- Impact rating
|
||||
- Risk calculation
|
||||
|
||||
evaluate:
|
||||
- Risk acceptance criteria
|
||||
- Prioritization
|
||||
- Treatment options
|
||||
methodology:
|
||||
approach: "Asset-based risk assessment"
|
||||
risk_formula: "Risk = Likelihood x Impact"
|
||||
scale: "1-5 for both likelihood and impact (total 1-25)"
|
||||
|
||||
likelihood_scale:
|
||||
1: "Rare - less than once per 5 years"
|
||||
2: "Unlikely - once per 2-5 years"
|
||||
3: "Possible - once per 1-2 years"
|
||||
4: "Likely - multiple times per year"
|
||||
5: "Almost Certain - monthly or more frequent"
|
||||
|
||||
impact_scale:
|
||||
1: "Negligible - minimal operational impact, no data loss"
|
||||
2: "Minor - limited impact, small data exposure, <$10K cost"
|
||||
3: "Moderate - significant impact, data breach <1K records, <$100K cost"
|
||||
4: "Major - severe impact, large data breach, <$1M cost, regulatory action"
|
||||
5: "Critical - catastrophic, massive breach, >$1M cost, business viability at risk"
|
||||
|
||||
risk_matrix:
|
||||
# Impact: 1 2 3 4 5
|
||||
likelihood_5: [5, 10, 15, 20, 25]
|
||||
likelihood_4: [4, 8, 12, 16, 20]
|
||||
likelihood_3: [3, 6, 9, 12, 15]
|
||||
likelihood_2: [2, 4, 6, 8, 10]
|
||||
likelihood_1: [1, 2, 3, 4, 5]
|
||||
|
||||
risk_appetite:
|
||||
accept: "Score 1-4 (low risk, accept with monitoring)"
|
||||
mitigate: "Score 5-14 (medium risk, implement controls to reduce)"
|
||||
escalate: "Score 15-25 (high/critical risk, immediate action required)"
|
||||
|
||||
treatment_options:
|
||||
mitigate: "Implement controls to reduce likelihood or impact"
|
||||
transfer: "Insurance or contractual transfer to third party"
|
||||
avoid: "Eliminate the risk by removing the activity or asset"
|
||||
accept: "Accept with documented management approval"
|
||||
|
||||
example_risk_register:
|
||||
- id: "RISK-001"
|
||||
asset: "Customer database"
|
||||
threat: "SQL injection attack"
|
||||
vulnerability: "Insufficient input validation"
|
||||
likelihood: 3
|
||||
impact: 4
|
||||
inherent_risk: 12
|
||||
treatment: "mitigate"
|
||||
controls: ["A.8.28 Secure coding", "A.8.8 Vulnerability management"]
|
||||
residual_likelihood: 1
|
||||
residual_impact: 4
|
||||
residual_risk: 4
|
||||
risk_owner: "CTO"
|
||||
|
||||
- id: "RISK-002"
|
||||
asset: "Source code repository"
|
||||
threat: "Insider theft of intellectual property"
|
||||
vulnerability: "Excessive access permissions"
|
||||
likelihood: 2
|
||||
impact: 5
|
||||
inherent_risk: 10
|
||||
treatment: "mitigate"
|
||||
controls: ["A.5.15 Access control", "A.8.3 Information access restriction"]
|
||||
residual_likelihood: 1
|
||||
residual_impact: 5
|
||||
residual_risk: 5
|
||||
risk_owner: "VP Engineering"
|
||||
|
||||
- id: "RISK-003"
|
||||
asset: "Cloud infrastructure"
|
||||
threat: "Cloud provider outage"
|
||||
vulnerability: "Single-region deployment"
|
||||
likelihood: 3
|
||||
impact: 3
|
||||
inherent_risk: 9
|
||||
treatment: "mitigate"
|
||||
controls: ["A.5.30 ICT readiness for business continuity", "A.8.14 Redundancy"]
|
||||
residual_likelihood: 3
|
||||
residual_impact: 2
|
||||
residual_risk: 6
|
||||
risk_owner: "Head of Infrastructure"
|
||||
```
|
||||
|
||||
## Statement of Applicability (SoA)
|
||||
|
||||
```yaml
|
||||
# ISO 27001:2022 Annex A Controls - Statement of Applicability
|
||||
soa_template:
|
||||
organizational_controls_5:
|
||||
"A.5.1":
|
||||
control: "Policies for information security"
|
||||
applicable: true
|
||||
justification: "Required to establish security governance"
|
||||
implementation: "Information security policy approved by CEO, reviewed annually"
|
||||
|
||||
"A.5.2":
|
||||
control: "Information security roles and responsibilities"
|
||||
applicable: true
|
||||
justification: "Required for accountability"
|
||||
implementation: "RACI matrix for security responsibilities, CISO appointed"
|
||||
|
||||
"A.5.7":
|
||||
control: "Threat intelligence"
|
||||
applicable: true
|
||||
justification: "Required for proactive threat management"
|
||||
implementation: "Subscribe to threat feeds, CVE monitoring, vendor advisories"
|
||||
|
||||
"A.5.15":
|
||||
control: "Access control"
|
||||
applicable: true
|
||||
justification: "Required for data protection"
|
||||
implementation: "RBAC via Okta, least-privilege IAM policies, quarterly access reviews"
|
||||
|
||||
"A.5.23":
|
||||
control: "Information security for use of cloud services"
|
||||
applicable: true
|
||||
justification: "Primary infrastructure is cloud-based"
|
||||
implementation: "AWS security baseline, CSP shared responsibility documented"
|
||||
|
||||
"A.5.29":
|
||||
control: "Information security during disruption"
|
||||
applicable: true
|
||||
justification: "Business continuity requirement"
|
||||
implementation: "BCP/DR plans tested annually, multi-AZ deployment"
|
||||
|
||||
"A.5.30":
|
||||
control: "ICT readiness for business continuity"
|
||||
applicable: true
|
||||
justification: "Ensure technology supports continuity"
|
||||
implementation: "DR runbooks, RTO/RPO defined, failover tested quarterly"
|
||||
|
||||
people_controls_6:
|
||||
"A.6.1":
|
||||
control: "Screening"
|
||||
applicable: true
|
||||
implementation: "Background checks for all employees before hiring"
|
||||
|
||||
"A.6.3":
|
||||
control: "Information security awareness, education and training"
|
||||
applicable: true
|
||||
implementation: "Annual security training, phishing simulations quarterly"
|
||||
|
||||
"A.6.5":
|
||||
control: "Responsibilities after termination or change of employment"
|
||||
applicable: true
|
||||
implementation: "Offboarding checklist, access revoked within 24 hours"
|
||||
|
||||
physical_controls_7:
|
||||
"A.7.1":
|
||||
control: "Physical security perimeters"
|
||||
applicable: false
|
||||
exclusion_justification: "No company-operated data centers, inherited from AWS"
|
||||
|
||||
technology_controls_8:
|
||||
"A.8.1":
|
||||
control: "User endpoint devices"
|
||||
applicable: true
|
||||
implementation: "MDM enrollment, disk encryption, screen lock policy"
|
||||
|
||||
"A.8.5":
|
||||
control: "Secure authentication"
|
||||
applicable: true
|
||||
implementation: "MFA required for all systems, SSO via Okta"
|
||||
|
||||
"A.8.8":
|
||||
control: "Management of technical vulnerabilities"
|
||||
applicable: true
|
||||
implementation: "Weekly vulnerability scans, 30-day patch SLA for critical"
|
||||
|
||||
"A.8.9":
|
||||
control: "Configuration management"
|
||||
applicable: true
|
||||
implementation: "Infrastructure as code, AWS Config rules, baseline hardening"
|
||||
|
||||
"A.8.15":
|
||||
control: "Logging"
|
||||
applicable: true
|
||||
implementation: "Centralized logging via CloudWatch + SIEM, 12-month retention"
|
||||
|
||||
"A.8.16":
|
||||
control: "Monitoring activities"
|
||||
applicable: true
|
||||
implementation: "SIEM alerting, 24/7 on-call rotation, anomaly detection"
|
||||
|
||||
"A.8.24":
|
||||
control: "Use of cryptography"
|
||||
applicable: true
|
||||
implementation: "TLS 1.2+, AES-256 at rest, KMS key management"
|
||||
|
||||
"A.8.25":
|
||||
control: "Secure development lifecycle"
|
||||
applicable: true
|
||||
implementation: "SAST/DAST in CI, code review required, dependency scanning"
|
||||
|
||||
"A.8.28":
|
||||
control: "Secure coding"
|
||||
applicable: true
|
||||
implementation: "OWASP guidelines, security code review, automated linting"
|
||||
```
|
||||
|
||||
## Internal Audit Program
|
||||
|
||||
```yaml
|
||||
internal_audit:
|
||||
schedule:
|
||||
frequency: "Annual full cycle, quarterly focused audits"
|
||||
cycle: "All ISMS clauses and applicable Annex A controls audited over 12 months"
|
||||
|
||||
audit_plan_template:
|
||||
audit_id: "IA-2025-Q1"
|
||||
scope: "Clauses 4-10, Annex A controls A.5.1-A.5.15"
|
||||
auditor: "Internal auditor (independent of audited area)"
|
||||
audit_dates: "2025-03-10 to 2025-03-14"
|
||||
areas:
|
||||
- area: "Access Control (A.5.15)"
|
||||
auditee: "IT Security Team"
|
||||
evidence_requested:
|
||||
- "Access review records from last quarter"
|
||||
- "Joiner/mover/leaver process records"
|
||||
- "Privileged access management logs"
|
||||
- area: "Risk Management (Clause 6.1)"
|
||||
auditee: "Risk Management Team"
|
||||
evidence_requested:
|
||||
- "Current risk register"
|
||||
- "Risk assessment methodology document"
|
||||
- "Management risk review meeting minutes"
|
||||
|
||||
finding_categories:
|
||||
major_nonconformity: "Requirement not met, significant risk to ISMS effectiveness"
|
||||
minor_nonconformity: "Requirement partially met, limited risk"
|
||||
observation: "Area for improvement, no requirement breach"
|
||||
positive_finding: "Notably effective implementation"
|
||||
|
||||
corrective_action:
|
||||
major: "Root cause analysis within 10 days, corrective action within 30 days"
|
||||
minor: "Corrective action within 60 days"
|
||||
observation: "Address in next ISMS review cycle"
|
||||
verification: "Auditor verifies corrective action effectiveness"
|
||||
```
|
||||
|
||||
## Management Review Meeting
|
||||
|
||||
```yaml
|
||||
management_review:
|
||||
frequency: "At least annually, recommended quarterly"
|
||||
attendees:
|
||||
required:
|
||||
- "CEO or Managing Director"
|
||||
- "CISO or Information Security Manager"
|
||||
- "Department heads"
|
||||
optional:
|
||||
- "Internal auditor"
|
||||
- "Risk manager"
|
||||
- "External consultant"
|
||||
|
||||
mandatory_inputs:
|
||||
- "Status of actions from previous management reviews"
|
||||
- "Changes in external and internal issues relevant to the ISMS"
|
||||
- "Information security performance (metrics and KPIs)"
|
||||
- "Audit results (internal and external)"
|
||||
- "Incident trends and nonconformities"
|
||||
- "Risk assessment results and risk treatment plan status"
|
||||
- "Interested party feedback"
|
||||
- "Opportunities for continual improvement"
|
||||
|
||||
mandatory_outputs:
|
||||
- "Decisions on continual improvement opportunities"
|
||||
- "Decisions on changes needed to the ISMS"
|
||||
- "Resource allocation decisions"
|
||||
- "Updated risk acceptance decisions"
|
||||
|
||||
kpis_to_report:
|
||||
- "Number and severity of security incidents"
|
||||
- "Vulnerability remediation SLA compliance"
|
||||
- "Security awareness training completion rate"
|
||||
- "Access review completion rate"
|
||||
- "Audit finding closure rate"
|
||||
- "Risk treatment plan progress"
|
||||
- "Patch compliance percentage"
|
||||
```
|
||||
|
||||
## ISO 27001 Certification Checklist
|
||||
|
||||
```yaml
|
||||
certification_checklist:
|
||||
stage_1_audit_preparation:
|
||||
- [ ] ISMS scope documented and approved
|
||||
- [ ] Information security policy published
|
||||
- [ ] Risk assessment methodology defined
|
||||
- [ ] Risk assessment completed with risk register
|
||||
- [ ] Risk treatment plan developed
|
||||
- [ ] Statement of Applicability completed
|
||||
- [ ] ISMS objectives defined with measurable targets
|
||||
- [ ] Internal audit program established
|
||||
- [ ] At least one full internal audit completed
|
||||
- [ ] Management review conducted with minutes documented
|
||||
- [ ] Document control process in place
|
||||
|
||||
stage_2_audit_preparation:
|
||||
- [ ] All Annex A controls implemented per SoA
|
||||
- [ ] Evidence of control operation for 3+ months
|
||||
- [ ] Corrective actions from internal audit tracked and closed
|
||||
- [ ] Security awareness training delivered and recorded
|
||||
- [ ] Incident management process operational with records
|
||||
- [ ] Supplier security assessments performed
|
||||
- [ ] Business continuity plan tested
|
||||
- [ ] All mandatory documented information available
|
||||
- [ ] Employees aware of security policy and their responsibilities
|
||||
|
||||
surveillance_audit_readiness:
|
||||
- [ ] All corrective actions from certification audit closed
|
||||
- [ ] Continuous internal audit schedule maintained
|
||||
- [ ] Management reviews conducted per schedule
|
||||
- [ ] Risk register updated with new threats and changes
|
||||
- [ ] Metrics demonstrate ISMS effectiveness
|
||||
- [ ] Changes to ISMS scope documented
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Management commitment
|
||||
- Risk-based approach
|
||||
- Document everything
|
||||
- Regular internal audits
|
||||
- Continuous improvement
|
||||
- Secure visible management commitment with a signed information security policy
|
||||
- Define ISMS scope carefully; too broad makes certification expensive, too narrow reduces value
|
||||
- Use an asset-based risk assessment approach to ensure comprehensive coverage
|
||||
- Maintain the Statement of Applicability as a living document aligned with the risk register
|
||||
- Conduct internal audits with auditors independent of the area being audited
|
||||
- Hold management review meetings quarterly rather than only annually
|
||||
- Integrate ISO 27001 controls into daily operations rather than treating them as a separate compliance exercise
|
||||
- Use metrics and KPIs to demonstrate ISMS effectiveness to auditors and management
|
||||
- Plan for the 3-year certification cycle: certification audit, then two surveillance audits
|
||||
- Start collecting evidence of control operation at least 3 months before the Stage 2 audit
|
||||
|
||||
@@ -9,69 +9,415 @@ metadata:
|
||||
|
||||
# PCI DSS Compliance
|
||||
|
||||
Implement PCI DSS requirements for payment card security.
|
||||
Implement PCI DSS v4.0 requirements for protecting cardholder data across the Cardholder Data Environment (CDE), including network segmentation, encryption, access controls, and ongoing testing.
|
||||
|
||||
## Requirements
|
||||
## When to Use
|
||||
|
||||
- Processing, storing, or transmitting payment card data
|
||||
- Scoping the Cardholder Data Environment for PCI assessment
|
||||
- Selecting the appropriate Self-Assessment Questionnaire (SAQ)
|
||||
- Implementing network segmentation to reduce CDE scope
|
||||
- Preparing for QSA assessment or ASV scanning
|
||||
|
||||
## SAQ Types and Applicability
|
||||
|
||||
```yaml
|
||||
saq_types:
|
||||
SAQ_A:
|
||||
description: "Card-not-present merchants using fully outsourced payment"
|
||||
applies_when:
|
||||
- All payment processing fully outsourced to PCI-compliant third party
|
||||
- No electronic storage, processing, or transmission of cardholder data
|
||||
- Only payment page redirects or iframes from compliant provider
|
||||
requirements: ~22 questions
|
||||
|
||||
SAQ_A_EP:
|
||||
description: "E-commerce merchants with website that affects payment security"
|
||||
applies_when:
|
||||
- E-commerce channel only
|
||||
- Website controls redirect to or loads payment page from third party
|
||||
- No direct processing but website could affect transaction security
|
||||
requirements: ~191 questions
|
||||
|
||||
SAQ_B:
|
||||
description: "Merchants with only imprint machines or standalone terminals"
|
||||
applies_when:
|
||||
- Only standalone POS terminals (dial-out or IP connected)
|
||||
- No electronic cardholder data storage
|
||||
- No e-commerce channel
|
||||
requirements: ~41 questions
|
||||
|
||||
SAQ_C:
|
||||
description: "Merchants with payment application systems connected to internet"
|
||||
applies_when:
|
||||
- Payment application connected to internet
|
||||
- No electronic cardholder data storage
|
||||
- No e-commerce channel
|
||||
requirements: ~160 questions
|
||||
|
||||
SAQ_D:
|
||||
description: "All other merchants and all service providers"
|
||||
applies_when:
|
||||
- Stores cardholder data electronically
|
||||
- Does not fit any other SAQ type
|
||||
- Service providers eligible for SAQ D
|
||||
requirements: "Full set of PCI DSS requirements"
|
||||
|
||||
scope_reduction_strategies:
|
||||
- Use tokenization to replace PAN with non-sensitive tokens
|
||||
- Use P2PE (Point-to-Point Encryption) validated solutions
|
||||
- Outsource payment processing to reduce your CDE footprint
|
||||
- Implement network segmentation to isolate CDE
|
||||
```
|
||||
|
||||
## PCI DSS v4.0 Requirements Overview
|
||||
|
||||
```yaml
|
||||
requirements:
|
||||
1_firewall:
|
||||
- Network segmentation
|
||||
- Firewall configuration
|
||||
- CDE isolation
|
||||
|
||||
3_protect_data:
|
||||
- Mask PAN display
|
||||
- Encrypt stored data
|
||||
- Key management
|
||||
|
||||
6_secure_systems:
|
||||
- Patch management
|
||||
- Secure development
|
||||
- Change control
|
||||
|
||||
8_access_control:
|
||||
- Unique IDs
|
||||
- MFA for remote access
|
||||
- Password policies
|
||||
|
||||
10_logging:
|
||||
- Audit trail
|
||||
- Time synchronization
|
||||
- Log retention (1 year)
|
||||
|
||||
11_testing:
|
||||
- Vulnerability scans
|
||||
- Penetration testing
|
||||
- IDS/IPS monitoring
|
||||
req_1_network_security:
|
||||
"1.1": "Network security controls defined and maintained"
|
||||
"1.2": "Network security controls configured and maintained"
|
||||
"1.3": "Network access to and from CDE is restricted"
|
||||
"1.4": "Network connections between trusted and untrusted networks controlled"
|
||||
"1.5": "Risks to CDE from devices connecting to untrusted networks mitigated"
|
||||
|
||||
req_2_secure_configuration:
|
||||
"2.1": "Secure configuration standards defined and applied"
|
||||
"2.2": "System components configured and managed securely"
|
||||
|
||||
req_3_protect_stored_data:
|
||||
"3.1": "Processes for protecting stored account data defined"
|
||||
"3.2": "Storage of account data is minimized"
|
||||
"3.3": "Sensitive authentication data not stored after authorization"
|
||||
"3.4": "PAN masked when displayed (first 6, last 4 maximum)"
|
||||
"3.5": "PAN secured wherever stored"
|
||||
"3.6": "Cryptographic keys managed securely"
|
||||
"3.7": "Key management procedures documented"
|
||||
|
||||
req_4_transmission_encryption:
|
||||
"4.1": "Strong cryptography protects cardholder data during transmission"
|
||||
"4.2": "PAN protected when sent via end-user messaging"
|
||||
|
||||
req_5_malware_protection:
|
||||
"5.1": "Processes to protect against malware defined"
|
||||
"5.2": "Malware prevented or detected and addressed"
|
||||
"5.3": "Anti-malware mechanisms active and maintained"
|
||||
"5.4": "Anti-phishing mechanisms protect against phishing"
|
||||
|
||||
req_6_secure_development:
|
||||
"6.1": "Secure development processes defined"
|
||||
"6.2": "Bespoke and custom software developed securely"
|
||||
"6.3": "Security vulnerabilities identified and addressed"
|
||||
"6.4": "Public-facing web applications protected against attacks"
|
||||
"6.5": "Changes to all system components managed securely"
|
||||
|
||||
req_7_access_restriction:
|
||||
"7.1": "Access to system components and data restricted by business need"
|
||||
"7.2": "Access appropriately defined and assigned"
|
||||
"7.3": "Access to system components and data managed via access control"
|
||||
|
||||
req_8_user_identification:
|
||||
"8.1": "Processes for user identification defined"
|
||||
"8.2": "User identification and accounts managed"
|
||||
"8.3": "Strong authentication established"
|
||||
"8.4": "MFA implemented for all access into CDE"
|
||||
"8.5": "MFA systems configured to prevent misuse"
|
||||
"8.6": "System and application accounts managed"
|
||||
|
||||
req_9_physical_access:
|
||||
"9.1": "Physical access controls defined"
|
||||
"9.2": "Physical access to CDE managed"
|
||||
"9.3": "Physical access for personnel and visitors authorized"
|
||||
"9.4": "Media with cardholder data managed securely"
|
||||
"9.5": "POI devices protected from tampering"
|
||||
|
||||
req_10_logging:
|
||||
"10.1": "Audit logging processes defined"
|
||||
"10.2": "Audit logs record required events"
|
||||
"10.3": "Audit logs protected from destruction and modification"
|
||||
"10.4": "Audit logs reviewed for anomalies"
|
||||
"10.5": "Audit log history retained"
|
||||
"10.6": "Time synchronization mechanisms configured"
|
||||
"10.7": "Audit logs retained for at least 12 months (3 months immediately available)"
|
||||
|
||||
req_11_testing:
|
||||
"11.1": "Security testing processes defined"
|
||||
"11.2": "Wireless access points managed"
|
||||
"11.3": "Vulnerabilities identified and addressed"
|
||||
"11.4": "External and internal penetration testing performed"
|
||||
"11.5": "Network intrusions and changes detected and responded to"
|
||||
"11.6": "Unauthorized changes to payment pages detected"
|
||||
|
||||
req_12_policies:
|
||||
"12.1": "Information security policy established"
|
||||
"12.2": "Acceptable use policies defined"
|
||||
"12.3": "Risks to CDE formally identified and managed"
|
||||
"12.4": "PCI DSS compliance managed"
|
||||
"12.5": "PCI DSS scope documented and validated"
|
||||
"12.6": "Security awareness program"
|
||||
"12.8": "Third-party service providers managed"
|
||||
"12.9": "TPSPs acknowledge responsibility for cardholder data"
|
||||
"12.10": "Security incidents responded to immediately"
|
||||
```
|
||||
|
||||
## Network Segmentation
|
||||
## Network Segmentation Architecture
|
||||
|
||||
```
|
||||
Internet --> DMZ --> Firewall --> CDE
|
||||
|
|
||||
Non-CDE <-- Firewall --
|
||||
┌──────────────────────────────────────┐
|
||||
│ INTERNET │
|
||||
└──────────────┬───────────────────────┘
|
||||
│
|
||||
┌──────────────▼───────────────────────┐
|
||||
│ DMZ (Public Subnet) │
|
||||
│ WAF → Load Balancer → Web Servers │
|
||||
└──────────────┬───────────────────────┘
|
||||
│ Firewall (Req 1.3)
|
||||
┌──────────────▼───────────────────────┐
|
||||
│ CDE (Cardholder Data Environment) │
|
||||
│ ┌─────────┐ ┌──────────┐ │
|
||||
│ │ Payment │ │ Card DB │ │
|
||||
│ │ App │ │(encrypted)│ │
|
||||
│ └─────────┘ └──────────┘ │
|
||||
│ ┌─────────┐ ┌──────────┐ │
|
||||
│ │Token Svc│ │ HSM/KMS │ │
|
||||
│ └─────────┘ └──────────┘ │
|
||||
└──────────────┬───────────────────────┘
|
||||
│ Firewall (Req 1.3)
|
||||
┌──────────────▼───────────────────────┐
|
||||
│ Non-CDE (Corporate Network) │
|
||||
│ App servers, internal tools │
|
||||
│ (no cardholder data) │
|
||||
└──────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Data Protection
|
||||
```bash
|
||||
# AWS Security Group for CDE isolation
|
||||
aws ec2 create-security-group \
|
||||
--group-name cde-app-sg \
|
||||
--description "CDE Application Security Group" \
|
||||
--vpc-id vpc-CDE
|
||||
|
||||
# Allow only HTTPS from WAF/ALB
|
||||
aws ec2 authorize-security-group-ingress \
|
||||
--group-id sg-CDE-APP \
|
||||
--protocol tcp --port 443 \
|
||||
--source-group sg-ALB
|
||||
|
||||
# CDE database - only accessible from CDE app servers
|
||||
aws ec2 create-security-group \
|
||||
--group-name cde-db-sg \
|
||||
--description "CDE Database Security Group" \
|
||||
--vpc-id vpc-CDE
|
||||
|
||||
aws ec2 authorize-security-group-ingress \
|
||||
--group-id sg-CDE-DB \
|
||||
--protocol tcp --port 5432 \
|
||||
--source-group sg-CDE-APP
|
||||
|
||||
# Deny all other inbound by default (security groups are deny-all by default in AWS)
|
||||
# Document all rules for Req 1.2 - firewall/security group documentation
|
||||
```
|
||||
|
||||
## Encryption and Tokenization
|
||||
|
||||
```yaml
|
||||
encryption:
|
||||
at_rest: AES-256
|
||||
in_transit: TLS 1.2+
|
||||
key_storage: HSM or dedicated key vault
|
||||
|
||||
tokenization:
|
||||
- Replace PAN with token
|
||||
- Store mapping securely
|
||||
- Reduce CDE scope
|
||||
encryption_requirements:
|
||||
stored_data_req_3:
|
||||
pan_encryption:
|
||||
algorithm: AES-256
|
||||
mode: GCM (preferred) or CBC with HMAC
|
||||
key_storage: HSM or dedicated key management service
|
||||
never_store:
|
||||
- Full track data (magnetic stripe)
|
||||
- CVV/CVC/CAV2
|
||||
- PIN / PIN block
|
||||
|
||||
pan_display_masking:
|
||||
rule: "Show maximum first 6 and last 4 digits"
|
||||
examples:
|
||||
masked: "4111 11** **** 1111"
|
||||
acceptable_for_business: "First 6 and last 4"
|
||||
implementation: "Apply masking at application layer before rendering"
|
||||
|
||||
key_management_req_3_6:
|
||||
- Generate keys using approved random number generator
|
||||
- Protect keys with key-encrypting keys (KEKs)
|
||||
- Store key components separately (split knowledge, dual control)
|
||||
- Rotate keys at least annually (or per crypto period)
|
||||
- Retire and replace keys when compromised
|
||||
- Document key custodian responsibilities
|
||||
|
||||
transmission_req_4:
|
||||
protocols:
|
||||
required: "TLS 1.2 or higher"
|
||||
prohibited: "SSL, TLS 1.0, TLS 1.1"
|
||||
cipher_suites:
|
||||
preferred:
|
||||
- TLS_AES_256_GCM_SHA384
|
||||
- TLS_CHACHA20_POLY1305_SHA256
|
||||
minimum: "128-bit key strength"
|
||||
certificate_management:
|
||||
- Use certificates from trusted CAs
|
||||
- Verify hostname and certificate validity
|
||||
- Monitor certificate expiration
|
||||
|
||||
tokenization_strategy:
|
||||
description: "Replace PAN with non-reversible token to reduce CDE scope"
|
||||
implementation:
|
||||
- Use format-preserving tokens (same length/format as PAN)
|
||||
- Token vault in isolated CDE segment
|
||||
- Token-to-PAN mapping encrypted and access-controlled
|
||||
- De-tokenization requires authenticated API call
|
||||
- Log all de-tokenization requests
|
||||
scope_benefit: "Systems using only tokens are out of PCI scope"
|
||||
```
|
||||
|
||||
## Vulnerability Management and Testing
|
||||
|
||||
```bash
|
||||
# Req 11.3 - Internal vulnerability scanning (quarterly minimum)
|
||||
# Using OpenVAS or Nessus
|
||||
openvas-cli --scan-target 10.10.0.0/24 --scan-name "CDE-Quarterly-Scan" \
|
||||
--profile "PCI DSS" --output pci-scan-$(date +%Y%m%d).xml
|
||||
|
||||
# Req 11.3 - External ASV scanning (quarterly, must pass)
|
||||
# Schedule with Approved Scanning Vendor (Qualys, Tenable, etc.)
|
||||
# ASV scan must show no vulnerabilities with CVSS >= 4.0
|
||||
|
||||
# Req 6.3 - Patch management
|
||||
# Check for critical patches on CDE systems
|
||||
yum check-update --security # RHEL/CentOS
|
||||
apt list --upgradable 2>/dev/null | grep -i security # Debian/Ubuntu
|
||||
|
||||
# Req 11.4 - Penetration testing (annual for external, internal, and segmentation)
|
||||
# Must be performed by qualified internal resource or third party
|
||||
# Test both network layer and application layer
|
||||
# Segmentation testing: verify CDE is isolated from non-CDE networks
|
||||
|
||||
# Req 11.5 - File integrity monitoring
|
||||
# Using AIDE (Advanced Intrusion Detection Environment)
|
||||
aide --init # Initialize baseline
|
||||
aide --check # Compare against baseline
|
||||
|
||||
# OSSEC FIM configuration for CDE systems
|
||||
# /var/ossec/etc/ossec.conf
|
||||
# <syscheck>
|
||||
# <frequency>3600</frequency>
|
||||
# <directories check_all="yes">/etc,/usr/bin,/usr/sbin</directories>
|
||||
# <directories check_all="yes">/opt/payment-app</directories>
|
||||
# </syscheck>
|
||||
```
|
||||
|
||||
## Logging and Monitoring (Req 10)
|
||||
|
||||
```yaml
|
||||
required_audit_events:
|
||||
"10.2.1": "All individual user accesses to cardholder data"
|
||||
"10.2.2": "All actions taken by any individual with root or admin privileges"
|
||||
"10.2.3": "Access to all audit trails"
|
||||
"10.2.4": "Invalid logical access attempts"
|
||||
"10.2.5": "Changes to identification and authentication credentials"
|
||||
"10.2.6": "Initialization, stopping, or pausing of audit logs"
|
||||
"10.2.7": "Creation and deletion of system-level objects"
|
||||
|
||||
log_entry_requirements:
|
||||
"10.3.1": "User identification"
|
||||
"10.3.2": "Type of event"
|
||||
"10.3.3": "Date and time"
|
||||
"10.3.4": "Success or failure indication"
|
||||
"10.3.5": "Origination of event"
|
||||
"10.3.6": "Identity or name of affected data/resource"
|
||||
|
||||
retention:
|
||||
minimum: "12 months total"
|
||||
immediately_available: "At least 3 months"
|
||||
archive: "Remaining months can be in archive storage"
|
||||
|
||||
time_synchronization:
|
||||
"10.6.1": "Synchronize clocks using NTP"
|
||||
"10.6.2": "Time data protected from unauthorized access"
|
||||
"10.6.3": "Time settings received from industry-accepted sources"
|
||||
ntp_config: |
|
||||
# /etc/ntp.conf or chrony.conf for CDE systems
|
||||
server 0.pool.ntp.org iburst
|
||||
server 1.pool.ntp.org iburst
|
||||
driftfile /var/lib/ntp/drift
|
||||
restrict default nomodify notrap nopeer noquery
|
||||
restrict 127.0.0.1
|
||||
```
|
||||
|
||||
## PCI DSS Compliance Checklist
|
||||
|
||||
```yaml
|
||||
pci_dss_checklist:
|
||||
scoping:
|
||||
- [ ] CDE boundaries identified and documented
|
||||
- [ ] All in-scope systems inventoried
|
||||
- [ ] Network segmentation validated
|
||||
- [ ] Data flow diagrams current and accurate
|
||||
- [ ] SAQ type determined (if applicable)
|
||||
- [ ] Third-party service providers identified
|
||||
|
||||
network_security:
|
||||
- [ ] Firewalls/security groups restrict CDE access
|
||||
- [ ] Default deny rules on all CDE boundaries
|
||||
- [ ] Wireless networks segmented from CDE
|
||||
- [ ] Remote access uses MFA
|
||||
- [ ] All firewall rules documented with business justification
|
||||
- [ ] Rules reviewed semi-annually
|
||||
|
||||
data_protection:
|
||||
- [ ] PAN masked when displayed (first 6, last 4 max)
|
||||
- [ ] Stored PAN encrypted with AES-256 or equivalent
|
||||
- [ ] Sensitive auth data not stored after authorization
|
||||
- [ ] Encryption keys managed per Req 3.6/3.7
|
||||
- [ ] TLS 1.2+ for all cardholder data transmission
|
||||
- [ ] Tokenization implemented where feasible
|
||||
|
||||
access_control:
|
||||
- [ ] Access restricted on need-to-know basis
|
||||
- [ ] Unique IDs for all users
|
||||
- [ ] MFA for all access into CDE
|
||||
- [ ] MFA for all remote/non-console admin access
|
||||
- [ ] Default/vendor passwords changed
|
||||
- [ ] Shared/group accounts not used (or tightly controlled)
|
||||
- [ ] Access reviewed at least every 6 months
|
||||
|
||||
monitoring:
|
||||
- [ ] Audit logs capture all required events (Req 10.2)
|
||||
- [ ] Log entries include all required fields (Req 10.3)
|
||||
- [ ] Logs protected from modification
|
||||
- [ ] Logs retained 12 months (3 months immediately available)
|
||||
- [ ] Time synchronization configured (NTP)
|
||||
- [ ] Daily log review process or automated alerting
|
||||
- [ ] File integrity monitoring on critical files
|
||||
|
||||
testing:
|
||||
- [ ] Internal vulnerability scans quarterly
|
||||
- [ ] External ASV scans quarterly (passing)
|
||||
- [ ] Internal penetration test annually
|
||||
- [ ] External penetration test annually
|
||||
- [ ] Segmentation test annually (or after changes)
|
||||
- [ ] Web application assessment annually (or WAF deployed)
|
||||
- [ ] IDS/IPS monitoring all CDE network traffic
|
||||
|
||||
policies:
|
||||
- [ ] Information security policy reviewed annually
|
||||
- [ ] Security awareness training for all personnel
|
||||
- [ ] Incident response plan documented and tested
|
||||
- [ ] Third-party service provider compliance confirmed
|
||||
- [ ] Risk assessment performed annually
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Minimize CDE scope
|
||||
- Use tokenization
|
||||
- Quarterly vulnerability scans
|
||||
- Annual penetration tests
|
||||
- ASV scan certification
|
||||
- Minimize CDE scope aggressively using tokenization, P2PE, and outsourced payment processing
|
||||
- Use network segmentation to isolate the CDE and reduce the number of in-scope systems
|
||||
- Never store sensitive authentication data (CVV, track data, PIN) after authorization
|
||||
- Implement MFA for all access into the CDE, not just remote access (v4.0 requirement)
|
||||
- Automate vulnerability scanning and patch management to maintain continuous compliance
|
||||
- Deploy file integrity monitoring on all CDE systems to detect unauthorized changes
|
||||
- Synchronize clocks across all CDE systems using NTP for accurate log correlation
|
||||
- Conduct internal and external penetration tests annually and after significant changes
|
||||
- Review all firewall and security group rules semi-annually with documented business justification
|
||||
- Maintain a current data flow diagram showing all cardholder data transmission and storage points
|
||||
|
||||
@@ -9,74 +9,378 @@ metadata:
|
||||
|
||||
# SOC 2 Compliance
|
||||
|
||||
Implement SOC 2 Trust Services Criteria for certification.
|
||||
Implement SOC 2 Trust Services Criteria controls, evidence collection, and continuous compliance monitoring for Type I and Type II audits.
|
||||
|
||||
## Trust Services Criteria
|
||||
## When to Use
|
||||
|
||||
- Preparing for a SOC 2 Type I or Type II audit
|
||||
- Mapping existing controls to Trust Services Criteria
|
||||
- Automating evidence collection for auditor requests
|
||||
- Building continuous compliance monitoring into CI/CD
|
||||
- Onboarding new services and ensuring SOC 2 control coverage
|
||||
|
||||
## Trust Services Criteria Detailed Checklist
|
||||
|
||||
```yaml
|
||||
criteria:
|
||||
security:
|
||||
- Access controls
|
||||
- Change management
|
||||
- Risk assessment
|
||||
- Incident response
|
||||
|
||||
availability:
|
||||
- System monitoring
|
||||
- Disaster recovery
|
||||
- Capacity planning
|
||||
- SLA management
|
||||
|
||||
processing_integrity:
|
||||
- Input validation
|
||||
- Processing completeness
|
||||
- Output accuracy
|
||||
|
||||
confidentiality:
|
||||
- Data classification
|
||||
- Encryption
|
||||
- Access restrictions
|
||||
|
||||
privacy:
|
||||
- Data collection notice
|
||||
- Consent management
|
||||
- Data retention
|
||||
security_common_criteria:
|
||||
CC1_control_environment:
|
||||
CC1.1: "Management demonstrates commitment to integrity and ethical values"
|
||||
CC1.2: "Board exercises oversight of internal controls"
|
||||
CC1.3: "Management establishes structure, authority, and responsibility"
|
||||
CC1.4: "Commitment to competence - hire and retain qualified personnel"
|
||||
CC1.5: "Individuals are held accountable for internal control responsibilities"
|
||||
evidence:
|
||||
- Code of conduct document
|
||||
- Organizational chart
|
||||
- Job descriptions with security responsibilities
|
||||
- Board meeting minutes discussing security
|
||||
- Background check policy and records
|
||||
|
||||
CC2_communication:
|
||||
CC2.1: "Entity obtains or generates relevant quality information"
|
||||
CC2.2: "Entity internally communicates information including objectives and responsibilities"
|
||||
CC2.3: "Entity communicates with external parties"
|
||||
evidence:
|
||||
- Security awareness training records
|
||||
- Internal security newsletters or updates
|
||||
- Customer-facing security documentation
|
||||
- Status page and incident communication records
|
||||
|
||||
CC3_risk_assessment:
|
||||
CC3.1: "Entity specifies objectives clearly to identify and assess risks"
|
||||
CC3.2: "Entity identifies risks to achievement of objectives"
|
||||
CC3.3: "Entity considers potential for fraud"
|
||||
CC3.4: "Entity identifies and assesses significant changes"
|
||||
evidence:
|
||||
- Annual risk assessment report
|
||||
- Risk register with ratings and treatment plans
|
||||
- Fraud risk assessment documentation
|
||||
- Change management records
|
||||
|
||||
CC4_monitoring:
|
||||
CC4.1: "Entity selects, develops, and performs ongoing/separate evaluations"
|
||||
CC4.2: "Entity evaluates and communicates internal control deficiencies"
|
||||
evidence:
|
||||
- Continuous monitoring dashboard screenshots
|
||||
- Internal audit reports
|
||||
- Vulnerability scan results
|
||||
- Penetration test reports
|
||||
|
||||
CC5_control_activities:
|
||||
CC5.1: "Entity selects and develops control activities to mitigate risks"
|
||||
CC5.2: "Entity selects and develops technology-based controls"
|
||||
CC5.3: "Entity deploys control activities through policies and procedures"
|
||||
evidence:
|
||||
- Information security policy
|
||||
- Access control procedures
|
||||
- Change management procedures
|
||||
- Encryption standards documentation
|
||||
|
||||
CC6_logical_access:
|
||||
CC6.1: "Logical access security over protected information assets"
|
||||
CC6.2: "Prior to access, users are registered and authorized"
|
||||
CC6.3: "Access to data, software, functions, and other IT resources is authorized and modified"
|
||||
CC6.6: "Logical access security measures against threats from outside system boundaries"
|
||||
CC6.7: "Transmission of data between parties is protected"
|
||||
CC6.8: "Controls to prevent or detect unauthorized or malicious software"
|
||||
evidence:
|
||||
- IAM credential report
|
||||
- MFA enforcement configuration
|
||||
- Access review completion records
|
||||
- Firewall and WAF configurations
|
||||
- TLS/encryption configurations
|
||||
- Endpoint protection deployment records
|
||||
|
||||
CC7_system_operations:
|
||||
CC7.1: "Detect anomalies and potential security incidents"
|
||||
CC7.2: "Monitor system components for anomalies"
|
||||
CC7.3: "Evaluate detected events and determine incidents"
|
||||
CC7.4: "Respond to identified security incidents"
|
||||
CC7.5: "Identify and remediate security incidents"
|
||||
evidence:
|
||||
- SIEM alert rules and dashboards
|
||||
- Monitoring configuration (CloudWatch, Datadog, etc.)
|
||||
- Incident response plan
|
||||
- Incident tickets and post-mortems
|
||||
|
||||
CC8_change_management:
|
||||
CC8.1: "Entity authorizes, designs, develops, configures, documents, tests, approves, and implements changes"
|
||||
evidence:
|
||||
- Change management policy
|
||||
- Pull request approval requirements
|
||||
- CI/CD pipeline configurations
|
||||
- Deployment records with approvals
|
||||
|
||||
CC9_risk_mitigation:
|
||||
CC9.1: "Entity identifies, selects, and develops risk mitigation activities"
|
||||
CC9.2: "Entity assesses and manages risks associated with vendors"
|
||||
evidence:
|
||||
- Risk treatment plans
|
||||
- Vendor assessment records
|
||||
- Business associate agreements
|
||||
- Insurance certificates
|
||||
|
||||
availability_criteria:
|
||||
A1.1: "System processing capacity and availability are maintained"
|
||||
A1.2: "Environmental protections and recovery measures"
|
||||
A1.3: "Recovery plan procedures to support system availability"
|
||||
evidence:
|
||||
- Uptime SLA documentation
|
||||
- Capacity monitoring dashboards
|
||||
- Disaster recovery plan
|
||||
- DR test results
|
||||
- Backup verification records
|
||||
|
||||
processing_integrity_criteria:
|
||||
PI1.1: "Entity obtains or generates, uses, and communicates quality information"
|
||||
evidence:
|
||||
- Input validation procedures
|
||||
- Data processing accuracy checks
|
||||
- Error handling and retry logic documentation
|
||||
- Output reconciliation records
|
||||
|
||||
confidentiality_criteria:
|
||||
C1.1: "Entity identifies and maintains confidential information"
|
||||
C1.2: "Entity disposes of confidential information"
|
||||
evidence:
|
||||
- Data classification policy
|
||||
- Encryption configurations
|
||||
- Data retention and destruction policies
|
||||
- Secure disposal records
|
||||
|
||||
privacy_criteria:
|
||||
P1-P8: "Privacy notice, choice, collection, use, disclosure, access, quality, monitoring"
|
||||
evidence:
|
||||
- Privacy policy (published)
|
||||
- Consent management records
|
||||
- Data processing inventory
|
||||
- DSAR handling procedures
|
||||
```
|
||||
|
||||
## Key Controls
|
||||
## Tool Mappings for Control Evidence
|
||||
|
||||
```yaml
|
||||
controls:
|
||||
control_to_tool_mapping:
|
||||
CC6.1_logical_access:
|
||||
- MFA enforcement
|
||||
- Role-based access
|
||||
- Access reviews
|
||||
|
||||
aws:
|
||||
- IAM credential report (aws iam generate-credential-report)
|
||||
- IAM Access Analyzer findings
|
||||
- AWS SSO configuration
|
||||
- GuardDuty findings
|
||||
azure:
|
||||
- Azure AD sign-in logs
|
||||
- Conditional Access policies
|
||||
- PIM role assignments
|
||||
github:
|
||||
- Organization member list and roles
|
||||
- Repository access permissions
|
||||
- Branch protection rules
|
||||
okta:
|
||||
- User status report
|
||||
- MFA enrollment report
|
||||
- Application assignment report
|
||||
|
||||
CC7.2_monitoring:
|
||||
- Log aggregation
|
||||
- Alert thresholds
|
||||
- Incident tracking
|
||||
|
||||
tools:
|
||||
- CloudWatch / Azure Monitor / Cloud Monitoring dashboards
|
||||
- Datadog / New Relic / Grafana alert configurations
|
||||
- SIEM (Splunk, Elastic, Sentinel) saved searches
|
||||
- PagerDuty / OpsGenie escalation policies
|
||||
evidence_format:
|
||||
- Dashboard screenshots with date stamps
|
||||
- Alert rule configuration exports
|
||||
- Incident response records from ticketing system
|
||||
|
||||
CC8.1_change_management:
|
||||
- Change requests
|
||||
- Approval workflows
|
||||
- Testing requirements
|
||||
tools:
|
||||
- GitHub/GitLab PR merge requirements
|
||||
- CI/CD pipeline configurations (GitHub Actions, Jenkins)
|
||||
- Terraform plan outputs
|
||||
- Deployment logs
|
||||
evidence_format:
|
||||
- PR with approvals and CI checks
|
||||
- Deployment audit trail
|
||||
- Change advisory board meeting notes (if applicable)
|
||||
```
|
||||
|
||||
## Evidence Collection
|
||||
## Evidence Collection Automation
|
||||
|
||||
```bash
|
||||
# Access review export
|
||||
aws iam generate-credential-report
|
||||
aws iam get-credential-report
|
||||
#!/usr/bin/env bash
|
||||
# collect-soc2-evidence.sh - Automated SOC 2 evidence collection
|
||||
# Run monthly or before audit requests
|
||||
|
||||
# Audit logs
|
||||
aws cloudtrail lookup-events --start-time $(date -d '30 days ago' --iso)
|
||||
EVIDENCE_DIR="./soc2-evidence/$(date +%Y-%m)"
|
||||
mkdir -p "$EVIDENCE_DIR"
|
||||
|
||||
echo "=== CC6.1 - Logical Access Evidence ==="
|
||||
|
||||
# AWS IAM credential report
|
||||
aws iam generate-credential-report
|
||||
sleep 10
|
||||
aws iam get-credential-report --output text --query Content | \
|
||||
base64 -d > "$EVIDENCE_DIR/aws-iam-credential-report.csv"
|
||||
|
||||
# AWS IAM Access Analyzer findings
|
||||
aws accessanalyzer list-findings \
|
||||
--analyzer-arn "arn:aws:access-analyzer:us-east-1:123456789012:analyzer/org-analyzer" \
|
||||
--filter '{"status": {"eq": ["ACTIVE"]}}' \
|
||||
> "$EVIDENCE_DIR/access-analyzer-findings.json"
|
||||
|
||||
# MFA enforcement status
|
||||
aws iam list-users --query 'Users[*].UserName' --output text | \
|
||||
tr '\t' '\n' | while read -r user; do
|
||||
mfa=$(aws iam list-mfa-devices --user-name "$user" --query 'MFADevices[0].SerialNumber' --output text)
|
||||
echo "$user,$mfa"
|
||||
done > "$EVIDENCE_DIR/mfa-status.csv"
|
||||
|
||||
# GitHub organization members and roles
|
||||
gh api orgs/YOUR_ORG/members --paginate --jq '.[] | [.login, .role_name // "member"] | @csv' \
|
||||
> "$EVIDENCE_DIR/github-org-members.csv"
|
||||
|
||||
# GitHub branch protection rules
|
||||
for repo in $(gh repo list YOUR_ORG --json name -q '.[].name'); do
|
||||
gh api repos/YOUR_ORG/$repo/branches/main/protection \
|
||||
> "$EVIDENCE_DIR/branch-protection-$repo.json" 2>/dev/null
|
||||
done
|
||||
|
||||
echo "=== CC7.2 - Monitoring Evidence ==="
|
||||
|
||||
# CloudTrail status
|
||||
aws cloudtrail get-trail-status --name org-audit-trail \
|
||||
> "$EVIDENCE_DIR/cloudtrail-status.json"
|
||||
|
||||
# Active CloudWatch alarms
|
||||
aws cloudwatch describe-alarms --state-value ALARM \
|
||||
> "$EVIDENCE_DIR/active-alarms.json"
|
||||
|
||||
# GuardDuty findings summary
|
||||
aws guardduty list-findings --detector-id DETECTOR_ID \
|
||||
--finding-criteria '{"criterion":{"severity":{"gte":4}}}' \
|
||||
> "$EVIDENCE_DIR/guardduty-findings.json"
|
||||
|
||||
echo "=== CC8.1 - Change Management Evidence ==="
|
||||
|
||||
# Recent deployments (GitHub Actions)
|
||||
gh run list --repo YOUR_ORG/YOUR_REPO --limit 50 --json conclusion,createdAt,displayTitle,headBranch \
|
||||
> "$EVIDENCE_DIR/recent-deployments.json"
|
||||
|
||||
# Pull requests merged in audit period
|
||||
gh pr list --repo YOUR_ORG/YOUR_REPO --state merged --limit 100 \
|
||||
--json number,title,author,mergedBy,mergedAt,reviews \
|
||||
> "$EVIDENCE_DIR/merged-prs.json"
|
||||
|
||||
echo "=== A1 - Availability Evidence ==="
|
||||
|
||||
# Backup status
|
||||
aws rds describe-db-snapshots --db-instance-identifier prod-db \
|
||||
--query 'DBSnapshots | sort_by(@, &SnapshotCreateTime) | [-5:]' \
|
||||
> "$EVIDENCE_DIR/rds-backup-snapshots.json"
|
||||
|
||||
# S3 replication status
|
||||
aws s3api get-bucket-replication --bucket prod-data-bucket \
|
||||
> "$EVIDENCE_DIR/s3-replication-config.json"
|
||||
|
||||
echo "Evidence collected in $EVIDENCE_DIR"
|
||||
tar -czf "$EVIDENCE_DIR.tar.gz" "$EVIDENCE_DIR"
|
||||
echo "Archive: $EVIDENCE_DIR.tar.gz"
|
||||
```
|
||||
|
||||
## Audit Preparation Timeline
|
||||
|
||||
```yaml
|
||||
audit_prep_timeline:
|
||||
12_months_before:
|
||||
- Select auditor firm and sign engagement letter
|
||||
- Perform gap assessment against TSC criteria
|
||||
- Remediate identified control gaps
|
||||
- Begin formal evidence collection cadence
|
||||
|
||||
6_months_before:
|
||||
- Conduct internal readiness assessment
|
||||
- Verify all controls are operating effectively
|
||||
- Complete risk assessment and update risk register
|
||||
- Ensure vendor assessments are current
|
||||
- Test disaster recovery procedures
|
||||
|
||||
3_months_before:
|
||||
- Run automated evidence collection and verify completeness
|
||||
- Conduct access review and remediate findings
|
||||
- Review and update all policies and procedures
|
||||
- Perform vulnerability scan and penetration test
|
||||
- Confirm all training records are current
|
||||
|
||||
1_month_before:
|
||||
- Prepare evidence request list responses
|
||||
- Organize evidence into auditor-friendly structure
|
||||
- Brief key personnel on audit interviews
|
||||
- Verify monitoring dashboards show healthy state
|
||||
- Confirm incident response records are complete
|
||||
|
||||
during_audit:
|
||||
- Designate audit liaison for request management
|
||||
- Provide timely evidence and clarifications
|
||||
- Track open auditor questions
|
||||
- Escalate issues to control owners promptly
|
||||
|
||||
after_audit:
|
||||
- Review draft report and provide management response
|
||||
- Create remediation plan for any exceptions
|
||||
- Communicate results to stakeholders
|
||||
- Update controls and processes based on findings
|
||||
- Begin next audit period evidence collection
|
||||
```
|
||||
|
||||
## Continuous Compliance Monitoring
|
||||
|
||||
```yaml
|
||||
# GitHub Actions workflow for continuous SOC 2 checks
|
||||
name: SOC2 Compliance Checks
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Weekly on Monday
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
access-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Check MFA enforcement
|
||||
run: |
|
||||
USERS_WITHOUT_MFA=$(aws iam generate-credential-report && sleep 5 && \
|
||||
aws iam get-credential-report --output text --query Content | \
|
||||
base64 -d | awk -F, '$4=="true" && $8=="false" {print $1}')
|
||||
if [ -n "$USERS_WITHOUT_MFA" ]; then
|
||||
echo "::error::Users without MFA: $USERS_WITHOUT_MFA"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Check for unused credentials
|
||||
run: |
|
||||
THRESHOLD=$(date -d '90 days ago' +%Y-%m-%dT%H:%M:%S)
|
||||
aws iam get-credential-report --output text --query Content | \
|
||||
base64 -d | awk -F, -v t="$THRESHOLD" '$5!="N/A" && $5<t {print $1" last used "$5}'
|
||||
|
||||
- name: Verify CloudTrail is logging
|
||||
run: |
|
||||
STATUS=$(aws cloudtrail get-trail-status --name org-audit-trail --query 'IsLogging' --output text)
|
||||
[ "$STATUS" = "True" ] || (echo "::error::CloudTrail logging stopped" && exit 1)
|
||||
|
||||
- name: Check GuardDuty is enabled
|
||||
run: |
|
||||
DETECTOR=$(aws guardduty list-detectors --query 'DetectorIds[0]' --output text)
|
||||
[ "$DETECTOR" != "None" ] || (echo "::error::GuardDuty not enabled" && exit 1)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Continuous compliance monitoring
|
||||
- Annual risk assessments
|
||||
- Regular control testing
|
||||
- Documentation maintenance
|
||||
- Start with a gap assessment to understand current control maturity before engaging an auditor
|
||||
- Automate evidence collection to reduce the burden of auditor requests and ensure consistency
|
||||
- Map each control to a specific tool, owner, and evidence artifact for traceability
|
||||
- Implement continuous monitoring rather than point-in-time checks for Type II readiness
|
||||
- Maintain a central evidence repository organized by control criteria
|
||||
- Conduct quarterly internal reviews to catch control drift before the audit period
|
||||
- Keep policies living documents with version history and annual review dates
|
||||
- Train all employees on their role in maintaining SOC 2 controls
|
||||
- Use the audit preparation timeline to avoid last-minute scrambling
|
||||
- Treat each auditor exception as an improvement opportunity rather than a failure
|
||||
|
||||
@@ -9,68 +9,471 @@ metadata:
|
||||
|
||||
# Access Review
|
||||
|
||||
Implement periodic access review processes.
|
||||
Implement periodic access review processes for AWS IAM, GitHub, Okta, and other identity providers, including automated reporting, certification workflows, and unused permission detection.
|
||||
|
||||
## Review Process
|
||||
## When to Use
|
||||
|
||||
- Conducting quarterly or annual access reviews for compliance (SOC 2, HIPAA, PCI DSS, ISO 27001)
|
||||
- Identifying and removing stale accounts and unused credentials
|
||||
- Certifying that current access levels match job responsibilities
|
||||
- Detecting excessive privileges and dormant service accounts
|
||||
- Generating evidence for auditor requests on access governance
|
||||
|
||||
## Access Review Process
|
||||
|
||||
```yaml
|
||||
access_review_workflow:
|
||||
1_extract:
|
||||
- Pull access data from systems
|
||||
- Generate access report
|
||||
|
||||
2_review:
|
||||
- Manager certification
|
||||
- Risk-based prioritization
|
||||
- Decision documentation
|
||||
|
||||
3_action:
|
||||
- Revoke unnecessary access
|
||||
- Update exceptions
|
||||
- Document decisions
|
||||
|
||||
4_report:
|
||||
- Compliance metrics
|
||||
- Remediation tracking
|
||||
1_scope:
|
||||
actions:
|
||||
- Define systems in scope for the review cycle
|
||||
- Identify review owners (managers, system owners)
|
||||
- Set review timeline and deadlines
|
||||
- Generate access inventory from all identity sources
|
||||
frequency:
|
||||
privileged_access: Quarterly
|
||||
standard_access: Semi-annually
|
||||
service_accounts: Quarterly
|
||||
api_keys: Monthly
|
||||
|
||||
2_extract:
|
||||
actions:
|
||||
- Pull current access data from all systems
|
||||
- Correlate identities across platforms (SSO mapping)
|
||||
- Enrich with last login and activity data
|
||||
- Flag accounts for review (inactive, over-privileged, orphaned)
|
||||
|
||||
3_review:
|
||||
actions:
|
||||
- Assign review items to appropriate managers
|
||||
- Manager certifies each user's access (approve/revoke/modify)
|
||||
- Risk-based prioritization (privileged users reviewed first)
|
||||
- Escalate non-responses after deadline
|
||||
decisions:
|
||||
approve: "Access is appropriate for current role"
|
||||
modify: "Access needs adjustment (reduce/change scope)"
|
||||
revoke: "Access is no longer needed"
|
||||
|
||||
4_remediate:
|
||||
actions:
|
||||
- Revoke access flagged for removal
|
||||
- Modify access as directed by reviewers
|
||||
- Document exceptions with justification
|
||||
- Confirm changes with system owners
|
||||
sla:
|
||||
revocations: "Complete within 5 business days of decision"
|
||||
modifications: "Complete within 10 business days"
|
||||
exceptions: "Approved by security team, documented, time-limited"
|
||||
|
||||
5_report:
|
||||
actions:
|
||||
- Generate completion metrics (% reviewed, % on time)
|
||||
- Document all decisions and actions taken
|
||||
- Archive evidence for compliance audits
|
||||
- Identify process improvements for next cycle
|
||||
```
|
||||
|
||||
## AWS IAM Review
|
||||
## AWS IAM Access Review Scripts
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# aws-iam-review.sh - Comprehensive IAM access review report
|
||||
|
||||
OUTPUT_DIR="./access-review/$(date +%Y-%m)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo "=== AWS IAM Access Review ==="
|
||||
|
||||
# Generate credential report
|
||||
aws iam generate-credential-report
|
||||
aws iam get-credential-report --output text --query Content | base64 -d
|
||||
aws iam generate-credential-report > /dev/null
|
||||
sleep 10
|
||||
aws iam get-credential-report --output text --query Content | \
|
||||
base64 -d > "$OUTPUT_DIR/credential-report.csv"
|
||||
|
||||
# Find inactive users
|
||||
aws iam list-users | jq -r '.Users[] | select(.PasswordLastUsed < "2024-01-01") | .UserName'
|
||||
echo "--- Users Without MFA ---"
|
||||
aws iam get-credential-report --output text --query Content | base64 -d | \
|
||||
awk -F, 'NR>1 && $4=="true" && $8=="false" {print $1}' | \
|
||||
tee "$OUTPUT_DIR/users-without-mfa.txt"
|
||||
|
||||
# List unused access keys
|
||||
aws iam get-access-key-last-used --access-key-id AKIAXXXXXXXX
|
||||
echo "--- Inactive Users (90+ days) ---"
|
||||
THRESHOLD=$(date -d '90 days ago' +%Y-%m-%dT%H:%M:%S 2>/dev/null || date -v-90d +%Y-%m-%dT%H:%M:%S)
|
||||
aws iam get-credential-report --output text --query Content | base64 -d | \
|
||||
awk -F, -v t="$THRESHOLD" 'NR>1 && $5!="N/A" && $5!="no_information" && $5<t {
|
||||
print $1","$5
|
||||
}' | tee "$OUTPUT_DIR/inactive-users.csv"
|
||||
|
||||
echo "--- Stale Access Keys (90+ days unused) ---"
|
||||
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
|
||||
for key_id in $(aws iam list-access-keys --user-name "$user" \
|
||||
--query 'AccessKeyMetadata[?Status==`Active`].AccessKeyId' --output text); do
|
||||
last_used=$(aws iam get-access-key-last-used --access-key-id "$key_id" \
|
||||
--query 'AccessKeyLastUsed.LastUsedDate' --output text)
|
||||
if [ "$last_used" = "None" ] || [ "$last_used" \< "$THRESHOLD" ]; then
|
||||
echo "$user,$key_id,$last_used"
|
||||
fi
|
||||
done
|
||||
done | tee "$OUTPUT_DIR/stale-access-keys.csv"
|
||||
|
||||
echo "--- Users With Admin Policies ---"
|
||||
for user in $(aws iam list-users --query 'Users[*].UserName' --output text); do
|
||||
policies=$(aws iam list-attached-user-policies --user-name "$user" \
|
||||
--query 'AttachedPolicies[*].PolicyName' --output text)
|
||||
if echo "$policies" | grep -qi "admin\|fullaccess"; then
|
||||
groups=$(aws iam list-groups-for-user --user-name "$user" \
|
||||
--query 'Groups[*].GroupName' --output text)
|
||||
echo "$user|policies:$policies|groups:$groups"
|
||||
fi
|
||||
done | tee "$OUTPUT_DIR/admin-users.txt"
|
||||
|
||||
echo "--- IAM Roles With Cross-Account Trust ---"
|
||||
for role in $(aws iam list-roles --query 'Roles[*].RoleName' --output text); do
|
||||
trust=$(aws iam get-role --role-name "$role" \
|
||||
--query 'Role.AssumeRolePolicyDocument' --output json 2>/dev/null)
|
||||
if echo "$trust" | grep -q '"AWS"' && echo "$trust" | grep -qv "$(aws sts get-caller-identity --query Account --output text)"; then
|
||||
echo "$role: $trust" | jq -c '.Statement[].Principal'
|
||||
fi
|
||||
done | tee "$OUTPUT_DIR/cross-account-roles.txt"
|
||||
|
||||
echo "--- Service Accounts (Programmatic Only) ---"
|
||||
aws iam get-credential-report --output text --query Content | base64 -d | \
|
||||
awk -F, 'NR>1 && $4=="false" && $9!="N/A" {print $1","$11","$16}' | \
|
||||
tee "$OUTPUT_DIR/service-accounts.csv"
|
||||
|
||||
echo "Report generated in $OUTPUT_DIR"
|
||||
```
|
||||
|
||||
## Automation
|
||||
## GitHub Access Review
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# github-access-review.sh - GitHub organization access audit
|
||||
|
||||
ORG="your-org"
|
||||
OUTPUT_DIR="./access-review/github/$(date +%Y-%m)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo "=== GitHub Organization Access Review ==="
|
||||
|
||||
echo "--- Organization Members ---"
|
||||
gh api orgs/$ORG/members --paginate \
|
||||
--jq '.[] | [.login, .site_admin] | @csv' \
|
||||
> "$OUTPUT_DIR/org-members.csv"
|
||||
|
||||
echo "--- Organization Owners ---"
|
||||
gh api "orgs/$ORG/members?role=admin" --paginate \
|
||||
--jq '.[] | .login' \
|
||||
> "$OUTPUT_DIR/org-owners.txt"
|
||||
|
||||
echo "--- Outside Collaborators ---"
|
||||
gh api orgs/$ORG/outside_collaborators --paginate \
|
||||
--jq '.[] | .login' \
|
||||
> "$OUTPUT_DIR/outside-collaborators.txt"
|
||||
|
||||
echo "--- Repository Access Per Repo ---"
|
||||
for repo in $(gh repo list $ORG --json name -q '.[].name' --limit 500); do
|
||||
echo "Repo: $repo"
|
||||
gh api "repos/$ORG/$repo/collaborators" --paginate \
|
||||
--jq '.[] | [.login, .role_name] | @csv' \
|
||||
> "$OUTPUT_DIR/repo-$repo-access.csv" 2>/dev/null
|
||||
done
|
||||
|
||||
echo "--- Team Memberships ---"
|
||||
for team in $(gh api orgs/$ORG/teams --paginate --jq '.[].slug'); do
|
||||
echo "Team: $team"
|
||||
gh api "orgs/$ORG/teams/$team/members" --paginate \
|
||||
--jq '.[] | .login' \
|
||||
> "$OUTPUT_DIR/team-$team-members.txt"
|
||||
done
|
||||
|
||||
echo "--- Pending Invitations ---"
|
||||
gh api orgs/$ORG/invitations --paginate \
|
||||
--jq '.[] | [.login, .email, .role, .created_at] | @csv' \
|
||||
> "$OUTPUT_DIR/pending-invitations.csv"
|
||||
|
||||
echo "--- Deploy Keys ---"
|
||||
for repo in $(gh repo list $ORG --json name -q '.[].name' --limit 500); do
|
||||
keys=$(gh api "repos/$ORG/$repo/keys" --jq '.[].title' 2>/dev/null)
|
||||
if [ -n "$keys" ]; then
|
||||
echo "$repo: $keys"
|
||||
fi
|
||||
done > "$OUTPUT_DIR/deploy-keys.txt"
|
||||
|
||||
echo "--- Branch Protection Rules ---"
|
||||
for repo in $(gh repo list $ORG --json name -q '.[].name' --limit 500); do
|
||||
protection=$(gh api "repos/$ORG/$repo/branches/main/protection" 2>/dev/null)
|
||||
if [ $? -eq 0 ]; then
|
||||
echo "$repo: protected"
|
||||
echo "$protection" | jq '{required_reviews: .required_pull_request_reviews.required_approving_review_count, dismiss_stale: .required_pull_request_reviews.dismiss_stale_reviews}' \
|
||||
> "$OUTPUT_DIR/branch-protection-$repo.json"
|
||||
else
|
||||
echo "$repo: NOT protected" >> "$OUTPUT_DIR/unprotected-repos.txt"
|
||||
fi
|
||||
done
|
||||
|
||||
echo "Report generated in $OUTPUT_DIR"
|
||||
```
|
||||
|
||||
## Okta Access Review
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# okta-access-review.sh - Okta user and application access audit
|
||||
# Requires OKTA_DOMAIN and OKTA_API_TOKEN environment variables
|
||||
|
||||
OUTPUT_DIR="./access-review/okta/$(date +%Y-%m)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
BASE_URL="https://${OKTA_DOMAIN}/api/v1"
|
||||
|
||||
echo "=== Okta Access Review ==="
|
||||
|
||||
echo "--- Active Users ---"
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users?filter=status+eq+%22ACTIVE%22&limit=200" | \
|
||||
jq -r '.[] | [.profile.email, .profile.firstName, .profile.lastName, .lastLogin, .created] | @csv' \
|
||||
> "$OUTPUT_DIR/active-users.csv"
|
||||
|
||||
echo "--- Suspended/Deprovisioned Users ---"
|
||||
for status in SUSPENDED DEPROVISIONED; do
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users?filter=status+eq+%22$status%22&limit=200" | \
|
||||
jq -r '.[] | [.profile.email, .status, .statusChanged] | @csv'
|
||||
done > "$OUTPUT_DIR/inactive-users.csv"
|
||||
|
||||
echo "--- Users Without MFA Enrolled ---"
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users?limit=200" | \
|
||||
jq -r '.[] | .id' | while read -r uid; do
|
||||
factors=$(curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users/$uid/factors" | jq 'length')
|
||||
if [ "$factors" -eq 0 ]; then
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users/$uid" | jq -r '.profile.email'
|
||||
fi
|
||||
done > "$OUTPUT_DIR/users-without-mfa.txt"
|
||||
|
||||
echo "--- Application Assignments ---"
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/apps?limit=200" | \
|
||||
jq -r '.[] | [.id, .label, .status] | @csv' | while IFS=, read -r app_id app_name status; do
|
||||
echo "App: $app_name"
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/apps/$app_id/users?limit=200" | \
|
||||
jq -r '.[] | [.credentials.userName // .profile.email, .status] | @csv'
|
||||
done > "$OUTPUT_DIR/app-assignments.csv"
|
||||
|
||||
echo "--- Admin Role Assignments ---"
|
||||
curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users?limit=200" | \
|
||||
jq -r '.[] | .id' | while read -r uid; do
|
||||
roles=$(curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users/$uid/roles" | jq -r '.[].type' 2>/dev/null)
|
||||
if [ -n "$roles" ]; then
|
||||
email=$(curl -s -H "Authorization: SSWS $OKTA_API_TOKEN" \
|
||||
"$BASE_URL/users/$uid" | jq -r '.profile.email')
|
||||
echo "$email: $roles"
|
||||
fi
|
||||
done > "$OUTPUT_DIR/admin-roles.txt"
|
||||
|
||||
echo "Report generated in $OUTPUT_DIR"
|
||||
```
|
||||
|
||||
## Unused Permission Detection
|
||||
|
||||
```python
|
||||
def generate_access_report():
|
||||
users = get_all_users()
|
||||
report = []
|
||||
|
||||
"""
|
||||
Detect unused IAM permissions using CloudTrail and IAM Access Analyzer.
|
||||
Generates recommendations for right-sizing access.
|
||||
"""
|
||||
import boto3
|
||||
import json
|
||||
import time
|
||||
from datetime import datetime, timedelta, timezone
|
||||
|
||||
|
||||
def analyze_iam_usage(days_lookback=90):
|
||||
"""Analyze IAM user and role activity against granted permissions."""
|
||||
iam = boto3.client("iam")
|
||||
|
||||
report = {
|
||||
"generated_at": datetime.now(timezone.utc).isoformat(),
|
||||
"lookback_days": days_lookback,
|
||||
"findings": [],
|
||||
}
|
||||
|
||||
users = iam.list_users()["Users"]
|
||||
for user in users:
|
||||
report.append({
|
||||
'user': user.email,
|
||||
'roles': user.roles,
|
||||
'last_login': user.last_login,
|
||||
'manager': user.manager,
|
||||
'review_status': 'pending'
|
||||
})
|
||||
|
||||
username = user["UserName"]
|
||||
|
||||
# Get service last accessed data
|
||||
job_id = iam.generate_service_last_accessed_details(
|
||||
Arn=user["Arn"]
|
||||
)["JobId"]
|
||||
|
||||
while True:
|
||||
result = iam.get_service_last_accessed_details(JobId=job_id)
|
||||
if result["JobStatus"] == "COMPLETED":
|
||||
break
|
||||
time.sleep(2)
|
||||
|
||||
threshold = datetime.now(timezone.utc) - timedelta(days=days_lookback)
|
||||
unused_services = []
|
||||
|
||||
for service in result["ServicesLastAccessed"]:
|
||||
last_accessed = service.get("LastAuthenticated")
|
||||
if last_accessed is None or last_accessed < threshold:
|
||||
unused_services.append({
|
||||
"service": service["ServiceNamespace"],
|
||||
"last_accessed": str(last_accessed) if last_accessed else "Never",
|
||||
})
|
||||
|
||||
if unused_services:
|
||||
report["findings"].append({
|
||||
"type": "unused_permissions",
|
||||
"user": username,
|
||||
"arn": user["Arn"],
|
||||
"unused_service_count": len(unused_services),
|
||||
"unused_services": unused_services[:10],
|
||||
"recommendation": "Review and remove unused service permissions",
|
||||
})
|
||||
|
||||
return report
|
||||
|
||||
|
||||
def detect_overprivileged_roles():
|
||||
"""Use IAM Access Analyzer to find overprivileged roles."""
|
||||
analyzer = boto3.client("accessanalyzer")
|
||||
|
||||
findings = analyzer.list_findings(
|
||||
analyzerArn="arn:aws:access-analyzer:us-east-1:123456789012:analyzer/org-analyzer",
|
||||
filter={
|
||||
"status": {"eq": ["ACTIVE"]},
|
||||
"resourceType": {"eq": ["AWS::IAM::Role"]},
|
||||
},
|
||||
)
|
||||
|
||||
return [
|
||||
{
|
||||
"resource": f["resource"],
|
||||
"resource_type": f["resourceType"],
|
||||
"condition": f.get("condition", {}),
|
||||
"principal": f.get("principal", {}),
|
||||
"action": f.get("action", []),
|
||||
"created_at": str(f["createdAt"]),
|
||||
}
|
||||
for f in findings.get("findings", [])
|
||||
]
|
||||
```
|
||||
|
||||
## Certification Workflow Automation
|
||||
|
||||
```yaml
|
||||
# GitHub Actions - Automated access review reminder and tracking
|
||||
name: Quarterly Access Review
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 9 1 1,4,7,10 *' # First day of each quarter
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
generate-review:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Generate access reports
|
||||
env:
|
||||
AWS_ACCESS_KEY_ID: ${{ secrets.AUDIT_AWS_KEY }}
|
||||
AWS_SECRET_ACCESS_KEY: ${{ secrets.AUDIT_AWS_SECRET }}
|
||||
OKTA_DOMAIN: ${{ secrets.OKTA_DOMAIN }}
|
||||
OKTA_API_TOKEN: ${{ secrets.OKTA_API_TOKEN }}
|
||||
run: |
|
||||
bash scripts/aws-iam-review.sh
|
||||
bash scripts/github-access-review.sh
|
||||
bash scripts/okta-access-review.sh
|
||||
|
||||
- name: Create review issue
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
run: |
|
||||
QUARTER="Q$(( ($(date +%-m) - 1) / 3 + 1 )) $(date +%Y)"
|
||||
MFA_MISSING=$(wc -l < access-review/$(date +%Y-%m)/users-without-mfa.txt)
|
||||
INACTIVE=$(wc -l < access-review/$(date +%Y-%m)/inactive-users.csv)
|
||||
STALE_KEYS=$(wc -l < access-review/$(date +%Y-%m)/stale-access-keys.csv)
|
||||
|
||||
gh issue create \
|
||||
--title "Access Review - $QUARTER" \
|
||||
--label "compliance,access-review" \
|
||||
--body "## Quarterly Access Review - $QUARTER
|
||||
|
||||
### Summary
|
||||
- Users without MFA: **$MFA_MISSING**
|
||||
- Inactive users (90+ days): **$INACTIVE**
|
||||
- Stale access keys: **$STALE_KEYS**
|
||||
|
||||
### Required Actions
|
||||
- [ ] Review and disable inactive users
|
||||
- [ ] Enforce MFA for non-compliant users
|
||||
- [ ] Rotate or deactivate stale access keys
|
||||
- [ ] Review admin/privileged access assignments
|
||||
- [ ] Review outside collaborators on GitHub
|
||||
- [ ] Certify remaining access is appropriate
|
||||
- [ ] Document exceptions with justification
|
||||
|
||||
### Deadline
|
||||
Complete within 30 days."
|
||||
|
||||
- name: Upload reports as artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: access-review-reports
|
||||
path: access-review/
|
||||
retention-days: 365
|
||||
```
|
||||
|
||||
## Access Review Checklist
|
||||
|
||||
```yaml
|
||||
access_review_checklist:
|
||||
preparation:
|
||||
- [ ] Define scope (systems, user populations, review period)
|
||||
- [ ] Assign review owners for each system
|
||||
- [ ] Extract current access data from all identity sources
|
||||
- [ ] Correlate identities across platforms via SSO mapping
|
||||
- [ ] Generate review packages for each manager
|
||||
|
||||
execution:
|
||||
- [ ] Managers notified with review assignments and deadline
|
||||
- [ ] Privileged access reviewed first (admin, root, service accounts)
|
||||
- [ ] Each user's access certified (approve, modify, or revoke)
|
||||
- [ ] Inactive accounts flagged for disable/removal
|
||||
- [ ] Stale credentials (keys, tokens) flagged for rotation
|
||||
- [ ] Outside collaborators and contractors verified
|
||||
- [ ] Service account ownership confirmed
|
||||
|
||||
remediation:
|
||||
- [ ] Revocations executed within SLA (5 business days)
|
||||
- [ ] Access modifications completed within SLA (10 business days)
|
||||
- [ ] Exceptions documented with business justification
|
||||
- [ ] Exception approvals recorded from security team
|
||||
- [ ] Changes verified in target systems
|
||||
|
||||
reporting:
|
||||
- [ ] Review completion rate documented (target: 100%)
|
||||
- [ ] Non-response escalations documented
|
||||
- [ ] Remediation actions summarized
|
||||
- [ ] Exception register updated
|
||||
- [ ] Evidence archived for audit (retained 3+ years)
|
||||
- [ ] Metrics compared to prior review cycle
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Quarterly reviews minimum
|
||||
- Risk-based frequency
|
||||
- Manager attestation
|
||||
- Automated revocation
|
||||
- Audit trail maintenance
|
||||
- Automate access data extraction to eliminate manual data gathering and reduce errors
|
||||
- Integrate access review with HR systems to automatically flag accounts for departed employees
|
||||
- Use risk-based review frequency: privileged access quarterly, standard access semi-annually
|
||||
- Provide managers with clear context: show last login date, permissions, and role to inform decisions
|
||||
- Set firm deadlines with escalation for non-response (no certification = automatic revocation)
|
||||
- Detect and eliminate orphaned accounts from contractors, former employees, and decommissioned services
|
||||
- Review service accounts and API keys alongside human accounts to prevent credential sprawl
|
||||
- Document all exceptions with business justification, approver, and expiration date
|
||||
- Track review metrics over time: completion rates, revocation rates, time to remediate
|
||||
- Archive all access review evidence for a minimum of 3 years for audit purposes
|
||||
|
||||
@@ -9,66 +9,495 @@ metadata:
|
||||
|
||||
# Asset Inventory
|
||||
|
||||
Maintain comprehensive IT asset tracking.
|
||||
Maintain comprehensive IT asset inventory using automated discovery, AWS Config rules, cloud asset discovery scripts, CMDB integration, and tagging enforcement for compliance and operational visibility.
|
||||
|
||||
## Asset Categories
|
||||
## When to Use
|
||||
|
||||
- Building or maintaining an IT asset inventory for compliance frameworks (ISO 27001, SOC 2, FedRAMP)
|
||||
- Implementing automated cloud resource discovery across accounts and regions
|
||||
- Enforcing tagging standards for cost allocation, ownership, and data classification
|
||||
- Integrating asset data with a CMDB for operational workflows
|
||||
- Preparing for audits that require a complete system component inventory
|
||||
|
||||
## Asset Categories and Schema
|
||||
|
||||
```yaml
|
||||
asset_types:
|
||||
hardware:
|
||||
- Servers
|
||||
- Network devices
|
||||
- Endpoints
|
||||
|
||||
software:
|
||||
- Applications
|
||||
- Operating systems
|
||||
- Licenses
|
||||
|
||||
cloud:
|
||||
- Compute instances
|
||||
- Storage
|
||||
- Databases
|
||||
|
||||
data:
|
||||
- Databases
|
||||
- File shares
|
||||
- Backups
|
||||
asset_categories:
|
||||
compute:
|
||||
cloud:
|
||||
- EC2 instances / Azure VMs / GCE instances
|
||||
- Lambda functions / Azure Functions / Cloud Functions
|
||||
- ECS/EKS clusters and tasks
|
||||
- Container images in registries
|
||||
on_premise:
|
||||
- Physical servers
|
||||
- Virtual machines (VMware, Hyper-V)
|
||||
|
||||
storage:
|
||||
- S3 buckets / Azure Storage / GCS buckets
|
||||
- EBS volumes / Managed Disks / Persistent Disks
|
||||
- RDS instances / Azure SQL / Cloud SQL
|
||||
- DynamoDB tables / Cosmos DB / Firestore
|
||||
- EFS / Azure Files / Filestore
|
||||
|
||||
network:
|
||||
- VPCs / VNets / VPC Networks
|
||||
- Load balancers (ALB, NLB, Azure LB, GCP LB)
|
||||
- DNS zones and records
|
||||
- VPN gateways and connections
|
||||
- CDN distributions
|
||||
|
||||
security:
|
||||
- IAM users, roles, and policies
|
||||
- KMS keys / Key Vault keys
|
||||
- Certificates (ACM, Key Vault, Certificate Manager)
|
||||
- Security groups / NSGs / Firewall rules
|
||||
- WAF configurations
|
||||
|
||||
applications:
|
||||
- SaaS subscriptions
|
||||
- Licensed software
|
||||
- Custom applications
|
||||
- APIs and integrations
|
||||
|
||||
endpoints:
|
||||
- Laptops and desktops
|
||||
- Mobile devices
|
||||
- Printers and peripherals
|
||||
|
||||
asset_record_schema:
|
||||
required_fields:
|
||||
asset_id: "Unique identifier (auto-generated)"
|
||||
name: "Human-readable name"
|
||||
type: "Category from above taxonomy"
|
||||
provider: "AWS / Azure / GCP / On-Premise / SaaS"
|
||||
account_or_subscription: "Cloud account ID"
|
||||
region: "Deployment region/location"
|
||||
owner: "Team or individual responsible"
|
||||
data_classification: "Public / Internal / Confidential / Restricted"
|
||||
environment: "Production / Staging / Development / Sandbox"
|
||||
status: "Active / Decommissioning / Retired"
|
||||
created_date: "When the asset was provisioned"
|
||||
last_seen: "Last automated discovery timestamp"
|
||||
|
||||
optional_fields:
|
||||
cost_center: "For cost allocation"
|
||||
compliance_scope: "SOC2 / HIPAA / PCI / None"
|
||||
backup_policy: "Backup schedule reference"
|
||||
dr_tier: "Critical / Essential / Standard / Non-essential"
|
||||
expiration_date: "For time-limited resources"
|
||||
tags: "Key-value pairs from cloud provider"
|
||||
dependencies: "Upstream and downstream services"
|
||||
```
|
||||
|
||||
## AWS Inventory
|
||||
## AWS Resource Discovery Script
|
||||
|
||||
```bash
|
||||
# List all resources
|
||||
aws resourcegroupstaggingapi get-resources
|
||||
#!/usr/bin/env bash
|
||||
# aws-asset-discovery.sh - Discover and inventory all AWS resources
|
||||
|
||||
# EC2 instances
|
||||
aws ec2 describe-instances --query 'Reservations[*].Instances[*].[InstanceId,InstanceType,State.Name]'
|
||||
OUTPUT_DIR="./asset-inventory/aws/$(date +%Y-%m-%d)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
ACCOUNT_ID=$(aws sts get-caller-identity --query Account --output text)
|
||||
|
||||
# AWS Config
|
||||
aws configservice describe-configuration-recorders
|
||||
echo "=== AWS Asset Discovery for Account $ACCOUNT_ID ==="
|
||||
|
||||
# EC2 Instances
|
||||
echo "--- EC2 Instances ---"
|
||||
aws ec2 describe-instances \
|
||||
--query 'Reservations[*].Instances[*].{
|
||||
InstanceId:InstanceId,
|
||||
Type:InstanceType,
|
||||
State:State.Name,
|
||||
AZ:Placement.AvailabilityZone,
|
||||
VpcId:VpcId,
|
||||
PrivateIP:PrivateIpAddress,
|
||||
PublicIP:PublicIpAddress,
|
||||
LaunchTime:LaunchTime,
|
||||
Name:Tags[?Key==`Name`].Value|[0],
|
||||
Owner:Tags[?Key==`Owner`].Value|[0],
|
||||
Environment:Tags[?Key==`Environment`].Value|[0]
|
||||
}' --output json | jq 'flatten' > "$OUTPUT_DIR/ec2-instances.json"
|
||||
|
||||
# RDS Databases
|
||||
echo "--- RDS Instances ---"
|
||||
aws rds describe-db-instances \
|
||||
--query 'DBInstances[*].{
|
||||
DBInstanceId:DBInstanceIdentifier,
|
||||
Engine:Engine,
|
||||
EngineVersion:EngineVersion,
|
||||
Class:DBInstanceClass,
|
||||
Status:DBInstanceStatus,
|
||||
MultiAZ:MultiAZ,
|
||||
Encrypted:StorageEncrypted,
|
||||
Endpoint:Endpoint.Address,
|
||||
BackupRetention:BackupRetentionPeriod
|
||||
}' --output json > "$OUTPUT_DIR/rds-instances.json"
|
||||
|
||||
# S3 Buckets
|
||||
echo "--- S3 Buckets ---"
|
||||
aws s3api list-buckets --query 'Buckets[*].{Name:Name,Created:CreationDate}' --output json | \
|
||||
jq -c '.[]' | while read -r bucket; do
|
||||
name=$(echo "$bucket" | jq -r '.Name')
|
||||
region=$(aws s3api get-bucket-location --bucket "$name" --query 'LocationConstraint' --output text 2>/dev/null)
|
||||
encryption=$(aws s3api get-bucket-encryption --bucket "$name" 2>/dev/null | jq -r '.ServerSideEncryptionConfiguration.Rules[0].ApplyServerSideEncryptionByDefault.SSEAlgorithm' 2>/dev/null)
|
||||
versioning=$(aws s3api get-bucket-versioning --bucket "$name" --query 'Status' --output text 2>/dev/null)
|
||||
echo "{\"Name\":\"$name\",\"Region\":\"${region:-us-east-1}\",\"Encryption\":\"${encryption:-none}\",\"Versioning\":\"${versioning:-Disabled}\"}"
|
||||
done | jq -s '.' > "$OUTPUT_DIR/s3-buckets.json"
|
||||
|
||||
# Lambda Functions
|
||||
echo "--- Lambda Functions ---"
|
||||
aws lambda list-functions \
|
||||
--query 'Functions[*].{
|
||||
Name:FunctionName,
|
||||
Runtime:Runtime,
|
||||
MemorySize:MemorySize,
|
||||
Timeout:Timeout,
|
||||
LastModified:LastModified,
|
||||
CodeSize:CodeSize
|
||||
}' --output json > "$OUTPUT_DIR/lambda-functions.json"
|
||||
|
||||
# VPCs and Security Groups
|
||||
echo "--- VPCs ---"
|
||||
aws ec2 describe-vpcs \
|
||||
--query 'Vpcs[*].{
|
||||
VpcId:VpcId,
|
||||
CidrBlock:CidrBlock,
|
||||
State:State,
|
||||
IsDefault:IsDefault,
|
||||
Name:Tags[?Key==`Name`].Value|[0]
|
||||
}' --output json > "$OUTPUT_DIR/vpcs.json"
|
||||
|
||||
echo "--- Security Groups ---"
|
||||
aws ec2 describe-security-groups \
|
||||
--query 'SecurityGroups[*].{
|
||||
GroupId:GroupId,
|
||||
GroupName:GroupName,
|
||||
VpcId:VpcId,
|
||||
Description:Description,
|
||||
IngressRuleCount:length(IpPermissions),
|
||||
EgressRuleCount:length(IpPermissionsEgress)
|
||||
}' --output json > "$OUTPUT_DIR/security-groups.json"
|
||||
|
||||
# IAM Users and Roles
|
||||
echo "--- IAM Users ---"
|
||||
aws iam list-users \
|
||||
--query 'Users[*].{UserName:UserName,Created:CreateDate,PasswordLastUsed:PasswordLastUsed}' \
|
||||
--output json > "$OUTPUT_DIR/iam-users.json"
|
||||
|
||||
echo "--- IAM Roles ---"
|
||||
aws iam list-roles \
|
||||
--query 'Roles[*].{RoleName:RoleName,Created:CreateDate,LastUsed:RoleLastUsed.LastUsedDate}' \
|
||||
--output json > "$OUTPUT_DIR/iam-roles.json"
|
||||
|
||||
# EKS Clusters
|
||||
echo "--- EKS Clusters ---"
|
||||
aws eks list-clusters --query 'clusters' --output json | jq -r '.[]' | while read -r cluster; do
|
||||
aws eks describe-cluster --name "$cluster" \
|
||||
--query 'cluster.{Name:name,Version:version,Status:status,Endpoint:endpoint,Created:createdAt}'
|
||||
done | jq -s '.' > "$OUTPUT_DIR/eks-clusters.json" 2>/dev/null
|
||||
|
||||
# KMS Keys
|
||||
echo "--- KMS Keys ---"
|
||||
aws kms list-keys --query 'Keys[*].KeyId' --output text | tr '\t' '\n' | while read -r key_id; do
|
||||
aws kms describe-key --key-id "$key_id" \
|
||||
--query 'KeyMetadata.{KeyId:KeyId,Description:Description,State:KeyState,Created:CreationDate,Manager:KeyManager}' 2>/dev/null
|
||||
done | jq -s '.' > "$OUTPUT_DIR/kms-keys.json"
|
||||
|
||||
# Generate summary
|
||||
echo "=== Inventory Summary ==="
|
||||
echo "EC2 Instances: $(jq 'length' "$OUTPUT_DIR/ec2-instances.json")"
|
||||
echo "RDS Instances: $(jq 'length' "$OUTPUT_DIR/rds-instances.json")"
|
||||
echo "S3 Buckets: $(jq 'length' "$OUTPUT_DIR/s3-buckets.json")"
|
||||
echo "Lambda Functions: $(jq 'length' "$OUTPUT_DIR/lambda-functions.json")"
|
||||
echo "VPCs: $(jq 'length' "$OUTPUT_DIR/vpcs.json")"
|
||||
echo "Security Groups: $(jq 'length' "$OUTPUT_DIR/security-groups.json")"
|
||||
echo "IAM Users: $(jq 'length' "$OUTPUT_DIR/iam-users.json")"
|
||||
echo "IAM Roles: $(jq 'length' "$OUTPUT_DIR/iam-roles.json")"
|
||||
|
||||
echo "Inventory saved to $OUTPUT_DIR"
|
||||
```
|
||||
|
||||
## Asset Database Schema
|
||||
## AWS Config Rules for Inventory Compliance
|
||||
|
||||
```bash
|
||||
# Enable AWS Config recorder
|
||||
aws configservice put-configuration-recorder \
|
||||
--configuration-recorder name=default,roleARN=arn:aws:iam::123456789012:role/aws-config-role \
|
||||
--recording-group allSupported=true,includeGlobalResourceTypes=true
|
||||
|
||||
# Start recording
|
||||
aws configservice start-configuration-recorder --configuration-recorder-name default
|
||||
|
||||
# Enable required-tags Config rule
|
||||
aws configservice put-config-rule --config-rule '{
|
||||
"ConfigRuleName": "required-tags",
|
||||
"Source": {
|
||||
"Owner": "AWS",
|
||||
"SourceIdentifier": "REQUIRED_TAGS"
|
||||
},
|
||||
"InputParameters": "{\"tag1Key\":\"Owner\",\"tag2Key\":\"Environment\",\"tag3Key\":\"CostCenter\",\"tag4Key\":\"DataClassification\"}",
|
||||
"Scope": {
|
||||
"ComplianceResourceTypes": [
|
||||
"AWS::EC2::Instance",
|
||||
"AWS::RDS::DBInstance",
|
||||
"AWS::S3::Bucket",
|
||||
"AWS::Lambda::Function"
|
||||
]
|
||||
}
|
||||
}'
|
||||
|
||||
# Config rule for encryption compliance
|
||||
aws configservice put-config-rule --config-rule '{
|
||||
"ConfigRuleName": "encrypted-volumes",
|
||||
"Source": {
|
||||
"Owner": "AWS",
|
||||
"SourceIdentifier": "ENCRYPTED_VOLUMES"
|
||||
}
|
||||
}'
|
||||
|
||||
aws configservice put-config-rule --config-rule '{
|
||||
"ConfigRuleName": "rds-storage-encrypted",
|
||||
"Source": {
|
||||
"Owner": "AWS",
|
||||
"SourceIdentifier": "RDS_STORAGE_ENCRYPTED"
|
||||
}
|
||||
}'
|
||||
|
||||
aws configservice put-config-rule --config-rule '{
|
||||
"ConfigRuleName": "s3-bucket-server-side-encryption-enabled",
|
||||
"Source": {
|
||||
"Owner": "AWS",
|
||||
"SourceIdentifier": "S3_BUCKET_SERVER_SIDE_ENCRYPTION_ENABLED"
|
||||
}
|
||||
}'
|
||||
|
||||
# Query AWS Config for all resources of a type
|
||||
aws configservice list-discovered-resources --resource-type AWS::EC2::Instance
|
||||
aws configservice list-discovered-resources --resource-type AWS::RDS::DBInstance
|
||||
|
||||
# Advanced query with AWS Config SQL
|
||||
aws configservice select-resource-config \
|
||||
--expression "SELECT resourceId, resourceType, tags, configuration.instanceType
|
||||
WHERE resourceType = 'AWS::EC2::Instance'
|
||||
AND tags.tag('Environment') = 'production'"
|
||||
|
||||
# Get compliance summary
|
||||
aws configservice get-compliance-summary-by-config-rule
|
||||
aws configservice get-compliance-summary-by-resource-type
|
||||
```
|
||||
|
||||
## Tagging Enforcement
|
||||
|
||||
```yaml
|
||||
asset:
|
||||
id: unique identifier
|
||||
name: display name
|
||||
type: hardware/software/cloud
|
||||
owner: responsible team
|
||||
classification: public/internal/confidential
|
||||
location: physical/cloud location
|
||||
status: active/retired/decommissioned
|
||||
created: timestamp
|
||||
updated: timestamp
|
||||
tags: []
|
||||
# AWS Tag Policy (applied via AWS Organizations)
|
||||
tag_policy:
|
||||
tags:
|
||||
Owner:
|
||||
tag_key:
|
||||
"@@assign": "Owner"
|
||||
enforced_for:
|
||||
"@@assign":
|
||||
- "ec2:instance"
|
||||
- "rds:db"
|
||||
- "s3:bucket"
|
||||
- "lambda:function"
|
||||
|
||||
Environment:
|
||||
tag_key:
|
||||
"@@assign": "Environment"
|
||||
tag_value:
|
||||
"@@assign":
|
||||
- "production"
|
||||
- "staging"
|
||||
- "development"
|
||||
- "sandbox"
|
||||
|
||||
DataClassification:
|
||||
tag_key:
|
||||
"@@assign": "DataClassification"
|
||||
tag_value:
|
||||
"@@assign":
|
||||
- "public"
|
||||
- "internal"
|
||||
- "confidential"
|
||||
- "restricted"
|
||||
|
||||
CostCenter:
|
||||
tag_key:
|
||||
"@@assign": "CostCenter"
|
||||
```
|
||||
|
||||
```hcl
|
||||
# Terraform - Enforce tags on all resources with default_tags
|
||||
provider "aws" {
|
||||
region = "us-east-1"
|
||||
|
||||
default_tags {
|
||||
tags = {
|
||||
ManagedBy = "terraform"
|
||||
Environment = var.environment
|
||||
Owner = var.team_name
|
||||
CostCenter = var.cost_center
|
||||
DataClassification = var.data_classification
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Multi-Cloud Discovery
|
||||
|
||||
```bash
|
||||
#!/usr/bin/env bash
|
||||
# multi-cloud-discovery.sh - Discover assets across AWS, Azure, and GCP
|
||||
|
||||
OUTPUT_DIR="./asset-inventory/multi-cloud/$(date +%Y-%m-%d)"
|
||||
mkdir -p "$OUTPUT_DIR"
|
||||
|
||||
echo "=== Multi-Cloud Asset Discovery ==="
|
||||
|
||||
# AWS - using Resource Groups Tagging API
|
||||
echo "--- AWS Resources ---"
|
||||
aws resourcegroupstaggingapi get-resources \
|
||||
--query 'ResourceTagMappingList[*].{ARN:ResourceARN,Tags:Tags}' \
|
||||
--output json > "$OUTPUT_DIR/aws-all-resources.json"
|
||||
echo "AWS resources: $(jq 'length' "$OUTPUT_DIR/aws-all-resources.json")"
|
||||
|
||||
# Azure - using Resource Graph
|
||||
echo "--- Azure Resources ---"
|
||||
az graph query -q "Resources | project name, type, location, resourceGroup, subscriptionId, tags" \
|
||||
--output json > "$OUTPUT_DIR/azure-all-resources.json" 2>/dev/null
|
||||
|
||||
# GCP - using Cloud Asset Inventory
|
||||
echo "--- GCP Resources ---"
|
||||
gcloud asset search-all-resources \
|
||||
--scope="organizations/ORG_ID" \
|
||||
--format=json > "$OUTPUT_DIR/gcp-all-resources.json" 2>/dev/null
|
||||
|
||||
# Find untagged resources
|
||||
echo "=== Untagged Resources ==="
|
||||
jq '[.[] | select(.Tags == null or .Tags == [])] | length' "$OUTPUT_DIR/aws-all-resources.json"
|
||||
|
||||
echo "Discovery complete. Results in $OUTPUT_DIR"
|
||||
```
|
||||
|
||||
## CMDB Integration
|
||||
|
||||
```python
|
||||
"""
|
||||
CMDB sync script - Normalize cloud assets and push to CMDB API.
|
||||
"""
|
||||
import json
|
||||
import requests
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
class CMDBSync:
|
||||
def __init__(self, cmdb_url, api_token):
|
||||
self.cmdb_url = cmdb_url
|
||||
self.headers = {
|
||||
"Authorization": f"Bearer {api_token}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
def normalize_aws_instance(self, instance):
|
||||
"""Convert AWS EC2 instance to common asset schema."""
|
||||
tags = {t["Key"]: t["Value"] for t in (instance.get("Tags") or [])}
|
||||
return {
|
||||
"asset_id": f"aws:{instance['InstanceId']}",
|
||||
"name": tags.get("Name", instance["InstanceId"]),
|
||||
"type": "compute",
|
||||
"provider": "aws",
|
||||
"region": instance.get("AZ", "unknown")[:-1],
|
||||
"configuration": {
|
||||
"instance_type": instance.get("Type"),
|
||||
"state": instance.get("State"),
|
||||
"vpc_id": instance.get("VpcId"),
|
||||
},
|
||||
"owner": tags.get("Owner", "unassigned"),
|
||||
"environment": tags.get("Environment", "unknown"),
|
||||
"data_classification": tags.get("DataClassification", "unknown"),
|
||||
"status": "active" if instance.get("State") == "running" else "stopped",
|
||||
"last_seen": datetime.now(timezone.utc).isoformat(),
|
||||
}
|
||||
|
||||
def sync_assets(self, assets):
|
||||
"""Push normalized assets to CMDB."""
|
||||
results = {"created": 0, "updated": 0, "errors": 0}
|
||||
for asset in assets:
|
||||
try:
|
||||
resp = requests.get(
|
||||
f"{self.cmdb_url}/assets/{asset['asset_id']}",
|
||||
headers=self.headers,
|
||||
)
|
||||
if resp.status_code == 200:
|
||||
requests.put(
|
||||
f"{self.cmdb_url}/assets/{asset['asset_id']}",
|
||||
headers=self.headers,
|
||||
json=asset,
|
||||
)
|
||||
results["updated"] += 1
|
||||
else:
|
||||
requests.post(
|
||||
f"{self.cmdb_url}/assets",
|
||||
headers=self.headers,
|
||||
json=asset,
|
||||
)
|
||||
results["created"] += 1
|
||||
except Exception:
|
||||
results["errors"] += 1
|
||||
return results
|
||||
```
|
||||
|
||||
## Asset Inventory Checklist
|
||||
|
||||
```yaml
|
||||
asset_inventory_checklist:
|
||||
discovery:
|
||||
- [ ] Automated discovery scripts running for all cloud accounts
|
||||
- [ ] Discovery covers all resource types (compute, storage, network, IAM)
|
||||
- [ ] Multi-region discovery enabled
|
||||
- [ ] On-premise assets cataloged
|
||||
- [ ] SaaS subscriptions inventoried
|
||||
- [ ] Discovery runs daily (minimum weekly)
|
||||
|
||||
classification:
|
||||
- [ ] Required tags defined (Owner, Environment, DataClassification, CostCenter)
|
||||
- [ ] Tag enforcement via AWS Organizations tag policies
|
||||
- [ ] Tag enforcement via Terraform default_tags
|
||||
- [ ] Tag enforcement via CI/CD policy checks (Checkov, OPA)
|
||||
- [ ] Untagged resource reports generated and tracked
|
||||
|
||||
configuration_management:
|
||||
- [ ] AWS Config enabled in all regions
|
||||
- [ ] Config rules enforce encryption, tagging, and security baselines
|
||||
- [ ] Configuration compliance summary reviewed weekly
|
||||
- [ ] Drift detection enabled for IaC-managed resources
|
||||
|
||||
cmdb:
|
||||
- [ ] CMDB sync automated from cloud discovery
|
||||
- [ ] Common schema defined across all providers
|
||||
- [ ] Reconciliation process identifies orphaned records
|
||||
- [ ] New resources auto-assigned default owner
|
||||
- [ ] Asset lifecycle tracked (created, active, decommissioning, retired)
|
||||
|
||||
governance:
|
||||
- [ ] Asset owners assigned and current
|
||||
- [ ] Quarterly inventory reconciliation conducted
|
||||
- [ ] Compliance scope tagging accurate (SOC2, HIPAA, PCI)
|
||||
- [ ] Asset inventory available for auditor review
|
||||
- [ ] Decommissioned assets tracked for data retention compliance
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Automated discovery
|
||||
- Regular reconciliation
|
||||
- Owner assignment
|
||||
- Classification tagging
|
||||
- Lifecycle tracking
|
||||
- Automate discovery rather than relying on manual inventory: cloud environments change too fast for spreadsheets
|
||||
- Use AWS Config, Azure Resource Graph, and GCP Cloud Asset Inventory as authoritative data sources
|
||||
- Enforce tagging at provisioning time through IaC defaults and policy-as-code guardrails
|
||||
- Assign every asset an owner: unowned resources become security and cost liabilities
|
||||
- Reconcile inventory regularly and investigate orphaned assets (CMDB record with no real resource and vice versa)
|
||||
- Track data classification as a mandatory tag to support compliance scoping decisions
|
||||
- Maintain asset lifecycle states to distinguish active resources from those being decommissioned
|
||||
- Integrate asset inventory with incident response to quickly identify affected systems during investigations
|
||||
- Export inventory data for compliance audits in accessible formats (CSV, JSON)
|
||||
- Review untagged and unclassified resource reports weekly to maintain inventory quality
|
||||
|
||||
@@ -9,73 +9,497 @@ metadata:
|
||||
|
||||
# Change Management
|
||||
|
||||
Implement structured change management processes.
|
||||
Implement structured change management processes covering change classification, CAB workflows, emergency change procedures, and automation for compliance with SOC 2, ITIL, and regulatory frameworks.
|
||||
|
||||
## Change Process
|
||||
## When to Use
|
||||
|
||||
```yaml
|
||||
change_workflow:
|
||||
1_request:
|
||||
- Change description
|
||||
- Risk assessment
|
||||
- Rollback plan
|
||||
- Testing evidence
|
||||
|
||||
2_review:
|
||||
- Technical review
|
||||
- Security review
|
||||
- CAB approval (if high risk)
|
||||
|
||||
3_schedule:
|
||||
- Change window
|
||||
- Communication
|
||||
- Resource allocation
|
||||
|
||||
4_implement:
|
||||
- Execute change
|
||||
- Verify success
|
||||
- Update documentation
|
||||
|
||||
5_review:
|
||||
- Post-implementation review
|
||||
- Lessons learned
|
||||
```
|
||||
- Establishing change management processes for production environments
|
||||
- Implementing change advisory board (CAB) workflows
|
||||
- Defining change classification and approval requirements
|
||||
- Configuring automated change tracking in CI/CD pipelines
|
||||
- Handling emergency changes with proper controls and documentation
|
||||
|
||||
## Change Classification
|
||||
|
||||
| Type | Risk | Approval | Example |
|
||||
|------|------|----------|---------|
|
||||
| Standard | Low | Pre-approved | Patching |
|
||||
| Normal | Medium | Manager | Config change |
|
||||
| Emergency | Variable | Expedited | Security fix |
|
||||
```yaml
|
||||
change_types:
|
||||
standard:
|
||||
risk: Low
|
||||
approval: Pre-approved (no per-change approval needed)
|
||||
lead_time: None (within maintenance window)
|
||||
examples:
|
||||
- Routine patching within tested patch sets
|
||||
- Certificate rotation with established procedure
|
||||
- Scaling operations (adding/removing instances within limits)
|
||||
- Pre-approved configuration changes
|
||||
- Log rotation and archival
|
||||
requirements:
|
||||
- Change must match an approved Standard Change template
|
||||
- Automated testing must pass
|
||||
- Documented rollback procedure exists
|
||||
- Within defined maintenance window
|
||||
|
||||
## Pull Request Template
|
||||
normal_low:
|
||||
risk: Low
|
||||
approval: Peer review (1 approver)
|
||||
lead_time: 2 business days
|
||||
examples:
|
||||
- Non-critical configuration changes
|
||||
- Feature flag toggles
|
||||
- Documentation updates to production systems
|
||||
- Adding monitoring dashboards or alerts
|
||||
|
||||
normal_medium:
|
||||
risk: Medium
|
||||
approval: Team lead + peer review (2 approvers)
|
||||
lead_time: 5 business days
|
||||
examples:
|
||||
- Application deployments with new features
|
||||
- Database schema changes (non-breaking)
|
||||
- Network rule modifications
|
||||
- Integration endpoint changes
|
||||
- Dependency version upgrades
|
||||
|
||||
normal_high:
|
||||
risk: High
|
||||
approval: CAB review required
|
||||
lead_time: 10 business days
|
||||
examples:
|
||||
- Infrastructure migrations
|
||||
- Breaking database schema changes
|
||||
- Major version upgrades (OS, runtime, database engine)
|
||||
- Changes to authentication or authorization systems
|
||||
- Multi-service coordinated deployments
|
||||
- Changes affecting data processing or compliance controls
|
||||
|
||||
emergency:
|
||||
risk: Variable
|
||||
approval: Emergency CAB (minimum 2 approvers from on-call)
|
||||
lead_time: None (immediate implementation)
|
||||
examples:
|
||||
- Security vulnerability remediation (active exploitation)
|
||||
- Production outage resolution
|
||||
- Data integrity emergency fixes
|
||||
- Regulatory compliance deadline fixes
|
||||
requirements:
|
||||
- Retroactive full documentation within 48 hours
|
||||
- Post-implementation review required
|
||||
- CAB retroactive review at next meeting
|
||||
```
|
||||
|
||||
## Change Request Template
|
||||
|
||||
```yaml
|
||||
change_request:
|
||||
metadata:
|
||||
id: "CR-YYYY-NNNN"
|
||||
title: ""
|
||||
requestor: ""
|
||||
date_submitted: ""
|
||||
target_date: ""
|
||||
change_type: "" # standard | normal_low | normal_medium | normal_high | emergency
|
||||
|
||||
description:
|
||||
summary: "Brief description of the change"
|
||||
detailed_description: "Full technical details of what will change"
|
||||
business_justification: "Why this change is needed"
|
||||
affected_systems: []
|
||||
affected_services: []
|
||||
affected_users: "Description of user impact"
|
||||
|
||||
risk_assessment:
|
||||
risk_level: "" # low | medium | high
|
||||
impact_if_failed: "What happens if the change fails"
|
||||
likelihood_of_failure: "" # low | medium | high
|
||||
risk_mitigation: "Steps to reduce risk"
|
||||
dependencies: "Other systems or changes this depends on"
|
||||
|
||||
implementation:
|
||||
change_window:
|
||||
start: ""
|
||||
end: ""
|
||||
maintenance_window: true
|
||||
implementation_steps:
|
||||
- step: "Step 1 description"
|
||||
responsible: "Person/team"
|
||||
estimated_duration: "X minutes"
|
||||
- step: "Step 2 description"
|
||||
responsible: "Person/team"
|
||||
estimated_duration: "X minutes"
|
||||
|
||||
testing:
|
||||
pre_change_testing:
|
||||
- "Unit tests pass"
|
||||
- "Integration tests pass"
|
||||
- "Staging deployment verified"
|
||||
post_change_verification:
|
||||
- "Health check endpoints responding"
|
||||
- "Key transactions processing successfully"
|
||||
- "No error rate increase in monitoring"
|
||||
- "Performance metrics within baseline"
|
||||
|
||||
rollback:
|
||||
rollback_plan: "Detailed steps to revert the change"
|
||||
rollback_trigger: "Conditions that trigger rollback"
|
||||
rollback_estimated_time: "X minutes"
|
||||
rollback_steps:
|
||||
- "Step 1: Revert deployment to previous version"
|
||||
- "Step 2: Verify rollback successful"
|
||||
- "Step 3: Notify stakeholders"
|
||||
data_rollback: "Describe any data migration rollback needed"
|
||||
|
||||
communication:
|
||||
stakeholders_notified: []
|
||||
notification_sent_date: ""
|
||||
status_page_update: true
|
||||
customer_notification_required: false
|
||||
|
||||
approvals:
|
||||
technical_reviewer: ""
|
||||
technical_approval_date: ""
|
||||
security_reviewer: ""
|
||||
security_approval_date: ""
|
||||
cab_approval_date: ""
|
||||
cab_notes: ""
|
||||
|
||||
closure:
|
||||
implementation_date: ""
|
||||
implementation_result: "" # success | partial | failed | rolled_back
|
||||
post_implementation_review: ""
|
||||
lessons_learned: ""
|
||||
follow_up_actions: []
|
||||
```
|
||||
|
||||
## CAB Workflow
|
||||
|
||||
```yaml
|
||||
cab_workflow:
|
||||
meeting_schedule:
|
||||
regular_cab: "Weekly, Thursday 2:00 PM"
|
||||
emergency_cab: "On-demand, minimum 2 members required"
|
||||
|
||||
cab_members:
|
||||
permanent:
|
||||
- Engineering Manager (Chair)
|
||||
- Security Team Representative
|
||||
- Infrastructure/SRE Lead
|
||||
- Release Manager
|
||||
advisory:
|
||||
- Business stakeholder (invited per change)
|
||||
- Database administrator (for DB changes)
|
||||
- Network engineer (for network changes)
|
||||
|
||||
agenda:
|
||||
1: "Review emergency changes from prior week"
|
||||
2: "Review high-risk change requests for upcoming window"
|
||||
3: "Review failed changes and lessons learned"
|
||||
4: "Discuss upcoming change freeze periods"
|
||||
5: "Review change metrics and trends"
|
||||
|
||||
decision_criteria:
|
||||
approve_when:
|
||||
- Risk assessment is complete and accurate
|
||||
- Testing evidence is provided
|
||||
- Rollback plan is documented and feasible
|
||||
- Change window is appropriate
|
||||
- Required approvals obtained
|
||||
- No conflicts with other scheduled changes
|
||||
request_changes_when:
|
||||
- Rollback plan is missing or incomplete
|
||||
- Testing is insufficient for the risk level
|
||||
- Impact assessment needs clarification
|
||||
- Change conflicts with another scheduled change
|
||||
deny_when:
|
||||
- Risk is unacceptable without mitigation
|
||||
- Change window conflicts with freeze period
|
||||
- Dependencies are not resolved
|
||||
- Compliance concerns are unaddressed
|
||||
```
|
||||
|
||||
## Emergency Change Procedure
|
||||
|
||||
```yaml
|
||||
emergency_change_process:
|
||||
definition: "A change required to restore service or prevent imminent security compromise"
|
||||
|
||||
step_1_declare:
|
||||
actions:
|
||||
- On-call engineer identifies need for emergency change
|
||||
- Incident commander approves emergency classification
|
||||
- Minimum 2 approvers from emergency CAB roster contacted
|
||||
- Document initial justification in incident channel
|
||||
|
||||
step_2_approve:
|
||||
approval_method:
|
||||
- Slack/Teams approval with screenshots preserved
|
||||
- Verbal approval over bridge call (documented in notes)
|
||||
- Emergency approvers can be any 2 of the following roles:
|
||||
- Engineering Manager
|
||||
- SRE/Infrastructure Lead
|
||||
- Security Team Lead
|
||||
- VP of Engineering
|
||||
timeout: "If no response in 15 minutes, escalate to next tier"
|
||||
|
||||
step_3_implement:
|
||||
actions:
|
||||
- Implement the minimum change needed to resolve the issue
|
||||
- Record all actions taken with timestamps
|
||||
- Monitor for successful resolution
|
||||
- Document any deviations from planned change
|
||||
|
||||
step_4_verify:
|
||||
actions:
|
||||
- Confirm service restoration
|
||||
- Verify no unintended side effects
|
||||
- Run post-change verification checks
|
||||
- Update status page and stakeholders
|
||||
|
||||
step_5_document:
|
||||
deadline: "Within 48 hours of implementation"
|
||||
required_documentation:
|
||||
- Complete change request form (retroactive)
|
||||
- Timeline of events and actions
|
||||
- Justification for emergency classification
|
||||
- Approval records (messages, emails)
|
||||
- Post-implementation verification results
|
||||
- Root cause analysis (what made it an emergency)
|
||||
- Preventive actions to avoid future emergency
|
||||
|
||||
step_6_review:
|
||||
actions:
|
||||
- CAB review at next regular meeting
|
||||
- Assess if emergency classification was appropriate
|
||||
- Identify process improvements
|
||||
- Track emergency change trends
|
||||
```
|
||||
|
||||
## Pull Request Template for Changes
|
||||
|
||||
```markdown
|
||||
## Change Description
|
||||
## Change Request
|
||||
|
||||
## Risk Level
|
||||
- [ ] Low - Standard change
|
||||
- [ ] Medium - Normal change
|
||||
- [ ] High - CAB required
|
||||
### Type
|
||||
- [ ] Standard (pre-approved, low risk)
|
||||
- [ ] Normal - Low Risk
|
||||
- [ ] Normal - Medium Risk
|
||||
- [ ] Normal - High Risk (CAB required)
|
||||
- [ ] Emergency (retroactive documentation required)
|
||||
|
||||
## Testing
|
||||
### Description
|
||||
<!-- What is being changed and why? -->
|
||||
|
||||
### Risk Assessment
|
||||
**Impact if failed:** <!-- What breaks? -->
|
||||
**Likelihood of failure:** Low / Medium / High
|
||||
**Affected services:** <!-- List services -->
|
||||
**User impact:** <!-- Will users notice? -->
|
||||
|
||||
### Testing Evidence
|
||||
- [ ] Unit tests pass
|
||||
- [ ] Integration tests pass
|
||||
- [ ] Staging deployment verified
|
||||
- [ ] Performance test completed (if applicable)
|
||||
- [ ] Security scan clean (if applicable)
|
||||
|
||||
## Rollback Plan
|
||||
### Rollback Plan
|
||||
<!-- How to revert if something goes wrong -->
|
||||
**Estimated rollback time:** <!-- X minutes -->
|
||||
**Data rollback needed:** Yes / No
|
||||
|
||||
## Stakeholders Notified
|
||||
- [ ] Operations
|
||||
- [ ] Security
|
||||
- [ ] Business owners
|
||||
### Deployment Plan
|
||||
**Target window:** <!-- Date and time -->
|
||||
**Estimated duration:** <!-- X minutes -->
|
||||
|
||||
### Post-Deployment Verification
|
||||
- [ ] Health checks passing
|
||||
- [ ] Error rates within baseline
|
||||
- [ ] Key transactions working
|
||||
- [ ] Monitoring dashboards reviewed
|
||||
|
||||
### Communication
|
||||
- [ ] Team notified
|
||||
- [ ] Stakeholders notified (if user-facing)
|
||||
- [ ] Status page updated (if applicable)
|
||||
|
||||
### Approvals Required
|
||||
- [ ] Peer review
|
||||
- [ ] Team lead (medium+ risk)
|
||||
- [ ] Security review (security-impacting changes)
|
||||
- [ ] CAB approval (high risk)
|
||||
```
|
||||
|
||||
## CI/CD Change Tracking Automation
|
||||
|
||||
```yaml
|
||||
# GitHub Actions - Automated change tracking
|
||||
name: Change Management
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened, synchronize, labeled]
|
||||
push:
|
||||
branches: [main]
|
||||
|
||||
jobs:
|
||||
classify-change:
|
||||
if: github.event_name == 'pull_request'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Classify change risk
|
||||
id: classify
|
||||
run: |
|
||||
FILES_CHANGED=$(gh pr diff ${{ github.event.pull_request.number }} --name-only)
|
||||
|
||||
# High risk indicators
|
||||
if echo "$FILES_CHANGED" | grep -qE 'terraform/|infrastructure/|migrations/|auth/|security/'; then
|
||||
echo "risk=high" >> $GITHUB_OUTPUT
|
||||
echo "::warning::High-risk change detected - CAB review may be required"
|
||||
elif echo "$FILES_CHANGED" | grep -qE 'config/|database/|api/'; then
|
||||
echo "risk=medium" >> $GITHUB_OUTPUT
|
||||
else
|
||||
echo "risk=low" >> $GITHUB_OUTPUT
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Add risk label
|
||||
run: |
|
||||
gh pr edit ${{ github.event.pull_request.number }} \
|
||||
--add-label "risk:${{ steps.classify.outputs.risk }}"
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Enforce approvals by risk
|
||||
if: steps.classify.outputs.risk == 'high'
|
||||
run: |
|
||||
APPROVALS=$(gh pr view ${{ github.event.pull_request.number }} \
|
||||
--json reviews --jq '[.reviews[] | select(.state=="APPROVED")] | length')
|
||||
if [ "$APPROVALS" -lt 2 ]; then
|
||||
echo "::error::High-risk changes require at least 2 approvals"
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
record-deployment:
|
||||
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Record deployment
|
||||
run: |
|
||||
CHANGE_ID="CR-$(date +%Y)-$(printf '%04d' ${{ github.run_number }})"
|
||||
echo "Change ID: $CHANGE_ID"
|
||||
echo "Deployed at: $(date -u +%Y-%m-%dT%H:%M:%SZ)"
|
||||
echo "Commit: ${{ github.sha }}"
|
||||
echo "Author: ${{ github.actor }}"
|
||||
|
||||
cat > /tmp/deployment-record.json <<EOF
|
||||
{
|
||||
"change_id": "$CHANGE_ID",
|
||||
"timestamp": "$(date -u +%Y-%m-%dT%H:%M:%SZ)",
|
||||
"commit": "${{ github.sha }}",
|
||||
"author": "${{ github.actor }}",
|
||||
"environment": "production",
|
||||
"status": "deployed"
|
||||
}
|
||||
EOF
|
||||
```
|
||||
|
||||
## Change Freeze Policy
|
||||
|
||||
```yaml
|
||||
change_freeze:
|
||||
definition: "Period during which non-emergency changes are prohibited"
|
||||
|
||||
scheduled_freezes:
|
||||
year_end:
|
||||
start: "December 15"
|
||||
end: "January 3"
|
||||
scope: "All production changes"
|
||||
major_events:
|
||||
- "Black Friday through Cyber Monday (e-commerce)"
|
||||
- "Tax filing deadline periods (financial services)"
|
||||
- "Open enrollment periods (healthcare)"
|
||||
|
||||
exceptions_during_freeze:
|
||||
allowed:
|
||||
- Security patches for actively exploited vulnerabilities
|
||||
- Changes required by regulatory deadline
|
||||
- Fixes for P1/SEV1 production incidents
|
||||
approval: "VP of Engineering + Security Lead"
|
||||
|
||||
communication:
|
||||
announcement: "2 weeks before freeze"
|
||||
reminder: "1 week and 1 day before freeze"
|
||||
daily_status: "During freeze period"
|
||||
lift_notification: "When freeze ends"
|
||||
```
|
||||
|
||||
## Change Management Metrics
|
||||
|
||||
```yaml
|
||||
metrics:
|
||||
change_success_rate:
|
||||
description: "Percentage of changes implemented without rollback or incident"
|
||||
target: ">95%"
|
||||
formula: "(successful changes / total changes) * 100"
|
||||
|
||||
emergency_change_rate:
|
||||
description: "Percentage of changes classified as emergency"
|
||||
target: "<5%"
|
||||
formula: "(emergency changes / total changes) * 100"
|
||||
|
||||
rollback_rate:
|
||||
description: "Percentage of changes that required rollback"
|
||||
target: "<3%"
|
||||
|
||||
mean_time_to_implement:
|
||||
description: "Average time from approval to implementation"
|
||||
target: "Varies by type"
|
||||
|
||||
cab_approval_time:
|
||||
description: "Average time from submission to CAB decision"
|
||||
target: "<5 business days for normal changes"
|
||||
```
|
||||
|
||||
## Change Management Checklist
|
||||
|
||||
```yaml
|
||||
change_management_checklist:
|
||||
process_setup:
|
||||
- [ ] Change types defined with classification criteria
|
||||
- [ ] Approval matrix documented (who approves what)
|
||||
- [ ] CAB established with regular meeting schedule
|
||||
- [ ] Emergency change procedure documented
|
||||
- [ ] Change request template created
|
||||
- [ ] Change freeze policy defined
|
||||
|
||||
tooling:
|
||||
- [ ] PR template includes change management fields
|
||||
- [ ] Automated risk classification in CI/CD
|
||||
- [ ] Branch protection enforces required approvals
|
||||
- [ ] Deployment records captured automatically
|
||||
- [ ] Change audit trail preserved (PR history, approvals)
|
||||
|
||||
compliance:
|
||||
- [ ] All production changes have documented approval
|
||||
- [ ] Rollback plans exist for every change
|
||||
- [ ] Post-implementation reviews conducted for failures
|
||||
- [ ] Emergency changes documented retroactively within 48 hours
|
||||
- [ ] Change metrics reported monthly
|
||||
- [ ] Audit trail retained for compliance period (1-3 years)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Clear change categories
|
||||
- Required approvals by risk
|
||||
- Rollback procedures documented
|
||||
- Post-change verification
|
||||
- Change freeze windows
|
||||
- Classify changes by risk level to apply proportionate controls without slowing low-risk work
|
||||
- Automate risk classification based on files changed, services affected, and deployment scope
|
||||
- Use PR approvals as the native change approval mechanism for code-driven changes
|
||||
- Require rollback plans for every change and test rollback procedures periodically
|
||||
- Track emergency changes as a key metric: a high rate indicates systemic process issues
|
||||
- Implement change freezes during critical business periods to protect stability
|
||||
- Conduct post-implementation reviews for all failed changes to drive improvement
|
||||
- Separate duty of implementation from duty of approval (no self-approving changes)
|
||||
- Capture deployment records automatically in CI/CD rather than relying on manual entry
|
||||
- Keep the CAB focused on high-risk decisions; do not bottleneck low-risk changes through CAB
|
||||
|
||||
@@ -9,61 +9,592 @@ metadata:
|
||||
|
||||
# Policy as Code
|
||||
|
||||
Automate policy enforcement through code.
|
||||
Automate policy enforcement through code using OPA/Rego, Kyverno, Checkov, and CI/CD integration to prevent compliance violations before they reach production.
|
||||
|
||||
## Open Policy Agent (OPA)
|
||||
## When to Use
|
||||
|
||||
- Enforcing security and compliance policies on infrastructure-as-code changes
|
||||
- Preventing misconfigured Kubernetes workloads from deploying
|
||||
- Automating guardrails in CI/CD pipelines for Terraform, CloudFormation, or Helm
|
||||
- Implementing organizational standards that must be consistently applied
|
||||
- Replacing manual approval gates with automated policy checks
|
||||
|
||||
## Open Policy Agent (OPA) Rego Policies
|
||||
|
||||
```rego
|
||||
# deny_public_buckets.rego
|
||||
package terraform.s3
|
||||
# deny_public_s3.rego - Deny S3 buckets with public access
|
||||
package terraform.aws.s3
|
||||
|
||||
deny[msg] {
|
||||
resource := input.resource.aws_s3_bucket[name]
|
||||
resource.acl == "public-read"
|
||||
msg := sprintf("S3 bucket '%s' has public ACL", [name])
|
||||
import rego.v1
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_s3_bucket"
|
||||
resource.change.after.acl == "public-read"
|
||||
msg := sprintf(
|
||||
"S3 bucket '%s' has public-read ACL. All buckets must be private. [Policy: no-public-s3]",
|
||||
[resource.address]
|
||||
)
|
||||
}
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_s3_bucket"
|
||||
resource.change.after.acl == "public-read-write"
|
||||
msg := sprintf(
|
||||
"S3 bucket '%s' has public-read-write ACL. This is strictly prohibited. [Policy: no-public-s3]",
|
||||
[resource.address]
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
## Kyverno (Kubernetes)
|
||||
```rego
|
||||
# require_encryption.rego - Require encryption on data stores
|
||||
package terraform.aws.encryption
|
||||
|
||||
import rego.v1
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_db_instance"
|
||||
not resource.change.after.storage_encrypted
|
||||
msg := sprintf(
|
||||
"RDS instance '%s' does not have storage encryption enabled. [Policy: require-rds-encryption]",
|
||||
[resource.address]
|
||||
)
|
||||
}
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_ebs_volume"
|
||||
not resource.change.after.encrypted
|
||||
msg := sprintf(
|
||||
"EBS volume '%s' is not encrypted. [Policy: require-ebs-encryption]",
|
||||
[resource.address]
|
||||
)
|
||||
}
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
resource.type == "aws_s3_bucket"
|
||||
not has_encryption(resource)
|
||||
msg := sprintf(
|
||||
"S3 bucket '%s' does not have default encryption configured. [Policy: require-s3-encryption]",
|
||||
[resource.address]
|
||||
)
|
||||
}
|
||||
|
||||
has_encryption(resource) if {
|
||||
resource.change.after.server_side_encryption_configuration[_]
|
||||
}
|
||||
```
|
||||
|
||||
```rego
|
||||
# require_tags.rego - Enforce mandatory tagging
|
||||
package terraform.aws.tags
|
||||
|
||||
import rego.v1
|
||||
|
||||
required_tags := {"Environment", "Owner", "CostCenter", "DataClassification"}
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
tags := object.get(resource.change.after, "tags", {})
|
||||
missing := required_tags - {key | tags[key]}
|
||||
count(missing) > 0
|
||||
msg := sprintf(
|
||||
"Resource '%s' is missing required tags: %v. [Policy: required-tags]",
|
||||
[resource.address, missing]
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```rego
|
||||
# restrict_regions.rego - Limit resource deployment to approved regions
|
||||
package terraform.aws.regions
|
||||
|
||||
import rego.v1
|
||||
|
||||
approved_regions := {"us-east-1", "us-west-2", "eu-west-1"}
|
||||
|
||||
deny contains msg if {
|
||||
resource := input.resource_changes[_]
|
||||
provider_config := input.configuration.provider_config.aws
|
||||
region := provider_config.expressions.region.constant_value
|
||||
not region in approved_regions
|
||||
msg := sprintf(
|
||||
"Resource '%s' is in region '%s'. Approved regions: %v. [Policy: approved-regions]",
|
||||
[resource.address, region, approved_regions]
|
||||
)
|
||||
}
|
||||
```
|
||||
|
||||
```bash
|
||||
# Evaluate OPA policies against Terraform plan
|
||||
terraform plan -out=tfplan
|
||||
terraform show -json tfplan > tfplan.json
|
||||
|
||||
# Run OPA evaluation
|
||||
opa eval \
|
||||
--data policies/ \
|
||||
--input tfplan.json \
|
||||
"data.terraform.aws.s3.deny" \
|
||||
--format pretty
|
||||
|
||||
# Use conftest for easier CI integration
|
||||
conftest test tfplan.json --policy policies/ --output table
|
||||
```
|
||||
|
||||
## Kyverno Kubernetes Policies
|
||||
|
||||
```yaml
|
||||
# require-labels.yaml - Enforce required labels on all pods
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-labels
|
||||
annotations:
|
||||
policies.kyverno.io/title: Require Labels
|
||||
policies.kyverno.io/category: Best Practices
|
||||
policies.kyverno.io/severity: medium
|
||||
spec:
|
||||
validationFailureAction: enforce
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: check-labels
|
||||
- name: check-required-labels
|
||||
match:
|
||||
resources:
|
||||
kinds:
|
||||
- Pod
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: "Label 'app' is required"
|
||||
message: >-
|
||||
Labels 'app.kubernetes.io/name', 'app.kubernetes.io/version',
|
||||
and 'team' are required on all Pods.
|
||||
pattern:
|
||||
metadata:
|
||||
labels:
|
||||
app: "?*"
|
||||
app.kubernetes.io/name: "?*"
|
||||
app.kubernetes.io/version: "?*"
|
||||
team: "?*"
|
||||
---
|
||||
# disallow-privileged.yaml - Block privileged containers
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-privileged-containers
|
||||
annotations:
|
||||
policies.kyverno.io/title: Disallow Privileged Containers
|
||||
policies.kyverno.io/category: Pod Security
|
||||
policies.kyverno.io/severity: high
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: deny-privileged
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: "Privileged containers are not allowed."
|
||||
pattern:
|
||||
spec:
|
||||
containers:
|
||||
- securityContext:
|
||||
privileged: "false"
|
||||
=(initContainers):
|
||||
- securityContext:
|
||||
privileged: "false"
|
||||
---
|
||||
# require-resource-limits.yaml - Enforce resource limits
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-resource-limits
|
||||
annotations:
|
||||
policies.kyverno.io/title: Require Resource Limits
|
||||
policies.kyverno.io/severity: medium
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: check-resource-limits
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: "All containers must have CPU and memory limits defined."
|
||||
pattern:
|
||||
spec:
|
||||
containers:
|
||||
- resources:
|
||||
limits:
|
||||
memory: "?*"
|
||||
cpu: "?*"
|
||||
---
|
||||
# disallow-latest-tag.yaml - Block usage of 'latest' image tag
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: disallow-latest-tag
|
||||
annotations:
|
||||
policies.kyverno.io/title: Disallow Latest Tag
|
||||
policies.kyverno.io/severity: medium
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: validate-image-tag
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: "Images must use a specific tag, not 'latest'."
|
||||
pattern:
|
||||
spec:
|
||||
containers:
|
||||
- image: "!*:latest & *:*"
|
||||
---
|
||||
# restrict-image-registries.yaml - Allow only approved registries
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: restrict-image-registries
|
||||
annotations:
|
||||
policies.kyverno.io/title: Restrict Image Registries
|
||||
policies.kyverno.io/severity: high
|
||||
spec:
|
||||
validationFailureAction: Enforce
|
||||
background: true
|
||||
rules:
|
||||
- name: validate-registries
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
validate:
|
||||
message: >-
|
||||
Images must come from approved registries:
|
||||
123456789012.dkr.ecr.us-east-1.amazonaws.com or ghcr.io/your-org.
|
||||
pattern:
|
||||
spec:
|
||||
containers:
|
||||
- image: "123456789012.dkr.ecr.*.amazonaws.com/* | ghcr.io/your-org/*"
|
||||
---
|
||||
# require-networkpolicy.yaml - Ensure namespaces have NetworkPolicies
|
||||
apiVersion: kyverno.io/v1
|
||||
kind: ClusterPolicy
|
||||
metadata:
|
||||
name: require-networkpolicy
|
||||
annotations:
|
||||
policies.kyverno.io/title: Require Network Policy
|
||||
policies.kyverno.io/severity: high
|
||||
spec:
|
||||
validationFailureAction: Audit
|
||||
background: true
|
||||
rules:
|
||||
- name: check-networkpolicy
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Deployment
|
||||
preconditions:
|
||||
all:
|
||||
- key: "{{request.object.metadata.namespace}}"
|
||||
operator: NotIn
|
||||
value: ["kube-system", "kube-public"]
|
||||
validate:
|
||||
message: "A NetworkPolicy must exist in namespace '{{request.object.metadata.namespace}}' before deploying workloads."
|
||||
deny:
|
||||
conditions:
|
||||
all:
|
||||
- key: "{{request.object.metadata.namespace}}"
|
||||
operator: AnyNotIn
|
||||
value: "{{request.object.metadata.namespace}}"
|
||||
```
|
||||
|
||||
## Checkov
|
||||
## Checkov Custom Checks
|
||||
|
||||
```python
|
||||
# custom_checks/require_s3_versioning.py
|
||||
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
|
||||
from checkov.common.models.enums import CheckResult, CheckCategories
|
||||
|
||||
|
||||
class S3Versioning(BaseResourceCheck):
|
||||
def __init__(self):
|
||||
name = "Ensure S3 bucket has versioning enabled"
|
||||
id = "CUSTOM_S3_001"
|
||||
supported_resources = ["aws_s3_bucket_versioning"]
|
||||
categories = [CheckCategories.BACKUP_AND_RECOVERY]
|
||||
super().__init__(name=name, id=id,
|
||||
categories=categories,
|
||||
supported_resources=supported_resources)
|
||||
|
||||
def scan_resource_conf(self, conf):
|
||||
versioning = conf.get("versioning_configuration", [{}])
|
||||
if isinstance(versioning, list):
|
||||
versioning = versioning[0] if versioning else {}
|
||||
status = versioning.get("status", ["Disabled"])
|
||||
if isinstance(status, list):
|
||||
status = status[0]
|
||||
return CheckResult.PASSED if status == "Enabled" else CheckResult.FAILED
|
||||
|
||||
|
||||
check = S3Versioning()
|
||||
```
|
||||
|
||||
```python
|
||||
# custom_checks/require_rds_backup.py
|
||||
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
|
||||
from checkov.common.models.enums import CheckResult, CheckCategories
|
||||
|
||||
|
||||
class RDSBackupRetention(BaseResourceCheck):
|
||||
def __init__(self):
|
||||
name = "Ensure RDS has backup retention of at least 7 days"
|
||||
id = "CUSTOM_RDS_001"
|
||||
supported_resources = ["aws_db_instance"]
|
||||
categories = [CheckCategories.BACKUP_AND_RECOVERY]
|
||||
super().__init__(name=name, id=id,
|
||||
categories=categories,
|
||||
supported_resources=supported_resources)
|
||||
|
||||
def scan_resource_conf(self, conf):
|
||||
retention = conf.get("backup_retention_period", [0])
|
||||
if isinstance(retention, list):
|
||||
retention = retention[0]
|
||||
return CheckResult.PASSED if int(retention) >= 7 else CheckResult.FAILED
|
||||
|
||||
|
||||
check = RDSBackupRetention()
|
||||
```
|
||||
|
||||
```bash
|
||||
# Scan Terraform
|
||||
checkov -d . --framework terraform
|
||||
# Run Checkov with custom checks
|
||||
checkov -d ./terraform \
|
||||
--framework terraform \
|
||||
--external-checks-dir ./custom_checks \
|
||||
--output cli \
|
||||
--compact
|
||||
|
||||
# Custom check
|
||||
from checkov.terraform.checks.resource.base_resource_check import BaseResourceCheck
|
||||
# Run specific check IDs
|
||||
checkov -d ./terraform \
|
||||
--check CUSTOM_S3_001,CUSTOM_RDS_001,CKV_AWS_18,CKV_AWS_19
|
||||
|
||||
class S3Encryption(BaseResourceCheck):
|
||||
def scan_resource_conf(self, conf):
|
||||
return CheckResult.PASSED if 'encryption' in conf else CheckResult.FAILED
|
||||
# Generate SARIF output for GitHub Advanced Security integration
|
||||
checkov -d ./terraform \
|
||||
--framework terraform \
|
||||
--output sarif \
|
||||
--output-file checkov-results.sarif
|
||||
|
||||
# Skip specific checks with documented justification
|
||||
checkov -d ./terraform \
|
||||
--skip-check CKV_AWS_145 \
|
||||
--skip-check CKV_AWS_79
|
||||
```
|
||||
|
||||
## CI/CD Pipeline Integration
|
||||
|
||||
```yaml
|
||||
# GitHub Actions - Policy enforcement in PR workflow
|
||||
name: Policy Checks
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'terraform/**'
|
||||
- 'kubernetes/**'
|
||||
|
||||
jobs:
|
||||
terraform-policy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Setup Terraform
|
||||
uses: hashicorp/setup-terraform@v3
|
||||
|
||||
- name: Terraform Init and Plan
|
||||
working-directory: terraform/
|
||||
run: |
|
||||
terraform init -backend=false
|
||||
terraform plan -out=tfplan
|
||||
terraform show -json tfplan > tfplan.json
|
||||
|
||||
- name: OPA Policy Check
|
||||
uses: open-policy-agent/setup-opa@v2
|
||||
with:
|
||||
version: latest
|
||||
- run: |
|
||||
RESULTS=$(opa eval \
|
||||
--data policies/ \
|
||||
--input terraform/tfplan.json \
|
||||
--format json \
|
||||
"data.terraform" | jq '.result[0].expressions[0].value')
|
||||
DENY_COUNT=$(echo "$RESULTS" | jq '[.. | .deny? // empty | .[] ] | length')
|
||||
if [ "$DENY_COUNT" -gt 0 ]; then
|
||||
echo "::error::Policy violations found:"
|
||||
echo "$RESULTS" | jq '.. | .deny? // empty | .[]'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
- name: Checkov Scan
|
||||
uses: bridgecrewio/checkov-action@v12
|
||||
with:
|
||||
directory: terraform/
|
||||
framework: terraform
|
||||
output_format: cli,sarif
|
||||
output_file_path: console,checkov-results.sarif
|
||||
soft_fail: false
|
||||
external_checks_dirs: custom_checks/
|
||||
|
||||
- name: Upload SARIF
|
||||
if: always()
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: checkov-results.sarif
|
||||
|
||||
kubernetes-policy:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Kyverno CLI
|
||||
run: |
|
||||
curl -LO https://github.com/kyverno/kyverno/releases/latest/download/kyverno-cli_linux_amd64.tar.gz
|
||||
tar -xzf kyverno-cli_linux_amd64.tar.gz
|
||||
sudo mv kyverno /usr/local/bin/
|
||||
|
||||
- name: Test Kyverno Policies
|
||||
run: |
|
||||
kyverno apply policies/kyverno/ \
|
||||
--resource kubernetes/manifests/ \
|
||||
--detailed-results \
|
||||
--output-format table
|
||||
|
||||
- name: Conftest Kubernetes Manifests
|
||||
uses: open-policy-agent/conftest-action@v2
|
||||
with:
|
||||
files: kubernetes/manifests/
|
||||
policy: policies/kubernetes/
|
||||
```
|
||||
|
||||
## Policy Exception Management
|
||||
|
||||
```yaml
|
||||
exception_workflow:
|
||||
request:
|
||||
fields:
|
||||
- policy_id: "Which policy needs an exception"
|
||||
- resource: "Specific resource requiring exception"
|
||||
- justification: "Business reason for the exception"
|
||||
- compensating_controls: "Alternative mitigations in place"
|
||||
- duration: "Temporary (with expiry) or permanent"
|
||||
- requestor: "Person requesting"
|
||||
- approver: "Security team member who approved"
|
||||
|
||||
approval_process:
|
||||
1: "Requestor submits exception with justification"
|
||||
2: "Security team reviews and assesses risk"
|
||||
3: "Compensating controls verified"
|
||||
4: "Exception approved or denied with rationale"
|
||||
5: "Exception documented in registry"
|
||||
6: "Automated enforcement updated to allow exception"
|
||||
|
||||
enforcement:
|
||||
opa: |
|
||||
# Exception list loaded as data
|
||||
# policies/exceptions.json
|
||||
# {"exceptions": [{"resource": "aws_s3_bucket.public_website", "policy": "no-public-s3", "expires": "2025-06-01"}]}
|
||||
kyverno: |
|
||||
# Use Kyverno PolicyException resource
|
||||
apiVersion: kyverno.io/v2beta1
|
||||
kind: PolicyException
|
||||
metadata:
|
||||
name: allow-public-website
|
||||
namespace: web
|
||||
spec:
|
||||
exceptions:
|
||||
- policyName: disallow-privileged-containers
|
||||
ruleNames:
|
||||
- deny-privileged
|
||||
match:
|
||||
any:
|
||||
- resources:
|
||||
kinds:
|
||||
- Pod
|
||||
names:
|
||||
- legacy-app-*
|
||||
|
||||
review_schedule:
|
||||
- Review all active exceptions quarterly
|
||||
- Expire temporary exceptions automatically
|
||||
- Re-justify permanent exceptions annually
|
||||
- Track exception count trends as a security metric
|
||||
```
|
||||
|
||||
## Policy Testing
|
||||
|
||||
```bash
|
||||
# Test OPA policies with mock input
|
||||
mkdir -p policies/tests
|
||||
|
||||
# Create test input
|
||||
cat > policies/tests/public_bucket_test.json <<'EOF'
|
||||
{
|
||||
"resource_changes": [{
|
||||
"address": "aws_s3_bucket.test",
|
||||
"type": "aws_s3_bucket",
|
||||
"change": {
|
||||
"after": {"acl": "public-read"}
|
||||
}
|
||||
}]
|
||||
}
|
||||
EOF
|
||||
|
||||
# Run test
|
||||
opa eval --data policies/ --input policies/tests/public_bucket_test.json \
|
||||
"data.terraform.aws.s3.deny" --format pretty
|
||||
# Should output the deny message
|
||||
|
||||
# OPA unit tests
|
||||
cat > policies/tests/s3_test.rego <<'EOF'
|
||||
package terraform.aws.s3_test
|
||||
|
||||
import rego.v1
|
||||
import data.terraform.aws.s3
|
||||
|
||||
test_deny_public_bucket if {
|
||||
result := s3.deny with input as {"resource_changes": [{"address": "test", "type": "aws_s3_bucket", "change": {"after": {"acl": "public-read"}}}]}
|
||||
count(result) > 0
|
||||
}
|
||||
|
||||
test_allow_private_bucket if {
|
||||
result := s3.deny with input as {"resource_changes": [{"address": "test", "type": "aws_s3_bucket", "change": {"after": {"acl": "private"}}}]}
|
||||
count(result) == 0
|
||||
}
|
||||
EOF
|
||||
|
||||
opa test policies/ -v
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Version control policies
|
||||
- Test policies in CI
|
||||
- Gradual rollout (warn → enforce)
|
||||
- Exception management
|
||||
- Version control all policies alongside the infrastructure code they govern
|
||||
- Start in audit/warn mode and transition to enforce after verifying no false positives
|
||||
- Write unit tests for every policy to catch regressions and verify intended behavior
|
||||
- Implement a formal exception process: never disable policies to bypass legitimate checks
|
||||
- Use policy results as PR status checks to block non-compliant merges
|
||||
- Layer policies: Checkov for static analysis, OPA for Terraform plan evaluation, Kyverno for runtime
|
||||
- Tag policies with compliance framework references (e.g., SOC 2 CC6.1, PCI Req 2.2)
|
||||
- Monitor policy violation trends over time to identify systemic issues
|
||||
- Provide clear, actionable error messages that explain how to fix violations
|
||||
- Roll out new policies gradually: inform teams, give a remediation window, then enforce
|
||||
|
||||
@@ -9,65 +9,504 @@ metadata:
|
||||
|
||||
# Vendor Management
|
||||
|
||||
Manage third-party vendor security risks.
|
||||
Implement a vendor risk management program covering vendor assessment questionnaires, risk scoring, contract tracking, SLA monitoring, and ongoing oversight for compliance with SOC 2, ISO 27001, and regulatory frameworks.
|
||||
|
||||
## Vendor Assessment
|
||||
## When to Use
|
||||
|
||||
- Onboarding new vendors that will access company data or systems
|
||||
- Conducting annual vendor risk assessments and reassessments
|
||||
- Negotiating security requirements in vendor contracts
|
||||
- Monitoring vendor SLA compliance and security posture
|
||||
- Preparing vendor management evidence for SOC 2 or ISO 27001 audits
|
||||
|
||||
## Vendor Risk Tiering
|
||||
|
||||
```yaml
|
||||
assessment_process:
|
||||
1_identify:
|
||||
- Catalog all vendors
|
||||
- Classify by risk tier
|
||||
|
||||
2_assess:
|
||||
- Security questionnaire
|
||||
- SOC 2 review
|
||||
- Penetration test results
|
||||
|
||||
3_contract:
|
||||
- Security requirements
|
||||
- Data processing agreement
|
||||
- SLAs
|
||||
|
||||
4_monitor:
|
||||
- Continuous monitoring
|
||||
- Annual reassessment
|
||||
- Incident notification
|
||||
vendor_risk_tiers:
|
||||
critical:
|
||||
criteria:
|
||||
- Processes or stores sensitive/regulated data (PII, PHI, PCI)
|
||||
- Single point of failure (no alternative vendor)
|
||||
- Has privileged access to production systems
|
||||
- Handles authentication or security-critical functions
|
||||
assessment_requirements:
|
||||
- Full security questionnaire (SIG or custom)
|
||||
- SOC 2 Type II report review (or equivalent)
|
||||
- Penetration test results review
|
||||
- On-site or virtual security assessment (optional)
|
||||
- Business continuity and DR plan review
|
||||
review_frequency: Annual
|
||||
contract_requirements:
|
||||
- Data processing agreement (DPA)
|
||||
- Business associate agreement (BAA) if PHI
|
||||
- Security SLA with breach notification timeline
|
||||
- Right to audit clause
|
||||
- Cyber insurance requirements
|
||||
examples:
|
||||
- Cloud infrastructure providers (AWS, Azure, GCP)
|
||||
- Identity providers (Okta, Azure AD)
|
||||
- Payment processors (Stripe, Adyen)
|
||||
- Primary database or CRM SaaS
|
||||
|
||||
high:
|
||||
criteria:
|
||||
- Accesses significant company data (internal or confidential)
|
||||
- Integrates with production systems via API
|
||||
- Processes customer-facing transactions
|
||||
- Substitution would cause significant business disruption
|
||||
assessment_requirements:
|
||||
- Security questionnaire
|
||||
- SOC 2 report review (Type I or Type II)
|
||||
- Compliance certifications verified
|
||||
review_frequency: Annual
|
||||
contract_requirements:
|
||||
- Data processing agreement
|
||||
- Security requirements appendix
|
||||
- Incident notification clause (72 hours)
|
||||
examples:
|
||||
- Email/marketing platforms (SendGrid, HubSpot)
|
||||
- Monitoring and logging SaaS (Datadog, Splunk)
|
||||
- CI/CD platforms (GitHub, GitLab)
|
||||
- Customer support platforms
|
||||
|
||||
medium:
|
||||
criteria:
|
||||
- Limited data access (internal data only)
|
||||
- Non-production system integration
|
||||
- Some business impact if unavailable
|
||||
assessment_requirements:
|
||||
- Abbreviated security questionnaire
|
||||
- Compliance certification verification
|
||||
review_frequency: Every 2 years
|
||||
contract_requirements:
|
||||
- Standard vendor terms with security clause
|
||||
- NDA
|
||||
examples:
|
||||
- Project management tools
|
||||
- HR platforms
|
||||
- Travel and expense systems
|
||||
|
||||
low:
|
||||
criteria:
|
||||
- No access to company data
|
||||
- No system integration
|
||||
- Easily replaceable
|
||||
assessment_requirements:
|
||||
- Basic due diligence (public info review)
|
||||
- Confirm no data sharing
|
||||
review_frequency: Every 3 years or on renewal
|
||||
contract_requirements:
|
||||
- Standard terms
|
||||
examples:
|
||||
- Office supply vendors
|
||||
- Facilities services
|
||||
- General consulting (no data access)
|
||||
```
|
||||
|
||||
## Risk Tiers
|
||||
|
||||
| Tier | Criteria | Assessment |
|
||||
|------|----------|------------|
|
||||
| Critical | Access to sensitive data | Full assessment, annual |
|
||||
| High | Significant data access | Questionnaire + SOC 2 |
|
||||
| Medium | Limited data access | Security questionnaire |
|
||||
| Low | No data access | Basic due diligence |
|
||||
|
||||
## Security Questionnaire
|
||||
## Vendor Assessment Questionnaire
|
||||
|
||||
```yaml
|
||||
categories:
|
||||
security_questionnaire:
|
||||
section_1_governance:
|
||||
questions:
|
||||
- "Do you have a documented information security policy?"
|
||||
- "Is there a designated CISO or security lead?"
|
||||
- "Do you conduct annual security risk assessments?"
|
||||
- "Do you have a security awareness training program?"
|
||||
- "What compliance certifications do you hold? (SOC 2, ISO 27001, etc.)"
|
||||
- "When was your last external security audit?"
|
||||
- "Do you carry cyber liability insurance? What coverage limits?"
|
||||
evidence_requested:
|
||||
- Information security policy (or summary)
|
||||
- SOC 2 Type II report (or bridge letter)
|
||||
- ISO 27001 certificate
|
||||
- Cyber insurance certificate
|
||||
|
||||
section_2_access_control:
|
||||
questions:
|
||||
- "How do you manage user access to systems containing our data?"
|
||||
- "Is multi-factor authentication enforced for all personnel?"
|
||||
- "How frequently do you conduct access reviews?"
|
||||
- "What is your process for revoking access upon employee termination?"
|
||||
- "Do you support SSO/SAML integration for customer access?"
|
||||
- "How do you manage privileged access?"
|
||||
evidence_requested:
|
||||
- Access management policy
|
||||
- MFA configuration documentation
|
||||
- Access review records (sample)
|
||||
|
||||
section_3_data_protection:
|
||||
questions:
|
||||
- "How is our data encrypted at rest?"
|
||||
- "How is our data encrypted in transit?"
|
||||
- "In which geographic regions is our data stored?"
|
||||
- "Do you use sub-processors? If so, provide a list."
|
||||
- "What is your data retention policy?"
|
||||
- "How is our data isolated from other customers? (multi-tenancy model)"
|
||||
- "Can you provide data export in standard formats upon request?"
|
||||
- "What is your data destruction process at contract end?"
|
||||
evidence_requested:
|
||||
- Encryption standards documentation
|
||||
- Sub-processor list
|
||||
- Data flow diagram showing customer data handling
|
||||
|
||||
section_4_vulnerability_management:
|
||||
questions:
|
||||
- "How frequently do you perform vulnerability scans?"
|
||||
- "How frequently do you conduct penetration tests?"
|
||||
- "What is your patch management SLA for critical vulnerabilities?"
|
||||
- "Do you have a responsible disclosure or bug bounty program?"
|
||||
- "How do you manage vulnerabilities in third-party dependencies?"
|
||||
evidence_requested:
|
||||
- Penetration test executive summary (last 12 months)
|
||||
- Vulnerability management policy
|
||||
- Patch management SLA documentation
|
||||
|
||||
section_5_incident_response:
|
||||
questions:
|
||||
- "Do you have a documented incident response plan?"
|
||||
- "What is your breach notification timeline?"
|
||||
- "Have you experienced a data breach in the last 3 years?"
|
||||
- "How would you notify us in the event of a security incident?"
|
||||
- "Do you conduct incident response tabletop exercises?"
|
||||
evidence_requested:
|
||||
- Incident response plan summary
|
||||
- Breach notification procedure
|
||||
|
||||
section_6_business_continuity:
|
||||
questions:
|
||||
- "Do you have a business continuity plan?"
|
||||
- "Do you have a disaster recovery plan?"
|
||||
- "What are your RTO and RPO targets?"
|
||||
- "How frequently do you test your DR plan?"
|
||||
- "What is your uptime SLA?"
|
||||
- "Do you have geographic redundancy?"
|
||||
evidence_requested:
|
||||
- BCP/DR plan summary
|
||||
- Uptime SLA documentation
|
||||
- Most recent DR test results
|
||||
|
||||
section_7_compliance:
|
||||
questions:
|
||||
- "Do you process data subject to GDPR, HIPAA, or PCI DSS?"
|
||||
- "How do you support our compliance obligations?"
|
||||
- "Do you have a Data Processing Agreement (DPA) template?"
|
||||
- "How do you handle data subject access requests (DSARs)?"
|
||||
- "Are you FedRAMP authorized? If so, at what impact level?"
|
||||
evidence_requested:
|
||||
- DPA template
|
||||
- Compliance certification documentation
|
||||
```
|
||||
|
||||
## Risk Scoring Model
|
||||
|
||||
```yaml
|
||||
risk_scoring:
|
||||
dimensions:
|
||||
data_sensitivity:
|
||||
weight: 30
|
||||
scores:
|
||||
1: "No access to company or customer data"
|
||||
2: "Access to public or non-sensitive internal data"
|
||||
3: "Access to internal confidential data"
|
||||
4: "Access to PII or customer financial data"
|
||||
5: "Access to regulated data (PHI, PCI, classified)"
|
||||
|
||||
system_access:
|
||||
weight: 25
|
||||
scores:
|
||||
1: "No system access"
|
||||
2: "Read-only access to non-production"
|
||||
3: "Read/write access to non-production or read-only production"
|
||||
4: "Read/write access to production systems"
|
||||
5: "Privileged/admin access to production or security systems"
|
||||
|
||||
business_criticality:
|
||||
weight: 20
|
||||
scores:
|
||||
1: "No operational dependency"
|
||||
2: "Minor convenience; easily replaced"
|
||||
3: "Moderate dependency; replacement in weeks"
|
||||
4: "Significant dependency; replacement in months"
|
||||
5: "Critical dependency; no viable alternative"
|
||||
|
||||
security_posture:
|
||||
weight: 15
|
||||
scores:
|
||||
5: "No certifications, no formal security program"
|
||||
4: "Some security controls but no external validation"
|
||||
3: "SOC 2 Type I or equivalent"
|
||||
2: "SOC 2 Type II within last 12 months"
|
||||
1: "Multiple certifications (SOC 2 + ISO 27001), strong program"
|
||||
|
||||
regulatory_exposure:
|
||||
weight: 10
|
||||
scores:
|
||||
1: "No regulatory requirements"
|
||||
2: "General data protection (GDPR basic)"
|
||||
3: "Industry-specific (HIPAA, PCI)"
|
||||
4: "Government (FedRAMP, ITAR)"
|
||||
5: "Multiple stringent regulations"
|
||||
|
||||
calculation:
|
||||
formula: "Sum of (dimension_score * dimension_weight) / 100"
|
||||
risk_levels:
|
||||
low: "Score 1.0 - 2.0"
|
||||
medium: "Score 2.1 - 3.0"
|
||||
high: "Score 3.1 - 4.0"
|
||||
critical: "Score 4.1 - 5.0"
|
||||
|
||||
example:
|
||||
vendor: "Payment Processor X"
|
||||
data_sensitivity: 5 # PCI data
|
||||
system_access: 4 # Production API integration
|
||||
business_criticality: 5 # No alternative
|
||||
security_posture: 2 # SOC 2 Type II
|
||||
regulatory_exposure: 3 # PCI DSS
|
||||
score: "(5*30 + 4*25 + 5*20 + 2*15 + 3*10) / 100 = 4.1 -> Critical"
|
||||
```
|
||||
|
||||
## Vendor Registry and Contract Tracking
|
||||
|
||||
```yaml
|
||||
vendor_registry_schema:
|
||||
vendor_info:
|
||||
vendor_id: "VND-NNNN"
|
||||
vendor_name: ""
|
||||
vendor_website: ""
|
||||
primary_contact_email: ""
|
||||
security_contact_email: ""
|
||||
vendor_category: "" # SaaS, IaaS, Consulting, etc.
|
||||
|
||||
risk_assessment:
|
||||
risk_tier: "" # critical, high, medium, low
|
||||
risk_score: 0.0
|
||||
last_assessment_date: ""
|
||||
next_assessment_date: ""
|
||||
assessment_status: "" # current, due, overdue
|
||||
open_findings: 0
|
||||
certifications:
|
||||
- type: "SOC 2 Type II"
|
||||
valid_until: ""
|
||||
report_on_file: true
|
||||
- type: "ISO 27001"
|
||||
valid_until: ""
|
||||
certificate_on_file: true
|
||||
|
||||
contract:
|
||||
contract_id: ""
|
||||
start_date: ""
|
||||
end_date: ""
|
||||
auto_renewal: true
|
||||
cancellation_notice_days: 90
|
||||
annual_value: 0
|
||||
terms:
|
||||
data_processing_agreement: true
|
||||
nda: true
|
||||
baa: false
|
||||
right_to_audit: true
|
||||
breach_notification_sla: "72 hours"
|
||||
data_return_clause: true
|
||||
data_destruction_clause: true
|
||||
cyber_insurance_required: true
|
||||
|
||||
data_access:
|
||||
data_types: []
|
||||
data_classification: ""
|
||||
data_location: []
|
||||
sub_processors: []
|
||||
|
||||
sla_tracking:
|
||||
uptime_sla: "99.9%"
|
||||
actual_uptime_last_month: ""
|
||||
support_response_sla: ""
|
||||
sla_breaches_ytd: 0
|
||||
|
||||
status: "" # active, under_review, offboarding, inactive
|
||||
owner: "" # Internal team/person responsible
|
||||
```
|
||||
|
||||
## SLA Monitoring
|
||||
|
||||
```python
|
||||
"""
|
||||
Vendor SLA monitoring - Track uptime and response time commitments.
|
||||
"""
|
||||
import requests
|
||||
from datetime import datetime, timezone
|
||||
|
||||
|
||||
class VendorSLAMonitor:
|
||||
def __init__(self, vendors_config):
|
||||
self.vendors = vendors_config
|
||||
|
||||
def check_uptime(self, vendor):
|
||||
"""Check vendor service availability."""
|
||||
results = []
|
||||
for endpoint in vendor.get("health_endpoints", []):
|
||||
try:
|
||||
resp = requests.get(
|
||||
endpoint["url"],
|
||||
timeout=endpoint.get("timeout", 10),
|
||||
headers=endpoint.get("headers", {}),
|
||||
)
|
||||
results.append({
|
||||
"endpoint": endpoint["url"],
|
||||
"status": resp.status_code,
|
||||
"response_time_ms": resp.elapsed.total_seconds() * 1000,
|
||||
"healthy": resp.status_code == endpoint.get("expected_status", 200),
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
except requests.RequestException as e:
|
||||
results.append({
|
||||
"endpoint": endpoint["url"],
|
||||
"status": "error",
|
||||
"error": str(e),
|
||||
"healthy": False,
|
||||
"timestamp": datetime.now(timezone.utc).isoformat(),
|
||||
})
|
||||
return results
|
||||
|
||||
def check_status_page(self, vendor):
|
||||
"""Check vendor status page for active incidents."""
|
||||
status_url = vendor.get("status_page_url")
|
||||
if not status_url:
|
||||
return None
|
||||
try:
|
||||
api_url = f"{status_url}/api/v2/summary.json"
|
||||
resp = requests.get(api_url, timeout=10)
|
||||
data = resp.json()
|
||||
return {
|
||||
"vendor": vendor["name"],
|
||||
"status": data.get("status", {}).get("indicator", "unknown"),
|
||||
"active_incidents": len(data.get("incidents", [])),
|
||||
"components": [
|
||||
{"name": c["name"], "status": c["status"]}
|
||||
for c in data.get("components", [])
|
||||
],
|
||||
}
|
||||
except Exception:
|
||||
return {"vendor": vendor["name"], "status": "unknown"}
|
||||
|
||||
def generate_sla_report(self, vendor_name, monthly_checks):
|
||||
"""Calculate monthly SLA compliance."""
|
||||
total = len(monthly_checks)
|
||||
healthy = sum(1 for c in monthly_checks if c.get("healthy"))
|
||||
uptime_pct = (healthy / total * 100) if total > 0 else 0
|
||||
avg_response = (
|
||||
sum(c.get("response_time_ms", 0) for c in monthly_checks if c.get("healthy"))
|
||||
/ max(healthy, 1)
|
||||
)
|
||||
return {
|
||||
"vendor": vendor_name,
|
||||
"period": datetime.now(timezone.utc).strftime("%Y-%m"),
|
||||
"total_checks": total,
|
||||
"healthy_checks": healthy,
|
||||
"uptime_percentage": round(uptime_pct, 3),
|
||||
"avg_response_time_ms": round(avg_response, 1),
|
||||
"sla_met": uptime_pct >= 99.9,
|
||||
}
|
||||
```
|
||||
|
||||
## Vendor Lifecycle Management
|
||||
|
||||
```yaml
|
||||
vendor_lifecycle:
|
||||
onboarding:
|
||||
step_1_request:
|
||||
- Business owner submits vendor request with use case
|
||||
- Procurement assigns vendor ID
|
||||
- Initial risk tier assessment based on data access and criticality
|
||||
|
||||
step_2_assess:
|
||||
- Send security questionnaire (appropriate to tier)
|
||||
- Review compliance certifications
|
||||
- Evaluate questionnaire responses
|
||||
- Score vendor risk
|
||||
|
||||
step_3_contract:
|
||||
- Negotiate security requirements based on risk tier
|
||||
- Execute DPA/BAA as required
|
||||
- Document data flows and access scope
|
||||
- Set SLA expectations
|
||||
|
||||
step_4_provision:
|
||||
- Configure integration with least privilege access
|
||||
- Enable audit logging for vendor access
|
||||
- Add to vendor registry
|
||||
- Schedule first reassessment
|
||||
|
||||
ongoing_management:
|
||||
monitoring:
|
||||
- Track SLA compliance monthly
|
||||
- Monitor vendor status pages for incidents
|
||||
- Review vendor security advisories
|
||||
- Track data sub-processor changes
|
||||
reassessment:
|
||||
- Conduct reassessment per tier schedule
|
||||
- Review updated SOC 2 / ISO 27001 reports
|
||||
- Verify certifications are current
|
||||
- Update risk score
|
||||
|
||||
offboarding:
|
||||
step_1_plan:
|
||||
- Data migration or transition to replacement vendor
|
||||
- Identify all integrations and access points
|
||||
- Communication plan for stakeholders
|
||||
|
||||
step_2_execute:
|
||||
- Revoke all API keys, credentials, and access
|
||||
- Request data return or destruction certificate
|
||||
- Remove vendor integrations from systems
|
||||
- Disable SSO/SAML connections
|
||||
|
||||
step_3_verify:
|
||||
- Confirm data destruction (written certification)
|
||||
- Verify all access revoked
|
||||
- Update vendor registry status to inactive
|
||||
- Archive vendor records for retention period
|
||||
```
|
||||
|
||||
## Vendor Management Checklist
|
||||
|
||||
```yaml
|
||||
vendor_management_checklist:
|
||||
program_setup:
|
||||
- [ ] Vendor risk tiering criteria defined
|
||||
- [ ] Security questionnaire template created
|
||||
- [ ] Risk scoring model documented
|
||||
- [ ] Vendor registry established
|
||||
- [ ] Onboarding and offboarding procedures documented
|
||||
- [ ] Contract security requirements defined per tier
|
||||
|
||||
ongoing_operations:
|
||||
- [ ] All active vendors cataloged in registry
|
||||
- [ ] Risk tier assigned to each vendor
|
||||
- [ ] Security assessments current (per tier schedule)
|
||||
- [ ] Compliance certifications on file and not expired
|
||||
- [ ] DPAs/BAAs signed for all vendors handling personal data
|
||||
- [ ] SLA monitoring active for critical and high-tier vendors
|
||||
- [ ] Sub-processor lists reviewed and tracked
|
||||
- [ ] Vendor security incidents tracked and assessed
|
||||
|
||||
governance:
|
||||
- Security policies
|
||||
- Risk management
|
||||
- Compliance certifications
|
||||
|
||||
technical:
|
||||
- Access controls
|
||||
- Encryption
|
||||
- Vulnerability management
|
||||
|
||||
operational:
|
||||
- Incident response
|
||||
- Business continuity
|
||||
- Change management
|
||||
- [ ] Vendor management policy approved and published
|
||||
- [ ] Roles and responsibilities assigned (owner per vendor)
|
||||
- [ ] Assessment findings tracked to remediation
|
||||
- [ ] Vendor risk reported to management quarterly
|
||||
- [ ] Offboarding includes data destruction verification
|
||||
- [ ] Evidence retained for compliance audit (3+ years)
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
- Tier-based assessments
|
||||
- Regular reassessment
|
||||
- Contract security terms
|
||||
- Incident notification requirements
|
||||
- Exit strategy planning
|
||||
- Tier vendors by risk before investing assessment effort: not every vendor needs a full security review
|
||||
- Use standardized questionnaires (SIG, CAIQ, or consistent custom template) for comparable assessments
|
||||
- Review SOC 2 Type II reports thoroughly, including complementary user entity controls
|
||||
- Include right-to-audit clauses in contracts for critical vendors even if you do not exercise them frequently
|
||||
- Monitor vendor status pages and set up alerts for outages affecting your services
|
||||
- Track sub-processor changes: your vendor's vendor is part of your supply chain risk
|
||||
- Maintain a vendor registry as a single source of truth for all vendor relationships
|
||||
- Conduct offboarding rigorously: revoke all access and obtain data destruction certificates
|
||||
- Score vendor risk quantitatively to enable consistent prioritization and trend analysis
|
||||
- Report vendor risk metrics to management quarterly as part of the overall risk management program
|
||||
|
||||
Reference in New Issue
Block a user