IaC Security: Securing Terraform, Docker & Kubernetes Before Deployment

SCR Security Research Team
February 1, 2026
21 min read
360 words
Share

Infrastructure as Code Is Infrastructure as Vulnerability

Infrastructure as Code (IaC) transformed how we deploy systems — but it also codified our misconfigurations. According to Bridgecrew's 2025 State of IaC Security Report:

FindingValue
IaC templates with at least one misconfiguration67%
Average misconfigurations per template12.7
Most common: overly permissive IAM policies38% of all misconfigs
Second: unencrypted data storage29%
Third: excessive network exposure22%

The Advantage: Unlike manual infrastructure, IaC misconfigurations can be found and fixed before deployment. This is the power of shift-left for infrastructure — scan the code, not the running system.


Terraform Security

The 10 Most Common Terraform Misconfigurations

#MisconfigurationRiskFix
1S3 bucket public accessData exposureblock_public_access = true
2Security group 0.0.0.0/0 on SSHUnauthorized accessRestrict to known CIDRs
3No encryption at restData exposureEnable encryption on all storage
4IAM * permissionsPrivilege escalationLeast-privilege policies
5No CloudTrail loggingNo audit trailEnable multi-region CloudTrail
6Default VPC usageShared network riskCreate custom VPC
7No MFA delete on S3Accidental/malicious deletionEnable MFA delete
8RDS publicly accessibleDatabase exposurepublicly_accessible = false
9No lifecycle policiesCost + stale resourcesSet TTLs and rotation
10Hardcoded secretsCredential exposureUse aws_secretsmanager_secret

Secure Terraform Patterns

# SECURE — S3 bucket with full security hardening
resource "aws_s3_bucket" "data" {
  bucket = "company-data-prod"
}

resource "aws_s3_bucket_public_access_block" "data" {
  bucket = aws_s3_bucket.data.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

resource "aws_s3_bucket_server_side_encryption_configuration" "data" {
  bucket = aws_s3_bucket.data.id
  rule {
    apply_server_side_encryption_by_default {
      sse_algorithm     = "aws:kms"
      kms_master_key_id = aws_kms_key.s3.arn
    }
  }
}

resource "aws_s3_bucket_versioning" "data" {
  bucket = aws_s3_bucket.data.id
  versioning_configuration { status = "Enabled" }
}

resource "aws_s3_bucket_logging" "data" {
  bucket        = aws_s3_bucket.data.id
  target_bucket = aws_s3_bucket.logs.id
  target_prefix = "s3-access-logs/"
}

Terraform Security Scanning Tools

ToolChecksIntegration
Checkov2,500+ rules across AWS/Azure/GCPCLI, CI/CD, IDE
tfsecTerraform-specific security rulesCLI, GitHub Actions
TerrascanMulti-IaC (Terraform, K8s, Helm)CLI, CI/CD
SentinelHashiCorp policy-as-codeTerraform Cloud/Enterprise
OPA/ConftestGeneral purpose policy engineAny IaC format

Docker Security

Docker Security Hardening Checklist

# SECURE — Production-ready Dockerfile

# 1. Use specific, minimal base image (not 'latest')
FROM node:20-alpine AS builder

# 2. Create non-root user
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

# 3. Set working directory
WORKDIR /app

# 4. Copy package files first (layer caching)
COPY package.json package-lock.json ./

# 5. Install dependencies (production only, ignore scripts)
RUN npm ci --only=production --ignore-scripts

# 6. Copy application code
COPY --chown=appuser:appgroup . .

# 7. Build application
RUN npm run build

# 8. Multi-stage build — production image
FROM node:20-alpine AS production

# 9. Install security updates
RUN apk update && apk upgrade --no-cache

# 10. Create non-root user in production image
RUN addgroup -g 1001 -S appgroup && \
    adduser -S appuser -u 1001 -G appgroup

WORKDIR /app

# 11. Copy only production artifacts
COPY --from=builder --chown=appuser:appgroup /app/dist ./dist
COPY --from=builder --chown=appuser:appgroup /app/node_modules ./node_modules

# 12. Drop all capabilities, run as non-root
USER appuser

# 13. Set read-only filesystem where possible
# (configure at runtime with --read-only flag)

# 14. Health check
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
  CMD wget -qO- http://localhost:3000/health || exit 1

# 15. Expose only required port
EXPOSE 3000

CMD ["node", "dist/server.js"]

Container Scanning

# Scan Docker image for vulnerabilities
trivy image myapp:latest --severity HIGH,CRITICAL

# Scan and fail on critical findings (CI/CD integration)
trivy image myapp:latest --severity CRITICAL --exit-code 1

# Scan Dockerfile for misconfigurations
trivy config ./Dockerfile

Kubernetes Security

K8s Security Fundamentals

AreaRiskControl
RBACOver-privileged service accountsLeast-privilege ClusterRoles
Pod SecurityContainers running as rootPod Security Standards (restricted)
NetworkUnrestricted pod-to-pod trafficNetwork Policies
SecretsSecrets in etcd unencryptedExternal secrets management
ImagesPulling untrusted imagesImage allowlists, signing
RuntimeContainer escapeSeccomp, AppArmor, read-only FS

Secure Pod Configuration

apiVersion: v1
kind: Pod
metadata:
  name: secure-app
spec:
  # Don't use default service account
  serviceAccountName: app-specific-sa
  automountServiceAccountToken: false

  securityContext:
    runAsNonRoot: true
    runAsUser: 1001
    fsGroup: 1001
    seccompProfile:
      type: RuntimeDefault

  containers:
    - name: app
      image: myregistry.com/app:v1.2.3@sha256:abc123...
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]
      resources:
        limits:
          cpu: "500m"
          memory: "256Mi"
        requests:
          cpu: "100m"
          memory: "128Mi"
      livenessProbe:
        httpGet:
          path: /health
          port: 3000
        initialDelaySeconds: 10
      volumeMounts:
        - name: tmp
          mountPath: /tmp

  volumes:
    - name: tmp
      emptyDir: {}

