SECURITE
Intermédiaire

Sécurité Informatique : Les Fondamentaux pour DevOps et SRE

Florian Courouge
20 min de lecture
3,399 mots
0 vues
Sécurité
DevSecOps
OWASP
Kubernetes
Hardening
Secrets
Zero Trust

Sécurité Informatique : Les Fondamentaux pour DevOps et SRE

La sécurité n'est plus l'affaire d'une équipe dédiée. Dans un monde DevOps, chaque ingénieur est responsable de la sécurité. Ce guide couvre les fondamentaux que tout professionnel IT doit maîtriser.

Les principes fondamentaux

La triade CIA

┌─────────────────────────────────────────────────────────────────┐
│                    TRIADE CIA                                    │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│                    ┌───────────────┐                            │
│                    │ CONFIDENTIALITÉ│                            │
│                    │   (Secrecy)    │                            │
│                    └───────┬───────┘                            │
│                            │                                     │
│            ┌───────────────┼───────────────┐                    │
│            │               │               │                     │
│            ▼               ▼               ▼                     │
│    ┌──────────────┐ ┌────────────┐ ┌─────────────────┐         │
│    │ Chiffrement  │ │  Contrôle  │ │   Gestion des   │         │
│    │ des données  │ │  d'accès   │ │     secrets     │         │
│    └──────────────┘ └────────────┘ └─────────────────┘         │
│                                                                  │
│    ┌───────────────┐              ┌───────────────┐             │
│    │   INTÉGRITÉ   │              │ DISPONIBILITÉ │             │
│    │  (Integrity)  │              │(Availability) │             │
│    └───────┬───────┘              └───────┬───────┘             │
│            │                              │                      │
│      ┌─────┴─────┐                  ┌─────┴─────┐               │
│      ▼           ▼                  ▼           ▼               │
│ ┌─────────┐ ┌─────────┐      ┌─────────┐ ┌─────────┐           │
│ │Checksums│ │Signatures│      │Redondance│ │ Backups │           │
│ │ /Hash   │ │numériques│      │   HA    │ │   DR    │           │
│ └─────────┘ └─────────┘      └─────────┘ └─────────┘           │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Principe du moindre privilège

# ❌ MAUVAIS : Accès admin complet
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
  name: dev-team-admin
subjects:
  - kind: Group
    name: developers
roleRef:
  kind: ClusterRole
  name: cluster-admin  # Trop de privilèges !

---
# ✅ BON : Accès limité au namespace
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: dev-team-access
  namespace: development  # Limité à un namespace
subjects:
  - kind: Group
    name: developers
roleRef:
  kind: Role
  name: developer-role

---
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  name: developer-role
  namespace: development
rules:
  # Lecture des pods et logs
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list", "watch"]

  # Déploiements limités
  - apiGroups: ["apps"]
    resources: ["deployments"]
    verbs: ["get", "list", "create", "update"]
    # Pas de "delete" !

  # Pas d'accès aux secrets !

Defense in Depth

┌─────────────────────────────────────────────────────────────────┐
│                  DEFENSE IN DEPTH                                │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│   Internet                                                       │
│       │                                                          │
│       ▼                                                          │
│   ┌───────────────────────────────────────┐ ← Couche 1          │
│   │           WAF / DDoS Protection        │   Périmètre        │
│   └───────────────────┬───────────────────┘                     │
│                       │                                          │
│                       ▼                                          │
│   ┌───────────────────────────────────────┐ ← Couche 2          │
│   │         Firewall / Load Balancer       │   Réseau           │
│   └───────────────────┬───────────────────┘                     │
│                       │                                          │
│                       ▼                                          │
│   ┌───────────────────────────────────────┐ ← Couche 3          │
│   │      Network Policies / Service Mesh   │   Micro-seg        │
│   └───────────────────┬───────────────────┘                     │
│                       │                                          │
│                       ▼                                          │
│   ┌───────────────────────────────────────┐ ← Couche 4          │
│   │      Application Security (OWASP)      │   Application      │
│   └───────────────────┬───────────────────┘                     │
│                       │                                          │
│                       ▼                                          │
│   ┌───────────────────────────────────────┐ ← Couche 5          │
│   │     Authentication / Authorization     │   Identité         │
│   └───────────────────┬───────────────────┘                     │
│                       │                                          │
│                       ▼                                          │
│   ┌───────────────────────────────────────┐ ← Couche 6          │
│   │       Data Encryption (at rest)        │   Données          │
│   └───────────────────────────────────────┘                     │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

