Collecter les journaux BeyondTrust Endpoint Privilege Management
Ce document explique comment ingérer des journaux BeyondTrust Endpoint Privilege Management (EPM) dans Google Security Operations à l'aide d'AWS S3. L'analyseur se concentre sur la transformation des données de journaux JSON brutes de BeyondTrust Endpoint en un format structuré conforme à l'Unified Data Model (UDM). Il initialise d'abord les valeurs par défaut de différents champs, puis analyse la charge utile JSON et mappe ensuite des champs spécifiques du journal brut dans les champs UDM correspondants de l'objet event.idm.read_only_udm
.
Avant de commencer
Assurez-vous de remplir les conditions suivantes :
- Instance Google SecOps
- Accès privilégié à AWS
- Accès privilégié à BeyondTrust Endpoint Privilege Management
Configurer AWS IAM pour l'ingestion Google SecOps
- Créez un utilisateur en suivant ce guide de l'utilisateur : Créer un utilisateur IAM.
- Sélectionnez l'utilisateur créé.
- Sélectionnez l'onglet Identifiants de sécurité.
- Cliquez sur Créer une clé d'accès dans la section Clés d'accès.
- Sélectionnez Service tiers comme Cas d'utilisation.
- Cliquez sur Suivant.
- Facultatif : Ajoutez un tag de description.
- Cliquez sur Créer une clé d'accès.
- Cliquez sur Télécharger le fichier CSV pour enregistrer la clé d'accès et la clé d'accès secrète pour référence ultérieure.
- Cliquez sur OK.
- Sélectionnez l'onglet Autorisations.
- Cliquez sur Ajouter des autorisations dans la section Règles d'autorisation.
- Sélectionnez Ajouter des autorisations.
- Sélectionnez Joindre directement des règles.
- Recherchez et sélectionnez la règle AmazonS3FullAccess.
- Cliquez sur Suivant.
- Cliquez sur Ajouter des autorisations.
Configurer BeyondTrust EPM pour l'accès à l'API
- Connectez-vous à la console Web BeyondTrust Privilege Management en tant qu'administrateur.
- Accédez à Configuration du système > API REST > Jetons.
- Cliquez sur Ajouter un jeton.
- Fournissez les informations de configuration suivantes :
- Nom : saisissez
Google SecOps Collector
. - Niveaux d'accès : sélectionnez Audit:Read et d'autres niveaux d'accès selon vos besoins.
- Nom : saisissez
- Enregistrez et copiez la valeur du jeton (il s'agit de votre BPT_API_TOKEN).
- Copiez votre URL de base de l'API. Elle est généralement
https://<your-epm-server>/api/v3
ou/api/v2
, selon votre version (vous l'utiliserez comme BPT_API_URL).
Créer un bucket AWS S3
- Connectez-vous à l'AWS Management Console.
- Accédez à la console AWS> Services > S3 > Créer un bucket.
- Fournissez les informations de configuration suivantes :
- Nom du bucket :
my-beyondtrust-logs
. - Région : [votre choix] > Créer.
- Nom du bucket :
Créer un rôle IAM pour EC2
- Connectez-vous à l'AWS Management Console.
- Accédez à la console AWS> Services > IAM > Rôles > Créer un rôle.
- Fournissez les informations de configuration suivantes :
- Entité approuvée : Service AWS > EC2 > Suivant.
- Associez l'autorisation AmazonS3FullAccess (ou une règle limitée à votre bucket) > Suivant.
- Nom du rôle :
EC2-S3-BPT-Writer
> Créer un rôle.
Facultatif : Lancez et configurez votre VM de collecte EC2
- Connectez-vous à l'AWS Management Console.
- Accédez à Services.
- Dans la barre de recherche, saisissez "EC2" et sélectionnez-le.
- Dans le tableau de bord EC2, cliquez sur Instances.
- Cliquez sur Lancer des instances.
- Fournissez les informations de configuration suivantes :
- Nom : saisissez
BPT-Log-Collector
. - AMI : sélectionnez Ubuntu Server 22.04 LTS.
- Type d'instance : t3.micro (ou plus grand), puis cliquez sur Suivant.
- Réseau : assurez-vous que le paramètre "Réseau" est défini sur votre VPC par défaut.
- Rôle IAM : sélectionnez le rôle IAM EC2-S3-BPT-Writer dans le menu.
- Attribuer automatiquement une adresse IP publique : Activer (ou assurez-vous de pouvoir y accéder à l'aide d'un VPN) > Suivant.
- Ajouter du stockage : conservez la configuration de stockage par défaut (8 Gio), puis cliquez sur Suivant.
- Sélectionnez Créer un groupe de sécurité.
- Règle entrante : cliquez sur Ajouter une règle.
- Type : sélectionnez SSH.
- Port : 22
- Source : votre adresse IP
- Cliquez sur Vérifier et lancer.
- Sélectionnez ou créez une paire de clés.
- Cliquez sur Download Key Pair (Télécharger la paire de clés).
- Enregistrez le fichier PEM téléchargé. Vous aurez besoin de ce fichier pour vous connecter à votre instance à l'aide de SSH.
- Nom : saisissez
Connectez-vous à votre machine virtuelle (VM) à l'aide de SSH :
chmod 400 ~/Downloads/your-key.pem ssh -i ~/Downloads/your-key.pem ubuntu@<EC2_PUBLIC_IP>
Installer les prérequis du collecteur
Exécutez la commande suivante :
# Update OS sudo apt update && sudo apt upgrade -y # Install Python, Git sudo apt install -y python3 python3-venv python3-pip git # Create & activate virtualenv python3 -m venv ~/bpt-venv source ~/bpt-venv/bin/activate # Install libraries pip install requests boto3
Créez un répertoire et un fichier d'état :
sudo mkdir -p /var/lib/bpt-collector sudo touch /var/lib/bpt-collector/last_run.txt sudo chown ubuntu:ubuntu /var/lib/bpt-collector/last_run.txt
Initialisez-le (par exemple, sur "il y a une heure") :
echo "$(date -u -d '1 hour ago' +%Y-%m-%dT%H:%M:%SZ)" > /var/lib/bpt-collector/last_run.txt
Déployer le script du collecteur Armis
Créez un dossier pour le projet :
mkdir ~/bpt-collector && cd ~/bpt-collector
Exportez les variables d'environnement requises (par exemple, dans
~/.bashrc
) :export BPT_API_URL="https://<your-subdomain>-services.pm.beyondtrustcloud.com" export BPT_CLIENT_ID="your-client-id" export BPT_CLIENT_SECRET="your-client-secret" export S3_BUCKET="my-bpt-logs" export S3_PREFIX="bpt/" export STATE_FILE="/var/lib/bpt-collector/last_run.txt" export PAGE_SIZE="100"
Créez
collector_bpt.py
et saisissez le code suivant :#!/usr/bin/env python3 import os, sys, json, boto3, requests from datetime import datetime, timezone, timedelta # ── UTILS ───────────────────────────────────────────────────────────────── def must_env(var): val = os.getenv(var) if not val: print(f"ERROR: environment variable {var} is required", file=sys.stderr) sys.exit(1) return val def ensure_state_file(path): d = os.path.dirname(path) if not os.path.isdir(d): os.makedirs(d, exist_ok=True) if not os.path.isfile(path): ts = (datetime.now(timezone.utc) - timedelta(hours=1))\ .strftime("%Y-%m-%dT%H:%M:%SZ") with open(path, "w") as f: f.write(ts) # ── CONFIG ───────────────────────────────────────────────────────────────── BPT_API_URL = must_env("BPT_API_URL") # for example, https://subdomain-services.pm.beyondtrustcloud.com CLIENT_ID = must_env("BPT_CLIENT_ID") CLIENT_SECRET = must_env("BPT_CLIENT_SECRET") S3_BUCKET = must_env("S3_BUCKET") S3_PREFIX = os.getenv("S3_PREFIX", "") # for example, "bpt/" STATE_FILE = os.getenv("STATE_FILE", "/var/lib/bpt-collector/last_run.txt") PAGE_SIZE = int(os.getenv("PAGE_SIZE", "100")) # ── END CONFIG ───────────────────────────────────────────────────────────── ensure_state_file(STATE_FILE) def read_last_run(): with open(STATE_FILE, "r") as f: ts = f.read().strip() return datetime.fromisoformat(ts.replace("Z", "+00:00")) def write_last_run(dt): with open(STATE_FILE, "w") as f: f.write(dt.strftime("%Y-%m-%dT%H:%M:%SZ")) def get_oauth_token(): resp = requests.post( f"{BPT_API_URL}/oauth/connect/token", headers={"Content-Type": "application/x-www-form-urlencoded"}, data={ "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET } ) resp.raise_for_status() return resp.json()["access_token"] def fetch_events(token, start, end): headers = {"Authorization": f"Bearer {token}"} offset = 0 while True: params = { "startTime": start, "endTime": end, "limit": PAGE_SIZE, "offset": offset } resp = requests.get( f"{BPT_API_URL}/management-api/v1/Audit/Events", headers=headers, params=params ) resp.raise_for_status() events = resp.json().get("events", []) if not events: break for e in events: yield e offset += PAGE_SIZE def upload_to_s3(obj, key): boto3.client("s3").put_object( Bucket=S3_BUCKET, Key=key, Body=json.dumps(obj).encode("utf-8") ) def main(): # 1) determine window start_dt = read_last_run() end_dt = datetime.now(timezone.utc) START = start_dt.strftime("%Y-%m-%dT%H:%M:%SZ") END = end_dt.strftime("%Y-%m-%dT%H:%M:%SZ") print(f"Fetching events from {START} to {END}") # 2) authenticate and fetch token = get_oauth_token() count = 0 for idx, evt in enumerate(fetch_events(token, START, END), start=1): key = f"{S3_PREFIX}{end_dt.strftime('%Y/%m/%d')}/evt_{int(end_dt.timestamp())}_{idx}.json" upload_to_s3(evt, key) count += 1 print(f"Uploaded {count} events") # 3) persist state write_last_run(end_dt) if __name__ == "__main__": main()
Rendez-le exécutable :
chmod +x collector_bpt.py
Planifier une exécution quotidienne avec Cron
Exécutez la commande suivante :
crontab -e
Ajoutez le job quotidien à minuit UTC :
0 0 * * * cd ~/bpt-collector && source ~/bpt-venv/bin/activate && ./collector_bpt.py >> ~/bpt-collector/bpt.log 2>&1
Configurer des flux
Il existe deux points d'entrée différents pour configurer les flux dans la plate-forme Google SecOps :
- Paramètres SIEM> Flux
- Plate-forme de contenu > Packs de contenu
Configurer des flux à partir de Paramètres SIEM > Flux
Pour configurer un flux, procédez comme suit :
- Accédez à Paramètres SIEM > Flux.
- Cliquez sur Add New Feed (Ajouter un flux).
- Sur la page suivante, cliquez sur Configurer un seul flux.
- Dans le champ Nom du flux, saisissez un nom pour le flux (par exemple, Journaux BeyondTrust EPM).
- Sélectionnez Amazon S3 comme Type de source.
- Sélectionnez Beyondtrust Endpoint Privilege Management comme type de journal.
- Cliquez sur Suivant.
Spécifiez les valeurs des paramètres d'entrée suivants :
- Région : région dans laquelle se trouve le bucket Amazon S3.
- URI S3 : URI du bucket (au format
s3://your-log-bucket-name/
). Remplacez les éléments suivants :your-log-bucket-name
: nom du bucket.
- L'URI est : sélectionnez Répertoire ou Répertoire incluant des sous-répertoires.
- Options de suppression de la source : sélectionnez l'option de suppression de votre choix.
- ID de clé d'accès : clé d'accès utilisateur ayant accès au bucket S3.
- Clé d'accès secrète : clé secrète de l'utilisateur ayant accès au bucket S3.
Cliquez sur Suivant.
Vérifiez la configuration de votre nouveau flux sur l'écran Finaliser, puis cliquez sur Envoyer.
Configurer des flux depuis le Hub de contenu
Indiquez les valeurs des champs suivants :
Région : région dans laquelle se trouve le bucket Amazon S3.
- URI S3 : URI du bucket (au format
s3://your-log-bucket-name/
). Remplacez les éléments suivants :your-log-bucket-name
: nom du bucket.
- L'URI est : sélectionnez Répertoire ou Répertoire incluant des sous-répertoires.
- Options de suppression de la source : sélectionnez l'option de suppression de votre choix.
- ID de clé d'accès : clé d'accès utilisateur ayant accès au bucket S3.
- Clé d'accès secrète : clé secrète de l'utilisateur ayant accès au bucket S3.
- URI S3 : URI du bucket (au format
Options avancées
- Nom du flux : valeur préremplie qui identifie le flux.
- Type de source : méthode utilisée pour collecter les journaux dans Google SecOps.
- Espace de noms de l'élément : espace de noms associé au flux.
- Libellés d'ingestion : libellés appliqués à tous les événements de ce flux.
Table de mappage UDM
Champ du journal | Mappage UDM | Logique |
---|---|---|
agent.id | principal.asset.attribute.labels.value | La valeur est extraite du champ agent.id du journal brut et mappée à un libellé avec la clé agent_id dans le tableau principal.asset.attribute.labels de l'UDM. |
agent.version | principal.asset.attribute.labels.value | La valeur est extraite du champ agent.version du journal brut et mappée à un libellé avec la clé agent_version dans le tableau principal.asset.attribute.labels de l'UDM. |
ecs.version | principal.asset.attribute.labels.value | La valeur est extraite du champ ecs.version du journal brut et mappée à un libellé avec la clé ecs_version dans le tableau principal.asset.attribute.labels de l'UDM. |
event_data.reason | metadata.description | La valeur est extraite du champ event_data.reason du journal brut et mappée au champ description de l'objet metadata dans l'UDM. |
event_datas.ActionId | metadata.product_log_id | La valeur est extraite du champ event_datas.ActionId du journal brut et mappée au champ product_log_id de l'objet metadata dans l'UDM. |
file.path | principal.file.full_path | La valeur est extraite du champ file.path du journal brut et mappée au champ full_path de l'objet principal.file dans l'UDM. |
headers.content_length | additional.fields.value.string_value | La valeur est extraite du champ headers.content_length du journal brut et mappée à un libellé avec la clé content_length dans le tableau additional.fields de l'UDM. |
headers.content_type | additional.fields.value.string_value | La valeur est extraite du champ headers.content_type du journal brut et mappée à un libellé avec la clé content_type dans le tableau additional.fields de l'UDM. |
headers.http_host | additional.fields.value.string_value | La valeur est extraite du champ headers.http_host du journal brut et mappée à un libellé avec la clé http_host dans le tableau additional.fields de l'UDM. |
headers.http_version | network.application_protocol_version | La valeur est extraite du champ headers.http_version du journal brut et mappée au champ application_protocol_version de l'objet network dans l'UDM. |
headers.request_method | network.http.method | La valeur est extraite du champ headers.request_method du journal brut et mappée au champ method de l'objet network.http dans l'UDM. |
host.hostname | principal.hostname | La valeur est extraite du champ host.hostname du journal brut et mappée au champ hostname de l'objet principal dans l'UDM. |
host.hostname | principal.asset.hostname | La valeur est extraite du champ host.hostname du journal brut et mappée au champ hostname de l'objet principal.asset dans l'UDM. |
host.ip | principal.asset.ip | La valeur est extraite du champ host.ip du journal brut et ajoutée au tableau ip dans l'objet principal.asset de l'UDM. |
host.ip | principal.ip | La valeur est extraite du champ host.ip du journal brut et ajoutée au tableau ip dans l'objet principal de l'UDM. |
host.mac | principal.mac | La valeur est extraite du champ host.mac du journal brut et ajoutée au tableau mac dans l'objet principal de l'UDM. |
host.os.platform | principal.platform | La valeur est définie sur MAC si le champ host.os.platform du journal brut est égal à macOS . |
host.os.version | principal.platform_version | La valeur est extraite du champ host.os.version du journal brut et mappée au champ platform_version de l'objet principal dans l'UDM. |
labels.related_item_id | metadata.product_log_id | La valeur est extraite du champ labels.related_item_id du journal brut et mappée au champ product_log_id de l'objet metadata dans l'UDM. |
process.command_line | principal.process.command_line | La valeur est extraite du champ process.command_line du journal brut et mappée au champ command_line de l'objet principal.process dans l'UDM. |
process.name | additional.fields.value.string_value | La valeur est extraite du champ process.name du journal brut et mappée à un libellé avec la clé process_name dans le tableau additional.fields de l'UDM. |
process.parent.name | additional.fields.value.string_value | La valeur est extraite du champ process.parent.name du journal brut et mappée à un libellé avec la clé process_parent_name dans le tableau additional.fields de l'UDM. |
process.parent.pid | principal.process.parent_process.pid | La valeur est extraite du champ process.parent.pid du journal brut, convertie en chaîne et mappée au champ pid de l'objet principal.process.parent_process dans l'UDM. |
process.pid | principal.process.pid | La valeur est extraite du champ process.pid du journal brut, convertie en chaîne et mappée au champ pid de l'objet principal.process dans l'UDM. |
user.id | principal.user.userid | La valeur est extraite du champ user.id du journal brut et mappée au champ userid de l'objet principal.user dans l'UDM. |
nom.utilisateur | principal.user.user_display_name | La valeur est extraite du champ user.name du journal brut et mappée au champ user_display_name de l'objet principal.user dans l'UDM. |
N/A | metadata.event_timestamp | Le code temporel de l'événement est défini sur celui de l'entrée de journal. |
N/A | metadata.event_type | Le type d'événement est défini sur GENERIC_EVENT si aucun principal n'est trouvé, sinon il est défini sur STATUS_UPDATE . |
N/A | network.application_protocol | Le protocole d'application est défini sur HTTP si le champ headers.http_version du journal brut contient HTTP . |
Vous avez encore besoin d'aide ? Obtenez des réponses de membres de la communauté et de professionnels Google SecOps.