Skip to content

OpSec Mistakes Primer

Why This Matters

Most security breaches are not sophisticated zero-days. They are preventable operational security failures: secrets committed to git, overly broad IAM permissions, unpatched systems, shared credentials, and default configurations left in production. These mistakes are the low-hanging fruit that attackers exploit first and most frequently. Knowing the common anti-patterns is the first step to not repeating them.

Secrets in Version Control

The Problem

Once a secret hits a git commit, it is in the history forever (until the repo is rewritten or rotated). Even if you delete the file in the next commit, git log -p reveals it. Public repos are scanned by bots within seconds.

War story: GitHub's own 2022 research found that over 10 million secrets were exposed in public commits in a single year. Automated scanners like TruffleHog and GitLeaks can find secrets in seconds — and so can attackers. AWS key pairs committed to public repos are typically exploited within 5 minutes for crypto mining.

Common Leaks

# Things that should NEVER be committed
.env                        # Environment files with credentials
*.pem, *.key               # TLS private keys
credentials.json            # Service account keys
kubeconfig                  # Cluster credentials
terraform.tfstate           # May contain secrets in plaintext
**/id_rsa, **/id_ed25519   # SSH private keys

Prevention

# .gitignore must include
.env
.env.*
*.pem
*.key
*.p12
credentials*.json
terraform.tfstate
terraform.tfstate.backup

# Pre-commit hooks: use tools like gitleaks, trufflehog, or detect-secrets
# Install gitleaks as pre-commit hook
gitleaks protect --staged

Remediation

If committed: rotate the secret immediately (assume compromised), rewrite history with git filter-repo, force-push, and invalidate cached copies (CI, images, artifacts).

Overly Broad Permissions

The Principle of Least Privilege

Grant only the minimum permissions needed for the task, for the shortest duration necessary.

Name origin: The Principle of Least Privilege was formalized by Jerome Saltzer and Michael Schroeder in their 1975 paper "The Protection of Information in Computer Systems." It is one of the oldest security principles still actively applied in modern systems.

Common IAM Mistakes

// NEVER do this in production
{
    "Effect": "Allow",
    "Action": "*",
    "Resource": "*"
}
# NEVER do this in Kubernetes
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dev-team
roleRef:
  kind: ClusterRole
  name: cluster-admin    # Too broad
subjects:
  - kind: Group
    name: developers

Fix: scope RBAC to specific namespaces, resources, and verbs. Use read-only roles where possible. Prefer Role over ClusterRole.

Unpatched Systems

Unpatched systems are the #1 entry point for attackers. Known CVEs have public exploits within days of disclosure.

Patching Discipline

# Automated security patching (Ubuntu/Debian)
apt install unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

# Check for available security updates
apt list --upgradable 2>/dev/null | grep -i security

# Kubernetes: check node versions
kubectl get nodes -o wide  # Compare against latest patch version

Establish a patching SLA: critical CVEs within 48 hours, high within 1 week, medium within 30 days.

Remember: Patching SLA mnemonic: "2-7-30" — 2 days for critical, 7 days for high, 30 days for medium. These numbers align with common compliance frameworks (PCI DSS, SOC 2). If you cannot articulate your patching SLA in an audit, you fail the audit.

Shared Credentials

Anti-Pattern

# The "team password" spreadsheet
prod_db_password: Summer2024!
aws_root_account: shared@company.com / CompanyPassword1
jump_host: ssh admin@bastion (password: admin123)

Why It Fails

  • No individual accountability (who made that change?)
  • Cannot revoke access for one person without rotating for everyone
  • Passwords get weaker as they are shared more broadly
  • Compliance frameworks require individual identities

Correct Approach

  • Individual accounts with SSO/SAML for every system
  • Service accounts with scoped, rotated credentials
  • Secrets manager (Vault, AWS Secrets Manager) for machine credentials
  • Break-glass procedures for emergency access with full audit trail

Default Configurations

Common Defaults Left in Production

System Default Risk
Kubernetes Dashboard Exposed without auth
Redis No password, bound to 0.0.0.0
MongoDB No auth, port 27017 open
Elasticsearch No auth, port 9200 open
Docker daemon TCP socket without TLS
etcd No client auth
Grafana admin/admin

Hardening Checklist

  • Change all default passwords before deployment
  • Bind services to localhost or private networks, not 0.0.0.0
  • Enable authentication on every data store
  • Disable unnecessary ports and protocols
  • Remove default/sample configurations and users

Container Security Mistakes

# Running as root (default, dangerous)
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y myapp
CMD ["myapp"]

# Correct: non-root user
FROM ubuntu:22.04
RUN apt-get update && apt-get install -y myapp && \
    useradd -r -s /usr/sbin/nologin appuser
USER appuser
CMD ["myapp"]

Other container anti-patterns: using latest tag, running privileged containers, not scanning images for CVEs, mounting the Docker socket into containers, and storing secrets in environment variables visible via docker inspect.

Default trap: The latest tag is not a version — it is whatever was pushed most recently. In production, latest means "I have no idea what version is running." Always pin to a specific digest or semantic version tag. Use image: nginx:1.25.3@sha256:abc123... for maximum reproducibility.

Gotcha: Mounting the Docker socket (-v /var/run/docker.sock:/var/run/docker.sock) gives the container full root access to the host. Any container with the Docker socket can create new privileged containers, read any volume, and escape to the host trivially. This is the equivalent of giving root SSH access.

Quick Self-Audit

  1. Any secrets in git history? (gitleaks detect)
  2. Any IAM roles with wildcard permissions?
  3. When was the last patch cycle?
  4. Any credentials shared between humans?
  5. Default passwords changed on all services?
  6. All data stores authenticated and encrypted in transit?
  7. Containers running as non-root?