- security
- l1
- topic-pack
- opsec-mistakes --- Portal | Level: L1: Foundations | Topics: OpSec Mistakes | Domain: Security
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 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
latesttag is not a version — it is whatever was pushed most recently. In production,latestmeans "I have no idea what version is running." Always pin to a specific digest or semantic version tag. Useimage: 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 givingrootSSH access.
Quick Self-Audit¶
- Any secrets in git history? (
gitleaks detect) - Any IAM roles with wildcard permissions?
- When was the last patch cycle?
- Any credentials shared between humans?
- Default passwords changed on all services?
- All data stores authenticated and encrypted in transit?
- Containers running as non-root?