OWASP Top 10 (2021)

Les 10 vulnérabilités les plus critiques

Rang Vulnérabilité Description Impact
A01 Broken Access Control Contrôle d'accès défaillant Élévation de privilèges
A02 Cryptographic Failures Chiffrement insuffisant Fuite de données
A03 Injection SQL, NoSQL, OS, LDAP injection Exécution de code
A04 Insecure Design Architecture non sécurisée Vulnérabilités structurelles
A05 Security Misconfiguration Configuration par défaut Exposition de données
A06 Vulnerable Components Dépendances vulnérables Supply chain attack
A07 Auth Failures Authentification faible Usurpation d'identité
A08 Software Integrity Intégrité du code non vérifiée Code malveillant
A09 Logging Failures Logs insuffisants Attaques non détectées
A10 SSRF Server-Side Request Forgery Accès réseau interne

Exemples et remediations

A03 - Injection SQL

# ❌ VULNÉRABLE : Injection SQL possible
def get_user_vulnerable(username: str):
    query = f"SELECT * FROM users WHERE username = '{username}'"
    # Si username = "admin' OR '1'='1" → Retourne TOUS les users !
    return db.execute(query)

# ✅ SÉCURISÉ : Requête paramétrée
def get_user_secure(username: str):
    query = "SELECT * FROM users WHERE username = %s"
    return db.execute(query, (username,))

# ✅ ENCORE MIEUX : ORM avec validation
from sqlalchemy import select
from pydantic import BaseModel, validator

class UserQuery(BaseModel):
    username: str

    @validator('username')
    def validate_username(cls, v):
        if not v.isalnum():
            raise ValueError('Username must be alphanumeric')
        return v

def get_user_orm(query: UserQuery):
    stmt = select(User).where(User.username == query.username)
    return session.execute(stmt).scalar()

A05 - Security Misconfiguration

# ❌ MAUVAIS : Pod avec privilèges root
apiVersion: v1
kind: Pod
metadata:
  name: vulnerable-pod
spec:
  containers:
    - name: app
      image: myapp:latest
      # Pas de securityContext = root par défaut !

---
# ✅ BON : Pod durci avec security context
apiVersion: v1
kind: Pod
metadata:
  name: secure-pod
spec:
  securityContext:
    runAsNonRoot: true
    runAsUser: 1000
    runAsGroup: 1000
    fsGroup: 1000
    seccompProfile:
      type: RuntimeDefault

  containers:
    - name: app
      image: myapp:latest
      securityContext:
        allowPrivilegeEscalation: false
        readOnlyRootFilesystem: true
        capabilities:
          drop:
            - ALL
        # Ajouter uniquement ce qui est nécessaire
        # capabilities:
        #   add:
        #     - NET_BIND_SERVICE

      # Ressources limitées
      resources:
        limits:
          memory: "256Mi"
          cpu: "500m"
        requests:
          memory: "128Mi"
          cpu: "100m"

      # Volumes en lecture seule sauf nécessité
      volumeMounts:
        - name: tmp
          mountPath: /tmp
        - name: config
          mountPath: /etc/app
          readOnly: true

  volumes:
    - name: tmp
      emptyDir: {}
    - name: config
      configMap:
        name: app-config

Hardening système Linux

Checklist de sécurisation

#!/bin/bash
# hardening-linux.sh - Script de sécurisation basique

set -euo pipefail

echo "=== HARDENING LINUX ==="

# 1. Mises à jour de sécurité
echo "[1/10] Mises à jour..."
apt update && apt upgrade -y
apt install -y unattended-upgrades
dpkg-reconfigure -plow unattended-upgrades

