Module 28 Security 5 labs

Cloud Security: AWS, Azure, GCP

Nắm vững bảo mật đa cloud: IAM least-privilege trên AWS/Azure/GCP, kiểm soát mạng (Security Group / NSG / VPC Firewall), mã hóa at-rest và in-transit, quản lý secret tập trung, và CSPM (Cloud Security Posture Management) tự động phát hiện misconfiguration trên toàn cloud estate.

Công cụ thực hành AWS CLI, Azure CLI, gcloud CLI, Prowler, ScoutSuite, Trivy
Nền tảng AWS, Microsoft Azure, Google Cloud Platform, Linux (WSL2)
Thời điểm phát hành 23/05/2026
Ngày biên soạn 23/05/2026
Người biên soạn Trần Văn Hòa — Microsoft Certified Trainer (MCT)

Mục tiêu học tập

1. Lý thuyết cốt lõi

1.1. Shared Responsibility Model

Mọi nhà cung cấp cloud đều phân chia rõ: nhà cung cấp chịu trách nhiệm "security OF the cloud" (physical, hypervisor, managed service infrastructure); khách hàng chịu trách nhiệm "security IN the cloud" (data, identity, app, OS configuration). Ranh giới dịch chuyển theo service tier: với IaaS khách hàng quản lý nhiều nhất; với SaaS/managed service nhà cung cấp xử lý nhiều hơn nhưng khách hàng vẫn phải cấu hình đúng (IAM, network, data classification).

1.2. IAM Least-Privilege — So sánh đa cloud

Khái niệmAWSAzureGCP
Identity cho workloadIAM Role + Instance ProfileManaged Identity (System/User)Service Account + Workload Identity
Policy ngôn ngữJSON Policy (allow/deny)ARM RBAC (built-in/custom role)IAM Role (predefined/custom)
Giới hạn trên cùngPermission BoundaryAzure Policy (deny assignment)Organization Policy Constraint
Temporary credentialSTS AssumeRoleManaged Identity token (IMDS)Service Account Key / Workload Identity Federation
Audit trailCloudTrailAzure Activity Log / Entra AuditCloud Audit Logs

Nguyên tắc chung cho IAM đa cloud

  • Không dùng root/owner account cho daily operation — tạo IAM user/service principal riêng.
  • Workload lấy credential qua IMDS (Instance Metadata Service) / Workload Identity — không dùng long-lived key.
  • Thường xuyên chạy IAM Access Analyzer (AWS) / Access Review (Azure) / IAM Recommender (GCP) để xóa quyền thừa.
  • Enable MFA cho tất cả human identity có quyền cao.

1.3. Network Security Controls

Mỗi cloud có lớp firewall riêng, hoạt động ở các level khác nhau:

1.4. Encryption At-Rest và In-Transit

At-rest: Mọi managed storage service đều encrypt by default (AES-256). Điểm quan trọng là ai giữ key:

In-transit: Enforce TLS 1.2+ (không chấp nhận TLS 1.0/1.1). AWS: dùng ACM certificate, ALB policy ELBSecurityPolicy-TLS13-1-2-2021-06. Azure: App Service minimum TLS version = 1.2, enforce HTTPS. GCP: Cloud Load Balancing SSL policy custom với TLS_1_2 minimum. Trong internal VPC cũng cần TLS (zero trust network).

1.5. Secret Management — Không Bao Giờ Hardcode

Pattern chuẩn: app lấy secret từ managed service qua SDK/IMDS, không qua environment variable hoặc config file commit vào git. So sánh:

Tính năngAWS Secrets ManagerAzure Key VaultGCP Secret Manager
Auto rotationCó (Lambda)Có (Event Grid)Manual / custom
Key managementTích hợp KMSTích hợp HSMTích hợp Cloud KMS
VersioningCó (AWSCURRENT/AWSPREVIOUS)Có (version ID)Có (version alias)
Access logCloudTrailKey Vault diagnostic logCloud Audit Logs

1.6. CSPM — Cloud Security Posture Management

CSPM tự động scan cloud configuration, so sánh với benchmark (CIS, NIST, PCI-DSS) và báo cáo misconfiguration. Hai công cụ open-source phổ biến:

2. Thực hành (Labs)

LAB-136

