Sécurité des conteneurs
1 - Menaces sur les conteneurs
1.1 Surface d'attaque
1.2 Risques principaux
| Risque | Description | Impact |
|---|---|---|
| Vulnerable dependencies | Libraries avec CVEs | Compromission |
| Malicious images | Images piégées | Backdoor |
| Privilege escalation | Container root | Escape |
| Secrets exposure | Secrets en clair | Data breach |
| Network exposure | Ports ouverts | Intrusion |
2 - Image Scanning
2.1 Trivy
# GitHub Action
- name: Trivy Image Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: 'myapp:latest'
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Upload Trivy results
uses: github/codeql-action/upload-sarif@v2
with:
sarif_file: 'trivy-results.sarif'
# CLI Trivy
trivy image myapp:latest
# Avec seuil
trivy image --exit-code 1 --severity CRITICAL,HIGH myapp:latest
# Scan filesystem
trivy fs --security-checks vuln,config .
# Scan IaC
trivy config ./terraform/
2.2 Grype (Anchore)
# Installer
curl -sSfL https://raw.githubusercontent.com/anchore/grype/main/install.sh | sh
# Scanner
grype myapp:latest
# Format JSON
grype myapp:latest -o json > results.json
# Avec threshold
grype myapp:latest --fail-on high
2.3 Snyk Container
- name: Snyk Container
uses: snyk/actions/docker@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
image: myapp:latest
args: --severity-threshold=high
3 - Dockerfile sécurisé
3.1 Best Practices
# ✅ Image de base minimale et vérifiée
FROM node:20-alpine@sha256:abc123...
# ✅ User non-root
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
# ✅ Copier seulement le nécessaire
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup src/ ./src/
# ✅ Exécuter en tant que non-root
USER appuser
# ✅ Port non privilégié
EXPOSE 8080
# ✅ Healthcheck
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:8080/health || exit 1
# ✅ Read-only filesystem compatible
CMD ["node", "src/index.js"]
3.2 Multi-stage builds
# Build stage
FROM node:20 AS builder
WORKDIR /app
COPY package*.json ./
RUN npm ci
COPY . .
RUN npm run build
# Production stage - image minimale
FROM gcr.io/distroless/nodejs20-debian11
WORKDIR /app
COPY --from=builder /app/dist ./dist
COPY --from=builder /app/node_modules ./node_modules
USER nonroot
EXPOSE 8080
CMD ["dist/index.js"]
3.3 Hadolint
# Linting Dockerfile
- name: Hadolint
uses: hadolint/hadolint-[email protected]
with:
dockerfile: Dockerfile
failure-threshold: warning
# CLI
hadolint Dockerfile
# Ignorer des règles
hadolint --ignore DL3008 --ignore DL3009 Dockerfile
4 - Runtime Security
4.1 Docker Security Options
# docker-compose.yml
services:
app:
image: myapp:latest
security_opt:
- no-new-privileges:true
- apparmor:docker-default
cap_drop:
- ALL
cap_add:
- NET_BIND_SERVICE
read_only: true
tmpfs:
- /tmp
user: "1001:1001"
4.2 Kubernetes Security Context
apiVersion: v1
kind: Pod
spec:
securityContext:
runAsNonRoot: true
runAsUser: 1001
runAsGroup: 1001
fsGroup: 1001
seccompProfile:
type: RuntimeDefault
containers:
- name: app
image: myapp:latest
securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
capabilities:
drop:
- ALL
resources:
limits:
cpu: "500m"
memory: "512Mi"
requests:
cpu: "100m"
memory: "128Mi"
4.3 Falco Runtime Detection
# Falco rule example
- rule: Shell Spawned in Container
desc: Detect shell spawned in container
condition: >
spawned_process and
container and
proc.name in (bash, sh, zsh)
output: >
Shell spawned in container
(user=%user.name container=%container.name
shell=%proc.name parent=%proc.pname)
priority: WARNING
tags: [container, shell]
- rule: Write to /etc
desc: Detect writes to /etc directory
condition: >
open_write and
container and
fd.name startswith /etc
output: >
Write to /etc in container
(file=%fd.name container=%container.name)
priority: ERROR
5 - Kubernetes Security
5.1 Pod Security Standards
# Namespace avec Pod Security
apiVersion: v1
kind: Namespace
metadata:
name: production
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted
5.2 Network Policies
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: deny-all
namespace: production
spec:
podSelector: {}
policyTypes:
- Ingress
- Egress
---
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
name: allow-frontend-to-backend
namespace: production
spec:
podSelector:
matchLabels:
app: backend
ingress:
- from:
- podSelector:
matchLabels:
app: frontend
ports:
- protocol: TCP
port: 8080
5.3 RBAC
# Service Account avec permissions minimales
apiVersion: v1
kind: ServiceAccount
metadata:
name: app-sa
namespace: production
---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
name: app-role
namespace: production
rules:
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "list"]
- apiGroups: [""]
resources: ["secrets"]
resourceNames: ["app-secrets"]
verbs: ["get"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
name: app-rolebinding
namespace: production
subjects:
- kind: ServiceAccount
name: app-sa
roleRef:
kind: Role
name: app-role
apiGroup: rbac.authorization.k8s.io
6 - Registry Security
6.1 Signing Images
# Cosign - Signer une image
cosign sign --key cosign.key myregistry.io/myapp:latest
# Vérifier la signature
cosign verify --key cosign.pub myregistry.io/myapp:latest
6.2 Admission Controller
# Kyverno policy - Images signées uniquement
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: verify-image-signature
spec:
validationFailureAction: enforce
background: false
rules:
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "myregistry.io/*"
attestors:
- count: 1
entries:
- keys:
publicKeys: |-
-----BEGIN PUBLIC KEY-----
...
-----END PUBLIC KEY-----
7 - CI/CD Security Pipeline
# Pipeline complet
stages:
- lint
- scan
- build
- sign
- deploy
dockerfile-lint:
stage: lint
script:
- hadolint Dockerfile
sast-scan:
stage: scan
script:
- trivy fs --security-checks vuln,config .
build-image:
stage: build
script:
- docker build -t $IMAGE .
- trivy image --exit-code 1 --severity HIGH,CRITICAL $IMAGE
- docker push $IMAGE
sign-image:
stage: sign
script:
- cosign sign --key $COSIGN_KEY $IMAGE
deploy:
stage: deploy
script:
- kubectl apply -f k8s/
only:
- main
Résumé
Dans ce chapitre, nous avons appris :
- Les menaces sur les conteneurs
- L'image scanning (Trivy, Grype, Snyk)
- Les Dockerfile sécurisés
- La runtime security (Falco)
- La sécurité Kubernetes (PSS, NetworkPolicy, RBAC)
- La signature des images
Prochaine étape
Dans le prochain chapitre, nous verrons Supply Chain Security.
→ Chapitre suivant : Supply Chain Security