Εισαγωγή
Αν δουλεύεις με containers στο Linux το 2026, σίγουρα έχεις ακούσει για το VoidLink malware. Ανακαλύφθηκε τον Ιανουάριο του 2026 και αποτελεί μια ξεκάθαρη υπενθύμιση: τα παραδοσιακά μέτρα ασφαλείας δεν αρκούν πια. Πρόκειται για ένα εξελιγμένο κακόβουλο λογισμικό που σχεδιάστηκε από την αρχή για να στοχεύει cloud και container περιβάλλοντα — κάτι που αλλάζει εντελώς τους κανόνες του παιχνιδιού.
Τα containers είναι πλέον ο de facto τρόπος deployment εφαρμογών. Docker, Kubernetes, Podman — κυριαρχούν παντού. Ωστόσο, η ευκολία χρήσης έχει ένα κόστος. Ένα container που τρέχει με root privileges, χωρίς seccomp profiles, χωρίς περιορισμούς capabilities και με ανεπαλήθευτα images; Αυτό είναι ουσιαστικά ένα τεράστιο κενό ασφαλείας που μπορεί να οδηγήσει σε πλήρη compromise ολόκληρου του host.
Ειλικρινά, η εξέλιξη των απειλών τα τελευταία χρόνια είναι τρομακτική. Από απλούς cryptominers φτάσαμε σε sophisticated kernel rootkits που μεταγλωττίζονται on-demand για το target σύστημα. Οι εισβολείς ξέρουν πλέον πώς να διαφεύγουν από container isolation, να εκμεταλλεύονται misconfigurations και να παραμένουν κρυμμένοι για μεγάλες περιόδους. Το 2026, δεν αρκεί απλά να τρέχουμε containers — πρέπει να τα τρέχουμε σωστά.
Σε αυτό το άρθρο θα δούμε αναλυτικά τις πιο σύγχρονες τεχνικές hardening: rootless Podman, custom seccomp profiles, signing με Sigstore, runtime sandboxing, και πώς όλα αυτά δουλεύουν μαζί για να δημιουργήσουν ένα defense-in-depth πλαίσιο ασφαλείας. Λοιπόν, ας αρχίσουμε.
Το Σημερινό Τοπίο Απειλών
VoidLink: Η Νέα Γενιά Cloud-Native Malware
Τον Δεκέμβριο του 2025 (και επίσημα ανακοινωμένο τον Ιανουάριο του 2026), ερευνητές ασφαλείας εντόπισαν το VoidLink — ένα cloud-native malware framework που αποτελεί πραγματικό ορόσημο. Με πάνω από 88.000 γραμμές κώδικα, είναι ένα από τα πιο sophisticated κακόβουλα λογισμικά που έχουν στοχεύσει ποτέ container περιβάλλοντα. Κάτι που κάνει εντύπωση: αναλύσεις δείχνουν ότι τμήματα του αναπτύχθηκαν με AI-assisted development tools, δίνοντας στους δημιουργούς του τη δυνατότητα να παράγουν πολύπλοκες λειτουργίες σε χρόνο ρεκόρ.
Χρησιμοποιεί τρίσταδιο μηχανισμό παράδοσης (three-stage delivery): ξεκινά με έναν minimal ELF binary γραμμένο σε Zig που μιμείται ένα kernel worker thread για να αποφύγει τον εντοπισμό. Μετά κατεβάζει και εκτελεί δευτερογενή payloads βάσει του περιβάλλοντος του θύματος. Το πιο ανησυχητικό; Η ικανότητά του να μεταγλωττίζει kernel rootkits on-demand, προσαρμοσμένα στη συγκεκριμένη έκδοση kernel μέσω του C2 server.
Οι δυνατότητες container escape εκμεταλλεύονται τεχνικές όπως exposed Docker socket exploits, cgroup escape μέσω notify_on_release και kernel vulnerabilities. Στοχεύει Docker, Kubernetes και μεγάλους cloud providers (AWS, GCP, Azure). Η πολυπλοκότητα του είναι τέτοια που το καθιστά μία από τις σοβαρότερες απειλές για containerized infrastructure σήμερα.
Supply Chain Attacks και Compromised Images
Πέρα από το runtime malware, το supply chain έχει γίνει ένα από τα πιο σημαντικά attack vectors. Backdoored container images στα δημόσια registries — συμπεριλαμβανομένου του Docker Hub — αυξάνονται δραματικά. Οι attackers ανεβάζουν images που φαίνονται legitimate, συχνά με ονόματα σχεδόν πανομοιότυπα με δημοφιλή projects, αλλά κρύβουν backdoors, cryptominers ή data exfiltration tools.
Τα compromised Helm charts είναι μια ακόμη σημαντική απειλή. Ένας attacker που εισάγει κακόβουλο κώδικα σε ένα ευρέως χρησιμοποιούμενο chart μπορεί να επηρεάσει χιλιάδες deployments. Το 2025 είδαμε περιπτώσεις dependency confusion attacks που εξαπάτησαν CI/CD pipelines ώστε να κατεβάσουν malicious charts από public repos αντί για τα private.
Και εδώ είναι το πραγματικό πρόβλημα: πολλοί οργανισμοί εξακολουθούν να μην κάνουν image scanning ούτε signature verification. Containers τρέχουν χωρίς κανείς να ξέρει αν το image έχει αλλοιωθεί, αν περιέχει γνωστά vulnerabilities, ή αν πράγματι προέρχεται από την αναμενόμενη πηγή.
Εξελιγμένες Τεχνικές Container Escape
Οι τεχνικές container escape έχουν γίνει αρκετά πιο sophisticated σε σχέση με παλιότερα. Εκτός από τα κλασικά exploits (το --privileged flag ή το exposed Docker socket), νέες μέθοδοι εκμεταλλεύονται λεπτές misconfigurations και kernel bugs. Τεχνικές όπως abuse του /proc/self/exe symlink, εκμετάλλευση του core_pattern για code execution στον host, και χρήση του release_agent σε cgroups αποτελούν πλέον standard εργαλεία. Η αύξηση των kernel vulnerabilities μεταξύ containers και host σημαίνει ότι ένα compromised container μπορεί πολύ εύκολα να αποκτήσει πρόσβαση στο host μέσω privilege escalation.
Rootless Containers με το Podman
Γιατί τα Rootless Containers Είναι Κρίσιμα
Ας πούμε τα πράγματα με το όνομά τους: το μεγαλύτερο πρόβλημα ασφαλείας με το παραδοσιακό Docker είναι ότι ο daemon τρέχει ως root. Αυτό σημαίνει ότι οποιοσδήποτε έχει πρόσβαση στο Docker socket, ουσιαστικά, έχει root access στο μηχάνημα. Ένα container escape ή ένα compromised container μπορεί να αξιοποιήσει αυτόν τον privileged daemon για horizontal movement ή privilege escalation.
Τα rootless containers εξαλείφουν αυτό το single point of failure. Τρέχοντας το container runtime ως unprivileged user, η attack surface μειώνεται κατά 60-80% σύμφωνα με security assessments. Ακόμα κι αν κάποιος κάνει escape, θα έχει μόνο τα privileges του συγκεκριμένου user — όχι root access.
Η Daemonless Αρχιτεκτονική του Podman
Το Podman (Pod Manager) σχεδιάστηκε εξαρχής ως daemonless. Αντί για κεντρικό daemon, χρησιμοποιεί fork-exec μοντέλο — κάθε container γίνεται απευθείας child process του Podman command. Στην πράξη αυτό σημαίνει:
- Δεν υπάρχει long-running privileged process που να αποτελεί target
- Κάθε container τρέχει με τα permissions του user που τον ξεκίνησε
- Το systemd μπορεί να διαχειρίζεται containers μέσω user sessions
- Καλύτερη resource isolation και reliability γενικότερα
Ρύθμιση Rootless Podman
Πρώτα πρέπει να ρυθμίσουμε τα subordinate user και group IDs. Το Linux χρησιμοποιεί user namespaces για να map-άρει UIDs μέσα στο container σε διαφορετικά UIDs στον host:
# Έλεγχος αν υπάρχουν ήδη subordinate IDs
cat /etc/subuid
cat /etc/subgid
# Αν δεν υπάρχουν, προσθήκη για τον user
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 username
# Επαλήθευση
grep username /etc/subuid /etc/subgid
Κάθε user χρειάζεται ένα range από 65536 subordinate IDs. Όταν ένα rootless container τρέχει μια διεργασία ως UID 0 (root) μέσα στο container, αυτή αντιστοιχίζεται στο UID 100000 στον host:
# Εγκατάσταση Podman (Fedora/RHEL)
sudo dnf install podman
# Εγκατάσταση Podman (Ubuntu/Debian)
sudo apt-get install podman
# Έλεγχος ότι το rootless mode λειτουργεί
podman info | grep -i rootless
# Πρέπει να δείξει: rootless: true
# Εκτέλεση ενός απλού rootless container
podman run --rm -it alpine whoami
# Θα εμφανίσει: root (μέσα στο container)
# Έλεγχος του πραγματικού UID στον host
ps aux | grep alpine
# Θα δείξει ότι η διεργασία τρέχει με το UID του user, όχι root
User Namespace Modes
Το Podman υποστηρίζει διαφορετικά user namespace modes ανάλογα με τις ανάγκες σου:
# Default mode: Το UID 0 στο container = subordinate UID range
podman run --rm alpine cat /proc/self/uid_map
# 0 100000 65536
# Keep-id mode: Διατήρηση του UID του host user μέσα στο container
# Χρήσιμο για mounted volumes χωρίς permission issues
podman run --rm --userns=keep-id alpine id
# uid=1000(username) gid=1000(username)
# Nomap mode: Custom UID mapping χωρίς mapping του user
podman run --rm --userns=nomap -v /home/user/data:/data alpine ls -ln /data
Από προσωπική εμπειρία, το --userns=keep-id είναι αυτό που θα χρησιμοποιήσεις πιο συχνά. Είναι ιδιαίτερα χρήσιμο όταν κάνεις mount local directories και θέλεις να διατηρηθούν τα permissions. Χωρίς αυτό, αρχεία που δημιουργούνται μέσα στο container ως root θα ανήκουν στο subordinate UID στον host — κάτι που μπορεί να σε τρελάνει με permission errors.
Networking με Pasta και Cgroups v2
Από το Podman 5.0, το default networking για rootless containers χρησιμοποιεί pasta (Passt + Pasta) — ένα πιο σύγχρονο replacement για το slirp4netns, με καλύτερη performance, πλήρη υποστήριξη IPv6 και πιο ασφαλή αρχιτεκτονική:
# Εκτέλεση web server σε rootless container με port mapping
podman run -d --name webserver -p 8080:80 nginx
# Test της σύνδεσης
curl http://localhost:8080
# Τα rootless containers απαιτούν cgroups v2
# Έλεγχος αν χρησιμοποιείται cgroups v2
mount | grep cgroup2
# Πρέπει να δείξει: cgroup2 on /sys/fs/cgroup type cgroup2
# Ορισμός resource limits σε rootless container
podman run -d --name limited-app \
--memory=512m \
--cpus=1.5 \
--pids-limit=100 \
myapp:latest
Seccomp Profiles: Φιλτράρισμα System Calls
Τι Είναι το Seccomp και Γιατί Μας Ενδιαφέρει
Το Seccomp (Secure Computing Mode) είναι ένας Linux kernel mechanism που περιορίζει ποιες system calls μπορεί να κάνει μια διεργασία. Κάθε container runtime εφαρμόζει ένα default seccomp profile που μπλοκάρει επικίνδυνες system calls — αυτές που θα μπορούσαν να χρησιμοποιηθούν για privilege escalation, kernel exploitation ή container escape.
Ο Linux kernel εκθέτει περίπου 330-440 system calls ανάλογα με την αρχιτεκτονική. Οι περισσότερες containerized εφαρμογές χρειάζονται μόνο 40-70 από αυτές. Οι υπόλοιπες; Καθαρή attack surface χωρίς κανένα λειτουργικό όφελος.
Το Default Seccomp Profile
Το default profile μπλοκάρει γύρω στις 44 επικίνδυνες system calls, μεταξύ των οποίων:
bpf— Berkeley Packet Filter, μπορεί να χρησιμοποιηθεί για kernel exploitationmount,umount2— Filesystem operations που απαιτούν privilegesptrace— Process debugging που επιτρέπει inspection/modificationreboot— System reboot (αυτό μάλλον δεν το θέλεις σε container!)unshare— Δημιουργία νέων namespacesadd_key,keyctl— Kernel keyring operations
# Εκτέλεση container με το default seccomp profile
podman run --rm alpine mount
# Θα αποτύχει: mount: permission denied
# Εκτέλεση χωρίς seccomp (ΜΟΝΟ για testing!)
podman run --rm --security-opt seccomp=unconfined alpine mount
Δημιουργία Custom Seccomp Profiles
Για μέγιστη ασφάλεια, θα πρέπει να φτιάξεις custom seccomp profiles που επιτρέπουν μόνο τις system calls που η εφαρμογή σου πραγματικά χρειάζεται. Ακούγεται κουραστικό, αλλά αξίζει τον κόπο:
{
"defaultAction": "SCMP_ACT_ERRNO",
"defaultErrnoRet": 1,
"architectures": [
"SCMP_ARCH_X86_64",
"SCMP_ARCH_X86",
"SCMP_ARCH_X32"
],
"syscalls": [
{
"names": [
"accept4", "access", "arch_prctl", "bind", "brk",
"chmod", "chown", "clone", "close", "connect",
"dup", "dup2", "epoll_create1", "epoll_ctl",
"epoll_wait", "execve", "exit", "exit_group",
"fchmod", "fchown", "fcntl", "fstat", "futex",
"getcwd", "getdents64", "getegid", "geteuid",
"getgid", "getpid", "getppid", "getuid",
"listen", "lseek", "mmap", "mprotect", "munmap",
"open", "openat", "read", "readlink", "recvfrom",
"rt_sigaction", "rt_sigprocmask", "rt_sigreturn",
"sendto", "set_robust_list", "set_tid_address",
"setgid", "setgroups", "setuid", "socket",
"stat", "uname", "write"
],
"action": "SCMP_ACT_ALLOW"
}
]
}
Η λογική είναι απλή: defaultAction: SCMP_ACT_ERRNO σημαίνει ότι κάθε system call που δεν είναι ρητά listed αποτυγχάνει. Το syscalls array επιτρέπει μόνο ό,τι χρειάζεται.
Auditing Syscalls με Strace
Πριν φτιάξεις custom profile, πρέπει να μάθεις ποιες system calls χρησιμοποιεί η εφαρμογή σου στην πράξη:
# Εκτέλεση container με strace για καταγραφή syscalls
podman run --rm --security-opt seccomp=unconfined \
alpine sh -c "apk add strace && strace -c -f -e trace=all echo 'test'" 2>&1 | tail -20
# Εναλλακτικά, χρήση bpftrace για real-time monitoring
sudo bpftrace -e 'tracepoint:raw_syscalls:sys_enter /comm == "myapp"/ { @[args->id] = count(); }'
Εφαρμογή Seccomp Profiles
# Αποθήκευση του custom profile σε αρχείο
# και εφαρμογή σε container
# Με Podman
podman run -d --name secure-nginx \
--security-opt seccomp=nginx-seccomp.json \
-p 8080:80 \
nginx:latest
# Με Docker
docker run -d --name secure-nginx \
--security-opt seccomp=nginx-seccomp.json \
-p 8080:80 \
nginx:latest
Ένα καλά ρυθμισμένο seccomp profile μπορεί να μπλοκάρει το 80-90% των διαθέσιμων syscalls. Αυτό κάνει τα kernel exploits και τα container escapes πολύ, πολύ πιο δύσκολα.
Linux Capabilities και Περιορισμός Privileges
Το Μοντέλο των Linux Capabilities
Τα Linux capabilities σπάνε τα root privileges σε ξεχωριστές μονάδες που χορηγούνται ανεξάρτητα. Υπάρχουν περίπου 41 capabilities στον σύγχρονο kernel, και κάποια είναι αρκετά επικίνδυνα για containers:
CAP_SYS_ADMIN— Πρόσβαση σε πολλές privileged operations (mount, namespace manipulation κ.λπ.)CAP_SYS_MODULE— Φόρτωση/αφαίρεση kernel modulesCAP_SYS_PTRACE— Debugging και inspection άλλων διεργασιώνCAP_NET_RAW— Raw sockets για packet sniffing, ARP spoofingCAP_NET_ADMIN— Network configuration changesCAP_SYS_RAWIO— Direct access σε hardware I/O ports
Dropping Capabilities και Hardening
# Αφαίρεση ΟΛΩΝ των capabilities και προσθήκη μόνο των απαραίτητων
podman run --rm \
--cap-drop=ALL \
--cap-add=CHOWN \
--cap-add=DAC_OVERRIDE \
--cap-add=SETUID \
--cap-add=SETGID \
--cap-add=NET_BIND_SERVICE \
nginx:latest
# Για στατική εφαρμογή χωρίς privileged ports
podman run --rm -p 8080:8080 \
--cap-drop=ALL \
--user 1000:1000 \
mystaticapp:latest
Read-Only Filesystems και No-New-Privileges
Ένα read-only root filesystem εμποδίζει κάποιον να τροποποιήσει binaries, να εγκαταστήσει backdoors ή να αλλάξει configurations μέσα στο container. Είναι ένα απλό βήμα που κάνει τεράστια διαφορά:
# Container με read-only filesystem
podman run -d --name readonly-nginx \
--read-only \
--tmpfs /var/cache/nginx:rw,noexec,nosuid \
--tmpfs /var/run:rw,noexec,nosuid \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
nginx:latest
# Πλήρης hardened container
podman run -d --name hardened-app \
--security-opt no-new-privileges \
--security-opt seccomp=/path/to/custom-profile.json \
--cap-drop=ALL \
--cap-add=NET_BIND_SERVICE \
--read-only \
--tmpfs /tmp:rw,noexec,nosuid,size=100m \
--user 1000:1000 \
-p 8080:8080 \
myapp:latest
Το no-new-privileges flag είναι κάτι που πολλοί ξεχνούν αλλά δεν θα έπρεπε. Εμποδίζει τις διεργασίες μέσα στο container να αποκτήσουν πρόσθετα privileges μέσω setuid binaries ή άλλους privilege escalation mechanisms.
Kubernetes SecurityContext
apiVersion: v1
kind: Pod
metadata:
name: hardened-pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1000
fsGroup: 1000
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
add:
- NET_BIND_SERVICE
volumeMounts:
- name: tmp
mountPath: /tmp
- name: cache
mountPath: /var/cache
volumes:
- name: tmp
emptyDir: {}
- name: cache
emptyDir: {}
Αυτός ο συνδυασμός — dropped capabilities, read-only filesystem, no-new-privileges και non-root user — δημιουργεί ένα πολύ restrictive περιβάλλον. Ακόμα κι αν κάποιος attacker εκτελέσει κώδικα μέσα στο pod, οι επιλογές του για lateral movement είναι ελάχιστες.
Ασφάλεια Supply Chain με Sigstore
Το Sigstore Ecosystem
Το Sigstore είναι ένα open-source project για keyless signing και verification software artifacts, συμπεριλαμβανομένων container images. Αποτελείται από τρία βασικά κομμάτια:
- Cosign: CLI tool για signing και verification container images
- Fulcio: Certificate Authority που εκδίδει short-lived signing certificates βασισμένα σε OIDC identity
- Rekor: Transparency log που καταγράφει κάθε signature για public auditability
Το μεγάλο πλεονέκτημα εδώ είναι το keyless signing. Αντί να διαχειρίζεσαι long-lived private keys (που είναι πονοκέφαλος και ρίσκο), χρησιμοποιείς την existing OIDC identity σου — Google, GitHub, Microsoft — για short-lived certificates που λήγουν σε λίγα λεπτά.
Signing Container Images με Cosign
# Εγκατάσταση Cosign
wget https://github.com/sigstore/cosign/releases/download/v2.2.3/cosign-linux-amd64
chmod +x cosign-linux-amd64
sudo mv cosign-linux-amd64 /usr/local/bin/cosign
# Build και push ενός container image
podman build -t myregistry.io/myapp:v1.0 .
podman push myregistry.io/myapp:v1.0
# Keyless signing με OIDC authentication
cosign sign --yes myregistry.io/myapp:v1.0
# Verification του signed image
cosign verify \
[email protected] \
--certificate-oidc-issuer=https://github.com/login/oauth \
myregistry.io/myapp:v1.0
Key-Based Signing για Private Infrastructure
# Δημιουργία key pair
cosign generate-key-pair
# Δημιουργεί cosign.key (private) και cosign.pub (public)
# Signing με private key
cosign sign --key cosign.key myregistry.io/myapp:v1.0
# Verification με public key
cosign verify --key cosign.pub myregistry.io/myapp:v1.0
# Για production, αποθήκευση σε KMS
cosign sign --key gcpkms://projects/PROJECT/locations/LOCATION/keyRings/RING/cryptoKeys/KEY \
myregistry.io/myapp:v1.0
Integration σε CI/CD Pipeline
Η ενσωμάτωση στο CI/CD είναι σχετικά απλή. Ορίστε ένα παράδειγμα με GitHub Actions:
# GitHub Actions workflow παράδειγμα
# .github/workflows/build-and-sign.yml
name: Build and Sign Container
on:
push:
branches: [main]
jobs:
build-sign:
runs-on: ubuntu-latest
permissions:
contents: read
packages: write
id-token: write # Απαραίτητο για keyless signing
steps:
- uses: actions/checkout@v4
- name: Install Cosign
uses: sigstore/cosign-installer@v3
- name: Login to Registry
uses: docker/login-action@v3
with:
registry: ghcr.io
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}
- name: Build and Push
uses: docker/build-push-action@v5
with:
push: true
tags: ghcr.io/${{ github.repository }}:${{ github.sha }}
- name: Sign Image
run: |
cosign sign --yes ghcr.io/${{ github.repository }}:${{ github.sha }}
Enforcement στο Kubernetes
Φυσικά, signing χωρίς enforcement δεν έχει νόημα. Ο Sigstore Policy Controller λειτουργεί ως admission controller που αρνείται unsigned images:
# Εγκατάσταση Policy Controller
kubectl apply -f https://github.com/sigstore/policy-controller/releases/download/v0.8.0/release.yaml
# Δημιουργία ClusterImagePolicy
apiVersion: policy.sigstore.dev/v1beta1
kind: ClusterImagePolicy
metadata:
name: require-signed-images
spec:
images:
- glob: "myregistry.io/**"
authorities:
- keyless:
url: https://fulcio.sigstore.dev
identities:
- issuer: https://github.com/login/oauth
subject: "[email protected]"
# Μόνο signed images θα επιτρέπονται πλέον
SBOM Attestations
Πέρα από τα signatures, το Cosign υποστηρίζει attestations — δηλαδή cryptographically signed metadata για το image. Αυτό μπορεί να περιλαμβάνει SBOM (Software Bill of Materials) για πλήρη ορατότητα στα dependencies:
# Δημιουργία SBOM με syft
syft packages myregistry.io/myapp:v1.0 -o spdx-json > sbom.spdx.json
# Attach SBOM ως attestation
cosign attest --yes --predicate sbom.spdx.json \
--type spdxjson \
myregistry.io/myapp:v1.0
# Verification και extraction του SBOM
cosign verify-attestation \
[email protected] \
--certificate-oidc-issuer=https://github.com/login/oauth \
--type spdxjson \
myregistry.io/myapp:v1.0 | jq -r .payload | base64 -d | jq .
Runtime Sandboxing: gVisor και Kata Containers
Τα Όρια των Παραδοσιακών Containers
Εδώ φτάνουμε σε ένα βασικό ζήτημα: τα παραδοσιακά Linux containers μοιράζονται τον kernel με τον host. Αυτό το shared kernel model σημαίνει ότι ένα kernel vulnerability μπορεί να οδηγήσει σε container escape. Για workloads που χρειάζονται πιο ισχυρή isolation — multi-tenant platforms, εκτέλεση untrusted code — χρειαζόμαστε κάτι παραπάνω.
gVisor: Application Kernel σε Go
Το gVisor είναι ουσιαστικά ένα user-space kernel γραμμένο σε Go. Αντί οι syscalls να φτάνουν στον host kernel, περνάνε μέσα από το Sentry process του gVisor. Σκέψου το σαν ένα επιπλέον layer μεταξύ container και kernel:
# Εγκατάσταση gVisor (runsc)
curl -fsSL https://gvisor.dev/archive.key | \
sudo gpg --dearmor -o /usr/share/keyrings/gvisor-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/gvisor-archive-keyring.gpg] \
https://storage.googleapis.com/gvisor/releases release main" | \
sudo tee /etc/apt/sources.list.d/gvisor.list
sudo apt-get update && sudo apt-get install -y runsc
# Ρύθμιση Docker για gVisor
sudo runsc install
sudo systemctl restart docker
# Εκτέλεση container με gVisor runtime
docker run --runtime=runsc --rm alpine uname -a
# Θα δείξει gVisor kernel version, όχι τον host kernel
# KVM platform mode για καλύτερη performance
sudo runsc install --runtime=runsc-kvm -- --platform=kvm
docker run --runtime=runsc-kvm --rm alpine dmesg | grep -i gvisor
Το gVisor μειώνει σημαντικά την attack surface αποκόπτοντας την πρόσβαση στον host kernel. Βέβαια, υπάρχει performance overhead (10-30% για CPU-intensive workloads) και δεν υποστηρίζει πλήρως κάθε syscall — κάτι που πρέπει να λάβεις υπόψη.
Kata Containers: Lightweight VMs
Αν θέλεις maximum isolation, τα Kata Containers τρέχουν κάθε container σε ξεχωριστό lightweight micro-VM με δικό του kernel:
# Εγκατάσταση Kata Containers
sudo apt-get install -y kata-runtime kata-containers
# Δημιουργία RuntimeClass στο Kubernetes
apiVersion: node.k8s.io/v1
kind: RuntimeClass
metadata:
name: kata
handler: kata
# Deploy Pod με Kata isolation
apiVersion: v1
kind: Pod
metadata:
name: kata-pod
spec:
runtimeClassName: kata
containers:
- name: nginx
image: nginx
# Verification - ξεχωριστό kernel
kubectl exec kata-pod -- uname -r
# Θα δείξει διαφορετικό kernel version από τον host
Πότε να Χρησιμοποιήσεις Ποιο
gVisor ταιριάζει σε web servers, stateless apps, και όπου χρειάζεσαι καλή density με γρήγορο startup. Kata Containers είναι η καλύτερη επιλογή για maximum isolation, untrusted workloads και πλήρη syscall compatibility. Και τα δύο είναι σαφώς πιο ασφαλή από τα παραδοσιακά containers, αλλά με διαφορετικά trade-offs σε performance.
SELinux και AppArmor για Containers
SELinux για Containers
Αν χρησιμοποιείς RHEL-based distro, το SELinux (Security-Enhanced Linux) εφαρμόζει type enforcement και Multi-Category Security labels. Πρακτικά, κάθε container παίρνει δικά του MCS labels που εμποδίζουν cross-container access:
# Έλεγχος κατάστασης SELinux
getenforce
# Πρέπει να επιστρέψει: Enforcing
# Εκτέλεση container με SELinux labels
podman run -d --name web nginx
ps auxZ | grep nginx
# system_u:system_r:container_t:s0:c123,c456
# Χειροκίνητος ορισμός SELinux label
podman run --security-opt label=level:s0:c100,c200 --rm alpine cat /proc/self/attr/current
# Mount volumes με SELinux context
sudo semanage fcontext -a -t container_file_t "/opt/myapp/data(/.*)?"
sudo restorecon -Rv /opt/myapp/data
# Το :Z flag ενημερώνει αυτόματα τα SELinux labels
podman run -v /opt/myapp/data:/data:Z --rm alpine ls -lZ /data
AppArmor για Containers
Σε Debian-based distributions, το AppArmor χρησιμοποιεί path-based access control. Λίγο πιο απλό στη ρύθμιση σε σχέση με το SELinux, αλλά λιγότερο granular:
# Δημιουργία custom AppArmor profile
cat << 'EOF' | sudo tee /etc/apparmor.d/containers/docker-nginx
#include <tunables/global>
profile docker-nginx flags=(attach_disconnected,mediate_deleted) {
#include <abstractions/base>
network inet stream,
network inet6 stream,
/etc/nginx/** r,
/usr/share/nginx/** r,
/var/log/nginx/** w,
/var/cache/nginx/** rw,
deny /proc/sys/kernel/** wl,
deny /sys/kernel/security/** rwlk,
capability net_bind_service,
capability setuid,
capability setgid,
deny capability sys_admin,
deny capability sys_module,
}
EOF
# Φόρτωση profile
sudo apparmor_parser -r -W /etc/apparmor.d/containers/docker-nginx
# Εκτέλεση container με custom AppArmor profile
podman run --security-opt apparmor=docker-nginx -d nginx
Εν τέλει, SELinux δίνει πιο granular control αλλά είναι πιο πολύπλοκο, ενώ AppArmor προσφέρει πιο εύκολο, path-based μοντέλο. Η επιλογή εξαρτάται κυρίως από τη distro σου. Αν τρέχεις Fedora/RHEL, πήγαινε SELinux. Ubuntu/Debian; AppArmor.
Πλήρης Checklist Hardening
Ας βάλουμε τα πράγματα σε σειρά. Αυτή η checklist καλύπτει τα βασικά (και όχι μόνο) για να θωρακίσεις τα containers σου.
Υψηλής Προτεραιότητας
- Χρήση rootless containers — Podman σε rootless mode, ρύθμιση /etc/subuid και /etc/subgid
- Αφαίρεση capabilities —
--cap-drop=ALLκαι προσθήκη μόνο των απαραίτητων, ποτέ--privileged - Seccomp profiles — Custom profiles ειδικά για την εφαρμογή, audit syscalls με strace
- Εκτέλεση ως non-root user —
--user UID:GIDήrunAsNonRoot: trueστο Kubernetes - Image signing και verification — Cosign signing, Policy Controller enforcement
- Read-only filesystem —
--read-onlyμε tmpfs mounts για writable directories - No-new-privileges —
--security-opt no-new-privileges
Μεσαίας Προτεραιότητας
- Image scanning — Trivy/Grype integration στο CI/CD, αυτόματη απόρριψη images με critical CVEs
- SELinux/AppArmor enforcement — Default ή custom policies σε enforcing mode
- Network policies — Kubernetes NetworkPolicies, default deny model
- Resource limits — CPU, memory, PID limits (μην αφήνεις containers χωρίς limits!)
- Secrets management — HashiCorp Vault ή Sealed Secrets, ποτέ hardcoded credentials
Enhanced Security
- Runtime sandboxing — gVisor ή Kata Containers για sensitive workloads
- SBOM generation — Syft/CycloneDX με Cosign attestations
- Runtime detection — Falco ή Tetragon για anomaly detection
- Immutable infrastructure — Rebuild και redeploy αντί για in-place changes
- Audit logging — Centralized logging για forensics και troubleshooting
Εντολές Γρήγορου Ελέγχου
Αυτές οι εντολές σε βοηθούν να ελέγξεις γρήγορα αν τα containers σου τρέχουν με τα σωστά security settings:
# Έλεγχος rootless mode
podman info | grep rootless
# Audit running containers
podman ps --format "{{.ID}} {{.Names}}" | while read id name; do
echo "=== Container: $name ==="
podman inspect $id | jq '.[] | {
User: .Config.User,
ReadonlyRootfs: .HostConfig.ReadonlyRootfs,
Privileged: .HostConfig.Privileged,
CapDrop: .HostConfig.CapDrop,
SecurityOpt: .HostConfig.SecurityOpt
}'
done
# Kubernetes pod security audit
kubectl get pods -A -o json | jq -r '
.items[] |
select(.spec.containers[].securityContext.allowPrivilegeEscalation != false) |
"\(.metadata.namespace)/\(.metadata.name)"
' | head -20
Συμπέρασμα
Η ασφάλεια containers το 2026 δεν είναι κάτι που μπορείς να αφήσεις για αργότερα. Απειλές σαν το VoidLink δείχνουν ξεκάθαρα ότι οι attackers έχουν αναπτύξει εξειδικευμένα εργαλεία για cloud-native περιβάλλοντα. Ένα μόνο security control δεν αρκεί.
Rootless containers εξαλείφουν τον privileged daemon. Seccomp profiles περιορίζουν τις syscalls. Dropped capabilities μειώνουν τα privileges. Sigstore ασφαλίζει το supply chain. Runtime sandboxing παρέχει kernel-level isolation. SELinux/AppArmor προσθέτει mandatory access control.
Όλα μαζί δημιουργούν ένα robust security posture που μπορεί να αντιμετωπίσει ακόμα και τις πιο εξελιγμένες επιθέσεις.
Το κλειδί είναι να ενσωματώσεις την ασφάλεια σε κάθε στάδιο του container lifecycle — build, signing, scanning, deployment, runtime monitoring. Με τα σωστά εργαλεία και πρακτικές, μπορείς να απολαύσεις τα οφέλη των containers χωρίς να θυσιάσεις την ασφάλεια. Και ειλικρινά, δεν υπάρχει δικαιολογία να μην το κάνεις.