AWS IAM Least-Privilege — Role, Permission Boundary và Access Analyzer

AWS CLI · AWS Console

🎯 Mục tiêu: Tạo IAM Role cho EC2 chỉ có quyền đọc một S3 bucket cụ thể; gắn Permission Boundary ngăn escalate; chạy IAM Access Analyzer phát hiện public access.

🧰 Công cụ / nền tảng: AWS CLI (đã config aws configure), AWS Console, tài khoản AWS (free tier đủ).

📦 Chuẩn bị: AWS CLI ≥ 2.x; IAM permission để tạo role và bucket.

▶️ Các bước:

# 1. Tạo S3 bucket test (tên phải unique toàn cầu)
BUCKET="sec-lab-$(date +%s)"
aws s3 mb s3://$BUCKET --region ap-southeast-1
aws s3 cp /etc/hostname s3://$BUCKET/test.txt
echo "Bucket: $BUCKET"

# 2. Tạo Permission Boundary policy — giới hạn trên cùng
cat > boundary.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowS3ReadOnly",
      "Effect": "Allow",
      "Action": ["s3:Get*", "s3:List*"],
      "Resource": "*"
    },
    {
      "Sid": "DenyEverythingElse",
      "Effect": "Deny",
      "NotAction": ["s3:Get*", "s3:List*"],
      "Resource": "*"
    }
  ]
}
EOF
BOUNDARY_ARN=$(aws iam create-policy \
  --policy-name S3ReadOnlyBoundary \
  --policy-document file://boundary.json \
  --query 'Policy.Arn' --output text)
echo "Boundary ARN: $BOUNDARY_ARN"

# 3. Tạo Trust Policy cho EC2
cat > trust.json <<'EOF'
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Principal": {"Service": "ec2.amazonaws.com"},
    "Action": "sts:AssumeRole"
  }]
}
EOF

# 4. Tạo IAM Role với Permission Boundary gắn sẵn
ROLE_ARN=$(aws iam create-role \
  --role-name ec2-s3-reader \
  --assume-role-policy-document file://trust.json \
  --permissions-boundary $BOUNDARY_ARN \
  --query 'Role.Arn' --output text)
echo "Role ARN: $ROLE_ARN"

# 5. Tạo inline policy chỉ cho phép đọc bucket cụ thể
cat > s3-policy.json <<EOF
{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::$BUCKET",
      "arn:aws:s3:::$BUCKET/*"
    ]
  }]
}
EOF
aws iam put-role-policy \
  --role-name ec2-s3-reader \
  --policy-name allow-specific-bucket \
  --policy-document file://s3-policy.json

# 6. Kiểm tra effective permission
aws iam simulate-principal-policy \
  --policy-source-arn $ROLE_ARN \
  --action-names s3:GetObject \
  --resource-arns arn:aws:s3:::$BUCKET/test.txt \
  --query 'EvaluationResults[0].EvalDecision'
# Expected: "allowed"

aws iam simulate-principal-policy \
  --policy-source-arn $ROLE_ARN \
  --action-names s3:DeleteObject \
  --resource-arns arn:aws:s3:::$BUCKET/test.txt \
  --query 'EvaluationResults[0].EvalDecision'
# Expected: "explicitDeny" (bị Permission Boundary chặn)

# 7. Bật IAM Access Analyzer (region-scoped)
aws accessanalyzer create-analyzer \
  --analyzer-name sec-lab-analyzer \
  --type ACCOUNT \
  --region ap-southeast-1

# 8. Xem findings (S3 bucket public, IAM role cross-account, etc.)
sleep 10
aws accessanalyzer list-findings \
  --analyzer-name sec-lab-analyzer \
  --region ap-southeast-1 \
  --query 'findings[*].{Type:findingType,Resource:resource,Status:status}' \
  --output table

✅ Kết quả mong đợi: simulate-principal-policy trả về allowed cho GetObject và explicitDeny cho DeleteObject. Access Analyzer tạo thành công; nếu bucket có public access sẽ hiện finding. Role không thể leo thang do Permission Boundary.

🧹 Cleanup:

