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
- Confirm identity and access boundaries.
- Patch high-risk hosts and dependencies.
- Scan repositories and images for secrets and vulnerabilities.
- Enforce Kubernetes pod and network policies.
- Validate backups and restore paths.
- Centralize logs and alert on high-signal events.
- Export evidence and map it to controls.
- 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