Aller au contenu

Integration

La sécurité runtime ne fonctionne pas en silo. Son efficacité depend de son integration avec les autres services de la plateforme.

Integration avec l'observabilité

Falco vers Loki

Les alertes Falco sont collectees par Alloy et poussees vers Loki pour centralisation et requetage.

graph LR
    Falco["Falco<br/>(stdout JSON)"] --> Alloy["Alloy<br/>(collecte)"] --> Loki["Loki<br/>(stockage)"] --> Grafana["Grafana<br/>(dashboard alertes)"]

Configuration du pipeline :

# Labels Loki ajoutes aux alertes Falco
# (via la configuration Alloy)
loki.process "falco_enrich" {
  stage.json {
    expressions = {
      priority = "priority",
      rule     = "rule",
      source   = "output_fields.container.name",
      image    = "output_fields.container.image.repository",
    }
  }
  stage.labels {
    values = {
      priority = "",
      rule     = "",
      source   = "",
    }
  }
  forward_to = [loki.write.default.receiver]
}

Dashboards Grafana sécurité

Créer un dossier Securite dans Grafana avec les dashboards suivants :

Dashboard Contenu Requête principale
Vue d'ensemble sécurité Nombre d'alertes par priorité, tendance 24h sum by (priority) (count_over_time({job="falco"}[1h]))
Alertes critiques Liste des alertes Critical/Error en temps reel {job="falco"} \| json \| priority =~ "Critical\|Error"
Violations OPA Contraintes violees, namespaces concernes gatekeeper_violations (Prometheus)
Vulnerabilites Trivy CVE critiques et hautes par namespace trivy_image_vulnerabilities{severity=~"Critical\|High"}
Performance sécurité Événements/s Falco, latence OPA, drops rate(falco_events_total[5m]), gatekeeper_request_duration

Alertes Grafana

# Alerte Grafana : Falco Critical
apiVersion: 1
groups:
  - name: security-runtime
    folder: Securite
    interval: 1m
    rules:
      - alert: FalcoCriticalAlert
        expr: |
          count_over_time({job="falco"} | json | priority = "Critical" [5m]) > 0
        for: 0m
        labels:
          severity: critical
          team: security
        annotations:
          summary: "Alerte Falco critique detectee"
          description: "{{ $labels.rule }} sur {{ $labels.source }}"

      - alert: GatekeeperViolationsHigh
        expr: |
          sum(gatekeeper_violations) > 10
        for: 5m
        labels:
          severity: warning
          team: security
        annotations:
          summary: "Nombre eleve de violations OPA/Gatekeeper"

      - alert: TrivyCriticalCVE
        expr: |
          sum(trivy_image_vulnerabilities{severity="Critical"}) > 0
        for: 15m
        labels:
          severity: warning
          team: security
        annotations:
          summary: "CVE critiques detectees dans des images deployees"

Integration avec le CI/CD

Trivy dans le pipeline

Trivy s'integre dans le pipeline CI/CD comme gate de sécurité avant le déploiement :

# .gitea/workflows/security-scan.yaml
name: Security Scan
on:
  push:
    branches: [main]
  pull_request:

jobs:
  trivy-scan:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Build image
        run: docker build -t ${{ github.repository }}:${{ github.sha }} .

      - name: Trivy vulnerability scan
        uses: aquasecurity/trivy-action@master
        with:
          image-ref: ${{ github.repository }}:${{ github.sha }}
          format: table
          exit-code: 1          # Echouer si des CVE critiques
          severity: CRITICAL,HIGH
          ignore-unfixed: true

      - name: Trivy config scan
        uses: aquasecurity/trivy-action@master
        with:
          scan-type: config
          scan-ref: ./k8s/
          format: table
          exit-code: 1
          severity: CRITICAL,HIGH

OPA/Conftest pour validation pre-déploiement

Avant de soumettre les manifestes au cluster, validez-les localement avec Conftest (OPA en mode CLI) :

# .gitea/workflows/policy-check.yaml
name: Policy Check
on:
  pull_request:
    paths:
      - 'k8s/**'

jobs:
  conftest:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - name: Install conftest
        run: |
          curl -L -o conftest.tar.gz \
            https://github.com/open-policy-agent/conftest/releases/download/v0.52.0/conftest_0.52.0_Linux_x86_64.tar.gz
          tar xzf conftest.tar.gz
          sudo mv conftest /usr/local/bin/

      - name: Validate Kubernetes manifests
        run: conftest test k8s/ --policy policy/ --all-namespaces
# policy/security.rego
package main

