Securing containerized environments is no longer optional — it’s essential. Whether you’re managing Docker, Kubernetes, or bare-metal servers, security misconfigurations can lead to major vulnerabilities.
This guide provides a complete, production-grade security checklist covering Docker Compose, Dockerfiles, Kubernetes Pods, and Linux servers — following industry best practices and CIS Benchmarks.
🔴 Malware Injection Scenarios Through Docker
One common attack scenario occurs when containers are left exposed to the internet without proper protections. For example, an exposed PostgreSQL container with weak or default credentials can be discovered by attackers using automated scanning tools like Masscan or Shodan. Once found, attackers can gain access using default passwords, SQL injection, or known CVEs, then execute commands to download and run malware such as cryptominers (kdevtmpfsi) or other malicious scripts inside the container. This malware can consume excessive CPU resources, attempt persistence via cron jobs, disable security tools, and even scan for other vulnerable systems. Similar risks exist for other services: exposed Redis containers can allow attackers to write malicious cron jobs or SSH keys; mounting the Docker socket or host filesystem gives attackers root access to the host; and using untrusted images can lead to supply chain attacks. The key takeaway is that any service exposed to 0.0.0.0 without proper authentication or restrictions is likely to be found and exploited within hours or days. Always bind internal services to 127.0.0.1, use strong passwords, avoid privileged mounts, and monitor container activity to prevent such attacks.
PRODUCTION SECURITY CHECKLIST
DOCKER SECURITY
☐ All services bound to 127.0.0.1 (not 0.0.0.0)
☐ Containers run as non-root user
☐ Read-only filesystem enabled where possible
☐ tmpfs mounted with noexec,nosuid
☐ Resource limits set (CPU, memory, PIDs)
☐ Security options: no-new-privileges=true
☐ Capabilities dropped (cap_drop: ALL)
☐ Secrets in environment variables or Docker secrets
☐ Images scanned with Trivy/Grype
☐ Images signed with Docker Content Trust
☐ Multi-stage builds used
☐ Health checks implemented
☐ Logging configured and centralized
KUBERNETES SECURITY
☐ Pod Security Standards enforced (restricted)
☐ Security contexts set (runAsNonRoot, readOnlyRootFilesystem)
☐ Network policies implemented (default deny)
☐ Resource quotas and limit ranges set
☐ RBAC with minimal permissions
☐ Secrets externalized (Vault/Sealed Secrets)
☐ Admission controllers enabled
☐ Image pull policies set to specific versions
☐ Service accounts with minimal permissions
☐ Namespaces isolated
☐ Audit logging enabled
BARE METAL SECURITY
☐ SSH hardened (no root, keys only, non-standard port)
☐ Firewall configured (UFW/iptables)
☐ Fail2ban installed and configured
☐ Automatic security updates enabled
☐ Audit logging configured (auditd)
☐ File integrity monitoring (AIDE)
☐ Kernel hardened (sysctl)
☐ Rootkit scanners running (rkhunter, chkrootkit)
☐ AppArmor/SELinux enabled
☐ Intrusion detection system deployed (Suricata/OSSEC)
☐ Log aggregation configured
☐ Backups automated and encrypted
☐ Monitoring stack deployed (Prometheus/ELK)
☐ Compliance scanning scheduled (Lynis, OpenSCAP)
ONGOING OPERATIONS
☐ Vulnerability scanning automated
☐ Security patches applied weekly
☐ Access logs reviewed daily
☐ Incident response plan documented
☐ Disaster recovery plan tested
☐ Security awareness training completed
☐ Third-party security audit scheduled
☐ Penetration testing scheduled quarterly
☐ Backup restoration tested monthly
☐ Security metrics tracked and reviewed
INCIDENT RESPONSE READY
☐ Incident response playbook created
☐ Emergency contacts documented
☐ Forensics tools installed
☐ Backup communication channels established
☐ Post-incident review process defined
Docker Compose Security Best Practices with example!!
✅ Network Security
Avoid exposing containers directly to the internet.
# ❌ Bad - Exposed to internet
ports:
- "5432:5432"
# ✅ Good - Localhost only
ports:
- "127.0.0.1:5432:5432"
# ✅ Best - No port binding (internal communication only)
# Remove ports section entirely for internal services
✅ Container Hardening
services:
app:
security_opt:
- no-new-privileges:true
user: "1000:1000"
read_only: true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
tmpfs:
- /tmp:noexec,nosuid,size=64M
- /run:noexec,nosuid,size=10M
- Prevent privilege escalation
- Run as a non-root user
- Limit container capabilities
- Mount
/tmpasnoexecto prevent malware execution
✅ Resource Limits
deploy:
resources:
limits:
cpus: '2.0'
memory: 2G
reservations:
cpus: '0.5'
memory: 512M
pids_limit: 200
restart: unless-stopped
- Prevent resource abuse
- Avoid “always” restart loops
✅ Secrets Management
Never hardcode secrets in Docker Compose files.
# ❌ Bad
environment:
DB_PASSWORD: mypassword123
# ✅ Good
env_file:
- .env
# ✅ Better
secrets:
- db_password
secrets:
db_password:
file: ./secrets/db_password.txt
✅ Volume Security
volumes:
- ./config:/app/config:ro
- app_data:/var/lib/app
# - /var/run/docker.sock:/var/run/docker.sock # ❌ DANGEROUS
- Use read-only mounts
- Prefer named volumes
- Avoid mounting Docker socket
✅ Network Isolation
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
- Keep backend services private
- Isolate internal communication
✅ Complete Secure Docker Compose Example
version: '3.8'
services:
web:
image: myapp:latest
user: "1000:1000"
read_only: true
security_opt:
- no-new-privileges:true
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
ports:
- "127.0.0.1:8080:8080"
env_file:
- .env
secrets:
- db_password
networks:
- frontend
- backend
tmpfs:
- /tmp:noexec,nosuid,size=100M
deploy:
resources:
limits:
cpus: '1.0'
memory: 1G
restart: unless-stopped
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:8080/health"]
db:
image: postgres:16-alpine
user: "70:70"
read_only: true
networks:
- backend
env_file:
- .env
secrets:
- db_password
tmpfs:
- /tmp:noexec,nosuid,size=64M
volumes:
- db_data:/var/lib/postgresql/data
networks:
frontend:
driver: bridge
backend:
driver: bridge
internal: true
volumes:
app_data:
db_data:
secrets:
db_password:
file: ./secrets/db_password.txt
Dockerfile Security Best Practices
✅ Base Image Security
- Never use the
latesttag - Use minimal or distroless images
# ✅ Secure Example
FROM node:20.10.0-alpine@sha256:abc123...
✅ Non-Root User
RUN addgroup -g 1000 appuser && \
adduser -D -u 1000 -G appuser appuser
USER appuser
✅ Multi-Stage Builds
Reduce image size and attack surface.
FROM node:20-alpine AS builder
RUN npm ci --only=production && npm run build
FROM node:20-alpine
COPY --from=builder /app/dist ./dist
USER appuser
✅ Secrets Management
Use BuildKit secrets, never embed them in images.
RUN --mount=type=secret,id=mysecret \
cat /run/secrets/mysecret > /app/config
✅ File Permissions
RUN chmod -R 755 /app && chmod 600 /app/secrets/*
✅ Health Checks
HEALTHCHECK --interval=30s --timeout=3s \
CMD curl -f http://localhost:8080/health || exit 1
Kubernetes Security Checklist
✅ Pod Security
Use restrictive Pod Security Contexts.
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
✅ Pod Security Standards (PSS)
Apply at the namespace level.
metadata:
labels:
pod-security.kubernetes.io/enforce: restricted
✅ Network Policies
Default deny, then allow selective ingress/egress.
kind: NetworkPolicy
policyTypes:
- Ingress
- Egress
✅ Secrets Management
Use External Secrets or Sealed Secrets instead of raw Kubernetes Secrets.
✅ RBAC
Assign minimal permissions using Roles and RoleBindings.
✅ Resource Quotas & Limit Ranges
Prevent resource overuse with quotas per namespace.
✅ Admission Controllers
Enable:
PodSecurity, LimitRanger, ResourceQuota, ServiceAccount, NodeRestriction
🧱 Bare-Metal Server Security Checklist
✅ SSH Security
- Disable root login
- Enforce key-based authentication
- Restrict users and IPs
PermitRootLogin no
PasswordAuthentication no
AllowUsers deployuser adminuser
✅ Firewall (UFW)
ufw default deny incoming
ufw allow 22/tcp comment 'SSH'
ufw allow 80,443/tcp comment 'Web Traffic'
ufw limit 22/tcp
✅ Fail2ban
Block brute-force login attempts automatically.
✅ Automatic Security Updates
Use unattended-upgrades to auto-install patches.
✅ User Management
Create non-root users and enforce strong password policies.
✅ Audit Logging
Monitor critical file and authentication changes using auditd.
✅ File Integrity Monitoring
Use AIDE for detecting unauthorized changes.
✅ Kernel Hardening
Apply sysctl rules:
net.ipv4.ip_forward = 0
kernel.kptr_restrict = 2
net.ipv4.tcp_syncookies = 1
✅ Docker Host Security
Configure /etc/docker/daemon.json for secure defaults:
{
"icc": false,
"userns-remap": "default",
"live-restore": true
}
✅ Rootkit Detection
Install and automate rkhunter and chkrootkit scans.
✅ AppArmor/SELinux
Always enable one for mandatory access control.
Security Scanning & Monitoring
✅ Trivy
trivy image myapp:latest
trivy config Dockerfile
✅ Grype
Alternative vulnerability scanner for CI/CD pipelines.
✅ Falco
Runtime threat detection for containers and Kubernetes.
Summary: Complete Container Security Checklist
| Layer | Key Practices |
|---|---|
| Docker Compose | Localhost binding, no root, secrets, resource limits |
| Dockerfile | Minimal base, multi-stage, no secrets, healthcheck |
| Kubernetes | Restricted PSS, RBAC, network policies, quotas |
| Bare Metal | Harden SSH, enable UFW, auditd, kernel sysctl |
| Monitoring | Trivy, Falco, Grype, Docker Scout |
Final Thoughts
Container security is a continuous process, not a one-time setup.
Follow these checklists regularly, integrate vulnerability scans into your CI/CD pipeline, and review security configurations quarterly.
By implementing these best practices, you’ll significantly reduce the attack surface across Docker, Kubernetes, and bare-metal infrastructure — achieving compliance and resilience in production.