Sécurité
Une page pour répondre à une revue de sécurité. Ce qui est conservé, ce qui est envoyé ailleurs, qui peut le lire, et les contrôles que mcpd n’a pas.
Tout ce qui suit vient du code source du daemon et de
spec/mcpd.spec.json, le fichier dont l’API est générée. Là où
un contrôle n’existe pas, cette page le dit, plutôt que de vous laisser
découvrir le manque dans un questionnaire.
Ce que mcpd stocke
mcpd stocke le texte des conversations de votre agent. C’est sa raison
d’être, et une revue doit partir de là. Un span de type
model_generation porte input et
output : le prompt exact envoyé au modèle et le texte exact
qui est revenu. Pour la plupart des utilisateurs, ce sont les données les
plus sensibles qu’ils détiennent.
Sur les six signaux, les champs libres stockés sont les suivants :
| Signal | Ce qu’il contient |
|---|---|
| Spans | input, output, attributes, metadata, et le message d’erreur |
| Logs | le message, et les fields structurés |
| Métriques | labels |
| Scores | reason, et metadata |
| Feedback | le comment en texte libre, et metadata |
| Notes d’aspect | la phrase, dans text |
À côté du payload, chaque signal porte des champs de corrélation qui
relient une exécution au reste de votre système : projet, environnement, nom
et version du service, session, thread, utilisateur, ressource, exécution,
requête, expérience et release. user_id est haché à l’entrée,
sauf si vous désactivez le masquage. Les autres sont stockés tels qu’ils ont
été envoyés : traitez chacun d’eux comme un champ par lequel une
personne peut être identifiée si vous y mettez une identité.
Le nombre de tokens et un montant de coût sont calculés à l’ingestion et stockés avec l’exécution. Rien de ce qui concerne l’appel au modèle n’est récupéré auprès d’un fournisseur.
Où c’est écrit
Un seul backend, choisi au démarrage. Par défaut, c’est un fichier SQLite
embarqué à ./mcpd.db, défini par --data. L’index
plein texte vit dans le même fichier : le stockage est donc un fichier à
sauvegarder et un fichier à protéger. --postgres-url ou
--clickhouse-url place plutôt les données dans une base que
vous exploitez vous-même.
Les exécutions durables, quand elles sont actives, vivent dans un second
fichier SQLite à côté du premier, quel que soit le stockage des traces. Ce
fichier garde l’entrée, l’état, la sortie et la corrélation de chaque
exécution, et l’entrée, la sortie et l’erreur de chaque étape, exactement
tels qu’ils ont été envoyés : sans masquage, avec user_id non
haché, parce qu’une étape ultérieure les relit et qu’une valeur réécrite
changerait ce que fait l’exécution. Les spans qu’une exécution écrit dans le
stockage des traces sont masqués comme tous les autres. Protégez le fichier
des exécutions comme vous protégez le stockage des traces, ou davantage. La
conservation et l’effacement par sujet l’atteignent tous deux, pour les
exécutions terminées ; voir
Exécutions.
mcpd ne chiffre pas ce qu’il écrit. Le chiffrement au repos est l’affaire du volume avec le stockage embarqué, et celle de la base avec les deux autres.
Ce qui quitte la machine
Avec une configuration par défaut, rien. Le daemon émet des requêtes sortantes à exactement quatre endroits, et chacun d’eux reste inactif tant que vous ne l’avez pas configuré. Il n’y a pas de vérification de mise à jour, pas de rapport d’usage et aucune télémétrie.
| Destination | Ce qui est envoyé | Inactif sauf si |
|---|---|---|
| Backend de stockage | Tout ce que mcpd stocke, par votre propre connexion. | Vous définissez --postgres-url ou --clickhouse-url. Le stockage par défaut est un fichier local et n’a besoin d’aucun réseau. |
| Transfert |
Chaque span stocké, réémis en OTLP JSON vers
{endpoint}/v1/traces. Cela inclut le
payload : input et output partent dans les
attributs input.value et output.value,
chacun plafonné à 32 KiB. Les valeurs déjà masquées le restent, et un
user_id haché est transféré sous forme de hachage.
| Vous définissez --forward-endpoint, ou un bloc forward dans le fichier de configuration. |
| Webhooks de moniteur et d’objectif | Un corps JSON fixe : l’identifiant et le nom du moniteur ou de l’objectif, le projet, la condition, ce qu’elle surveille, le seuil, la mesure, le nombre d’échantillons, un détail d’une ligne et un horodatage. Aucun texte de prompt ou de complétion ne s’y trouve. | Un moniteur ou un objectif du fichier de configuration nomme un webhook. |
| Modèle juge |
Le critère, plus la question de l’exécution et sa réponse, chacune
tronquée à 4 000 caractères, envoyés en POST vers
{endpoint}/chat/completions. C’est le seul
chemin qui envoie du texte de prompt et de complétion stocké à un
fournisseur de modèles.
| Un bloc judge du fichier de configuration nomme un endpoint, un modèle et au moins un critère, et un appelant demande ce scorer par son nom. |
L’interface que le binaire sert à / est compilée dans le
binaire. Ses polices, ses styles et ses scripts viennent de la même origine,
et elle n’émet aucune requête vers un tiers.
Clés et portées
L’authentification se fait par clés d’API. Il n’y a pas de compte, pas de
session et pas de connexion. Une clé est présentée sous la forme
Authorization: Bearer … ou dans l’en-tête
X-Mcpd-Key, et les deux valeurs sont hachées en SHA-256 avant
d’être comparées : ni la longueur de la clé configurée ni la position du
premier caractère erroné ne sont observables par le temps de réponse.
Il y a six portées, de la plus étroite à la plus large :
| Portée | Ce qu’elle permet | Pourquoi elle est une portée à part |
|---|---|---|
ingest | Écrire des signaux. Ne rien lire. | Cette clé vit dans chaque conteneur qui émet un span, y compris ceux qui exécutent du code tiers. Elle ne doit pas ouvrir en plus tous les prompts stockés. |
read | Lire les signaux stockés. | C’est en lecture que se trouvent les prompts. |
write | Modifier les données stockées : scores, retours, datasets, expériences et acquittements. | Plusieurs de ces handlers lisent un payload au passage. Capturer une exécution de production dans un dataset lit l’input et l’output de cette exécution, ce qui est plus qu’émettre un span. |
execute | Démarrer, annuler et signaler des exécutions, et leur publier des événements. | Démarrer une exécution dépense de l’argent et agit sur vos systèmes. Une clé qui peut enregistrer un score ne doit pas pouvoir aussi lancer du travail. |
worker | Réclamer des étapes et rendre compte de ce qui leur est arrivé. | C’est la clé du processus qui exécute le corps des étapes. Une réclamation renvoie l’input de l’étape et l’output de chaque étape précédente, donc elle lit des payloads, et elle ne doit pas pouvoir aussi démarrer des exécutions ni modifier des données stockées. |
admin | Détruire des données, et lire le journal d’audit. | Supprimer n’est pas lire. |
La portée exigée par un endpoint est déclarée endpoint par endpoint dans la spécification et publiée dans la référence API HTTP générée : il y a donc une seule réponse à « de quoi cet endpoint a-t-il besoin ? » plutôt qu’une réponse par document.
Le repli, et ce qu’il implique pour une clé que vous avez déjà
Une liste write non définie se replie sur la liste
ingest, et une liste admin non définie se replie
sur la liste read. Des listes execute et
worker non définies se replient sur la liste
write, donc sur la liste ingest quand celle-ci
n’est pas définie non plus. C’est ce qui empêche une mise à jour de
révoquer l’accès sur tous les déploiements configurés avant l’existence des
portées plus étroites. Une clé admin est aussi acceptée pour
execute et worker, pour qu’un opérateur puisse
piloter une exécution à la main. Ensemble, ces règles veulent dire que sur
un daemon qui ne détient qu’une clé d’ingestion et une clé de lecture :
- la clé d’ingestion attache aussi des scores et des retours, démarre des exécutions et réclame des étapes, et
- la clé de lecture supprime aussi des traces et, par le repli admin, démarre des exécutions et réclame des étapes.
Une clé de lecture est donc une clé de suppression tant que vous n’avez pas
défini --admin-key, et les deux clés atteignent la surface des
exécutions tant que vous n’avez pas défini --execute-key,
--worker-key et --admin-key. C’est sur cette base
qu’il faut distribuer les clés. Rien ne se restreint tout seul. Chaque
portée a son option, --ingest-key, --read-key,
--write-key, --admin-key,
--execute-key et --worker-key, et chacune prend
une liste ; toutes peuvent aussi être définies par projet dans le
fichier de configuration.
Sans clé, toutes les portées sont ouvertes
Ce défaut est délibéré : un premier essai sur un portable ne doit pas exiger
d’identifiant. C’est aussi ce qu’il y a de plus dangereux sur une instance
exposée, donc quand le daemon écoute sur une adresse autre que la boucle
locale, il journalise au démarrage un avertissement par surface ouverte à
un appelant qui ne devrait pas l’atteindre. Chaque avertissement nomme la
surface et les options qui la ferment. Lancez mcpd config
avant d’exposer un port : la commande affiche la configuration à
laquelle une invocation se résout, puis se termine. Elle porte une ligne
par portée, et elle nomme la liste sur laquelle une portée s’est
repliée :
admin auth key required (read keys) vous dit que les clés
de lecture peuvent encore supprimer.
Docker et Kubernetes
montre la sortie.
fermé avant d’être joignable
mcpd --ingest-key "$INGEST" --read-key "$READ" Un projet est une frontière de cloisonnement
Une clé configurée au niveau supérieur donne accès à tous les projets. Une
clé inscrite à la place sous un projet nommé dans le bloc
projects du fichier de configuration donne accès à ce projet et
à aucun autre, et chaque projet tient sa propre liste par portée. Une même
clé peut apparaître sous plusieurs projets : c’est ainsi qu’une équipe
plateforme détient une clé couvrant ce qu’elle exploite sans détenir le
serveur entier.
Sur le chemin d’écriture, la vérification se fait par signal, pas par requête :
- Un lot peut nommer plusieurs projets. Les signaux qui nomment un projet que la clé n’atteint pas sont écartés et signalés comme rejetés dans la réponse, avec le motif : une flotte pointée sur le mauvais projet se découvre donc dans la réponse qu’elle lit déjà. Le reste du lot est stocké.
Sur le chemin de lecture, chaque endpoint qui nomme un projet le compare aux
droits de la clé : traces, spans, logs, métriques, scores, retours,
analytics, datasets, expériences et journal d’audit. Les éléments d’un
dataset sont adressés par l’identifiant du dataset, et le daemon cherche
quel projet possède ce dataset puis y compare la clé : une clé qui ne peut
pas ouvrir le projet ne peut pas lire les cas qui en ont été capturés. Un
identifiant qui n’existe pas est refusé de la même façon qu’un identifiant
que la clé n’a pas le droit d’atteindre, donc l’endpoint ne peut pas servir
à tester quels identifiants existent. Trois endpoints ne prennent aucun projet et sont joignables par toute clé
qui détient la portée de lecture, quels que soient les projets auxquels
elle est limitée : la liste des projets, le catalogue des scorers et
l’ensemble des capacités du stockage
(GET /v1/api/capabilities). La liste des projets est filtrée
par l’autorisation de la clé, donc une clé limitée à un projet ne voit que
les projets qu’elle atteint. Sur une instance partagée, les noms de projet
sont souvent des noms d’équipes ou de clients. L’état des moniteurs et
l’état des objectifs y figuraient, jusqu’à ce qu’ils soient filtrés par
l’autorisation de projet de la clé, ce qu’ils auraient toujours dû
être : un moniteur nomme le projet qu’il surveille et porte le webhook
vers lequel il poste.
L’API ne nomme que l’hôte de ce webhook, car l’URL d’un webhook est souvent sa propre clé d’accès.
Un refus dit lequel des deux problèmes se pose
Une clé absente ou inconnue donne un 401, et une clé reconnue que cet endpoint ou ce projet n’admet pas donne un 403. La distinction est opérationnelle : un 401 vous envoie chercher une faute de frappe dans un identifiant, un 403 vous dit que l’identifiant est bon et que les droits ne le sont pas.
$ curl -sw "%{http_code} " "$MCPD/v1/api/traces?project=acme"401 this endpoint needs an API key; send it as `Authorization: Bearer <key>`$ curl … -H "authorization: Bearer $TYPO"401 that key is not one this daemon knows$ curl … -H "authorization: Bearer $INGEST"403 that key is not valid for reading$ curl … -H "authorization: Bearer $BILLING_READ"403 that key does not reach the project "acme" Ce dernier message est le même que le projet existe ou non. Confirmer quels noms de projets sont réels est une petite fuite, et sur un daemon partagé ces noms sont ceux de vos clients.
Le journal d’audit
Restituer un payload stocké hors de mcpd est enregistré, et chaque
suppression l’est aussi : la question « qui a lu cette exécution ? » a donc
une réponse. Une ligne porte le moment, la clé, l’action, le projet, la
trace ou le dataset nommé, et le fait que la requête ait été servie ou
refusée. GET /v1/api/audit la relit, de la plus récente à la
plus ancienne, et exige la portée admin.
Dix-huit actions sont enregistrées : get_trace,
list_spans, list_logs,
list_feedback, list_dataset_items,
capture_into_dataset,
read_trace_aspect_notes, get_theme, get_theme_flow, list_theme_history, list_theme_examples,
delete_trace, erase_subject, et cinq sur les
exécutions durables : list_runs, get_run,
list_run_events, get_step_cause et
cancel_run. La frontière est le payload, pas la requête.
Lister des traces renvoie une projection de noms, de durées, de nombres de
tokens et de coûts, sans aucun prompt ni aucune complétion : ce n’est donc
pas enregistré. Ouvrir l’une de ces traces l’est, parce que c’est là qu’est
le texte. Lister des exécutions durables est enregistré, parce que chaque
exécution de la liste porte son entrée, sa sortie et son état tels qu’ils
ont été envoyés. Une lecture refusée d’une exécution est enregistrée sous le
projet qui possède l’exécution.
Les appels de worker ne sont pas enregistrés : réclamer une étape, déclarer
qu’elle a démarré, le heartbeat, le complete et le fail. Un bail porte bien
l’entrée de l’étape et les sorties des étapes qui la précèdent, mais la
boucle du worker réclame de nombreuses fois par minute, et une ligne par
réclamation noierait les lectures faites par une personne. Traitez la clé
worker comme une clé qui lit chaque payload des exécutions
qu’elle sert. Démarrer une exécution n’est pas enregistré non plus ; un
nouvel envoi d’une clé d’idempotence encore retenue reçoit en réponse
l’exécution que cette clé a démarrée, donc traitez la clé
execute comme une clé qui peut lire les exécutions qu’elle
démarre.
Cinq propriétés qu’un auditeur doit connaître :
- La clé n’est jamais écrite. Le principal est une
empreinte SHA-256 tronquée de la clé, qui distingue une clé d’une autre
sans stocker de vérificateur. Une requête sans clé sur un daemon ouvert
est enregistrée comme
anonymous. - La personne vient de votre ingress, ou de nulle part.
mcpd n’a pas de comptes utilisateurs : seule, une ligne attribue une
action à une clé et non à un humain. Le champ
subjectporte une identité quand--trusted-identity-headeret--trusted-proxy-cidrsont tous deux définis, et il est vide sinon, ce qui est le cas par défaut. mcpd n’établit jamais cette identité lui-même : il enregistre ce qu’une adresse que vous avez nommée a affirmé. - Un refus est enregistré aussi. La ligne est écrite avant que le 403 ne soit renvoyé, parce qu’une tentative refusée sur le projet d’une autre équipe est exactement la ligne pour laquelle un journal d’audit existe. Lire le journal n’est pas enregistré : il grossirait sans qu’aucun payload ne soit lu, et la ligne ne dirait rien que le détenteur de la clé d’administration ne sache déjà.
- Il enregistre quelle clé, et en option qui. La clé est
stockée sous forme d’empreinte tronquée, jamais la clé elle-même, donc
le journal n’est pas un endroit où s’accumulent des identifiants. Cela
répond à « une clé d’administration a supprimé ceci » et non à « quelle
personne l’a fait », parce que mcpd n’a pas de fournisseur d’identité. Si vous voulez la seconde réponse, donnez-lui l’en-tête que
votre ingress pose déjà et les adresses autorisées à le poser :
--trusted-identity-header x-forwarded-useravec--trusted-proxy-cidr 10.42.0.0/16. Les deux moitiés sont obligatoires. Un en-tête est une chaîne que peut envoyer quiconque atteint le port, donc mcpd ne le lit que depuis une adresse que vous avez nommée ; une requête venue d’ailleurs est servie normalement et sa ligne ne porte aucun sujet, ce qui est la réponse honnête plutôt qu’une supposition. Sans aucune des deux options, aucun sujet n’est jamais enregistré, et c’est le comportement par défaut. - La conservation ne l’élague pas. La passe de conservation retire les spans, les logs, les métriques, les traces, les scores, les retours, les notes d’aspect et les exécutions durables terminées. Elle laisse le journal d’audit : un journal qui expirerait en même temps que les données qu’il décrit ne pourrait pas dire qui a lu les données supprimées.
- Il ne fait jamais échouer la requête qu’il décrit. Une
ligne qui ne peut pas être écrite est journalisée et comptée dans
mcpd_audit_write_failures_totalsur/metrics, et la lecture aboutit quand même. Un journal d’audit capable de faire tomber le chemin de lecture est un risque pire que celui qu’il évite : surveillez ce compteur plutôt que de compter sur l’échec des requêtes.
Ce qu’aucune clé ne couvre
- L’interface. Le balisage servi à
/l’est à quiconque atteint le port. Les données derrière ne le sont pas : chaque requête qu’elle émet passe par l’API de consultation et rencontre les mêmes clés. Il n’y a pas d’écran de connexion dans le daemon. - Les sondes et
/metrics. Sans authentification, délibérément, parce qu’un kubelet et un collecteur ne peuvent pas porter de clé. Ils rapportent l’état, la version, la durée de fonctionnement, le nom du backend de stockage et des compteurs, jamais des payloads. - L’origine. L’API de consultation et les sondes répondent
avec
Access-Control-Allow-Origin: *, donc une page de navigateur, quelle que soit son origine, peut les appeler. mcpd n’utilise ni cookie ni identifiant ambiant, si bien qu’une telle page doit quand même détenir une clé : une politique d’origine permissive ne lui en fournit pas.
Ce que le masquage fait, et ce qu’il ne peut pas faire
Le masquage est actif par défaut et s’exécute avant l’écriture, en troisième
étape du pipeline d’ingestion. Une valeur qu’une règle reconnaît
n’atteint jamais la base de données, ce qui est une garantie différente de
celle d’une valeur supprimée après coup, et c’est cette différence qu’un
auditeur interroge. Ce qu’il ne peut pas faire, c’est garder les octets
hors du réseau : ils l’ont déjà traversé quand la règle s’exécute. Pour
cela, les SDK portent MCPD_CAPTURE_PAYLOADS=false, qui retire
input et output avant qu’un span ne soit mis en
file, et laisse tout le reste dessus.
-
Decode
OTLP or native, gzip or plain
-
Sample
Decided once, at the root
-
Redact
Before anything is written
-
Price
From the configured price book
-
Store
One transaction per batch
Redaction is stage three, not a cleanup job. A value that a rule matches never reaches storage at all.
Une règle est un ensemble de sous-chaînes en minuscules et un mode. Une clé correspond quand son nom, mis en minuscules, contient l’une d’elles. Les valeurs reconnues sont remplacées, tronquées, hachées par une empreinte à clé, ou supprimées. Quand une clé reconnue porte un objet, tout ce qui se trouve dessous est masqué aussi, quel que soit le nom des clés filles, et un secret envoyé comme nombre JSON est traité comme un secret envoyé comme chaîne. La table complète des règles est sur Masquage et conservation.
Voici maintenant la limite, celle qu’une revue demandera et qu’aucune
configuration ne supprime. Les règles reconnaissent des noms de
champs, pas la forme d’une valeur. Une clé d’API qu’un utilisateur
a collée au milieu d’une phrase, et qui arrive sous un champ nommé
content ou question, n’est reconnue par rien dans
le pipeline. Le nom du champ est innocent, donc la valeur est stockée en
entier.
Le masquage arrête donc une chose précise :
- Il arrête la capture structurée des secrets et des identifiants, là où un secret arrive dans un champ nommé d’après ce qu’il est.
- Il n’analyse pas le texte libre, et ce n’est pas un classificateur de contenu.
- Il ne détecte pas un secret à sa forme ni à son entropie.
- Il ne touche ni au nom d’un span ni au message d’un enregistrement de log. Ce sont des identifiants qui servent au regroupement plutôt que des payloads, et les réécrire casserait ce regroupement. Ne construisez pas un nom de span à partir de l’adresse e-mail d’un client.
- Il ne touche pas au stockage des exécutions. Les payloads d’une exécution durable sont ses données de travail et sont gardés tels qu’ils ont été envoyés ; seuls les spans qu’une exécution écrit dans le stockage des traces sont masqués.
Un contrôle travaille bien sur la forme, et c’est une détection après coup
plutôt qu’une prévention : le scorer intégré
no_leaked_secret pénalise une exécution dont la
réponse porte un jeton qui ressemble à un secret, par préfixe connu
ou par une longue suite alphanumérique à casse mixte. Il note une exécution
déjà stockée. Il ne supprime rien, il ne lit pas le prompt, et il ne
s’exécute que si vous le demandez. Voyez-le comme un moyen d’apprendre que
cela s’est produit, pas comme une deuxième ligne de masquage.
Deux bornes s’appliquent qu’une règle ait reconnu quelque chose ou non. Toute chaîne de plus de 128 KiB est tronquée sur une frontière de caractère, avec un marqueur, et le parcours s’arrête à une profondeur de 32. Les deux existent pour qu’un seul document emballé ne décide pas de la taille de votre base ni ne sature un CPU.
--no-redact désactive tout le pipeline, ce qui n’a de sens que
si quelque chose en amont masque déjà. Là où le masquage hache un
identifiant, définissez --redact-key : la clé intégrée est une
constante dans du code publié, et les identifiants viennent de petits
espaces, donc une empreinte sans clé se retrouve en devinant les candidats.
Rétention et effacement
--retention-days supprime les signaux plus vieux que ce nombre
de jours. Omettez l’option et rien n’est jamais supprimé, ce qui est le bon
défaut : un outil qui jetterait sans bruit les traces du mois dernier se
ferait découvrir au pire moment.
La passe s’exécute sur un minuteur horaire dans le daemon, jamais dans la
requête d’un utilisateur. Elle couvre chaque projet nommé dans le bloc
projects, chacun avec son propre retention_days
lorsqu’il en définit un et avec la valeur globale sinon, plus le projet par
défaut. Un projet sans fenêtre nulle part n’est pas élagué du tout.
Pour une demande concernant une personne,
DELETE /v1/api/subjects/{user_id}?project=… retire toutes
les exécutions enregistrées au nom de cette personne dans le projet, avec
les spans, les logs, les métriques, les scores et les retours qui y sont
attachés, et répond par un compte par signal. L’identifiant est haché avec
la même clé avant d’être comparé, donc la demande fonctionne sur des
identifiants hachés sans que vous ayez à en hacher un vous-même, et la
réponse indique combien d’identifiants stockés étaient sous une forme que la
demande n’a pas pu reconnaître. Pour retirer une exécution plutôt qu’une
personne, DELETE /v1/api/traces/{trace_id} la retire avec
tout ce qui y est attaché, en une seule transaction. Les deux exigent la
portée admin et les deux sont enregistrées dans le journal
d’audit.
Masquage et conservation
détaille la demande d’effacement et ce qu’elle peut ou ne peut pas promettre.
Ce que mcpd ne fait pas
Rien de ce qui suit n’existe dans cette version. Ces manques sont listés parce qu’une revue qui les découvre plus tard est un plus mauvais résultat qu’une revue qui les lit ici.
| Absent de mcpd | Que faire à la place |
|---|---|
| La terminaison TLS. mcpd parle du HTTP en clair. Les prompts et les complétions traversent ce lien en entier. | Terminez le TLS sur votre ingress ou dans votre mesh, et gardez le port du daemon hors de tout réseau non fiable. |
| Le SSO. Aucune intégration avec un fournisseur d’identité, pas d’OIDC, pas de SAML, pas d’écran de connexion. | Placez l’interface derrière le proxy d’identité que vous exploitez déjà. L’identité d’une personne est celle que ce proxy établit. |
| SCIM. Aucun provisionnement d’utilisateurs ni de groupes, parce que mcpd n’a pas la notion de compte utilisateur. | Provisionnez au niveau du proxy. Dans mcpd, l’unité est une clé, ajoutée à une liste ou retirée d’une liste. |
| Des rôles au-delà des six portées. L’autorisation est une portée et une liste de projets. Il n’y a pas de rôle par endpoint et pas de groupe. | Séparez par clé et par projet. Si une distinction compte, faites-en un projet. |
| Établir qui est une personne. Le journal d’audit peut enregistrer un sujet, mais mcpd ne détermine jamais qui appelle. Il enregistre ce que votre ingress a affirmé, et seulement depuis une adresse que vous avez nommée. | Définissez --trusted-identity-header et --trusted-proxy-cidr pour que l’en-tête que votre proxy pose déjà atteigne la ligne d’audit. Sans eux, une ligne attribue une action à une clé, et émettre une clé par équipe resserre l’empreinte. |
Le cycle de vie des clés. Les clés sont des listes lues au démarrage. Il n’y a pas d’expiration, pas de calendrier de rotation et pas de quota par clé. Le plafond qui existe est par projet : --quota-per-minute borne le nombre de signaux qu’un projet peut stocker en une minute, quelle que soit la clé qui les a envoyés. | Configurez plusieurs clés, une par équipe ou par flotte, pour que révoquer l’une soit une modification et un redémarrage plutôt qu’un redéploiement de tout ce qui émet un span. Faites d’une flotte son propre projet si elle a besoin de son propre plafond. |
| Le chiffrement au repos. mcpd écrit des fichiers en clair ou des lignes en clair. | Chiffrez le volume, ou utilisez une base qui le fait. |
Signaler quelque chose
Les vulnérabilités présumées vont à [email protected] avec
« Security » en objet, pas dans une issue publique. Le fichier
SECURITY.md du dépôt indique le périmètre et ce que contient un
bon rapport. Une instance sans authentification que quelqu’un a choisi
d’exposer est un comportement par défaut documenté, pas une vulnérabilité.
Tout ce qui passe outre une clé qui était configurée, outre une
règle de masquage configurée, ou par-dessus une frontière de projet en est
une.
La suite
- La table des règles, les quatre modes et le hachage à clé : Masquage et conservation.
- Ce qu’il faut régler avant qu’un port soit joignable : Docker et Kubernetes.
- Toutes les options et variables d’environnement : Configuration.
- Joindre une instance déployée depuis du code ou depuis un agent : Intégrer.