# 2. Désactiver les services inutiles
echo "[2/10] Désactivation services inutiles..."
systemctl disable --now cups avahi-daemon bluetooth 2>/dev/null || true

# 3. Configuration SSH sécurisée
echo "[3/10] Hardening SSH..."
cat > /etc/ssh/sshd_config.d/hardening.conf << 'EOF'
# Authentification
PermitRootLogin no
PasswordAuthentication no
PubkeyAuthentication yes
AuthenticationMethods publickey
MaxAuthTries 3
LoginGraceTime 20

# Sécurité
X11Forwarding no
AllowTcpForwarding no
AllowAgentForwarding no
PermitTunnel no

# Algorithmes sécurisés
KexAlgorithms curve25519-sha256,curve25519-sha256@libssh.org
Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com
MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com

# Timeouts
ClientAliveInterval 300
ClientAliveCountMax 2
EOF
systemctl restart sshd

# 4. Configuration du firewall
echo "[4/10] Configuration UFW..."
apt install -y ufw
ufw default deny incoming
ufw default allow outgoing
ufw allow ssh
ufw allow 80/tcp
ufw allow 443/tcp
ufw --force enable

# 5. Paramètres sysctl sécurisés
echo "[5/10] Hardening kernel..."
cat > /etc/sysctl.d/99-hardening.conf << 'EOF'
# Protection réseau
net.ipv4.conf.all.send_redirects = 0
net.ipv4.conf.default.send_redirects = 0
net.ipv4.conf.all.accept_redirects = 0
net.ipv4.conf.default.accept_redirects = 0
net.ipv4.conf.all.accept_source_route = 0
net.ipv4.conf.default.accept_source_route = 0
net.ipv4.icmp_echo_ignore_broadcasts = 1
net.ipv4.icmp_ignore_bogus_error_responses = 1
net.ipv4.conf.all.rp_filter = 1
net.ipv4.conf.default.rp_filter = 1
net.ipv4.tcp_syncookies = 1

# Protection mémoire
kernel.randomize_va_space = 2
kernel.kptr_restrict = 2
kernel.dmesg_restrict = 1
kernel.perf_event_paranoid = 3

# Limiter core dumps
fs.suid_dumpable = 0
EOF
sysctl -p /etc/sysctl.d/99-hardening.conf

# 6. Permissions fichiers critiques
echo "[6/10] Permissions fichiers..."
chmod 600 /etc/shadow
chmod 644 /etc/passwd
chmod 600 /etc/gshadow
chmod 644 /etc/group
chmod 700 /root

# 7. Configuration auditd
echo "[7/10] Configuration audit..."
apt install -y auditd
cat > /etc/audit/rules.d/hardening.rules << 'EOF'
# Suppression des règles existantes
-D

# Buffer size
-b 8192

# Surveillance des fichiers sensibles
-w /etc/passwd -p wa -k identity
-w /etc/shadow -p wa -k identity
-w /etc/group -p wa -k identity
-w /etc/sudoers -p wa -k sudo_changes
-w /etc/ssh/sshd_config -p wa -k ssh_config

# Surveillance des commandes privilégiées
-a always,exit -F path=/usr/bin/sudo -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged
-a always,exit -F path=/usr/bin/su -F perm=x -F auid>=1000 -F auid!=4294967295 -k privileged

# Surveillance des modifications système
-a always,exit -F arch=b64 -S execve -F euid=0 -F auid>=1000 -F auid!=4294967295 -k root_commands
EOF
systemctl restart auditd

# 8. Fail2ban
echo "[8/10] Installation Fail2ban..."
apt install -y fail2ban
cat > /etc/fail2ban/jail.local << 'EOF'
[DEFAULT]
bantime = 3600
findtime = 600
maxretry = 3

[sshd]
enabled = true
port = ssh
filter = sshd
logpath = /var/log/auth.log
maxretry = 3
EOF
systemctl enable --now fail2ban

# 9. Limites utilisateurs
echo "[9/10] Configuration limites..."
cat >> /etc/security/limits.conf << 'EOF'
* hard core 0
* hard maxlogins 10
* soft nofile 65535
* hard nofile 65535
EOF

