Security7 min read571 lines

Learning state

Track this guide

Saved in this browser only. No account required.

Security & Compliance Master Class

Engineering-grade reference manual for practical DevOps security, compliance controls, and audit-ready automation.

Overview

Security is a production operating discipline, not a final checklist. This guide focuses on command-line workflows and repeatable controls that infrastructure engineers can use across containers, Kubernetes, CI/CD, cloud accounts, Linux hosts, and compliance programs.

Use it as a practical companion to the Docker, Kubernetes, Terraform, Ansible, CI/CD, monitoring, and troubleshooting guides.

Core Principles

  • Default deny: only open what is required.
  • Least privilege: grant narrow permissions for a specific job.
  • Defense in depth: combine identity, network, runtime, secrets, logging, and backup controls.
  • Evidence first: every important control should produce inspectable output.
  • Automate repeatability: manual hardening does not scale.
  • Rotate and revoke: assume credentials eventually leak.
  • Log security events centrally and test alerting.

Security Baseline Checklist

Area Baseline Control Evidence
Identity MFA, SSO, least-privilege roles IAM policy exports, access reviews
Hosts patched OS, firewall, SSH key auth package and firewall reports
Containers signed/scanned images, non-root users scan reports, Dockerfiles
Kubernetes RBAC, network policies, admission controls policy manifests, audit logs
Secrets vault-backed storage, rotation secret inventory, rotation log
CI/CD protected branches, secret scanning, SAST workflow logs, scan artifacts
Compliance mapped controls and evidence trail control matrix, dated exports

Container Image Security

Scan images before promotion. Trivy is a practical default because it can scan images, filesystems, repos, SBOMs, and IaC.

trivy image nginx:latest

Fail CI builds on high and critical vulnerabilities:

trivy image --severity HIGH,CRITICAL --exit-code 1 nginx:latest

Generate JSON evidence for audit storage:

trivy image --format json --output trivy-nginx-report.json nginx:latest

Scan a Dockerfile and build context:

trivy fs --scanners vuln,secret,misconfig .

Use pinned images rather than floating tags:

docker pull nginx@sha256:REPLACE_WITH_DIGEST

Inspect image users and entrypoints:

docker image inspect nginx:latest --format '{{json .Config.User}} {{json .Config.Entrypoint}} {{json .Config.Cmd}}'

Docker Runtime Hardening

Prefer read-only filesystems and explicit writable mounts:

docker run --read-only --tmpfs /tmp:rw,noexec,nosuid,size=64m nginx:latest

Drop Linux capabilities by default and add back only what is required:

docker run --cap-drop ALL --cap-add NET_BIND_SERVICE nginx:latest

Run as a non-root user when the image supports it:

docker run --user 10001:10001 nginx:latest

Limit memory and CPU to reduce blast radius:

docker run --memory 256m --cpus 0.5 nginx:latest

Review running container security posture:

docker inspect web --format '{{json .HostConfig.SecurityOpt}} {{json .HostConfig.CapDrop}} {{json .HostConfig.ReadonlyRootfs}}'

Kubernetes Security Baseline

Check RBAC grants:

kubectl auth can-i --list --namespace production

Validate a service account can only do what it needs:

kubectl auth can-i get pods --as system:serviceaccount:production:webapp -n production

Find pods running as root:

kubectl get pods -A -o jsonpath='{range .items[*]}{.metadata.namespace}{"/"}{.metadata.name}{" user="}{.spec.securityContext.runAsUser}{"\n"}{end}'

Apply a restricted pod security standard label:

kubectl label namespace production pod-security.kubernetes.io/enforce=restricted

Example pod security context:

apiVersion: v1
kind: Pod
metadata:
  name: secure-web
spec:
  securityContext:
    runAsNonRoot: true
    seccompProfile:
      type: RuntimeDefault
  containers:
    - name: web
      image: nginx:1.27
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop: ["ALL"]

Kubernetes Network Policies

Default-deny ingress in a namespace:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-ingress
  namespace: production
spec:
  podSelector: {}
  policyTypes:
    - Ingress

Allow traffic from the ingress controller to web pods:

apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: allow-ingress-to-web
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: web
  ingress:
    - from:
        - namespaceSelector:
            matchLabels:
              kubernetes.io/metadata.name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

List active network policies:

kubectl get networkpolicy -A

Secrets Management

Never store real secrets in Git. Use placeholders in examples and a dedicated secrets system in production.

Detect accidental secrets before commit:

gitleaks detect --source . --redact

Scan staged changes:

gitleaks protect --staged --redact

Use age for simple encrypted files:

age -r age1examplepublicrecipient -o secrets.env.age secrets.env

Decrypt only in trusted environments:

age -d -i ~/.config/age/keys.txt secrets.env.age

Kubernetes secret creation with placeholder values:

kubectl create secret generic app-config \
  --from-literal=DATABASE_URL='[REDACTED]' \
  --from-literal=TOKEN_VALUE='[REDACTED]' \
  --dry-run=client -o yaml

HashiCorp Vault Operations

Check Vault health:

vault status

Write a placeholder secret for a development path:

vault kv put secret/dev/webapp username='demo-user' password='[REDACTED]'

Read metadata without printing sensitive values in logs:

vault kv metadata get secret/dev/webapp

Create a narrow policy:

path "secret/data/dev/webapp" {
  capabilities = ["read"]
}

Apply the policy:

vault policy write webapp-readonly webapp-readonly.hcl

TLS and Certificate Operations

Inspect a certificate chain:

