Audit Logging Should Be Enabled And Shipped Off-Cluster
More Info:
Advisory: Kubernetes API audit logging should be enabled and forwarded to an external, tamper-resistant store so control-plane activity is retained independently of the cluster.
Risk Level
Medium
Address
Security
Compliance Standards
- Cloudanix Best Practice
Triage and Remediation
- Remediation
Remediation
Manual Steps
-
Review EKS control-plane logging configuration (CloudWatch integration)
- On any machine with AWS CLI configured, list current control plane log types:
aws eks describe-cluster \--name YOUR_CLUSTER_NAME \--query 'cluster.logging.clusterLogging'
- Confirm that
"audit"is present in theenabledlist. If not, plan to enable it.
- On any machine with AWS CLI configured, list current control plane log types:
-
Enable/ensure EKS audit logging to CloudWatch Logs
- Using AWS CLI on any machine with appropriate IAM permissions, enable audit logging:
aws eks update-cluster-config \--name YOUR_CLUSTER_NAME \--logging '{"clusterLogging": [{"types": ["audit"],"enabled": true}]}'
- Alternatively in the AWS console: EKS → your cluster → Configuration → Logging → Manage logging → check Audit → Save changes.
- Using AWS CLI on any machine with appropriate IAM permissions, enable audit logging:
-
Verify audit events are being produced
- In the AWS console, open CloudWatch Logs → Log groups, find the group named like:
/aws/eks/YOUR_CLUSTER_NAME/cluster - Open the log group and check that a stream with recent timestamps contains Kubernetes API audit records (JSON entries with fields like
verb,user,objectRef). - From CLI, confirm the log group exists and has recent events:
aws logs describe-log-groups \--log-group-name-prefix "/aws/eks/YOUR_CLUSTER_NAME/cluster"aws logs filter-log-events \--log-group-name "/aws/eks/YOUR_CLUSTER_NAME/cluster" \--limit 5
- In the AWS console, open CloudWatch Logs → Log groups, find the group named like:
-
Configure and verify off-cluster, tamper-resistant retention of audit logs
- Decide on an external store (commonly: S3 + KMS + write-only IAM, or a third-party SIEM).
- For S3 via CloudWatch Logs subscription (any machine with AWS CLI):
- Create or identify a target S3 bucket with appropriate bucket policy preventing non-audited deletion/modification.
- Create a CloudWatch Logs subscription filter to a Kinesis stream, Firehose, or Lambda that writes to that S3 bucket. Example (Firehose):
aws logs put-subscription-filter \--log-group-name "/aws/eks/YOUR_CLUSTER_NAME/cluster" \--filter-name eks-audit-to-firehose \--filter-pattern "" \--destination-arn arn:aws:firehose:REGION:ACCOUNT_ID:deliverystream/YOUR_FIREHOSE_STREAM
- In your chosen destination (e.g., S3 bucket, SIEM console), verify new Kubernetes audit records are appearing.
-
Harden retention and access controls for the external store
- For S3:
- Configure bucket versioning and lifecycle policies to meet your retention requirements.
- Use a KMS CMK and limit key and bucket access to a small, audited set of roles.
- Optionally configure Object Lock (Compliance or Governance mode) to prevent tampering if compliant with your requirements.
- For SIEM/other: ensure write-only or tightly controlled delete privileges and auditable access logs.
- For S3:
-
Re-verify configuration and document the control
- Re-run:
Confirmaws eks describe-cluster \--name YOUR_CLUSTER_NAME \--query 'cluster.logging.clusterLogging'
"audit"is enabled. - Capture evidence (screenshots/CLI output) of:
- EKS audit logging enabled.
- CloudWatch log group receiving audit records.
- Off-cluster destination receiving the same records and showing appropriate retention/immutability controls.
- Re-run:
Using kubectl
kubectl cannot enable or configure Kubernetes API audit logging on Amazon EKS because this is controlled by the EKS/CloudTrail/CloudWatch/S3 configuration in AWS, not by Kubernetes API objects. To address this finding, configure audit logging and off-cluster forwarding in the AWS console, CLI, or IaC as described in the Manual Steps section.
Automation
#!/usr/bin/env bash
#
# EKS API audit logging state reporter
# Requirements:
# - aws CLI configured with credentials
# - jq installed
#
# Usage:
# ./eks_audit_logging_report.sh <aws-region>
#
# Output:
# One line per cluster with:
# - Cluster name
# - Cluster ARN
# - Logging types and whether they are enabled
# - Whether audit logging is enabled
# - Whether an external, tamper-resistant sink is detected (CloudTrail + CloudWatch Logs / S3)
#
# NOTES:
# - This script assesses control-plane API audit logging at the EKS level.
# - It does NOT change any configuration.
# - “Problem” means: audit logging is disabled OR logs are not going to a durable,
# off-cluster destination.
set -euo pipefail
REGION="${1:-}"
if [ -z "$REGION" ]; then
echo "Usage: $0 <aws-region>" >&2
exit 1
fi
command -v aws >/dev/null 2>&1 || { echo "aws CLI not found in PATH" >&2; exit 1; }
command -v jq >/dev/null 2>&1 || { echo "jq not found in PATH" >&2; exit 1; }
echo "Region: $REGION"
echo "Timestamp: $(date -u +"%Y-%m-%dT%H:%M:%SZ")"
echo
# Get all EKS clusters in the region
CLUSTERS_JSON="$(aws eks list-clusters --region "$REGION" --output json)"
CLUSTER_NAMES=($(echo "$CLUSTERS_JSON" | jq -r '.clusters[]?'))
if [ "${#CLUSTER_NAMES[@]}" -eq 0 ]; then
echo "No EKS clusters found in region $REGION."
exit 0
fi
printf '%-30s %-60s %-30s %-15s %-25s\n' \
"CLUSTER_NAME" "CLUSTER_ARN" "AUDIT_LOG_ENABLED" "PROBLEM" "NOTES"
printf '%*s\n' 168 | tr ' ' '-'
for CLUSTER in "${CLUSTER_NAMES[@]}"; do
DESC="$(aws eks describe-cluster --name "$CLUSTER" --region "$REGION" --output json)"
CLUSTER_ARN=$(echo "$DESC" | jq -r '.cluster.arn')
LOGGING_JSON=$(echo "$DESC" | jq -r '.cluster.logging.clusterLogging')
# Determine if audit logging is enabled
AUDIT_LOG_ENABLED=$(echo "$LOGGING_JSON" \
| jq -r '.[] | select(.types[]? == "audit") | .enabled' 2>/dev/null || true)
if [ -z "$AUDIT_LOG_ENABLED" ]; then
AUDIT_LOG_ENABLED="false"
fi
PROBLEM="false"
NOTES=""
if [ "$AUDIT_LOG_ENABLED" != "true" ]; then
PROBLEM="true"
NOTES="audit logging disabled"
fi
# Check CloudTrail and its destinations as a proxy for "shipped off-cluster"
# This is heuristic and must be reviewed.
TRAILS_JSON="$(aws cloudtrail describe-trails --region "$REGION" --output json)"
CT_HAS_ORG_OR_MULTI_REGION=false
CT_HAS_S3=false
CT_HAS_CLOUDWATCH=false
if echo "$TRAILS_JSON" | jq -e '.trailList | length > 0' >/dev/null 2>&1; then
# Any trail with IsOrganizationTrail or IsMultiRegionTrail set?
if echo "$TRAILS_JSON" | jq -e '.trailList[] | select(.IsOrganizationTrail == true or .IsMultiRegionTrail == true)' >/dev/null 2>&1; then
CT_HAS_ORG_OR_MULTI_REGION=true
fi
# Any trail with S3BucketName set?
if echo "$TRAILS_JSON" | jq -e '.trailList[] | select(.S3BucketName != null and .S3BucketName != "")' >/dev/null 2>&1; then
CT_HAS_S3=true
fi
# Any trail with CloudWatchLogsLogGroupArn set?
if echo "$TRAILS_JSON" | jq -e '.trailList[] | select(.CloudWatchLogsLogGroupArn != null and .CloudWatchLogsLogGroupArn != "")' >/dev/null 2>&1; then
CT_HAS_CLOUDWATCH=true
fi
fi
# Heuristic: if audit logging is enabled AND there is at least one
# CloudTrail trail sending to S3 and/or CloudWatch Logs, we consider logs
# "likely" shipped off-cluster. This still needs human review.
if [ "$AUDIT_LOG_ENABLED" = "true" ]; then
if [ "$CT_HAS_S3" = "true" ] || [ "$CT_HAS_CLOUDWATCH" = "true" ]; then
if [ -z "$NOTES" ]; then
NOTES="audit logging enabled; CloudTrail with S3/CloudWatch detected (review configuration)"
else
NOTES="$NOTES; CloudTrail with S3/CloudWatch detected (review configuration)"
fi
else
PROBLEM="true"
if [ -z "$NOTES" ]; then
NOTES="audit logging enabled but no CloudTrail trail with S3/CloudWatch destination detected"
else
NOTES="$NOTES; no CloudTrail trail with S3/CloudWatch destination detected"
fi
fi
fi
printf '%-30s %-60s %-30s %-15s %-25s\n' \
"$CLUSTER" "$CLUSTER_ARN" "$AUDIT_LOG_ENABLED" "$PROBLEM" "$NOTES"
done
How to run (any machine with AWS CLI access):
- Save as
eks_audit_logging_report.shand make executable:chmod +x eks_audit_logging_report.sh - Run for a region:
./eks_audit_logging_report.sh us-east-1
Interpreting output (what indicates a problem):
AUDIT_LOG_ENABLEDisfalse→ audit logging not enabled for that EKS cluster (non-compliant).PROBLEMistrueandNOTEScontains any of:audit logging disabledaudit logging enabled but no CloudTrail trail with S3/CloudWatch destination detected
These clusters require manual review and, if appropriate, enabling of API audit logging and configuration of a durable, off-cluster log destination.