# 10. Banner de connexion
echo "[10/10] Banner légal..."
cat > /etc/issue.net << 'EOF'
***************************************************************************
                       AUTHORIZED ACCESS ONLY
***************************************************************************
This system is for authorized use only. All activities are monitored and
logged. Unauthorized access will be prosecuted to the full extent of the law.
***************************************************************************
EOF

echo "=== HARDENING TERMINÉ ==="
echo "Redémarrage recommandé pour appliquer tous les changements."

Gestion des secrets

HashiCorp Vault : Configuration de base

# Configuration Vault pour production
storage "raft" {
  path    = "/opt/vault/data"
  node_id = "vault-1"

  retry_join {
    leader_api_addr = "https://vault-2.internal:8200"
  }
  retry_join {
    leader_api_addr = "https://vault-3.internal:8200"
  }
}

listener "tcp" {
  address       = "0.0.0.0:8200"
  tls_cert_file = "/opt/vault/certs/vault.crt"
  tls_key_file  = "/opt/vault/certs/vault.key"

  # TLS 1.3 uniquement
  tls_min_version = "tls13"
}

# Auto-unseal avec AWS KMS
seal "awskms" {
  region     = "eu-west-1"
  kms_key_id = "alias/vault-unseal"
}

api_addr      = "https://vault.internal:8200"
cluster_addr  = "https://vault-1.internal:8201"
ui            = true

# Audit logging
telemetry {
  prometheus_retention_time = "30s"
  disable_hostname          = true
}
# Activer le moteur de secrets KV v2
vault secrets enable -path=secret kv-v2

# Stocker un secret
vault kv put secret/myapp/config \
  database_url="postgres://user:pass@db:5432/app" \
  api_key="sk-xxx-secret-key"

# Configurer l'auth Kubernetes
vault auth enable kubernetes

vault write auth/kubernetes/config \
  kubernetes_host="https://kubernetes.default.svc:443" \
  kubernetes_ca_cert=@/var/run/secrets/kubernetes.io/serviceaccount/ca.crt

# Créer une policy pour l'application
vault policy write myapp-policy - <<EOF
path "secret/data/myapp/*" {
  capabilities = ["read"]
}
EOF

# Créer un rôle Kubernetes
vault write auth/kubernetes/role/myapp \
  bound_service_account_names=myapp-sa \
  bound_service_account_namespaces=production \
  policies=myapp-policy \
  ttl=1h
# Injection automatique de secrets via Vault Agent
apiVersion: apps/v1
kind: Deployment
metadata:
  name: myapp
  namespace: production
spec:
  template:
    metadata:
      annotations:
        # Activer l'injection Vault
        vault.hashicorp.com/agent-inject: "true"
        vault.hashicorp.com/role: "myapp"

        # Injecter les secrets dans des fichiers
        vault.hashicorp.com/agent-inject-secret-config: "secret/data/myapp/config"
        vault.hashicorp.com/agent-inject-template-config: |
          {{- with secret "secret/data/myapp/config" -}}
          DATABASE_URL={{ .Data.data.database_url }}
          API_KEY={{ .Data.data.api_key }}
          {{- end }}

    spec:
      serviceAccountName: myapp-sa
      containers:
        - name: app
          image: myapp:latest
          env:
            # Utiliser le fichier injecté
            - name: CONFIG_FILE
              value: /vault/secrets/config
          volumeMounts:
            - name: vault-secrets
              mountPath: /vault/secrets
              readOnly: true

Secrets Kubernetes avec External Secrets Operator

# ExternalSecret qui synchronise depuis Vault
apiVersion: external-secrets.io/v1beta1
kind: ExternalSecret
metadata:
  name: myapp-secrets
  namespace: production
spec:
  refreshInterval: 1h

  secretStoreRef:
    kind: ClusterSecretStore
    name: vault-backend

  target:
    name: myapp-secrets
    creationPolicy: Owner
    template:
      type: Opaque
      data:
        # Template Go pour formater les secrets
        DATABASE_URL: "{{ .database_url }}"
        API_KEY: "{{ .api_key }}"

  data:
    - secretKey: database_url
      remoteRef:
        key: secret/data/myapp/config
        property: database_url

    - secretKey: api_key
      remoteRef:
        key: secret/data/myapp/config
        property: api_key

