Référence de l’analyseur
Aperçu
Lakewatch est en aperçu privé. Pour demander l'accès, contactez l'équipe de votre compte Databricks.
Un analyseur est un document YAML qui décrit les colonnes de sortie d’une source de logs de sécurité. Un analyseur est versionné et stocké dans un catalogue et un schéma Unity Catalog ; la modification d’un analyseur ajoute une nouvelle version immuable, de sorte qu’une source de données qui pin une version n’est pas affectée par des modifications ultérieures. Cette page décrit la syntaxe de l'analyseur acceptée par Lakewatch.
Structure du document
description: Product audit events # what this log source is
fields:
- name: event_name
type: STRING
required: true
validate:
in: ['sale', 'buy']
- name: source_ip
type: STRING
observables: [ip]
- name: actor
type: VARIANT
fields:
- name: id
type: STRING
observables: [actor_id]
time:
coalesce:
- path: published
time_formats: ['#rfc3339']
Vous pouvez fournir description dans le YAML comme indiqué ici, ou le définir dans l’interface utilisateur lorsque vous créez l’analyseur (recommandé).
Heure de l'événement
Chaque analyseur produit une colonne time : le timestamp de l’événement sur lequel Lakewatch clusters les données. Vous devez le définir avec un bloc time de haut niveau plutôt qu’avec un champ dans fields; un analyseur personnalisé sans bloc de ce type sera rejeté. Parce que Lakewatch produit toujours la colonne time et qu'il s'agit toujours d'un TIMESTAMP, vous ne définissez pas son name, type ou required. N’ajoutez pas de champ time à fields: le nom est réservé à la colonne produite par le bloc de haut niveau time, et un analyseur syntaxique qui le déclare comme un champ est rejeté.
Le bloc time de niveau supérieur prend une liste coalesce : une liste hiérarchisée de sources que Lakewatch évalue dans l'ordre, en utilisant la première valeur non nulle. Si chaque source est nulle, Lakewatch revient au temps d'ingestion et définit lw_meta.missing_timestamp sur true. Chaque source est l'une des suivantes :
-
Chemin : lire la valeur à partir d’un emplacement source :
YAMLtime:
coalesce:
- path: published
time_formats: ['#rfc3339'] -
Chemin avec une expression régulière : extrayez la valeur d’un emplacement source à l’aide d’une expression régulière :
YAMLtime:
coalesce:
- path: raw_message
regexp: '<pattern>'
match_group_index: 1 # which capture group holds the value; defaults to 1
time_formats: ['#unix']
time_formats utilise les mêmes ensembles de formats et modèles qu’un champ TIMESTAMP. Voir Timestamps.
Comme time n’est pas un champ, il n’est pas utilisé dans la classification. Pour exiger que le champ de l'heure de l'événement source soit présent pour qu'un événement corresponde, ajoutez-le en tant que champ required dans fields.
Attributs de champ
Attribut | S’applique à | Description |
|---|---|---|
| Tous les champs | Nom de la colonne de sortie. Également la clé source default, sauf si |
| Tous les champs | Type de colonne de sortie. Voir Types pris en charge. |
| Tous les champs | Si |
| Tous les champs | Emplacement source à partir duquel lire la valeur lorsqu'il est différent de |
| Tous les champs | Description du champ, affichée pour vous aider à rédiger des règles de détection. |
|
| Liste des formats pour analyser le timestamp avec. Voir Timestamps. |
|
| Un seul validateur. Voir Validateurs. |
|
| Un ou plusieurs types d’observables à extraire. Voir Observables. |
|
| Règles de correspondance de clé pour extraire les observables du contenu imbriqué. Voir Rechercher des observables. |
|
| Définitions de champs imbriqués. |
|
| La définition des éléments du tableau. |
Types pris en charge
STRING, BOOLEAN, TINYINT (BYTE), SMALLINT (SHORT), INT (INTEGER), BIGINT (LONG), FLOAT (REAL), DOUBLE, DECIMAL(p,s), TIMESTAMP, TIMESTAMP_LTZ, TIMESTAMP_NTZ, DATE, BINARY, VARIANT, STRUCT, et ARRAY.
Seuls les types répertoriés ci-dessus sont pris en charge. Pour modéliser des objets imbriqués, utilisez VARIANT avec fields en interne. Un VARIANT tolère des champs supplémentaires non déclarés sans interrompre l’analyseur.
Choisir entre STRUCT et VARIANT
STRUCT et VARIANT modélisent des données imbriquées, mais font des compromis opposés. Un STRUCT déclare la forme à l’avance : vous listez ses membres et leurs types, et la colonne analysée contient exactement ce que vous déclarez. Un VARIANT conserve la valeur brute et semi-structurée intacte, et vous la lisez par chemin au moment de la query, par exemple avec try_variant_get. Vous pouvez toujours déclarer fields sous VARIANT, mais cela ne fait qu’attacher la validation et les observables à ces chemins ; cela ne supprime pas le reste de la valeur.
Utilisez STRUCT lorsque vous connaissez la forme et que vous souhaitez un contrat stable et typé :
- La forme réside dans la spécification du parser, elle est donc versionnée et révisée. Les requêtes, les détections et les tableaux de bord lisent chaque membre comme un type connu et ne sont pas interrompus lorsque les données sources changent.
- Les lectures sont moins coûteuses. Un
STRUCTest une colonne typée réelle à laquelle vous accédez avec la notation par point et que Spark peut élaguer, alors qu’unVARIANTest décodé à chaque lecture. - Le coût est la rigidité : tout ce que vous ne déclarez pas est abandonné, et vous devez mettre à jour le parser lorsque la forme change.
Utilisez VARIANT lorsque la forme est inconnue, en évolution ou trop volumineuse pour modéliser champ par champ :
- Il se maintient avec les changements de schéma de son propre chef. Des sources telles qu’AWS CloudTrail et Okta ajoutent des champs sans changement de l’analyseur.
- Il gère des formes mixtes ou contradictoires, comme un chemin qui est un objet dans un événement et une chaîne dans un autre, qu'un seul type
STRUCTne peut pas représenter. - Elle préserve la charge utile d'origine en vue d'un traitement ultérieur et permet de déposer les données d'abord et de les modéliser ensuite.
- Il monte en charge pour les charges utiles avec des centaines de champs sans qu'il soit nécessaire de déclarer chacun d'eux.
- Le coût est que vous n'obtenez aucun typage ou validation lors de l'ingestion, et que vous payez pour extraire des champs à chaque query. Un modèle courant consiste à promouvoir les quelques champs que vous query le plus en champs typés.
Timestamps
Un champ TIMESTAMP est analysé selon les formats listés dans time_formats, essayés dans l'ordre, en utilisant parse_timestamp. Chaque entrée est soit un mot-clé de format prédéfini, soit un modèle de date/heure littéral de Spark :
Entrée | Signification |
|---|---|
| La famille RFC 3339. |
| Autres mots-clés de format prédéfinis. |
| Timestamp Unix numérique, en secondes. Les valeurs en millisecondes ne sont pas détectées. |
| Modèle littéral. |
Listez les formats réellement utilisés par les Logs afin que l'analyse ne soit pas trop permissive.
Validateurs
Un validateur impose une condition sur un champ STRING. Utilisez exactement une des options suivantes :
Validateur | Vrai si |
|---|---|
| La valeur est l'une des chaînes de caractères listées. |
| La valeur ne fait pas partie des chaînes répertoriées. |
| La valeur contient au moins une sous-chaîne répertoriée. |
| La valeur ne contient aucune des sous-chaînes répertoriées. |
Observables
Marquer un champ avec observables extrait sa valeur dans la colonne partagée lw_observables pour la recherche de menaces. Chaque type d’observable capture un type spécifique de valeur et remplit un ou plusieurs champs dans lw_observables:
Observable | Captures | Peuple dans |
|---|---|---|
| Adresses IPv4 ou IPv6 |
|
| Noms de domaine |
|
| Hostname ou adresses IP lorsque la valeur est une IP |
|
| L'hôte d'une URL (un hostname ou une IP) |
|
| Hachages MD5, SHA-1 et SHA-256 |
|
| Un ARN AWS, ainsi que l’identifiant du compte, l’identifiant de l’instance, l’e-mail et le nom d’utilisateur qu’il contient |
|
| ID de compte AWS à 12 chiffres |
|
| ID d'instance EC2 |
|
| Identifiant de requête ou de trace |
|
| Un nom d'utilisateur |
|
| Une adresse e-mail et sa partie nom d'utilisateur |
|
| L'hôte d'une valeur |
|
| Une adresse MAC |
|
| ID d'acteur |
|
| Numéro de série |
|
| Un identifiant CVE, comme |
|
| Tag de ressource |
|
| Un ID de technique MITRE ATT&CK, tel que |
|
lw_observables est une colonne STRUCT gérée par Lakewatch ajoutée à chaque table analysée, avec un champ ARRAY<STRING> par type observable. Lakewatch normalise, déduplique et trie les valeurs de chaque champ et ignore les valeurs non valides ; un champ sans valeur est []. La colonne est présente sur chaque événement, même lorsque le parseur ne déclare aucune observable.
Scanner les observables
Utilisez l’attribut facultatif scan_for_observables sur un champ VARIANT pour extraire des observables à partir de clés situées n’importe où dans la valeur imbriquée, y compris le contenu non déclaré dans fields. L'attribut n'est pas valide sur d'autres types de champs.
Mappez chaque type d'observable que vous souhaitez extraire vers un objet de correspondance explicite. Un moteur de correspondance peut utiliser equals, ends_with et contains; chaque clé accepte un tableau de chaînes de noms de clé. La correspondance des clés ne fait pas la distinction entre les majuscules et les minuscules.
- name: request
type: VARIANT
scan_for_observables:
ip:
equals: [sourceIp, destinationIp]
ends_with: [IpAddress]
contains: [clientIp]
email:
equals: [principalEmail]
ends_with: [Email]
Lakewatch examine récursivement les clés dans la valeur VARIANT. Lorsqu’une clé correspond à une règle configurée, Lakewatch valide la valeur candidate pour ce type observable et ajoute des valeurs valides au champ correspondant dans lw_observables. La correspondance utilise uniquement les règles de l'analyseur ; Lakewatch n'ajoute pas de règles de correspondance intégrées.
Champs imbriqués et en tableau
Utilisez VARIANT avec fields pour les objets imbriqués, et ARRAY avec element pour les listes. Un élément est défini comme un champ, mais sans name, required ou path:
- name: resources
type: ARRAY
path: resources
element:
type: VARIANT
fields:
- name: accountId
type: STRING
observables: [aws_account_id]
- name: source_ips
type: ARRAY
path: sourceIps
element:
type: STRING
observables: [ip]
Classification
Une source de données peut avoir plusieurs parseurs attachés. Chaque événement entrant est comparé aux parseurs dans l’ordre dans lequel ils sont déclarés et finit dans le premier parseur avec lequel il correspond. Un événement correspond lorsque ses champs requis sont présents, que ses champs typés sont convertis en types déclarés et que tous les validateurs réussissent. Les événements qui ne correspondent pas sont acheminés vers un sentinel de lettre morte au lieu de faire échouer le pipeline.
Analyseurs intégrés et personnalisés
Lakewatch fournit des parseurs intégrés pour les sources de logs de sécurité courantes et vous permet de créer des parseurs personnalisés pour les sources qui ne sont pas couvertes ou que vous souhaitez personnaliser. Les deux utilisent le format YAML décrit ci-dessus.
Découvrir et inspecter les parsers intégrés
Lorsque vous attachez des analyseurs à une source de données, vous pouvez parcourir les analyseurs intégrés disponibles et sélectionner ceux qui correspondent à votre source. Examinez un analyseur intégré pour voir les colonnes qu’il produit (ses champs, types, validateurs et observables) avant de l’attacher, afin de vérifier qu’il correspond aux événements que vous ingérez.
Les analyseurs intégrés sont en lecture seule. Pour personnaliser un analyseur, utilisez l’action Cloner sur l’analyseur afin de créer un analyseur personnalisé modifiable à partir de la définition de l’analyseur intégré, puis ajustez-le et enregistrez-le. Le clonage est une fonctionnalité de l’interface utilisateur qui copie la définition dans un nouvel analyseur personnalisé (version 1). Il ne s’agit pas d’une opération de clonage côté serveur, et l’analyseur intégré lui-même reste inchangé.
Créer un parser personnalisé
Pour créer un analyseur personnalisé, fournissez un document YAML au format décrit ci-dessus et enregistrez-le dans un catalogue et un schéma Unity Catalog. La création d’un analyseur stocke la définition sous la version 1 et vous enregistre en tant que propriétaire de l’analyseur.
Définissez soigneusement vos champs, car un événement qui ne correspond à aucun analyseur est acheminé vers une sentinelle de lettres mortes plutôt que d’échouer le pipeline. Un événement dont les valeurs ne sont pas converties en types déclarés ou qui échoue à un validateur ne correspondra pas à l’analyseur.
Gestion des versions et suppression
La modification d’un analyseur ajoute une nouvelle version immuable. Les versions existantes ne sont jamais modifiées et les numéros de version ne sont jamais réutilisés. Le serveur attribue à chaque nouvelle version le numéro suivant dans la séquence. Seul le propriétaire de l'analyseur peut ajouter une version.
Une source de données pin une version spécifique, de sorte qu’elle continue d’utiliser cette version jusqu’à ce que vous la changiez explicitement. Voir Modifier la version d’un analyseur.
Vous pouvez supprimer un parser personnalisé entier, ce qui supprime toutes ses versions. Il n'existe aucune opération pour supprimer une version individuelle. Seul le propriétaire peut supprimer un parser, et la suppression est bloquée tant qu’une source de données fait référence à une version du parser. Avant de supprimer, détachez le parser de chaque source de données qui l’utilise (remplacez chaque source de données par un autre parser et enregistrez la modification), puis supprimez le parser.
Limites
Un document d'analyseur fait au maximum 256 Ko.