Raccogliere i log di contesto di Jamf Pro
Questo documento spiega come importare i log di contesto di Jamf Pro (contesto del dispositivo e dell'utente) in Google Security Operations utilizzando AWS S3 tramite Lambda e la pianificazione di EventBridge.
Prima di iniziare
- Istanza Google SecOps
- Accesso con privilegi al tenant Jamf Pro
- Accesso con privilegi ad AWS (S3, IAM, Lambda, EventBridge)
Configurare il ruolo API Jamf
- Accedi all'UI web di Jamf.
- Vai a Impostazioni > sezione Sistema > Ruoli e client API.
- Seleziona la scheda Ruoli API.
- Fai clic su New (Nuovo).
- Inserisci un nome visualizzato per il ruolo API (ad esempio,
context_role
). In Privilegi del ruolo API Jamf Pro, digita il nome di un privilegio e selezionalo dal menu.
- Inventario computer
- Inventario dispositivi mobili
Fai clic su Salva.
Configura il client API Jamf
- In Jamf Pro, vai a Impostazioni > sezione Sistema > Ruoli e client API.
- Seleziona la scheda Clienti API.
- Fai clic su New (Nuovo).
- Inserisci un nome visualizzato per il client API (ad esempio,
context_client
). - Nel campo Ruoli API, aggiungi il ruolo
context_role
che hai creato in precedenza. - In Durata del token di accesso, inserisci il tempo in secondi per la validità dei token di accesso.
- Fai clic su Salva.
- Fai clic su Modifica.
- Fai clic su Abilita client API.
- Fai clic su Salva.
Configurare il client secret di Jamf
- In Jamf Pro, vai al client API appena creato.
- Fai clic su Genera segreto client.
- In una schermata di conferma, fai clic su Crea secret.
- Salva i seguenti parametri in una posizione sicura:
- URL di base:
https://<your>.jamfcloud.com
- ID client: UUID.
- Client secret: il valore viene mostrato una sola volta.
- URL di base:
Configura il bucket AWS S3 e IAM per Google SecOps
- Crea un bucket Amazon S3 seguendo questa guida utente: Creazione di un bucket.
- Salva il nome e la regione del bucket per riferimento futuro (ad esempio,
jamfpro
). - Crea un utente seguendo questa guida utente: Creazione di un utente IAM.
- Seleziona l'utente creato.
- Seleziona la scheda Credenziali di sicurezza.
- Fai clic su Crea chiave di accesso nella sezione Chiavi di accesso.
- Seleziona Servizio di terze parti come Caso d'uso.
- Fai clic su Avanti.
- (Facoltativo) Aggiungi il tag della descrizione.
- Fai clic su Crea chiave di accesso.
- Fai clic su Scarica file CSV per salvare la chiave di accesso e la chiave di accesso segreta per riferimento futuro.
- Fai clic su Fine.
- Seleziona la scheda Autorizzazioni.
- Fai clic su Aggiungi autorizzazioni nella sezione Criteri per le autorizzazioni.
- Seleziona Aggiungi autorizzazioni.
- Seleziona Allega direttamente i criteri.
- Cerca e seleziona il criterio AmazonS3FullAccess.
- Fai clic su Avanti.
- Fai clic su Aggiungi autorizzazioni.
Configura il ruolo e il criterio IAM per i caricamenti S3
JSON delle policy (sostituisci
jamfpro
se hai inserito un nome del bucket diverso):{ "Version": "2012-10-17", "Statement": [ { "Sid": "AllowPutJamfObjects", "Effect": "Allow", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::jamfpro/*" } ] }
Vai alla console AWS > IAM > Policy > Crea policy > scheda JSON.
Copia e incolla le norme.
Fai clic su Avanti > Crea policy.
Vai a IAM > Ruoli > Crea ruolo > Servizio AWS > Lambda.
Allega il criterio appena creato.
Assegna al ruolo il nome
WriteJamfToS3Role
e fai clic su Crea ruolo.
Crea la funzione Lambda
- Nella console AWS, vai a Lambda > Funzioni > Crea funzione.
- Fai clic su Crea autore da zero.
- Fornisci i seguenti dettagli di configurazione:
Impostazione | Valore |
---|---|
Nome | jamf_pro_to_s3 |
Tempo di esecuzione | Python 3.13 |
Architettura | x86_64 |
Autorizzazioni | WriteJamfToS3Role |
Dopo aver creato la funzione, apri la scheda Codice, elimina lo stub e inserisci il seguente codice (
jamf_pro_to_s3.py
):import os import io import json import gzip import time import logging from datetime import datetime, timezone import boto3 import requests log = logging.getLogger() log.setLevel(logging.INFO) BASE_URL = os.environ.get("JAMF_BASE_URL", "").rstrip("/") CLIENT_ID = os.environ.get("JAMF_CLIENT_ID") CLIENT_SECRET = os.environ.get("JAMF_CLIENT_SECRET") S3_BUCKET = os.environ.get("S3_BUCKET") S3_PREFIX = os.environ.get("S3_PREFIX", "jamf-pro/context/") PAGE_SIZE = int(os.environ.get("PAGE_SIZE", "200")) SECTIONS = [ "GENERAL", "HARDWARE", "OPERATING_SYSTEM", "USER_AND_LOCATION", "DISK_ENCRYPTION", "SECURITY", "EXTENSION_ATTRIBUTES", "APPLICATIONS", "CONFIGURATION_PROFILES", "LOCAL_USER_ACCOUNTS", "CERTIFICATES", "SERVICES", "PRINTERS", "SOFTWARE_UPDATES", "GROUP_MEMBERSHIPS", "CONTENT_CACHING", "STORAGE", "FONTS", "PACKAGE_RECEIPTS", "PLUGINS", "ATTACHMENTS", "LICENSED_SOFTWARE", "IBEACONS", "PURCHASING", ] s3 = boto3.client("s3") def _now_iso(): return datetime.now(timezone.utc).isoformat() def get_token(): """OAuth2 client credentials > access_token""" url = f"{BASE_URL}/api/oauth/token" data = { "grant_type": "client_credentials", "client_id": CLIENT_ID, "client_secret": CLIENT_SECRET, } headers = {"Content-Type": "application/x-www-form-urlencoded"} r = requests.post(url, data=data, headers=headers, timeout=30) r.raise_for_status() j = r.json() return j["access_token"], int(j.get("expires_in", 1200)) def fetch_page(token: str, page: int): """GET /api/v1/computers-inventory with sections & pagination""" url = f"{BASE_URL}/api/v1/computers-inventory" params = [("page", page), ("page-size", PAGE_SIZE)] + [("section", s) for s in SECTIONS] hdrs = {"Authorization": f"Bearer {token}", "Accept": "application/json"} r = requests.get(url, params=params, headers=hdrs, timeout=60) r.raise_for_status() return r.json() def to_context_event(item: dict) -> dict: inv = item.get("inventory", {}) or {} general = inv.get("general", {}) or {} hardware = inv.get("hardware", {}) or {} osinfo = inv.get("operatingSystem", {}) or {} loc = inv.get("location", {}) or inv.get("userAndLocation", {}) or {} computer = { "udid": general.get("udid") or hardware.get("udid"), "deviceName": general.get("name") or general.get("deviceName"), "serialNumber": hardware.get("serialNumber") or general.get("serialNumber"), "model": hardware.get("model") or general.get("model"), "osVersion": osinfo.get("version") or general.get("osVersion"), "osBuild": osinfo.get("build") or general.get("osBuild"), "macAddress": hardware.get("macAddress"), "alternateMacAddress": hardware.get("wifiMacAddress"), "ipAddress": general.get("ipAddress"), "reportedIpV4Address": general.get("reportedIpV4Address"), "reportedIpV6Address": general.get("reportedIpV6Address"), "modelIdentifier": hardware.get("modelIdentifier"), "assetTag": general.get("assetTag"), } user_block = { "userDirectoryID": loc.get("username") or loc.get("userDirectoryId"), "emailAddress": loc.get("emailAddress"), "realName": loc.get("realName"), "phone": loc.get("phone") or loc.get("phoneNumber"), "position": loc.get("position"), "department": loc.get("department"), "building": loc.get("building"), "room": loc.get("room"), } return { "webhook": {"name": "api.inventory"}, "event_type": "ComputerInventory", "event_action": "snapshot", "event_timestamp": _now_iso(), "event_data": { "computer": {k: v for k, v in computer.items() if v not in (None, "")}, **{k: v for k, v in user_block.items() if v not in (None, "")}, }, "_jamf": { "id": item.get("id"), "inventory": inv, }, } def write_ndjson_gz(objs, when: datetime): buf = io.BytesIO() with gzip.GzipFile(filename="-", mode="wb", fileobj=buf, mtime=int(time.time())) as gz: for obj in objs: line = json.dumps(obj, separators=(",", ":")) + "\n" gz.write(line.encode("utf-8")) buf.seek(0) prefix = S3_PREFIX.strip("/") + "/" if S3_PREFIX else "" key = f"{prefix}{when:%Y/%m/%d}/jamf_pro_context_{int(when.timestamp())}.ndjson.gz" s3.put_object(Bucket=S3_BUCKET, Key=key, Body=buf.getvalue()) return key def lambda_handler(event, context): assert BASE_URL and CLIENT_ID and CLIENT_SECRET and S3_BUCKET, "Missing required env vars" token, _ttl = get_token() page = 0 total = 0 batch = [] now = datetime.now(timezone.utc) while True: payload = fetch_page(token, page) results = payload.get("results") or payload.get("computerInventoryList") or [] if not results: break for item in results: batch.append(to_context_event(item)) total += 1 if len(batch) >= 5000: key = write_ndjson_gz(batch, now) log.info("wrote %s records to s3://%s/%s", len(batch), S3_BUCKET, key) batch = [] if len(results) < PAGE_SIZE: break page += 1 if batch: key = write_ndjson_gz(batch, now) log.info("wrote %s records to s3://%s/%s", len(batch), S3_BUCKET, key) return {"ok": True, "count": total}
Vai a Configurazione > Variabili di ambiente > Modifica > Aggiungi nuova variabile di ambiente.
Inserisci le seguenti variabili di ambiente, sostituendole con i tuoi valori.
Variabili di ambiente
Chiave Esempio S3_BUCKET
jamfpro
S3_PREFIX
jamf-pro/context/
AWS_REGION
Seleziona la tua regione JAMF_CLIENT_ID
Inserisci l'ID client Jamf JAMF_CLIENT_SECRET
Inserisci il client secret di Jamf JAMF_BASE_URL
Inserisci l'URL di Jamf, sostituisci <your>
inhttps://<your>.jamfcloud.com
PAGE_SIZE
200
Dopo aver creato la funzione, rimani sulla relativa pagina (o apri Lambda > Funzioni > la tua funzione).
Seleziona la scheda Configurazione.
Nel riquadro Configurazione generale, fai clic su Modifica.
Modifica Timeout impostando 5 minuti (300 secondi) e fai clic su Salva.
Creare una pianificazione EventBridge
- Vai a Amazon EventBridge > Scheduler > Crea pianificazione.
- Fornisci i seguenti dettagli di configurazione:
- Programmazione ricorrente: tariffa (
1 hour
). - Target: la tua funzione Lambda.
- Nome:
jamfpro-context-schedule-1h
.
- Programmazione ricorrente: tariffa (
- Fai clic su Crea pianificazione.
Configura un feed in Google SecOps per importare i log di contesto di Jamf Pro
- Vai a Impostazioni SIEM > Feed.
- Fai clic su + Aggiungi nuovo feed.
- Nel campo Nome feed, inserisci un nome per il feed (ad esempio,
Jamf Pro Context logs
). - Seleziona Amazon S3 V2 come Tipo di origine.
- Seleziona Contesto Jamf Pro come Tipo di log.
- Fai clic su Avanti.
- Specifica i valori per i seguenti parametri di input:
- URI S3: l'URI del bucket
s3://jamfpro/jamf-pro/context/
- Sostituisci
jamfpro
con il nome effettivo del bucket.
- Sostituisci
- Opzioni di eliminazione dell'origine: seleziona l'opzione di eliminazione in base alle tue preferenze.
- Età massima del file: includi i file modificati nell'ultimo numero di giorni. Il valore predefinito è 180 giorni.
- ID chiave di accesso: chiave di accesso utente con accesso al bucket S3.
- Chiave di accesso segreta: chiave segreta dell'utente con accesso al bucket S3.
- Spazio dei nomi dell'asset: lo spazio dei nomi dell'asset.
- Etichette di importazione: l'etichetta da applicare agli eventi di questo feed.
- URI S3: l'URI del bucket
- Fai clic su Avanti.
- Controlla la nuova configurazione del feed nella schermata Finalizza e fai clic su Invia.
Hai bisogno di ulteriore assistenza? Ricevi risposte dai membri della community e dai professionisti di Google SecOps.