Configuration de sécurité
English: en/configuration.md
Source : services/control-plane/src/eai/config.py (préfixe EAI_, listes séparées par des virgules ou en JSON). La configuration générale est décrite dans operations/configuration.md.
Réglages EAI_*
| Réglage | Défaut | Recommandation production |
|---|---|---|
EAI_ENVIRONMENT | dev | prod (active la garde de démarrage) |
EAI_AUTH_DEV | false | false (obligatoire) |
EAI_AUTH_SPIFFE | false | true derrière un proxy de confiance |
EAI_CORS_ORIGINS | vide | origines exactes du portail, jamais * |
EAI_ALLOWED_HOSTS | * | noms publics de l'API |
EAI_TRUSTED_PROXIES | 127.0.0.1/32,::1/128 | CIDR des pods de la passerelle (ex. 10.42.0.0/16), jamais 0.0.0.0/0 |
EAI_DOCS_ENABLED | true | false |
EAI_SECURITY_HEADERS_ENABLED | true | true |
EAI_HSTS_ENABLED | false | true |
EAI_HSTS_MAX_AGE_SECONDS | 31536000 | 31536000 |
EAI_MAX_BODY_BYTES | 10485760 (10 Mio) | 10485760 |
EAI_MAX_UPLOAD_BYTES | 104857600 (100 Mio) | selon l'usage |
EAI_MAX_JSON_DEPTH | 64 | 64 |
EAI_RATE_LIMIT_ENABLED | true | true |
EAI_RATE_LIMIT_PER_MINUTE | 1200 | à ajuster, voir runbooks.md |
EAI_RATE_LIMIT_GATEWAY_PER_MINUTE | 600 | à ajuster |
EAI_RATE_LIMIT_AUTH_FAILURES_PER_MINUTE | 20 | 20 ou moins |
EAI_REDIS_URL | vide (compteurs en mémoire) | rediss:// dès plusieurs réplicas, stocké dans OpenBao |
EAI_JWT_ALGORITHMS | RS256,RS384,RS512,PS256,PS384,ES256,ES384 | RS256,PS256,ES256 ; jamais HS* ni none |
EAI_JWT_LEEWAY_SECONDS | 30 | 30 |
EAI_EGRESS_PRIVATE_ALLOWLIST | vide | services internes exacts hôte:port |
EAI_EGRESS_ALLOWED_PORTS | 80,443,8443 | 443 |
EAI_EGRESS_ALLOW_HTTP | false | false (obligatoire) |
EAI_EGRESS_DNS_TIMEOUT_SECONDS | 3.0 | 3.0 |
EAI_PROMPT_INJECTION_DEFAULT_MODE | detect | block |
EAI_MASTER_KEY / EAI_OPENBAO_URL | vide | l'un des deux est obligatoire ; OpenBao recommandé |
EAI_DATABASE_URL | local | sslmode=verify-full avec CA |
Garde de démarrage en production
eai.security.startup.enforce appelle problems(settings) et refuse de démarrer quand EAI_ENVIRONMENT=prod en listant chaque problème (en staging, il avertit). Il refuse :
EAI_AUTH_DEVactivé ;*dansEAI_CORS_ORIGINSouEAI_ALLOWED_HOSTS;EAI_EGRESS_ALLOW_HTTPactivé ;- un réseau trop large dans
EAI_TRUSTED_PROXIES; - un algorithme symétrique ou
nonedansEAI_JWT_ALGORITHMS(symmetric_algorithms) ; - ni
EAI_MASTER_KEYniEAI_OPENBAO_URL; EAI_DOCS_ENABLEDactivé,EAI_HSTS_ENABLEDdésactivé ;EAI_DATABASE_URLsanssslmode=require,verify-caouverify-full(database_tls) ;EAI_LIVEKIT_URLdéfini avecEAI_LIVEKIT_API_KEY/EAI_LIVEKIT_API_SECRETabsents, égaux aux valeurs du serveur LiveKit de développement (devkey/secret) ou avec un secret de moins de 32 octets. Horsdevettest, la création d'une session temps réel est alors refusée (503livekit_not_configuredoulivekit_secret_weak).
Tests : test_security_http.py::test_startup_guard_refuses_each_unsafe_prod_setting, test_security_http.py::test_startup_guard_lists_every_problem_and_warns_in_staging.
Réglages de sécurité par tenant
eai.security.tenant_settings (defaults, merge, load, validate) :
| Clé | Défaut | Valeurs |
|---|---|---|
prompt_injection_mode | EAI_PROMPT_INJECTION_DEFAULT_MODE | off, detect, block |
untrusted_content_wrapping | true | booléen |
output_links | strip_untrusted | allow, strip_untrusted |
output_link_allowlist | [] | noms d'hôtes publics (*.suffixe admis), 200 au plus |
egress_allowed_hosts | [] | noms d'hôtes publics ; s'il est non vide, la sortie du tenant est restreinte à cette liste |
API : GET /v1/security/settings et PUT /v1/security/settings (rôles tenant_admin ou security_officer, modification auditée). Tests : test_security_api.py::test_settings_get_put_persist_and_audit, test_security_api.py::test_settings_validation, test_security_api.py::test_settings_roles, test_security_tenant_settings.py::test_validate_rejects_bad_values.
API de posture
GET /v1/security/posture (rôles security_officer ou tenant_admin) renvoie les protections actives (eai.domains.security.posture.protections), les contrôles (checks) et un résumé (summary). Identifiants des contrôles : auth_dev_disabled, cors_restricted, allowed_hosts_restricted, hsts_enabled, docs_disabled, secrets_backend, oidc_configured, egress_http_disabled, egress_private_allowlist_narrow, rate_limit_enabled, trusted_proxies_restricted, spiffe_behind_proxy, database_tls, jwt_algorithms_asymmetric, prompt_injection_enabled. Tests : test_security_api.py::test_posture_roles_and_shape, test_security_api.py::test_posture_check_logic. Contrat : control-plane-v1.md.
Clés Helm (deploy/helm/enterprise-ai/values-prod.yaml)
global.environment: prod,global.networkPolicy: cilium,global.secretsMode: external-secrets.controlPlane.config:EAI_ENVIRONMENT,EAI_AUTH_DEV,EAI_AUTH_SPIFFE,EAI_ALLOWED_HOSTS,EAI_TRUSTED_PROXIES,EAI_DOCS_ENABLED,EAI_HSTS_ENABLED,EAI_MAX_BODY_BYTES,EAI_MAX_UPLOAD_BYTES,EAI_RATE_LIMIT_ENABLED,EAI_JWT_ALGORITHMS,EAI_EGRESS_PRIVATE_ALLOWLIST,EAI_EGRESS_ALLOWED_PORTS,EAI_EGRESS_ALLOW_HTTP,EAI_PROMPT_INJECTION_DEFAULT_MODE, URL TLS des services de données.controlPlane.secrets.keys: valeurs sensibles lues dans OpenBao (EAI_DATABASE_URL,EAI_REDIS_URL,EAI_MASTER_KEY, ...).controlPlane.secretFormats: formats exigés (EAI_REDIS_URLen^rediss://,EAI_DATABASE_URLavecsslmode=verify-full,EAI_NATS_URLen^tls://).
Tests : test_platform_security_manifests.py::test_prod_security_settings, test_platform_security_manifests.py::test_prod_configmap_renders_security_settings.
Réglages d'identité, de politique et de clés
Défauts relevés dans services/control-plane/src/eai/config.py.
| Réglage | Défaut | Recommandation production |
|---|---|---|
EAI_OIDC_ISSUER / EAI_OIDC_AUDIENCE | vide | émetteur du realm Keycloak ; audience du client (rend aud obligatoire) |
EAI_OIDC_JWKS_URL | vide (découverte depuis l'émetteur) | vide sauf JWKS servi ailleurs |
EAI_OIDC_GROUP_SYNC | true | true (correspondances de groupes appliquées à chaque connexion) |
EAI_OIDC_PLATFORM_ADMIN_GROUPS | navin-platform-admins | groupe d'annuaire restreint |
EAI_OIDC_TENANT_ADMIN_GROUP_PREFIX | navin-admins- | préfixe réservé aux administrateurs |
EAI_LOCAL_AUTH_ENABLED | true | false si tout passe par Keycloak |
EAI_LOCAL_AUTH_MAX_FAILURES / EAI_LOCAL_AUTH_LOCKOUT_SECONDS | 5 / 900 | 5 / 900 ou plus strict |
EAI_LOCAL_PASSWORD_MIN_LENGTH | 12 | 12 ou plus |
EAI_LOCAL_SESSION_TTL_SECONDS | 28800 | selon la politique de session |
EAI_OPA_URL | vide (moteur intégré seul) | URL TLS d'OPA |
EAI_OPA_DELIVERY | push | bundle avec EAI_POLICY_BUNDLE_SIGNING_KEY_FILE |
EAI_POLICY_DEFAULT | allow | deny pour refuser ce qu'aucune règle ne couvre |
EAI_OPENFGA_URL / EAI_OPENFGA_STORE_ID / EAI_OPENFGA_MODEL_ID | vide (tuples intégrés) | magasin OpenFGA dédié, avec EAI_OPENFGA_API_TOKEN |
EAI_KMS_PROVIDER | none | openbao ou pkcs11 |
EAI_CONTEXT_REQUIRED | false | true (obligatoire) |
EAI_CONTEXT_SIGNING_KEY_REF | vide | référence OpenBao (obligatoire) |
EAI_BREAK_GLASS_MAX_SECONDS | 3600 | 3600 ou moins |
EAI_APPROVAL_TTL_SECONDS | 86400 (lu par eai.domains.approvals.service) | selon les délais de décision |
EAI_FILE_SCAN_MODE | dev | enforce (obligatoire) |
EAI_METRICS_TOKEN | vide | 32 caractères minimum (obligatoire) |
Autres refus de la garde de démarrage
En plus de la liste ci-dessus, eai.security.startup.problems refuse en production :
EAI_METRICS_TOKENabsent ou de moins de 32 caractères ;EAI_RUNTIME_DATABASE_URLsans TLS,EAI_REDIS_URLsansrediss://, Redis sansEAI_REDIS_PASSWORD_FILE;EAI_OPENBAO_URLetEAI_OPENBAO_TOKENabsents ouEAI_AUDIT_SIGNING=hmac;EAI_CONTEXT_REQUIREDdésactivé,EAI_CONTEXT_SIGNING_KEYen clair sansEAI_CONTEXT_SIGNING_KEY_REF, ou aucune clé ;EAI_AUDIT_SEAL_SIGNING_KEY_REFabsent,EAI_AUDIT_PAYLOAD_KEKdifférent deopenbao-transit,EAI_AUDIT_CLOCK_SKEW_SECONDShors de 0 à 3600 ;EAI_OPA_DELIVERYdifférent depushoubundle, oubundlesansEAI_POLICY_BUNDLE_SIGNING_KEY_FILE;EAI_KMS_PROVIDERinconnu, oupkcs11sansEAI_KMS_PKCS11_MODULE,EAI_KMS_PKCS11_TOKEN_LABELetEAI_KMS_PKCS11_PIN_FILE;EAI_FILE_SCAN_MODEdifférent deenforce, un moteur ClamAV ou YARA-X horshttps://ou sans clé Bearer ;EAI_REALTIME_CONSENT_REQUIRED=false;EAI_SOC_WEBHOOK_SECRETde moins de 32 caractères,EAI_SOC_CORRELATION_WINDOW_SECONDShors de 60 à 86400 ;EAI_STORAGE_PRESIGN_MAX_SECONDShors de 1 à 86400,EAI_STORAGE_CAPABILITY_MAX_AGE_SECONDShors de 60 à 604800,EAI_STORAGE_CA_BUNDLEillisible ;EAI_BOOTSTRAP_ADMIN_PASSWORDfaible quand les comptes locaux sont actifs.
La procédure complète de durcissement est dans hardening.md.