Aller au contenu principal

Configuration de Sécurité

Ce guide couvre les paramètres de sécurité introduits dans les Correctifs de Sécurité P0 Team Coca (ADR-018).

Résumé

Toutes les fonctionnalités de sécurité sont activées par défaut avec des paramètres sécurisés. Vous n'avez besoin de les configurer que si vous personnalisez votre environnement.

Référence Rapide

FonctionnalitéParamètreDéfautRollback
Audience JWTALLOWED_AUDIENCESstoa-mcp-gateway,accountDéfinir à ""
CORSCORS_ORIGINSListe blancheDéfinir à "*"
Limites SSESSE_LIMITER_ENABLEDtrueDéfinir à false
Proxies de ConfianceSSE_TRUSTED_PROXIES"" (aucun)Configurer les CIDRs

Validation de l'Audience JWT (CAB-938)

Ce que ça Fait

Valide que les tokens JWT ont été émis spécifiquement pour le MCP Gateway, empêchant la réutilisation des tokens d'autres clients Keycloak.

Configuration

# Variables d'environnement
ALLOWED_AUDIENCES=stoa-mcp-gateway,account

# Ou dans settings.py
allowed_audiences: str = "stoa-mcp-gateway,account"

Paramètres

ParamètreTypeDéfautDescription
ALLOWED_AUDIENCESstringstoa-mcp-gateway,accountListe d'audiences JWT valides séparées par des virgules

Configuration Keycloak

Assurez-vous que votre client Keycloak est configuré pour inclure l'audience :

{
"clientId": "stoa-mcp-gateway",
"protocolMappers": [
{
"name": "audience-mapper",
"protocol": "openid-connect",
"protocolMapper": "oidc-audience-mapper",
"config": {
"included.client.audience": "stoa-mcp-gateway",
"access.token.claim": "true"
}
}
]
}

Test

# Décoder un token et vérifier l'audience
echo $TOKEN | cut -d. -f2 | base64 -d | jq '.aud'
# Doit inclure : "stoa-mcp-gateway"

Rollback

Si la validation JWT casse l'authentification :

# Désactiver la validation de l'audience (temporaire !)
export ALLOWED_AUDIENCES=""
attention

Désactiver la validation de l'audience est un risque de sécurité. À utiliser uniquement pour le débogage.


Configuration CORS (CAB-950)

Ce que ça Fait

Restreint les origines pouvant effectuer des requêtes cross-origin vers l'API, empêchant les attaques CSRF et d'exfiltration de données.

Configuration

# Variables d'environnement
CORS_ORIGINS=https://console.stoa.dev,https://portal.stoa.dev
CORS_ALLOW_METHODS=GET,POST,PUT,DELETE,OPTIONS
CORS_ALLOW_HEADERS=Authorization,Content-Type,X-Request-ID,X-Tenant-ID
CORS_EXPOSE_HEADERS=X-Request-ID,X-Trace-ID
CORS_MAX_AGE=600

Paramètres

ParamètreTypeDéfautDescription
CORS_ORIGINSstringVoir ci-dessousOrigines autorisées séparées par des virgules
CORS_ALLOW_METHODSstringGET,POST,PUT,DELETE,OPTIONSMéthodes HTTP autorisées
CORS_ALLOW_HEADERSstringAuthorization,Content-Type,...En-têtes de requête autorisés
CORS_EXPOSE_HEADERSstringX-Request-ID,X-Trace-IDEn-têtes exposés au navigateur
CORS_MAX_AGEint600Durée du cache preflight (secondes)

Origines par Défaut

https://console.stoa.dev
https://portal.stoa.dev
https://console.gostoa.dev
https://portal.gostoa.dev

Développement Local

Pour le développement local, ajoutez des origines localhost :

CORS_ORIGINS=https://console.stoa.dev,http://localhost:3000,http://localhost:5173

Test

# Tester le preflight CORS
curl -X OPTIONS ${STOA_API_URL}/v1/tools \
-H "Origin: https://evil.com" \
-H "Access-Control-Request-Method: GET" \
-v

# Doit retourner 403 ou Access-Control-Allow-Origin manquant

Rollback

# Autoriser toutes les origines (NON RECOMMANDÉ !)
export CORS_ORIGINS="*"
danger

N'utilisez jamais CORS_ORIGINS="*" en production. Cela permet à n'importe quel site web d'effectuer des requêtes authentifiées vers votre API.


Limites de Connexions SSE (CAB-939)

Ce que ça Fait

Prévient les attaques d'épuisement de connexions (Slowloris) en limitant les connexions SSE par IP, par tenant et globalement.

Configuration

# Variables d'environnement
SSE_LIMITER_ENABLED=true
SSE_TRUSTED_PROXIES=10.100.0.0/16 # CIDR de votre cluster

Paramètres

ParamètreTypeDéfautDescription
SSE_LIMITER_ENABLEDbooltrueActiver/désactiver le rate limiting
SSE_TRUSTED_PROXIESstring""CIDRs des proxies de confiance pour X-Forwarded-For

Limites Intégrées

Ces limites sont codées en dur mais peuvent être ajustées dans le code :

LimiteValeurDescription
MAX_PER_IP10Connexions max depuis une seule IP
MAX_PER_TENANT100Connexions max par tenant
MAX_TOTAL5000Limite de connexions globale
IDLE_TIMEOUT30sFermer les connexions inactives après
MAX_DURATION1hDurée maximale de connexion
RATE_LIMIT_PER_MIN5Nouvelles connexions par IP par minute

