Security posture is not a checkbox
Most organizations treat security as a set of controls to implement: enable encryption, configure firewalls, set up IAM roles. Check, check, check. Then a misconfigured S3 bucket leaks customer data, and everyone asks how it happened despite βpassing the audit.β
The problem is treating security as a state rather than a posture β a continuous, measurable, adaptive position that evolves with your infrastructure.
Security posture answers: at any given moment, how defensible is our infrastructure against known attack patterns? Not βdid we configure it correctly six months agoβ but βis it correct right now?β
The anatomy of security posture
Security posture has four dimensions:
1. Visibility β what do you actually have?
You cannot secure what you cannot see. The first step is a complete inventory:
# Infrastructure inventory dimensions
compute:
- VMs, containers, serverless functions
- Who created them, when, why
- What software runs on them
- What network access they have
identity:
- Service accounts, IAM roles, API keys
- Who has access to what
- When was access last used
- Overprivileged accounts
data:
- Where is sensitive data stored
- Who can access it
- Is it encrypted at rest and in transit
- What are the retention policies
network:
- Ingress and egress rules
- Public-facing endpoints
- Internal service-to-service paths
- DNS configurationsIn Kubernetes, this means knowing every workload, every ServiceAccount, every NetworkPolicy gap:
# Find pods running as root
kubectl get pods -A -o json | jq -r '
.items[] |
select(.spec.containers[].securityContext.runAsUser == 0
or .spec.containers[].securityContext.runAsUser == null) |
"\(.metadata.namespace)/\(.metadata.name)"'
# Find ServiceAccounts with cluster-admin
kubectl get clusterrolebindings -o json | jq -r '
.items[] |
select(.roleRef.name == "cluster-admin") |
.subjects[]? | "\(.kind): \(.namespace)/\(.name)"'
# Find namespaces without NetworkPolicies
for ns in $(kubectl get ns -o name | cut -d/ -f2); do
count=$(kubectl get netpol -n $ns --no-headers 2>/dev/null | wc -l)
[ "$count" -eq 0 ] && echo "NO NETPOL: $ns"
done2. Configuration β is everything set up correctly?
Misconfigurations are the number one cause of cloud breaches. CSPM tools continuously scan for:
# Common misconfigurations by severity
critical:
- Public S3 buckets with sensitive data
- Databases accessible from the internet
- IAM roles with * permissions
- Unencrypted secrets in environment variables
- Containers running as root with host network
high:
- Security groups allowing 0.0.0.0/0 on non-web ports
- Missing MFA on admin accounts
- Default credentials on managed services
- Missing encryption at rest
- Overprivileged service accounts
medium:
- Missing access logging on storage
- Outdated TLS versions (< 1.2)
- Missing resource tags for ownership
- No lifecycle policies on object storage
- Missing pod security standards3. Compliance β do you meet regulatory requirements?
Compliance frameworks map to specific controls:
CIS Benchmark β Technical configuration baselines
SOC 2 β Operational security controls
ISO 27001 β Information security management
GDPR / EU CRA β Data protection and cyber resilience
PCI DSS β Payment card data security
NIST 800-53 β Federal information systemsThe key insight: compliance is a byproduct of good security posture, not the other way around. If your posture is strong, compliance mostly takes care of itself.
4. Response β how fast can you detect and react?
Detection time β How quickly do you spot a misconfiguration?
Remediation time β How quickly can you fix it?
Blast radius β How much damage can occur before containment?
Recovery time β How quickly can you restore normal operations?Building CSPM with policy-as-code
The most effective approach: define security policies as code, enforce them automatically, and measure continuously.
Kubernetes: Kyverno policies
# Deny privileged containers
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: deny-privileged
spec:
validationFailureAction: Enforce
rules:
- name: deny-privileged-containers
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Privileged containers are not allowed"
pattern:
spec:
containers:
- securityContext:
privileged: "false"
---
# Require resource limits
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-limits
spec:
validationFailureAction: Enforce
rules:
- name: require-cpu-memory-limits
match:
any:
- resources:
kinds:
- Pod
validate:
message: "CPU and memory limits are required"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"For more on Kyverno, see my post on Kyverno graduating in the CNCF.
Cloud: Open Policy Agent / Terraform
# OPA policy: deny public S3 buckets
package aws.s3
deny[msg] {
input.resource.type == "aws_s3_bucket"
not input.resource.values.block_public_access
msg := sprintf("S3 bucket '%s' must have public access blocked",
[input.resource.name])
}
deny[msg] {
input.resource.type == "aws_s3_bucket"
input.resource.values.acl == "public-read"
msg := sprintf("S3 bucket '%s' has public-read ACL",
[input.resource.name])
}CI/CD enforcement
# GitHub Actions: security gate
- name: Scan Terraform
run: |
tfsec . --format json > tfsec-results.json
CRITICAL=$(cat tfsec-results.json | jq '[.results[] | select(.severity == "CRITICAL")] | length')
if [ "$CRITICAL" -gt 0 ]; then
echo "::error::$CRITICAL critical security issues found"
exit 1
fi
- name: Scan Kubernetes manifests
run: |
kubescape scan . --format json --output kubescape-results.json
SCORE=$(cat kubescape-results.json | jq '.summaryDetails.complianceScore')
if (( $(echo "$SCORE < 80" | bc -l) )); then
echo "::error::Security score $SCORE below threshold (80)"
exit 1
fiThe security posture scorecard
Measure your posture with a scorecard:
security_posture_scorecard:
identity_and_access:
mfa_enabled_all_admins: true # Weight: 10
no_overprivileged_roles: true # Weight: 8
service_accounts_audited: true # Weight: 7
unused_credentials_removed: true # Weight: 6
score: 31/31
network:
no_public_databases: true # Weight: 10
deny_default_network_policies: true # Weight: 9
tls_1_2_minimum: true # Weight: 7
egress_filtered: false # Weight: 6
score: 26/32
data:
encryption_at_rest: true # Weight: 9
encryption_in_transit: true # Weight: 9
backup_tested: true # Weight: 8
sensitive_data_classified: false # Weight: 7
score: 26/33
compute:
no_privileged_containers: true # Weight: 9
images_scanned: true # Weight: 8
resource_limits_enforced: true # Weight: 7
pod_security_standards: true # Weight: 7
score: 31/31
overall: 114/127 = 89.8%This score gets tracked over time. Regressions trigger alerts. Improvements get celebrated.
Integrating with your platform
For teams building internal developer platforms, security posture should be a first-class metric on every teamβs dashboard β not buried in a security teamβs spreadsheet.
When developers can see their teamβs security score and understand exactly what to fix, posture improves organically. The security team shifts from gatekeeper to enabler.
Need help building a security posture program for your cloud infrastructure? Get in touch for security assessments, policy-as-code implementation, and compliance automation.