openssl s_client -connect example.com:443 -servername example.com -showcerts </dev/null

Show certificate dates:

echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

Check a local certificate file:

openssl x509 -in cert.pem -noout -subject -issuer -dates -fingerprint -sha256

Request a Let's Encrypt certificate with certbot webroot:

certbot certonly --webroot -w /var/www/html -d example.com

Reload NGINX after renewal:

nginx -t && systemctl reload nginx

Linux Host Hardening

List listening services:

ss -tulpn

Check failed SSH logins:

journalctl -u ssh --since "24 hours ago" | grep -i failed

Audit users with login shells:

awk -F: '$7 !~ /(nologin|false)$/ {print $1, $7}' /etc/passwd

List sudoers configuration safely:

visudo -c && grep -R "^[^#]" /etc/sudoers /etc/sudoers.d 2>/dev/null

Enable uncomplicated firewall defaults:

ufw default deny incoming
ufw default allow outgoing
ufw allow OpenSSH
ufw enable

Review firewall status:

ufw status verbose

Cloud IAM Review

AWS identity check:

aws sts get-caller-identity

List AWS access keys for a user:

aws iam list-access-keys --user-name alice

Generate an AWS credential report:

aws iam generate-credential-report
aws iam get-credential-report --query Content --output text | base64 --decode > credential-report.csv

Azure identity check:

az account show --query '{name:name, tenant:tenantId, user:user.name}' -o table

List Azure role assignments for a principal:

az role assignment list --assignee USER_OR_SERVICE_PRINCIPAL_ID -o table

CI/CD Security Gates

Run secret scanning in pull requests:

name: security
on: [pull_request]
jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: gitleaks/gitleaks-action@v2

Run dependency review for GitHub projects:

name: dependency-review
on: [pull_request]
jobs:
  dependency-review:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/dependency-review-action@v4

Scan container images in CI:

name: image-scan
on: [push]
jobs:
  trivy:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: aquasecurity/trivy-action@master
        with:
          scan-type: fs
          severity: HIGH,CRITICAL
          exit-code: '1'

Policy as Code

Evaluate Kubernetes manifests with Conftest:

conftest test k8s/*.yaml

Example Rego policy to require non-root containers:

package kubernetes.security

deny[msg] {
  input.kind == "Pod"
  container := input.spec.containers[_]
  not container.securityContext.runAsNonRoot
  msg := sprintf("container %s must set runAsNonRoot", [container.name])
}

Run Checkov against Terraform:

checkov -d terraform/

Run tfsec against Terraform:

tfsec terraform/

Compliance Evidence Workflow

Create a dated evidence directory:

mkdir -p evidence/$(date +%Y-%m-%d)

Capture host patch status:

apt list --upgradable > evidence/$(date +%Y-%m-%d)/apt-upgradable.txt

Capture Kubernetes policy state:

kubectl get networkpolicy -A -o yaml > evidence/$(date +%Y-%m-%d)/networkpolicies.yaml

Capture cloud identity state:

aws iam get-account-summary > evidence/$(date +%Y-%m-%d)/aws-account-summary.json

Hash evidence files:

find evidence/$(date +%Y-%m-%d) -type f -print0 | sort -z | xargs -0 shasum -a 256 > evidence/$(date +%Y-%m-%d)/SHA256SUMS

SOC 2 Control Mapping Starter

SOC 2 Area Example Control Evidence Command
CC6 Access Control Quarterly access review aws iam get-account-authorization-details
CC7 Operations Vulnerability monitoring trivy image --format json
CC8 Change Management PR approvals and CI gates gh pr view --json reviews,statusCheckRollup
A1 Availability Backup and restore tests restore logs, monitoring screenshots
C1 Confidentiality Secrets not stored in Git gitleaks detect --redact

Incident Response Commands

Preserve current process state:

ps auxww > incident-processes.txt

Capture network connections:

ss -tupna > incident-connections.txt

Capture recent auth logs:

journalctl --since "6 hours ago" > incident-journal.txt

Export Kubernetes events:

kubectl get events -A --sort-by=.lastTimestamp > incident-k8s-events.txt

Quarantine a Kubernetes deployment by scaling it down:

kubectl scale deployment compromised-app --replicas=0 -n production

Practical Security Review Order

  1. Confirm identity and access boundaries.
  2. Patch high-risk hosts and dependencies.
  3. Scan repositories and images for secrets and vulnerabilities.
  4. Enforce Kubernetes pod and network policies.
  5. Validate backups and restore paths.
  6. Centralize logs and alert on high-signal events.
  7. Export evidence and map it to controls.
  8. Schedule the next access and vulnerability review.

Common Pitfalls

  • Treating compliance as screenshots instead of repeatable evidence.
  • Letting administrator access become the default troubleshooting path.
  • Storing real credentials in examples, tickets, chat logs, or wiki pages.
  • Ignoring container runtime settings because the image scan passed.
  • Enabling audit logs without testing retrieval during an incident.
  • Creating policies that are never enforced in CI or admission control.

Quick Reference

# repo secret scan
gitleaks detect --source . --redact

# container vulnerability scan
trivy image --severity HIGH,CRITICAL nginx:latest

# Kubernetes RBAC check
kubectl auth can-i --list -n production

# Kubernetes network policies
kubectl get networkpolicy -A

# Linux listening ports
ss -tulpn

# TLS certificate dates
echo | openssl s_client -connect example.com:443 -servername example.com 2>/dev/null | openssl x509 -noout -dates

# AWS caller identity
aws sts get-caller-identity

# Azure account identity
az account show -o table