---
# ClusterSecretStore pour Vault
apiVersion: external-secrets.io/v1beta1
kind: ClusterSecretStore
metadata:
  name: vault-backend
spec:
  provider:
    vault:
      server: "https://vault.internal:8200"
      path: "secret"
      version: "v2"
      auth:
        kubernetes:
          mountPath: "kubernetes"
          role: "external-secrets"
          serviceAccountRef:
            name: external-secrets-sa
            namespace: external-secrets

Network Security

Network Policies Kubernetes

# Politique par défaut : deny all
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: default-deny-all
  namespace: production
spec:
  podSelector: {}  # Tous les pods
  policyTypes:
    - Ingress
    - Egress

---
# Autoriser uniquement le trafic nécessaire
apiVersion: networking.k8s.io/v1
kind: NetworkPolicy
metadata:
  name: api-network-policy
  namespace: production
spec:
  podSelector:
    matchLabels:
      app: api-backend

  policyTypes:
    - Ingress
    - Egress

  ingress:
    # Autoriser depuis l'ingress controller
    - from:
        - namespaceSelector:
            matchLabels:
              name: ingress-nginx
          podSelector:
            matchLabels:
              app.kubernetes.io/name: ingress-nginx
      ports:
        - protocol: TCP
          port: 8080

    # Autoriser depuis le frontend
    - from:
        - podSelector:
            matchLabels:
              app: frontend
      ports:
        - protocol: TCP
          port: 8080

  egress:
    # Autoriser vers la base de données
    - to:
        - podSelector:
            matchLabels:
              app: postgresql
      ports:
        - protocol: TCP
          port: 5432

    # Autoriser vers Redis
    - to:
        - podSelector:
            matchLabels:
              app: redis
      ports:
        - protocol: TCP
          port: 6379

    # Autoriser DNS
    - to:
        - namespaceSelector: {}
          podSelector:
            matchLabels:
              k8s-app: kube-dns
      ports:
        - protocol: UDP
          port: 53

mTLS avec Istio

# Activer mTLS strict pour tout le mesh
apiVersion: security.istio.io/v1beta1
kind: PeerAuthentication
metadata:
  name: default
  namespace: istio-system
spec:
  mtls:
    mode: STRICT

---
# Authorization Policy : Zero Trust
apiVersion: security.istio.io/v1beta1
kind: AuthorizationPolicy
metadata:
  name: api-authz
  namespace: production
spec:
  selector:
    matchLabels:
      app: api-backend

  # Deny par défaut, puis allow explicite
  action: ALLOW

  rules:
    # Autoriser le frontend
    - from:
        - source:
            principals:
              - "cluster.local/ns/production/sa/frontend-sa"
      to:
        - operation:
            methods: ["GET", "POST"]
            paths: ["/api/*"]

    # Autoriser les health checks depuis Kubernetes
    - from:
        - source:
            namespaces: ["kube-system"]
      to:
        - operation:
            methods: ["GET"]
            paths: ["/health/*"]

Scan de vulnérabilités

Pipeline CI/CD sécurisé

stages:
  - build
  - security-scan
  - deploy

variables:
  DOCKER_IMAGE: registry.company.com/myapp

# Scan des dépendances
dependency-scan:
  stage: security-scan
  image: aquasec/trivy:latest
  script:
    # Scan du code source
    - trivy fs --severity HIGH,CRITICAL --exit-code 1 .

    # Générer rapport SBOM
    - trivy fs --format spdx-json -o sbom.json .
  artifacts:
    reports:
      cyclonedx: sbom.json

# Scan SAST (Static Application Security Testing)
sast-scan:
  stage: security-scan
  image: semgrep/semgrep:latest
  script:
    - semgrep scan --config=auto --error --json -o sast-report.json .
  artifacts:
    reports:
      sast: sast-report.json
  allow_failure: false