aws s3 rb s3://$BUCKET --force
aws iam delete-role-policy --role-name ec2-s3-reader --policy-name allow-specific-bucket
aws iam delete-role --role-name ec2-s3-reader
aws iam delete-policy --policy-arn $BOUNDARY_ARN
aws accessanalyzer delete-analyzer --analyzer-name sec-lab-analyzer --region ap-southeast-1
rm -f boundary.json trust.json s3-policy.json
LAB-137

Azure Key Vault + Managed Identity — Secret Zero Problem Solved

Azure CLI · Azure Portal

🎯 Mục tiêu: Tạo Azure Key Vault, lưu một database connection string, tạo VM với System-assigned Managed Identity và cấp quyền đọc secret — app lấy secret qua IMDS mà không cần credential hardcode.

🧰 Công cụ / nền tảng: Azure CLI (az login đã thực hiện), Azure Portal, subscription active.

📦 Chuẩn bị: Azure CLI ≥ 2.50; resource group sẵn hoặc tạo mới.

▶️ Các bước:

# 0. Biến môi trường
RG="sec-lab-rg"
LOC="southeastasia"
KV="kv-seclab-$(date +%s | tail -c 6)"  # max 24 chars
VM="seclab-vm"

# 1. Tạo Resource Group
az group create --name $RG --location $LOC

# 2. Tạo Key Vault với RBAC authorization (mới hơn Access Policy)
az keyvault create \
  --name $KV \
  --resource-group $RG \
  --location $LOC \
  --enable-rbac-authorization true \
  --sku standard
echo "Key Vault: $KV"
KV_ID=$(az keyvault show --name $KV --resource-group $RG --query id -o tsv)

# 3. Lưu secret (database connection string giả)
az keyvault secret set \
  --vault-name $KV \
  --name "db-connection-string" \
  --value "Server=prod-db.internal;Database=app;User=appuser;Password=SuperSecret123!"

# 4. Tạo VM với System-assigned Managed Identity
az vm create \
  --resource-group $RG \
  --name $VM \
  --image Ubuntu2204 \
  --size Standard_B1s \
  --assign-identity \
  --admin-username azureuser \
  --generate-ssh-keys \
  --no-wait
echo "VM creating (background)..."

# 5. Lấy Principal ID của Managed Identity
az vm wait --resource-group $RG --name $VM --created
PRINCIPAL_ID=$(az vm show \
  --resource-group $RG --name $VM \
  --query identity.principalId -o tsv)
echo "Managed Identity Principal: $PRINCIPAL_ID"

# 6. Gán role "Key Vault Secrets User" cho Managed Identity
az role assignment create \
  --assignee $PRINCIPAL_ID \
  --role "Key Vault Secrets User" \
  --scope $KV_ID
# Role "Key Vault Secrets User" = chỉ Get + List secret values

# 7. SSH vào VM và thử lấy secret qua IMDS + REST API
VM_IP=$(az vm list-ip-addresses \
  --resource-group $RG --name $VM \
  --query "[0].virtualMachine.network.publicIpAddresses[0].ipAddress" -o tsv)
echo "VM IP: $VM_IP"

# Chạy lệnh sau bên trong VM (dùng az vm run-command để demo):
az vm run-command invoke \
  --resource-group $RG \
  --name $VM \
  --command-id RunShellScript \
  --scripts '
    # Lấy access token từ IMDS (không cần credential)
    TOKEN=$(curl -s -H "Metadata: true" \
      "http://169.254.169.254/metadata/identity/oauth2/token?\
api-version=2018-02-01&resource=https%3A%2F%2Fvault.azure.net" \
      | python3 -c "import sys,json; print(json.load(sys.stdin)[\"access_token\"])")

    # Dùng token để đọc secret từ Key Vault
    SECRET=$(curl -s -H "Authorization: Bearer $TOKEN" \
      "https://'"$KV"'.vault.azure.net/secrets/db-connection-string?api-version=7.4" \
      | python3 -c "import sys,json; print(json.load(sys.stdin)[\"value\"])")
    echo "Secret retrieved: ${SECRET:0:30}..."
  ' \
  --query 'value[0].message' -o tsv

# 8. Confirm: từ máy local (không có MI) KHÔNG lấy được
az keyvault secret show \
  --vault-name $KV \
  --name db-connection-string \
  --query value -o tsv
# Expected: lấy được nếu user local có role assignment;
# nếu không: (AuthorizationFailed) - minh họa access control hoạt động