Proxies de Confiance

important

Si vous êtes derrière un load balancer ou un contrôleur ingress, vous devez configurer les proxies de confiance pour obtenir les IPs client exactes.

# Exemple EKS
SSE_TRUSTED_PROXIES=10.100.0.0/16

# Exemple GKE
SSE_TRUSTED_PROXIES=10.0.0.0/8

# Plusieurs CIDRs
SSE_TRUSTED_PROXIES=10.100.0.0/16,172.16.0.0/12

Si non configuré, le limiteur utilisera l'IP de connexion directe (qui peut être votre load balancer).

Test

# Tester le rate limit (doit obtenir 429 à la 11ème connexion)
for i in {1..15}; do
curl -N "${STOA_API_URL}/mcp/sse" \
-H "Authorization: Bearer $TOKEN" &
done

# Vérifier les connexions actives (si vous avez accès)
curl ${STOA_API_URL}/metrics | grep stoa_sse

Codes de Réponse

CodeSignificationEn-tête
429Too Many RequestsRetry-After: 60

Rollback

# Désactiver le rate limiting SSE
export SSE_LIMITER_ENABLED=false

Sécurité des Conteneurs (CAB-945)

Pod Security Standards

STOA applique le Pod Security Standard restricted sur le namespace stoa-system :

apiVersion: v1
kind: Namespace
metadata:
name: stoa-system
labels:
pod-security.kubernetes.io/enforce: restricted
pod-security.kubernetes.io/audit: restricted
pod-security.kubernetes.io/warn: restricted

Contexte de Sécurité des Deployments

Tous les deployments STOA utilisent ce contexte de sécurité :

spec:
automountServiceAccountToken: false # Sauf si nécessaire
securityContext:
runAsNonRoot: true
fsGroup: 1000

containers:
- securityContext:
runAsUser: 1000
runAsGroup: 1000
readOnlyRootFilesystem: true
allowPrivilegeEscalation: false
capabilities:
drop: [ALL]
seccompProfile:
type: RuntimeDefault

Network Policies

Le control plane est isolé avec NetworkPolicy :

# Ingress : Uniquement depuis MCP Gateway et Ingress
ingress:
- from:
- podSelector:
matchLabels:
app: stoa-mcp-gateway
- from:
- namespaceSelector:
matchLabels:
name: ingress-nginx

# Egress : Uniquement vers les services connus
egress:
- to:
- podSelector:
matchLabels:
app: postgresql
- to:
- podSelector:
matchLabels:
app: redis
# ... et DNS

Vérification

# Vérifier l'application PSS
kubectl get ns stoa-system -o jsonpath='{.metadata.labels}' | jq

# Vérifier le contexte de sécurité
kubectl get pod -n stoa-system -o jsonpath='{.items[0].spec.securityContext}'

# Tester NetworkPolicy (doit échouer depuis un pod aléatoire)
kubectl run test --rm -it --image=alpine -- wget -O- http://stoa-control-plane:8080

Monitoring

Métriques Prometheus (P1)

Ces métriques seront ajoutées dans une release de suivi :

# Connexions SSE actives
stoa_sse_connections_total{tenant="acme"}

# Connexions SSE rejetées
stoa_sse_rejected_total{reason="ip_limit"}

# Échecs de validation JWT
stoa_jwt_validation_failures_total{reason="invalid_audience"}

Alertes

Alertes recommandées :

- alert: STOAHighSSERejectionRate
expr: rate(stoa_sse_rejected_total[5m]) > 10
labels:
severity: warning
annotations:
summary: Taux de rejet SSE élevé (possible attaque)

- alert: STOAJWTValidationFailures
expr: rate(stoa_jwt_validation_failures_total[5m]) > 5
labels:
severity: warning
annotations:
summary: Échecs de validation JWT détectés

Dépannage

Erreurs de Validation JWT

Symptôme : 401 Unauthorized avec le message "Invalid audience"

Solution :

  1. Vérifier l'audience du token : echo $TOKEN | cut -d. -f2 | base64 -d | jq '.aud'
  2. Vérifier la configuration du mapper client Keycloak
  3. Désactiver temporairement : ALLOWED_AUDIENCES=""

Erreurs CORS

Symptôme : La console du navigateur affiche des erreurs "CORS policy"

Solution :

  1. Vérifier que l'origine est dans la liste blanche : echo $CORS_ORIGINS
  2. Ajouter l'origine : CORS_ORIGINS="...,https://your-domain.com"
  3. Vérifier les fautes de frappe (http vs https)

Rate Limiting SSE

Symptôme : 429 Too Many Requests avec Retry-After: 60

Solution :

  1. Vérifier s'il s'agit de trafic légitime ou d'une attaque
  2. Vérifier la détection de l'IP client : curl -v ... | grep X-Forwarded-For
  3. Configurer les proxies de confiance : SSE_TRUSTED_PROXIES=...
  4. Désactiver temporairement : SSE_LIMITER_ENABLED=false

Sécurité des Conteneurs

Symptôme : Le pod ne démarre pas avec "container has runAsNonRoot and image will run as root"

Solution :

  1. Utiliser une image de base non-root
  2. Ajouter USER 1000 au Dockerfile
  3. Définir runAsUser dans le deployment

Références


Dernière mise à jour : 2026-01-25 — Audit de Sécurité Team Coca