# Scan de l'image Docker
container-scan:
  stage: security-scan
  image: aquasec/trivy:latest
  needs: ["build"]
  script:
    - trivy image --severity HIGH,CRITICAL --exit-code 1 ${DOCKER_IMAGE}:${CI_COMMIT_SHA}
  allow_failure: false

# Scan des secrets accidentels
secrets-scan:
  stage: security-scan
  image: trufflesecurity/trufflehog:latest
  script:
    - trufflehog git file://. --since-commit HEAD~10 --fail
  allow_failure: false

# Scan IaC (Terraform, Kubernetes)
iac-scan:
  stage: security-scan
  image: bridgecrew/checkov:latest
  script:
    - checkov -d . --framework terraform,kubernetes --output junitxml > checkov-report.xml
  artifacts:
    reports:
      junit: checkov-report.xml

# Scan DAST en staging
dast-scan:
  stage: security-scan
  image: owasp/zap2docker-stable:latest
  needs: ["deploy-staging"]
  script:
    - zap-baseline.py -t https://staging.company.com -r zap-report.html
  artifacts:
    paths:
      - zap-report.html
  when: manual

Dockerfile sécurisé

# Étape 1 : Build
FROM golang:1.21-alpine AS builder

# Utilisateur non-root pour le build
RUN adduser -D -g '' appuser

WORKDIR /build

# Copier uniquement les fichiers de dépendances d'abord (cache)
COPY go.mod go.sum ./
RUN go mod download

# Copier le code source
COPY . .

# Build statique avec flags de sécurité
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 \
    go build -ldflags="-w -s -X main.version=${VERSION}" \
    -o /app ./cmd/server

# Étape 2 : Image finale minimale
FROM scratch

# Métadonnées
LABEL org.opencontainers.image.source="https://github.com/company/app"
LABEL org.opencontainers.image.vendor="Company"

# Copier les certificats CA pour HTTPS
COPY --from=builder /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/

# Copier l'utilisateur non-root
COPY --from=builder /etc/passwd /etc/passwd

# Copier le binaire
COPY --from=builder /app /app

# Utiliser l'utilisateur non-root
USER appuser

# Port non privilégié
EXPOSE 8080

# Healthcheck
HEALTHCHECK --interval=30s --timeout=3s --start-period=5s --retries=3 \
    CMD ["/app", "healthcheck"]

# Point d'entrée
ENTRYPOINT ["/app"]

Checklist sécurité DevOps

## Infrastructure
- [ ] Firewall configuré (deny by default)
- [ ] SSH durci (clés uniquement, no root)
- [ ] Updates automatiques activées
- [ ] Audit logging activé
- [ ] Fail2ban ou équivalent

## Kubernetes
- [ ] RBAC configuré (least privilege)
- [ ] Network Policies en place
- [ ] Pod Security Standards appliqués
- [ ] Secrets chiffrés (Vault, Sealed Secrets)
- [ ] Images scannées et signées
- [ ] Resource limits sur tous les pods

## Application
- [ ] Validation des inputs
- [ ] Requêtes SQL paramétrées
- [ ] Headers de sécurité (CSP, HSTS)
- [ ] Rate limiting
- [ ] Logs d'audit

## CI/CD
- [ ] Scan SAST dans le pipeline
- [ ] Scan des dépendances
- [ ] Scan des images Docker
- [ ] Scan des secrets
- [ ] Scan IaC

## Secrets
- [ ] Pas de secrets en clair dans le code
- [ ] Rotation automatique
- [ ] Accès audité
- [ ] Secrets scope minimal

Conclusion

La sécurité en DevOps repose sur trois piliers :

  1. Shift Left : Intégrer la sécurité dès le développement
  2. Automatisation : Scans automatiques dans les pipelines
  3. Zero Trust : Ne faire confiance à rien par défaut

:::warning Rappel important : La sécurité n'est jamais "terminée". C'est un processus continu d'amélioration, de veille et d'adaptation aux nouvelles menaces. :::


Ressources complémentaires

F

Florian Courouge

Expert DevOps & Kafka | Consultant freelance specialise dans les architectures distribuees et le streaming de donnees.

Articles similaires