# 9. Xem audit log access Key Vault
az monitor activity-log list \
  --resource-id $KV_ID \
  --start-time $(date -u -d '30 minutes ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
    || date -u -v -30M +%Y-%m-%dT%H:%M:%SZ) \
  --query '[*].{operation:operationName.value,caller:caller,time:eventTimestamp}' \
  --output table

✅ Kết quả mong đợi: VM lấy được secret qua IMDS token mà không có credential nào hardcode; output hiển thị 30 ký tự đầu của connection string. Audit log ghi lại access event với caller là Managed Identity object ID.

🧹 Cleanup: az group delete --name $RG --yes --no-wait

LAB-138

GCP IAM Role Review — IAM Recommender và Workload Identity

gcloud CLI · Google Cloud Console

🎯 Mục tiêu: Kiểm tra IAM binding hiện tại trong GCP project; dùng IAM Recommender để tìm quyền thừa; tạo Service Account tối thiểu quyền và demo Workload Identity Federation cho CI/CD.

🧰 Công cụ / nền tảng: gcloud CLI (gcloud auth login), Google Cloud Console, GCP project active.

📦 Chuẩn bị: gcloud CLI ≥ 450; project ID sẵn sàng.

▶️ Các bước:

# 0. Set project
PROJECT=$(gcloud config get-value project)
echo "Project: $PROJECT"

# 1. List tất cả IAM binding ở project level
gcloud projects get-iam-policy $PROJECT \
  --format='table(bindings.role,bindings.members)' 2>/dev/null \
  || gcloud projects get-iam-policy $PROJECT --format=json \
     | python3 -c "
import sys, json
policy = json.load(sys.stdin)
for b in policy.get('bindings', []):
    print(b['role'], '->', ', '.join(b['members'][:3]))
"

# 2. Tìm Service Account có Editor hoặc Owner role (nguy hiểm)
gcloud projects get-iam-policy $PROJECT --format=json \
  | python3 -c "
import sys, json
policy = json.load(sys.stdin)
dangerous = ['roles/editor', 'roles/owner', 'roles/iam.securityAdmin']
for b in policy.get('bindings', []):
    if b['role'] in dangerous:
        sas = [m for m in b['members'] if 'serviceAccount' in m]
        if sas:
            print('WARN:', b['role'], '->', sas)
"

# 3. Tạo Service Account least-privilege cho Cloud Storage read-only
gcloud iam service-accounts create storage-reader \
  --display-name="Storage Reader SA" \
  --description="Read-only access to specific bucket"

SA_EMAIL="storage-reader@$PROJECT.iam.gserviceaccount.com"

# 4. Tạo bucket test
BUCKET="gcp-sec-lab-$(date +%s)"
gsutil mb -l asia-southeast1 gs://$BUCKET
echo "test content" | gsutil cp - gs://$BUCKET/test.txt

# 5. Gán roles/storage.objectViewer chỉ trên bucket (resource-level binding)
gsutil iam ch serviceAccount:$SA_EMAIL:objectViewer gs://$BUCKET
echo "Granted objectViewer on gs://$BUCKET to $SA_EMAIL"

# 6. Verify binding
gsutil iam get gs://$BUCKET | python3 -c "
import sys, json
policy = json.load(sys.stdin)
for b in policy.get('bindings', []):
    print(b['role'], '->', b['members'])
"

# 7. Xem IAM Recommender suggestions (cần API enabled)
gcloud services enable recommender.googleapis.com --quiet 2>/dev/null || true

gcloud recommender recommendations list \
  --project=$PROJECT \
  --location=global \
  --recommender=google.iam.policy.Recommender \
  --format='table(name.basename(),description,stateInfo.state,priority)' \
  --limit=10 2>/dev/null \
  || echo "No recommendations yet (may need 30+ days of usage data)"

# 8. Demo Workload Identity Federation concept (GitHub Actions)
# Tạo Workload Identity Pool cho GitHub Actions
gcloud iam workload-identity-pools create "github-pool" \
  --location="global" \
  --description="WIF pool for GitHub Actions" \
  --display-name="GitHub Actions Pool" 2>/dev/null || echo "Pool exists"

POOL_ID=$(gcloud iam workload-identity-pools describe github-pool \
  --location=global --format='value(name)')