# Interdire les images sans tag ou avec :latest
deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  endswith(container.image, ":latest")
  msg := sprintf("Image avec tag :latest interdite : %s", [container.image])
}

deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  not contains(container.image, ":")
  msg := sprintf("Image sans tag interdite : %s", [container.image])
}

# Exiger un securityContext
deny[msg] {
  input.kind == "Deployment"
  container := input.spec.template.spec.containers[_]
  not container.securityContext.runAsNonRoot
  msg := sprintf("runAsNonRoot requis pour : %s", [container.name])
}

Integration avec l'IAM

RBAC Kubernetes aligne sur Keycloak

Les groupes Keycloak sont mappes sur des ClusterRoles Kubernetes pour controler l'accès aux ressources de sécurité :

Groupe Keycloak ClusterRole Kubernetes Accès sécurité
security-admins security-admin Lecture/écriture des regles Falco, contraintes OPA, rapports Trivy
platform-engineers security-viewer Lecture des alertes et rapports, pas de modification des regles
developers security-none Aucun accès direct aux composants de sécurité
# clusterrole-security-admin.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: security-admin
rules:
  # Gatekeeper
  - apiGroups: ["templates.gatekeeper.sh"]
    resources: ["constrainttemplates"]
    verbs: ["get", "list", "watch", "create", "update", "delete"]
  - apiGroups: ["constraints.gatekeeper.sh"]
    resources: ["*"]
    verbs: ["get", "list", "watch", "create", "update", "delete"]
  # Trivy reports
  - apiGroups: ["aquasecurity.github.io"]
    resources: ["vulnerabilityreports", "configauditreports", "exposedsecretreports"]
    verbs: ["get", "list", "watch", "delete"]
  # Falco namespace
  - apiGroups: [""]
    resources: ["pods", "pods/log"]
    verbs: ["get", "list"]
    # Restreint au namespace falco-system via RoleBinding
# clusterrole-security-viewer.yaml
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
  name: security-viewer
rules:
  - apiGroups: ["constraints.gatekeeper.sh"]
    resources: ["*"]
    verbs: ["get", "list", "watch"]
  - apiGroups: ["aquasecurity.github.io"]
    resources: ["vulnerabilityreports", "configauditreports"]
    verbs: ["get", "list", "watch"]

Accès aux dashboards Grafana

Les dashboards de sécurité dans Grafana sont proteges par RBAC :

# Grafana provisioning : permissions du dossier Securite
apiVersion: 1
providers:
  - name: security-dashboards
    folder: Securite
    folderUid: security-folder
    permissions:
      - role: Admin
        permission: Edit
      - teamId: security-team    # Equipe securite
        permission: View
      # Pas d'acces pour les autres equipes

Integration avec la politique de classification

Regles Falco par niveau de classification

Les regles Falco sont differenciees selon le niveau de classification du namespace :

# Regles strictes pour les namespaces Restreint
- rule: Any Process Execution in Restricted Namespace
  desc: Detect any new process in a namespace classified as Restricted
  condition: >
    spawned_process and container and
    k8s.ns.name in (vault-system, pki-system, secrets-management)
  output: >
    Execution de processus dans un namespace Restreint
    (namespace=%k8s.ns.name process=%proc.name user=%user.name
     container=%container.name image=%container.image.repository)
  priority: NOTICE
  tags: [classification_restricted]

# Regles standard pour les namespaces Confidentiel
- rule: Shell in Confidential Namespace
  desc: Detect shell spawned in a namespace classified as Confidential
  condition: >
    spawned_process and container and
    proc.name in (bash, sh, zsh) and
    k8s.ns.name in (production, staging, cicd)
  output: >
    Shell dans un namespace Confidentiel
    (namespace=%k8s.ns.name shell=%proc.name user=%user.name
     container=%container.name)
  priority: WARNING
  tags: [classification_confidential]

Contraintes OPA par classification de namespace

# Contrainte OPA : les namespaces Restreint n'acceptent que des images signees
apiVersion: constraints.gatekeeper.sh/v1beta1
kind: K8sAllowedRepos
metadata:
  name: restricted-ns-signed-images-only
spec:
  enforcementAction: deny
  match:
    kinds:
      - apiGroups: [""]
        kinds: ["Pod"]
    namespaces:
      - vault-system
      - pki-system
  parameters:
    repos:
      - "registry.internal.company.io/signed/"

Classification et sécurité runtime

La politique de classification (voir Classification et zones de confiance) définit les niveaux de protection requis. La sécurité runtime implemente ces niveaux via des regles Falco et des contraintes OPA adaptées à chaque classification. Les namespaces Restreint ont les regles les plus strictes, les namespaces Interne les plus permissives.