D8.2 Container Runtime Security ⚙️
Το πρόβλημα με τα defaults
# Default container — εντελώς ανοιχτό:
docker run nginx
# Τρέχει ως root UID 0 ❌
# Μπορεί να κάνει setuid ❌
# Filesystem εγγράψιμο ❌
# Έχει ~14 capabilities ❌
no-new-privileges — Αποφυγή privilege escalation
tasks:
- name: Container με no-new-privileges
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
security_opts:
- "no-new-privileges:true" # ← ΔΕΝ μπορεί να αποκτήσει νέα privileges
# μέσω setuid/setgid binaries
💡 Τι κάνει: Ακόμα κι αν υπάρχει
setuidbinary μέσα στο container, δεν θα εκτελεστεί με elevated privileges.
read_only — Read-only filesystem
tasks:
- name: Read-only container
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
read_only: true # ← filesystem μόνο για ανάγνωση!
# Αλλά χρειαζόμαστε κάποια writable directories:
tmpfs:
/tmp: "size=128m,mode=1777" # ← temp files
/run: "size=32m" # ← PID files, sockets
/app/cache: "size=64m" # ← app cache
volumes:
# Μόνο ό,τι ΠΡΕΠΕΙ να είναι writable
- "app_data:/app/data:rw"
- "/etc/myapp/config:/app/config:ro" # ← config: read-only!
⚠️ Συνηθισμένη παγίδα: Πολλά apps γράφουν σε
/tmpή/run. Μεread_only: true, αυτά αποτυγχάνουν αν δεν ορίσειςtmpfs.
Non-root user
tasks:
# ── Εκτέλεση ως συγκεκριμένος user ──────────
- name: Non-root container
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
user: "1000:1000" # ← UID:GID (όχι root=0)
# ή
user: "myapp" # ← username (πρέπει να υπάρχει στο image)
# ── Volume permissions (πριν το container) ───
- name: Σωστά permissions για non-root
ansible.builtin.file:
path: /opt/myapp/data
state: directory
owner: "1000"
group: "1000"
mode: '0750'
- name: Deploy container
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
user: "1000:1000"
volumes:
- "/opt/myapp/data:/app/data:rw"
Capabilities — Minimal permissions
tasks:
# ── Drop ALL capabilities ─────────────────
- name: Container χωρίς capabilities
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
capabilities: [] # ← ΚΑΝΕΝΑ capability
cap_drop:
- ALL # ← ρητό drop all
# ── Προσθήκη μόνο ό,τι χρειάζεται ────────
- name: Nginx με minimal capabilities
community.docker.docker_container:
name: nginx_secure
image: nginx:alpine
state: started
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # ← μόνο για ports < 1024
# ΌΛΑ τα άλλα dropped:
# No: SYS_ADMIN, SYS_PTRACE, NET_RAW, etc.
# ── Common capabilities per service type ──
# Web server: NET_BIND_SERVICE
# Database: IPC_LOCK (για shared memory)
# Monitoring: SYS_PTRACE (για process info)
seccomp — System call filtering
tasks:
# ── Default Docker seccomp profile ────────
- name: Container με default seccomp
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
# default seccomp εφαρμόζεται αυτόματα
# (μπλοκάρει ~44 επικίνδυνα syscalls)
# ── Custom seccomp profile ─────────────────
- name: Container με custom seccomp
community.docker.docker_container:
name: myapp
image: myapp:latest
state: started
security_opts:
- "no-new-privileges:true"
- "seccomp={{ lookup('file', 'files/seccomp/myapp-profile.json') | to_json }}"
Πλήρης hardened container
tasks:
- name: "🔒 Hardened nginx container"
community.docker.docker_container:
name: nginx_hardened
image: nginx:1.25-alpine
state: started
restart_policy: unless-stopped
# ── User ────────────────────────────────
user: "101:101" # ← nginx user (non-root)
# ── Filesystem ──────────────────────────
read_only: true
tmpfs:
/tmp: "size=32m,mode=1777"
/var/cache/nginx: "size=128m"
/var/run: "size=16m"
# ── Capabilities ────────────────────────
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE # ← port 80/443
# ── Privileges ──────────────────────────
security_opts:
- "no-new-privileges:true"
# ── Resources ───────────────────────────
memory: "128m"
cpus: "0.5"
pids_limit: 50 # ← max 50 processes
# ── Ports ───────────────────────────────
ports:
- "127.0.0.1:80:80" # ← μόνο localhost (πίσω από reverse proxy)
# ΌΧΙ: "80:80" (expose σε όλες τις interfaces)
# ── Volumes ─────────────────────────────
volumes:
- "/etc/nginx/nginx.conf:/etc/nginx/nginx.conf:ro"
- "/var/www/html:/usr/share/nginx/html:ro"
- "/etc/ssl:/etc/ssl:ro"
# ── Logging ─────────────────────────────
log_driver: json-file
log_options:
max-size: "10m"
max-file: "3"
# ── Labels ──────────────────────────────
labels:
security: hardened
managed-by: ansible
Σύνοψη D8.2
Container Runtime Security
│
├── no-new-privileges:
│ └── security_opts: ["no-new-privileges:true"]
│
├── read_only + tmpfs:
│ ├── read_only: true
│ └── tmpfs: {/tmp: "size=128m", /run: "size=32m"}
│
├── Non-root user:
│ └── user: "1000:1000"
│
├── Capabilities:
│ ├── cap_drop: [ALL]
│ └── cap_add: [NET_BIND_SERVICE] ← μόνο ό,τι χρειάζεται
│
└── Resources:
├── memory: "128m"
├── cpus: "0.5"
└── pids_limit: 50