echo "Pool: $POOL_ID"

# Tạo OIDC provider cho GitHub
gcloud iam workload-identity-pools providers create-oidc "github-provider" \
  --location="global" \
  --workload-identity-pool="github-pool" \
  --issuer-uri="https://token.actions.githubusercontent.com" \
  --attribute-mapping="google.subject=assertion.sub,attribute.repository=assertion.repository" \
  --attribute-condition="assertion.repository=='your-org/your-repo'" \
  2>/dev/null || echo "Provider exists or needs org/repo substitution"

echo "WIF configured — GitHub Actions can now AssumeRole without service account key"

✅ Kết quả mong đợi: IAM binding list hiển thị rõ các role nguy hiểm nếu có; SA storage-reader chỉ có objectViewer trên bucket cụ thể; Workload Identity Pool tạo thành công — GitHub Actions có thể lấy GCP token mà không cần service account key file.

🧹 Cleanup:

gsutil rm -r gs://$BUCKET
gcloud iam service-accounts delete $SA_EMAIL --quiet
gcloud iam workload-identity-pools delete github-pool --location=global --quiet 2>/dev/null || true
LAB-139

Cloud Logging Baseline — CloudTrail, Azure Monitor, GCP Audit Logs

AWS CLI · Azure CLI · gcloud CLI

🎯 Mục tiêu: Bật và xác nhận audit log trên cả ba cloud; truy vấn event IAM thay đổi trong 1 giờ qua; thiết lập alert khi root/admin login.

🧰 Công cụ / nền tảng: AWS CLI, Azure CLI, gcloud CLI — ba cloud accounts.

📦 Chuẩn bị: Đăng nhập sẵn cả ba CLI.

▶️ Phần A: AWS CloudTrail

# 1. Kiểm tra CloudTrail đang bật (trail nào đang active)
aws cloudtrail describe-trails --include-shadow-trails false \
  --query 'trailList[*].{Name:Name,MultiRegion:IsMultiRegionTrail,LogFileValidation:LogFileValidationEnabled}' \
  --output table

# 2. Xem IAM event trong 1 giờ qua
aws cloudtrail lookup-events \
  --lookup-attributes AttributeKey=EventSource,AttributeValue=iam.amazonaws.com \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
    || date -u -v -1H +%Y-%m-%dT%H:%M:%SZ) \
  --query 'Events[*].{Time:EventTime,Name:EventName,User:Username,Source:EventSource}' \
  --output table \
  --max-results 20

# 3. Tạo CloudWatch Metric Filter + Alert cho root login
# (yêu cầu CloudTrail ghi vào CloudWatch Logs)
TRAIL_LOG_GROUP=$(aws cloudtrail describe-trails \
  --query 'trailList[0].CloudWatchLogsLogGroupArn' --output text \
  | sed 's/.*log-group://' | sed 's/:.*//')

if [ -n "$TRAIL_LOG_GROUP" ] && [ "$TRAIL_LOG_GROUP" != "None" ]; then
  # Tạo metric filter cho root login
  aws logs put-metric-filter \
    --log-group-name "$TRAIL_LOG_GROUP" \
    --filter-name RootLoginDetection \
    --filter-pattern '{ $.userIdentity.type = "Root" && $.eventType = "AwsConsoleSignIn" }' \
    --metric-transformations \
      metricName=RootLoginCount,metricNamespace=SecurityAlerts,metricValue=1

  echo "Metric filter created: RootLoginDetection"
else
  echo "No CloudWatch Logs integration — create trail with CW Logs first"
fi

▶️ Phần B: Azure — Activity Log & Alert

# 4. Xem Azure Activity Log — IAM changes trong 1 giờ qua
az monitor activity-log list \
  --start-time $(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null \
    || date -u -v -1H +%Y-%m-%dT%H:%M:%SZ) \
  --filter "eventName eq 'Write'" \
  --query '[?contains(operationName.value,`Microsoft.Authorization`)].
    {time:eventTimestamp,op:operationName.value,caller:caller,status:status.value}' \
  --output table \
  --max-events 20

# 5. Kiểm tra Diagnostic Settings đang ghi đủ category
SUB=$(az account show --query id -o tsv)
az monitor diagnostic-settings list \
  --resource /subscriptions/$SUB \
  --query '[*].{name:name,categories:logs[*].category}' \
  --output json 2>/dev/null | head -20

