Exercices et Projets
1 - Exercices pratiques
Exercice 1 : Pipeline SAST
Objectif : Configurer un pipeline avec SAST scanning.
Solution
# .github/workflows/sast.yml
name: SAST Pipeline
on: [push, pull_request]
jobs:
sast:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: GitLeaks Secrets Scan
uses: gitleaks/gitleaks-action@v2
- name: Semgrep SAST
uses: returntocorp/semgrep-action@v1
with:
config: >-
p/security-audit
p/owasp-top-ten
- name: SonarQube Scan
uses: sonarsource/sonarqube-scan-action@master
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: ${{ secrets.SONAR_HOST_URL }}
Exercice 2 : Container Security
Objectif : Scanner et sécuriser une image Docker.
Solution
# Dockerfile sécurisé
FROM node:20-alpine@sha256:abc123...
RUN addgroup -g 1001 -S appgroup && \
adduser -u 1001 -S appuser -G appgroup
WORKDIR /app
COPY --chown=appuser:appgroup package*.json ./
RUN npm ci --only=production
COPY --chown=appuser:appgroup src/ ./src/
USER appuser
EXPOSE 8080
HEALTHCHECK --interval=30s --timeout=3s \
CMD wget -q --spider http://localhost:8080/health || exit 1
CMD ["node", "src/index.js"]
# Pipeline scan
- name: Build Image
run: docker build -t myapp:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: myapp:${{ github.sha }}
format: 'sarif'
output: 'trivy-results.sarif'
severity: 'CRITICAL,HIGH'
exit-code: '1'
- name: Hadolint
uses: hadolint/hadolint-[email protected]
with:
dockerfile: Dockerfile
Exercice 3 : Kyverno Policies
Objectif : Créer des policies Kubernetes avec Kyverno.
Solution
# require-security-context.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-security-context
spec:
validationFailureAction: enforce
rules:
- name: require-run-as-non-root
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Pods must run as non-root"
pattern:
spec:
securityContext:
runAsNonRoot: true
containers:
- securityContext:
allowPrivilegeEscalation: false
readOnlyRootFilesystem: true
---
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: require-resource-limits
spec:
validationFailureAction: enforce
rules:
- name: require-limits
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Containers must have resource limits"
pattern:
spec:
containers:
- resources:
limits:
memory: "?*"
cpu: "?*"
Exercice 4 : Secrets avec Vault
Objectif : Intégrer HashiCorp Vault dans Kubernetes.
Solution
# Installation Vault
helm install vault hashicorp/vault \
--set "server.dev.enabled=true"
# Configuration
kubectl exec -it vault-0 -- vault secrets enable -path=secret kv-v2
kubectl exec -it vault-0 -- vault kv put secret/myapp/config \
api_key="sk-12345" \
db_password="secure-pass"
# Auth Kubernetes
kubectl exec -it vault-0 -- vault auth enable kubernetes
kubectl exec -it vault-0 -- vault write auth/kubernetes/config \
kubernetes_host="https://$KUBERNETES_PORT_443_TCP_ADDR:443"
# Role
kubectl exec -it vault-0 -- vault write auth/kubernetes/role/myapp \
bound_service_account_names=myapp-sa \
bound_service_account_namespaces=default \
policies=myapp-policy \
ttl=1h
# Pod avec Vault injection
apiVersion: v1
kind: Pod
metadata:
name: myapp
annotations:
vault.hashicorp.com/agent-inject: "true"
vault.hashicorp.com/role: "myapp"
vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
spec:
serviceAccountName: myapp-sa
containers:
- name: app
image: myapp:latest
2 - Projet complet : Secure Application Deployment
Architecture
Pipeline complet
# .github/workflows/secure-deploy.yml
name: Secure Deployment Pipeline
on:
push:
branches: [main]
pull_request:
branches: [main]
env:
REGISTRY: ghcr.io
IMAGE_NAME: ${{ github.repository }}
jobs:
# Stage 1: Pre-commit checks
pre-commit:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Secrets Detection
uses: gitleaks/gitleaks-action@v2
- name: Commit Lint
uses: pre-commit/[email protected]
# Stage 2: SAST
sast:
runs-on: ubuntu-latest
needs: pre-commit
steps:
- uses: actions/checkout@v4
- name: Semgrep
uses: returntocorp/semgrep-action@v1
with:
config: p/security-audit p/owasp-top-ten
- name: CodeQL Analysis
uses: github/codeql-action/analyze@v2
# Stage 3: SCA
sca:
runs-on: ubuntu-latest
needs: pre-commit
steps:
- uses: actions/checkout@v4
- name: Snyk SCA
uses: snyk/actions/node@master
env:
SNYK_TOKEN: ${{ secrets.SNYK_TOKEN }}
with:
args: --severity-threshold=high
# Stage 4: Build & Container Scan
build:
runs-on: ubuntu-latest
needs: [sast, sca]
permissions:
contents: read
packages: write
steps:
- uses: actions/checkout@v4
- name: Hadolint
uses: hadolint/hadolint-[email protected]
- name: Build Image
run: docker build -t ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} .
- name: Trivy Scan
uses: aquasecurity/trivy-action@master
with:
image-ref: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
format: 'sarif'
exit-code: '1'
severity: 'CRITICAL,HIGH'
- name: Generate SBOM
run: syft ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }} -o cyclonedx-json > sbom.json
- name: Sign Image
run: cosign sign --key env://COSIGN_KEY ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
env:
COSIGN_KEY: ${{ secrets.COSIGN_KEY }}
- name: Push Image
run: docker push ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}:${{ github.sha }}
# Stage 5: IaC Scan
iac-scan:
runs-on: ubuntu-latest
needs: build
steps:
- uses: actions/checkout@v4
- name: Checkov
uses: bridgecrewio/checkov-action@master
with:
directory: kubernetes/
framework: kubernetes
# Stage 6: Deploy
deploy:
runs-on: ubuntu-latest
needs: iac-scan
if: github.ref == 'refs/heads/main'
environment: production
steps:
- uses: actions/checkout@v4
- name: Deploy to Kubernetes
run: |
kubectl apply -f kubernetes/
env:
KUBECONFIG: ${{ secrets.KUBECONFIG }}
Kyverno Policies
# policies/security-policies.yaml
apiVersion: kyverno.io/v1
kind: ClusterPolicy
metadata:
name: secure-deployment
spec:
validationFailureAction: enforce
rules:
# Require signed images
- name: verify-signature
match:
any:
- resources:
kinds:
- Pod
verifyImages:
- imageReferences:
- "ghcr.io/*"
attestors:
- entries:
- keyless:
subject: "*@github.com"
issuer: "https://token.actions.githubusercontent.com"
# Require security context
- name: require-security-context
match:
any:
- resources:
kinds:
- Pod
validate:
message: "Security context required"
pattern:
spec:
securityContext:
runAsNonRoot: true
containers:
- securityContext:
allowPrivilegeEscalation: false
3 - Quiz de révision
-
Qu'est-ce que "Shift Left" en sécurité ?
-
Quelle est la différence entre SAST et DAST ?
-
Qu'est-ce qu'un SBOM ?
-
Comment sécuriser les secrets dans Kubernetes ?
-
Qu'est-ce que Kyverno ?
Réponses
-
Shift Left : Intégrer la sécurité plus tôt dans le cycle de développement pour détecter les problèmes avant la production.
-
SAST : Analyse statique du code source (avant exécution). DAST : Analyse dynamique de l'application en cours d'exécution.
-
SBOM (Software Bill of Materials) : Liste complète des composants, versions et dépendances d'une application.
-
Utiliser un Secrets Manager externe (Vault, AWS Secrets Manager) avec External Secrets Operator ou Vault Agent Injector.
-
Kyverno : Policy engine Kubernetes-native pour valider, muter et générer des ressources.
4 - Certifications
Certifications recommandées
| Certification | Fournisseur | Focus |
|---|---|---|
| Certified Kubernetes Security (CKS) | CNCF | K8s Security |
| AWS Security Specialty | AWS | AWS Security |
| GIAC Cloud Security (GCLD) | SANS | Cloud Security |
| Certified DevSecOps Professional (CDP) | Practical DevSecOps | DevSecOps |
| CompTIA Security+ | CompTIA | Fundamentals |
Ressources
Résumé du cours
Félicitations ! Vous avez complété le cours DevSecOps.
Vous maîtrisez maintenant :
- Les principes Shift Left
- Les outils SAST et DAST
- La sécurité des conteneurs
- La Supply Chain Security
- La gestion des secrets
- Compliance as Code
- L'Incident Response
Prochaines étapes
- Pratiquer avec des labs
- Passer la certification CKS
- Implémenter DevSecOps dans votre organisation
- Contribuer à des projets open source de sécurité