Kubernetes Network Policy (Zero-Trust Networking)

# Default deny all traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
spec:
  podSelector: {}
  policyTypes:
    - Ingress
    - Egress

---
# Allow only specific traffic
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-frontend-to-api
spec:
  podSelector:
    matchLabels:
      app: api-server
  policyTypes:
    - Ingress
  ingress:
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - port: 3000
          protocol: TCP

IaC Security in CI/CD

Automated IaC Security Pipeline

# Combined IaC security scanning
name: IaC Security
on: [pull_request]

jobs:
  terraform:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Checkov Terraform scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: ./terraform
          framework: terraform
          soft_fail: false  # Block PR on failures

  docker:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: Hadolint Dockerfile lint
        uses: hadolint/hadolint-action@v3.1.0
        with:
          dockerfile: Dockerfile
      - name: Build and scan
        run: |
          docker build -t test:latest .
          trivy image test:latest --exit-code 1 --severity HIGH,CRITICAL

  kubernetes:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - name: KubeSec scan
        run: |
          kubesec scan k8s/*.yaml
      - name: Checkov K8s scan
        uses: bridgecrewio/checkov-action@master
        with:
          directory: ./k8s
          framework: kubernetes

Further Reading

Editorial standards

Published by SecureCodeReviews

This article is part of our original AI security and cybersecurity content library. We show publish and update dates, keep company and policy pages public, and update important guidance when material changes affect readers.

Named author: SCR Security Research Team
Published: Feb 1, 2026
Update status: current publication version

Questions or corrections?

Review our editorial standards, learn more about the company, or contact us if a page needs clarification.

AI Security Audit

Planning an AI feature launch or security review?

We assess prompt injection paths, data leakage, tool use, access control, and unsafe AI workflows before they become production problems.

Manual review for agent, prompt, and retrieval attack paths
Actionable remediation guidance for your AI stack
Coverage for LLM apps, MCP integrations, and internal AI tools

Talk to SecureCodeReviews

Get a scoped review path fast

Manual review
Actionable fixes
Fast turnaround
Security-focused

Advertisement