# 6. Tạo Alert Rule khi có role assignment thay đổi
RG="monitoring-rg"
az group create --name $RG --location southeastasia --output none

az monitor action-group create \
  --name SecurityAlerts \
  --resource-group $RG \
  --short-name SecAlert \
  --output none

az monitor activity-log alert create \
  --name "IAM-RoleAssignment-Change" \
  --resource-group $RG \
  --scopes /subscriptions/$SUB \
  --condition category=Administrative and operationName=Microsoft.Authorization/roleAssignments/write \
  --action-group SecurityAlerts \
  --description "Alert when any role assignment is created" \
  --output table

▶️ Phần C: GCP — Cloud Audit Logs

# 7. Xem GCP IAM audit events trong 1 giờ qua
gcloud logging read \
  'logName="projects/'$(gcloud config get-value project)'/logs/cloudaudit.googleapis.com%2Factivity"
   AND protoPayload.serviceName="iam.googleapis.com"' \
  --freshness=1h \
  --format='table(timestamp,protoPayload.methodName,protoPayload.authenticationInfo.principalEmail)' \
  --limit=20

# 8. Kiểm tra Data Access audit log đã bật chưa (cần cho compliance)
gcloud projects get-iam-policy $(gcloud config get-value project) \
  --format=json | python3 -c "
import sys, json
p = json.load(sys.stdin)
audit = p.get('auditConfigs', [])
if audit:
    for a in audit:
        print(a['service'], '->', [l['logType'] for l in a.get('auditLogConfigs', [])])
else:
    print('WARNING: No custom audit config — Data Access logs may be disabled')
    print('Recommendation: Enable DATA_READ, DATA_WRITE for sensitive services')
"

# 9. Bật Data Access log cho Cloud Storage (ví dụ)
cat > audit-policy.json <<'EOF'
{
  "auditConfigs": [{
    "service": "storage.googleapis.com",
    "auditLogConfigs": [
      {"logType": "DATA_READ"},
      {"logType": "DATA_WRITE"}
    ]
  }]
}
EOF
# Note: merge với existing policy trước khi apply trong production
echo "To apply: gcloud projects set-iam-policy PROJECT audit-policy.json"
echo "WARNING: Merge with existing policy first to avoid removing other bindings"

✅ Kết quả mong đợi: Mỗi cloud hiện được danh sách IAM events trong 1 giờ. AWS metric filter tạo thành công. Azure alert rule active với condition role assignment write. GCP in ra warning nếu Data Access log chưa bật.

🧹 Cleanup: az group delete --name monitoring-rg --yes --no-wait; xóa AWS metric filter nếu không cần; GCP không tạo resource có chi phí.

LAB-140

CSPM với Prowler — Scan AWS và Đọc Security Posture Report

Python · Prowler · AWS CLI

🎯 Mục tiêu: Cài Prowler, chạy scan AWS account với CIS benchmark, đọc HTML report, xác định và remediate ít nhất 3 finding Critical/High.

🧰 Công cụ / nền tảng: Python 3.9+, pip, AWS credentials (read-only SecurityAudit policy đủ), Linux/WSL2.

📦 Chuẩn bị: AWS credentials có SecurityAudit managed policy hoặc ReadOnlyAccess; Python 3.9+.

▶️ Các bước:

# 1. Cài Prowler trong virtualenv
python3 -m venv prowler-env
source prowler-env/bin/activate   # Linux/WSL2
# prowler-env\Scripts\activate   # Windows PowerShell

pip install prowler
prowler --version
# Expected: Prowler vX.X.X (3.x hoặc 4.x)

# 2. Xác nhận AWS credentials
aws sts get-caller-identity
# Expected: JSON với Account, UserId, Arn

# 3. Chạy scan nhanh — chỉ check nhóm IAM và S3 (để tiết kiệm thời gian)
prowler aws \
  --services iam s3 \
  --compliance cis_level1_aws \
  --output-formats html json \
  --output-directory ./prowler-output \
  --log-level ERROR

# 4. Xem summary sau khi scan
ls -la ./prowler-output/
# Có file .html và .json

# 5. Parse JSON để xem top findings
python3 <<'PYEOF'
import json, glob

files = glob.glob("./prowler-output/*.json")
findings = []
for f in files:
    with open(f) as fh:
        for line in fh:
            try:
                r = json.loads(line)
                if r.get("Status") == "FAIL":
                    findings.append({
                        "severity": r.get("Severity",""),
                        "check": r.get("CheckID",""),
                        "title": r.get("CheckTitle","")[:60],
                        "region": r.get("Region","global"),
                        "resource": str(r.get("ResourceId",""))[:40]
                    })
            except: pass

# Sort by severity
sev_order = {"critical": 0, "high": 1, "medium": 2, "low": 3, "informational": 4}
findings.sort(key=lambda x: sev_order.get(x["severity"].lower(), 99))

print(f"\nTotal FAIL: {len(findings)}")
print(f"{'SEV':<12} {'CHECK':<25} {'TITLE':<60}")
print("-" * 100)
for f in findings[:20]:
    print(f"{f['severity']:<12} {f['check']:<25} {f['title']:<60}")
PYEOF

# 6. Chạy scan đầy đủ (tất cả service — mất 15-30 phút)
# prowler aws --compliance cis_level2_aws --output-formats html json --output-directory ./prowler-full

# 7. Ví dụ remediate finding thường gặp: S3 bucket không bật versioning
# (sau khi tìm thấy trong report)
# aws s3api put-bucket-versioning \
#   --bucket BUCKET_NAME \
#   --versioning-configuration Status=Enabled

# 8. Ví dụ remediate: disable unused IAM access key > 90 days
# aws iam list-access-keys --user-name USERNAME
# aws iam update-access-key --access-key-id KEYID --status Inactive --user-name USERNAME

# 9. Chạy lại check cụ thể để verify fix
# prowler aws --checks s3_bucket_versioning_enabled \
#   --output-formats json --output-directory ./prowler-verify

✅ Kết quả mong đợi: Prowler chạy thành công, sinh ra file HTML có thể mở trong browser. JSON output parse được danh sách FAIL findings sắp xếp theo severity. Ít nhất xác định được 3 finding Critical/High với CheckID cụ thể để remediate.

🧹 Cleanup:

deactivate
rm -rf prowler-env prowler-output prowler-full 2>/dev/null || true

3. Tình huống doanh nghiệp thực tế

Bối cảnh

Một tập đoàn bán lẻ vận hành workload trên cả AWS (e-commerce) và Azure (ERP + M365). Sau breach nhỏ — attacker lấy được access key từ git history — CISO yêu cầu zero-standing-privilege và full auditability trong 60 ngày.

Giải pháp theo lớp

  • Tuần 1 — Incident Response: Revoke tất cả long-lived access key; rotate secret trong AWS Secrets Manager và Azure Key Vault; bật CloudTrail + Azure Activity Log với CloudWatch/Sentinel integration.
  • Tuần 2–3 — IAM Remediation: Chạy Prowler + ScoutSuite để baseline. Xóa unused IAM user; chuyển workload sang IAM Role (AWS) và Managed Identity (Azure). Gắn Permission Boundary cho tất cả developer role.
  • Tuần 4–5 — Network Hardening: Review Security Group / NSG: xóa rule 0.0.0.0/0 không cần thiết; bật VPC Flow Logs và NSG Flow Logs export vào S3/Storage Account. Bật AWS GuardDuty và Azure Defender for Cloud (Plan Standard).
  • Tuần 6–7 — Encryption & Secret: Bật KMS customer-managed key cho RDS, S3, EBS. Enforce TLS 1.2 minimum trên tất cả load balancer. Migrate app secret từ hardcode env var sang Secrets Manager / Key Vault — scan git history với truffleHog để tìm leak.
  • Tuần 8 — CSPM CI/CD Integration: Tích hợp Prowler vào GitHub Actions — pipeline fail nếu có finding Critical mới. Setup weekly Secure Score report từ Defender for Cloud gửi email CISO.
  • Kết quả: Zero long-lived key; Secure Score AWS từ 42% lên 78% (CIS Level 1 compliant); Azure Defender Secure Score 81%; audit trail đầy đủ cho PCI-DSS requirement 10.

📚 Nguồn tham khảo

Module 27: Kubernetes Security Module 29: FinOps for Cloud & DevOps
Zalo