收集 IBM MaaS360 日志

支持的平台:

本文档介绍了如何使用 Google Cloud Storage V2 将 IBM MaaS360 日志注入到 Google Security Operations 中。

IBM MaaS360 是一个统一端点管理 (UEM) 平台,提供移动设备管理、应用安全和威胁管理功能。它会生成安全事件,包括管理员登录报告、管理员更改审核、合规性规则事件、设备注册事件、操作历史记录事件,以及安全威胁事件,例如短信和电子邮件钓鱼式攻击以及恶意软件检测。

准备工作

确保您满足以下前提条件:

  • Google SecOps 实例
  • 启用了 Cloud Storage、Cloud Run、Pub/Sub 和 Cloud Scheduler API 的 GCP 项目
  • 创建和管理 GCS 存储分区的权限
  • 管理 GCS 存储分区的 IAM 政策的权限
  • 创建 Cloud Run 服务、Pub/Sub 主题和 Cloud Scheduler 作业的权限
  • 使用管理员账号对 IBM MaaS360 门户网站的特权访问权限
  • 为您的 IBM MaaS360 账号启用 API 访问权限(必须由 IBM 支持人员启用此功能)
  • 分配了 Web Service - Access Keys 角色的管理员账号

为您的 IBM MaaS360 账号启用 API 访问权限

您必须先为 MaaS360 账号启用 API 访问权限,然后才能生成 API 凭据。请向 IBM MaaS360 支持团队提交请求,以启用您账号的 Web Services API 功能。启用后,MaaS360 门户的设置菜单下会显示 Web Services API 选项。

创建 Web 服务 API 角色

如需访问该 API,您的管理员账号必须拥有 Web Service - Access Keys 权限。如果您的账号尚未拥有此角色,请创建一个:

  1. 登录 IBM MaaS360 门户
  2. 依次前往设置 > 门户管理 > 角色
  3. 点击添加角色
  4. 基本信息部分,提供以下信息:
    • 角色名称:输入一个描述性名称(例如 Web Service API)。
    • 角色说明:输入 Permissions to access the Web Service API
  5. 对于选择创建模式,选择新建,然后点击下一步
  6. 从可用的访问权限中,选择 Web 服务 - 访问密钥
  7. 点击保存

向管理员账号分配 API 角色

  1. 登录 IBM MaaS360 门户
  2. 依次前往设置 > 门户管理 > 管理员
  3. 点击将用于 API 集成的管理员账号旁边的修改,或创建一个新的管理员账号。
  4. 向账号添加 Web 服务 API 角色。
  5. 点击继续
  6. 退出账号,然后使用更新后的管理员账号重新登录,以使角色变更生效。

生成应用访问密钥

  1. 使用具有 Web Service API 角色的管理员账号登录 IBM MaaS360 门户网站
  2. 依次前往设置 > Web 服务 API > 管理访问密钥
  3. 点击生成访问密钥
  4. 选择 MaaS360 Web 服务作为密钥类型。
  5. 密钥名称字段中,输入一个描述性名称(例如 Chronicle Integration)。
  6. 点击生成
  7. 在系统提示时,使用管理员密码进行身份验证。
  8. 记录密钥生成后显示的以下凭据:

    • 应用 ID
    • 应用版本
    • 平台 ID
    • 应用访问密钥

查找您的结算 ID

  1. 登录 IBM MaaS360 门户
  2. 将鼠标悬停在门户右上角的个人资料菜单上。
  3. 复制账号 ID 值。这是您的结算 ID。

确定 API 基本网址

API 基础网址取决于您的 MaaS360 实例。您的实例由结算 ID 的第一位数字确定:

结算 ID 以以下内容开头 实例 API 基本网址
1 M1 https://services.fiberlink.com
2 M2 https://services.m2.maas360.com
3 M3 https://services.m3.maas360.com
4 M4 https://services.m4.maas360.com

测试 API 访问权限

  • 在继续进行集成之前,请先测试您的凭据:

    # Replace with your actual values
    API_BASE="https://services.m3.maas360.com"
    BILLING_ID="your-billing-id"
    USERNAME="your-admin-username"
    PASSWORD="your-admin-password"
    APP_ID="your-app-id"
    APP_VERSION="your-app-version"
    PLATFORM_ID="3"
    APP_ACCESS_KEY="your-app-access-key"
    
    # Test authentication
    curl -v -X POST "${API_BASE}/auth-apis/auth/1.0/authenticate/${BILLING_ID}" \
        -H "Content-Type: application/xml" \
        -H "Accept: application/json" \
        -d "<authRequest><maaS360AdminAuth><billingID>${BILLING_ID}</billingID><platformID>${PLATFORM_ID}</platformID><appID>${APP_ID}</appID><appVersion>${APP_VERSION}</appVersion><appAccessKey>${APP_ACCESS_KEY}</appAccessKey><userName>${USERNAME}</userName><password>${PASSWORD}</password></maaS360AdminAuth></authRequest>"
    

成功的响应会返回一个包含 authToken 的 JSON 对象。

创建 Google Cloud Storage 存储桶

  1. 前往 Google Cloud 控制台
  2. 选择您的项目或创建新项目。
  3. 在导航菜单中,依次前往 Cloud Storage > 存储分区
  4. 点击创建存储分区
  5. 提供以下配置详细信息:

    设置
    为存储桶命名 输入一个全局唯一的名称(例如 maas360-device-logs
    位置类型 根据您的需求进行选择(区域级、双区域、多区域)
    位置 选择营业地点(例如 us-central1
    存储类别 标准(建议用于经常访问的日志)
    访问权限控制 均匀(推荐)
    保护工具 可选:启用对象版本控制或保留政策
  6. 点击创建

为 Cloud Run 函数创建服务账号

Cloud Run 函数需要一个服务账号,该账号具有写入 GCS 存储桶的权限,并且可以由 Pub/Sub 调用。

创建服务账号

  1. GCP 控制台中,依次前往 IAM 和管理 > 服务账号
  2. 点击创建服务账号
  3. 提供以下配置详细信息:
    • 服务账号名称:输入 maas360-logs-collector-sa
    • 服务账号说明:输入 Service account for Cloud Run function to collect IBM MaaS360 logs
  4. 点击创建并继续
  5. 向此服务账号授予对项目的访问权限部分中,添加以下角色:
    1. 点击选择角色
    2. 搜索并选择 Storage Object Admin
    3. 点击 + 添加其他角色
    4. 搜索并选择 Cloud Run Invoker
    5. 点击 + 添加其他角色
    6. 搜索并选择 Cloud Functions Invoker
  6. 点击继续
  7. 点击完成

必须拥有这些角色,才能:

  • Storage Object Admin:将日志写入 GCS 存储桶并管理状态文件
  • Cloud Run Invoker:允许 Pub/Sub 调用函数
  • Cloud Functions Invoker:允许调用函数

授予对 GCS 存储桶的 IAM 权限

向服务账号授予对 GCS 存储桶的写入权限:

  1. 前往 Cloud Storage > 存储分区
  2. 点击您的存储桶名称 (maas360-device-logs)。
  3. 前往权限标签页。
  4. 点击授予访问权限
  5. 提供以下配置详细信息:
    • 添加主账号:输入服务账号电子邮件地址 (maas360-logs-collector-sa@PROJECT_ID.iam.gserviceaccount.com)
    • 分配角色:选择 Storage Object Admin
  6. 点击保存

创建 Pub/Sub 主题

创建一个 Pub/Sub 主题,Cloud Scheduler 将向该主题发布消息,而 Cloud Run 函数将订阅该主题。

  1. GCP 控制台中,前往 Pub/Sub > 主题
  2. 点击创建主题
  3. 提供以下配置详细信息:
    • 主题 ID:输入 maas360-logs-trigger
    • 将其他设置保留为默认值
  4. 点击创建

创建 Cloud Run 函数以收集日志

Cloud Run 函数将由来自 Cloud Scheduler 的 Pub/Sub 消息触发,以从 IBM MaaS360 API 中提取日志并将其写入 GCS。

如需创建用于收集日志的 Cloud Run 函数,请执行以下操作:

  1. GCP 控制台中,前往 Cloud Run
  2. 点击创建服务
  3. 选择函数(使用内嵌编辑器创建函数)。
  4. 配置部分中,提供以下配置详细信息:

    设置
    Service 名称 maas360-logs-collector
    区域 选择与您的 GCS 存储桶匹配的区域(例如 us-central1
    运行时 选择 Python 3.12 或更高版本
  5. 触发器(可选)部分中:

    1. 点击 + 添加触发器
    2. 选择 Cloud Pub/Sub
    3. 选择 Cloud Pub/Sub 主题中,选择 maas360-logs-trigger
    4. 点击保存
  6. 身份验证部分中:

    1. 选择需要进行身份验证
    2. 检查 Identity and Access Management (IAM)
  7. 向下滚动并展开容器、网络、安全性

  8. 前往安全性标签页:

    • 服务账号:选择 maas360-logs-collector-sa
  9. 前往容器标签页:

    1. 点击变量和密钥
    2. 为每个环境变量点击+ 添加变量

      变量名称 示例值 说明
      GCS_BUCKET maas360-device-logs GCS 存储桶名称
      GCS_PREFIX maas360-logs 日志文件的前缀
      STATE_KEY maas360-logs/state.json 状态文件路径
      API_BASE https://services.m3.maas360.com 您实例的 MaaS360 API 基本网址
      BILLING_ID your-billing-id MaaS360 账号 ID(结算 ID)
      USERNAME your-admin-username 管理员用户名
      PASSWORD your-admin-password 管理员密码
      APP_ID your-app-id 生成的访问密钥中的应用 ID
      APP_VERSION 1.0 根据生成的访问密钥获取的应用版本
      PLATFORM_ID your-platform-id 根据生成的访问密钥获取的平台 ID
      APP_ACCESS_KEY your-app-access-key 从生成的访问密钥中获取应用访问密钥
      PAGE_SIZE 250 每页的记录数(25、50、100、200、250)
      MAX_PAGES 100 每次运行要提取的最大网页数
      LOOKBACK_HOURS 24 初始回溯期
  10. 变量和 Secret 部分中,向下滚动到请求

    • 请求超时:输入 600 秒(10 分钟)
  11. 前往设置标签页:

    • 资源部分中:
      • 内存:选择 512 MiB 或更高值
      • CPU:选择 1
    • 修订版本伸缩部分中:
      • 实例数下限:输入 0
      • 实例数上限:输入 100
  12. 点击创建

  13. 等待服务创建完成(1-2 分钟)。

  14. 创建服务后,系统会自动打开内嵌代码编辑器

添加函数代码

  1. 入口点字段中输入 main
  2. 在内嵌代码编辑器中,创建两个文件:

    • main.py:

      import functions_framework
      from google.cloud import storage
      import json
      import os
      import urllib3
      from datetime import datetime, timezone, timedelta
      import xml.etree.ElementTree as ET
      
      # Initialize HTTP client with timeouts
      http = urllib3.PoolManager(
          timeout=urllib3.Timeout(connect=10.0, read=60.0),
          retries=False,
      )
      
      # Initialize Storage client
      storage_client = storage.Client()
      
      # Environment variables
      GCS_BUCKET = os.environ.get('GCS_BUCKET')
      GCS_PREFIX = os.environ.get('GCS_PREFIX', 'maas360-logs')
      STATE_KEY = os.environ.get('STATE_KEY', 'maas360-logs/state.json')
      API_BASE = os.environ.get('API_BASE', '').rstrip('/')
      BILLING_ID = os.environ.get('BILLING_ID')
      USERNAME = os.environ.get('USERNAME')
      PASSWORD = os.environ.get('PASSWORD')
      APP_ID = os.environ.get('APP_ID')
      APP_VERSION = os.environ.get('APP_VERSION', '1.0')
      PLATFORM_ID = os.environ.get('PLATFORM_ID', '3')
      APP_ACCESS_KEY = os.environ.get('APP_ACCESS_KEY')
      PAGE_SIZE = int(os.environ.get('PAGE_SIZE', '250'))
      MAX_PAGES = int(os.environ.get('MAX_PAGES', '100'))
      LOOKBACK_HOURS = int(os.environ.get('LOOKBACK_HOURS', '24'))
      
      def authenticate():
          """Authenticate to MaaS360 API and return auth token."""
          url = f"{API_BASE}/auth-apis/auth/1.0/authenticate/{BILLING_ID}"
      
          auth_xml = (
              '<authRequest>'
              '<maaS360AdminAuth>'
              f'<billingID>{BILLING_ID}</billingID>'
              f'<platformID>{PLATFORM_ID}</platformID>'
              f'<appID>{APP_ID}</appID>'
              f'<appVersion>{APP_VERSION}</appVersion>'
              f'<appAccessKey>{APP_ACCESS_KEY}</appAccessKey>'
              f'<userName>{USERNAME}</userName>'
              f'<password>{PASSWORD}</password>'
              '</maaS360AdminAuth>'
              '</authRequest>'
          )
      
          response = http.request(
              'POST', url,
              body=auth_xml.encode('utf-8'),
              headers={
                  'Content-Type': 'application/xml',
                  'Accept': 'application/json',
              }
          )
      
          if response.status != 200:
              raise Exception(
                  f"Authentication failed with status {response.status}: "
                  f"{response.data.decode('utf-8')}"
              )
      
          data = json.loads(response.data.decode('utf-8'))
          auth_token = data.get('authResponse', {}).get('authToken')
          if not auth_token:
              raise Exception(
                  f"No authToken in response: {response.data.decode('utf-8')}"
              )
      
          print("Successfully obtained MaaS360 auth token")
          return auth_token
      
      def fetch_devices(auth_token, last_reported_epoch_ms=None):
          """
          Fetch device records from MaaS360 Device Search API.
      
          Args:
              auth_token: Authentication token
              last_reported_epoch_ms: Filter for devices reported after this epoch (ms)
      
          Returns:
              List of device records as dicts
          """
          url = f"{API_BASE}/device-apis/devices/2.0/search/customer/{BILLING_ID}"
      
          headers = {
              'Authorization': f'MaaS token="{auth_token}"',
              'Accept': 'application/json',
          }
      
          all_devices = []
          page_number = 1
      
          while page_number <= MAX_PAGES:
              params = {
                  'pageSize': str(PAGE_SIZE),
                  'pageNumber': str(page_number),
                  'sortAttribute': 'lastReported',
                  'sortOrder': 'asc',
              }
      
              if last_reported_epoch_ms:
                  params['match'] = 'lastReportedSince'
                  params['lastReportedInEpochms'] = str(last_reported_epoch_ms)
      
              query_string = '&'.join(
                  [f"{k}={v}" for k, v in params.items()]
              )
              request_url = f"{url}?{query_string}"
      
              try:
                  response = http.request('GET', request_url, headers=headers)
      
                  if response.status == 429:
                      print("Rate limited (429). Stopping pagination.")
                      break
      
                  if response.status == 401:
                      print("Auth token expired (401). Re-authenticating...")
                      auth_token = authenticate()
                      headers['Authorization'] = f'MaaS token="{auth_token}"'
                      continue
      
                  if response.status != 200:
                      print(
                          f"HTTP Error: {response.status} - "
                          f"{response.data.decode('utf-8')}"
                      )
                      break
      
                  data = json.loads(response.data.decode('utf-8'))
      
                  devices = data.get('devices', {}).get('device', [])
                  if isinstance(devices, dict):
                      devices = [devices]
      
                  if not devices:
                      print(f"No more devices at page {page_number}")
                      break
      
                  all_devices.extend(devices)
                  print(
                      f"Page {page_number}: Retrieved {len(devices)} devices "
                      f"(total: {len(all_devices)})"
                  )
      
                  if len(devices) < PAGE_SIZE:
                      break
      
                  page_number += 1
      
              except Exception as e:
                  print(f"Error fetching devices page {page_number}: {e}")
                  break
      
          print(f"Retrieved {len(all_devices)} total device records")
          return all_devices, auth_token
      
      def fetch_action_history(auth_token, last_reported_epoch_ms=None):
          """
          Fetch action history events from MaaS360.
      
          Args:
              auth_token: Authentication token
              last_reported_epoch_ms: Filter for events after this epoch (ms)
      
          Returns:
              List of action history records as dicts
          """
          url = (
              f"{API_BASE}/device-apis/devices/1.0/"
              f"searchActionHistory/{BILLING_ID}"
          )
      
          headers = {
              'Authorization': f'MaaS token="{auth_token}"',
              'Accept': 'application/json',
          }
      
          all_actions = []
          page_number = 1
      
          while page_number <= MAX_PAGES:
              params = {
                  'pageSize': str(PAGE_SIZE),
                  'pageNumber': str(page_number),
              }
      
              query_string = '&'.join(
                  [f"{k}={v}" for k, v in params.items()]
              )
              request_url = f"{url}?{query_string}"
      
              try:
                  response = http.request('GET', request_url, headers=headers)
      
                  if response.status == 429:
                      print("Rate limited (429). Stopping pagination.")
                      break
      
                  if response.status == 401:
                      print("Auth token expired (401). Re-authenticating...")
                      auth_token = authenticate()
                      headers['Authorization'] = f'MaaS token="{auth_token}"'
                      continue
      
                  if response.status != 200:
                      print(
                          f"HTTP Error: {response.status} - "
                          f"{response.data.decode('utf-8')}"
                      )
                      break
      
                  data = json.loads(response.data.decode('utf-8'))
      
                  actions = data.get('actionHistory', {}).get('action', [])
                  if isinstance(actions, dict):
                      actions = [actions]
      
                  if not actions:
                      print(
                          f"No more action history at page {page_number}"
                      )
                      break
      
                  all_actions.extend(actions)
                  print(
                      f"Action history page {page_number}: "
                      f"{len(actions)} records "
                      f"(total: {len(all_actions)})"
                  )
      
                  if len(actions) < PAGE_SIZE:
                      break
      
                  page_number += 1
      
              except Exception as e:
                  print(
                      f"Error fetching action history "
                      f"page {page_number}: {e}"
                  )
                  break
      
          print(f"Retrieved {len(all_actions)} action history records")
          return all_actions, auth_token
      
      @functions_framework.cloud_event
      def main(cloud_event):
          """
          Cloud Run function triggered by Pub/Sub to fetch IBM MaaS360
          device and action history logs and write to GCS.
          """
          required_vars = [
              GCS_BUCKET, API_BASE, BILLING_ID,
              USERNAME, PASSWORD, APP_ID, APP_ACCESS_KEY,
          ]
          if not all(required_vars):
              print('Error: Missing required environment variables')
              return
      
          try:
              bucket = storage_client.bucket(GCS_BUCKET)
              state = load_state(bucket)
              now = datetime.now(timezone.utc)
      
              last_reported_epoch_ms = None
              if isinstance(state, dict) and state.get('last_reported_epoch_ms'):
                  last_reported_epoch_ms = int(
                      state['last_reported_epoch_ms']
                  )
                  overlap_ms = 2 * 60 * 1000
                  last_reported_epoch_ms = last_reported_epoch_ms - overlap_ms
              else:
                  lookback = now - timedelta(hours=LOOKBACK_HOURS)
                  last_reported_epoch_ms = int(
                      lookback.timestamp() * 1000
                  )
      
              print(
                  f"Fetching logs since epoch ms: {last_reported_epoch_ms} "
                  f"({datetime.fromtimestamp(last_reported_epoch_ms / 1000, tz=timezone.utc).isoformat()})"
              )
      
              auth_token = authenticate()
      
              devices, auth_token = fetch_devices(
                  auth_token, last_reported_epoch_ms
              )
              actions, auth_token = fetch_action_history(
                  auth_token, last_reported_epoch_ms
              )
      
              all_records = []
              newest_epoch_ms = last_reported_epoch_ms
      
              for device in devices:
                  device['_maas360_record_type'] = 'device'
                  all_records.append(device)
                  epoch_ms = device.get('lastReportedInEpochms')
                  if epoch_ms:
                      try:
                          epoch_val = int(epoch_ms)
                          if epoch_val > newest_epoch_ms:
                              newest_epoch_ms = epoch_val
                      except (ValueError, TypeError):
                          pass
      
              for action in actions:
                  action['_maas360_record_type'] = 'action_history'
                  all_records.append(action)
      
              if not all_records:
                  print("No new records found.")
                  save_state(
                      bucket,
                      int(now.timestamp() * 1000),
                  )
                  return
      
              timestamp = now.strftime('%Y%m%d_%H%M%S')
              object_key = (
                  f"{GCS_PREFIX}/maas360_logs_{timestamp}.ndjson"
              )
              blob = bucket.blob(object_key)
      
              ndjson = '\n'.join(
                  [
                      json.dumps(r, ensure_ascii=False, default=str)
                      for r in all_records
                  ]
              ) + '\n'
              blob.upload_from_string(
                  ndjson, content_type='application/x-ndjson'
              )
      
              print(
                  f"Wrote {len(all_records)} records to "
                  f"gs://{GCS_BUCKET}/{object_key}"
              )
      
              save_state(bucket, newest_epoch_ms)
      
              print(
                  f"Successfully processed {len(all_records)} records "
                  f"(devices: {len(devices)}, "
                  f"actions: {len(actions)})"
              )
      
          except Exception as e:
              print(f'Error processing logs: {str(e)}')
              raise
      
      def load_state(bucket):
          """Load state from GCS."""
          try:
              blob = bucket.blob(STATE_KEY)
              if blob.exists():
                  return json.loads(blob.download_as_text())
          except Exception as e:
              print(f"Warning: Could not load state: {e}")
          return {}
      
      def save_state(bucket, last_reported_epoch_ms):
          """Save the last reported epoch timestamp to GCS state file."""
          try:
              state = {
                  'last_reported_epoch_ms': last_reported_epoch_ms,
                  'last_run': datetime.now(timezone.utc).isoformat(),
              }
              blob = bucket.blob(STATE_KEY)
              blob.upload_from_string(
                  json.dumps(state, indent=2),
                  content_type='application/json',
              )
              print(
                  f"Saved state: last_reported_epoch_ms="
                  f"{last_reported_epoch_ms}"
              )
          except Exception as e:
              print(f"Warning: Could not save state: {e}")
      
    • requirements.txt:

      functions-framework==3.*
      google-cloud-storage==2.*
      urllib3>=2.0.0
      
  3. 点击部署以保存并部署该函数。

  4. 等待部署完成(2-3 分钟)。

创建 Cloud Scheduler 作业

Cloud Scheduler 会定期向 Pub/Sub 主题发布消息,从而触发 Cloud Run 函数。

  1. GCP Console 中,前往 Cloud Scheduler
  2. 点击创建作业
  3. 提供以下配置详细信息:

    设置
    名称 maas360-logs-collector-hourly
    区域 选择与 Cloud Run 函数相同的区域
    频率 0 * * * *(每小时一次,整点时)
    时区 选择时区(建议选择世界协调时间 [UTC])
    目标类型 Pub/Sub
    主题 选择maas360-logs-trigger
    消息正文 {}(空 JSON 对象)
  4. 点击创建

时间表频率选项

根据日志量和延迟时间要求选择频次:

频率 Cron 表达式 使用场景
每隔 15 分钟 */15 * * * * 大批量、低延迟
每小时 0 * * * * 标准(推荐)
每 6 小时 0 */6 * * * 低成交量、批处理
每天 0 0 * * * 历史数据收集

测试集成

  1. Cloud Scheduler 控制台中,找到您的作业 (maas360-logs-collector-hourly)。
  2. 点击强制运行以手动触发作业。
  3. 等待几秒钟。
  4. 前往 Cloud Run > 服务
  5. 点击 maas360-logs-collector
  6. 点击日志标签页。
  7. 验证函数是否已成功执行。请查找以下内容:

    Fetching logs since epoch ms: XXXXXXXXXXXXX (YYYY-MM-DDTHH:MM:SS+00:00)
    Successfully obtained MaaS360 auth token
    Page 1: Retrieved X devices (total: X)
    Action history page 1: X records (total: X)
    Wrote X records to gs://maas360-device-logs/maas360-logs/maas360_logs_YYYYMMDD_HHMMSS.ndjson
    Successfully processed X records (devices: X, actions: X)
    
  8. 前往 Cloud Storage > 存储分区

  9. 点击 maas360-device-logs

  10. 转到 maas360-logs/ 文件夹。

  11. 验证是否已创建具有当前时间戳的新 .ndjson 文件。

如果您在日志中看到错误,请执行以下操作:

  • HTTP 401:验证 USERNAMEPASSWORDAPP_IDAPP_ACCESS_KEY 环境变量是否正确
  • HTTP 403:验证管理员账号是否已分配 Web Service - Access Keys 角色
  • HTTP 429:速率限制 - 函数将停止分页,并在下次预定运行时恢复
  • 缺少环境变量:验证是否已在 Cloud Run 函数配置中设置所有必需的变量

检索 Google SecOps 服务账号

  1. 依次前往 SIEM 设置 > Feed
  2. 点击添加新 Feed
  3. 点击配置单个 Feed
  4. Feed 名称字段中,输入 Feed 的名称(例如 IBM MaaS360 Device Logs)。
  5. 选择 Google Cloud Storage V2 作为来源类型
  6. 选择 IBM MaaS360 作为日志类型
  7. 点击获取服务账号。系统会显示一个唯一的服务账号电子邮件地址,例如:

    secops-12345678@secops-gcp-prod.iam.gserviceaccount.com
    
  8. 复制此电子邮件地址,以便在下一步中使用。

  9. 点击下一步

  10. 为以下输入参数指定值:

    • 存储桶网址:输入带有前缀路径的 GCS 存储桶 URI:

      gs://maas360-device-logs/maas360-logs/
      
    • 来源删除选项:根据您的偏好选择删除选项:

      • 永不:转移后永不删除任何文件(建议用于测试)。
      • 删除已转移的文件:在成功转移后删除文件。
      • 删除已转移的文件和空目录:成功转移后删除文件和空目录。
    • 文件存在时间上限:包含在过去指定天数内修改的文件(默认值为 180 天)

    • 资产命名空间资产命名空间

    • 注入标签:要应用于此 Feed 中事件的标签

  11. 点击下一步

  12. 最终确定界面中查看新的 Feed 配置,然后点击提交

向 Google SecOps 服务账号授予 IAM 权限

Google SecOps 服务账号需要您的 GCS 存储桶的 Storage Object Viewer 角色。

如需向 Google SecOps 服务账号授予 IAM 权限,请执行以下操作:

  1. 前往 Cloud Storage > 存储分区
  2. 点击 maas360-device-logs
  3. 前往权限标签页。
  4. 点击授予访问权限
  5. 提供以下配置详细信息:
    • 添加主账号:粘贴 Google SecOps 服务账号电子邮件地址
    • 分配角色:选择 Storage Object Viewer
  6. 点击保存

UDM 映射表

日志字段 UDM 映射 逻辑
firstRegisteredInEpochms additional.fields 转换为字符串并合并为键值对,包括 firstRegisteredInEpochms、lastRegisteredInEpochms、lastMdmRegisteredInEpochms、lastReportedInEpochms、isSupervisedDevice、testDevice、maas360ManagedStatus、itunesStoreAccountEnabled、installedDateInEpochms、sourceID、imeiEsn、installedDate、mailboxDeviceId、mailboxManaged、mailboxLastReportedInEpochms、mailboxLastReported、lastReported、policyComplianceState、userDomain、maas360Environment、encryptionStatus、enrollmentMode、deviceStatus、appComplianceState、ruleComplianceState、selectiveWipeStatus、jailbreakStatus、platformName、deviceOwner、ownership、customAssetNumber、deviceName、modelId、passcodeCompliance、mdmMailboxDeviceId、mdmPolicy、appVersion
lastRegisteredInEpochms additional.fields
lastMdmRegisteredInEpochms additional.fields
lastReportedInEpochms additional.fields
isSupervisedDevice additional.fields
testDevice additional.fields
maas360ManagedStatus additional.fields
itunesStoreAccountEnabled additional.fields
installedDateInEpochms additional.fields
sourceID additional.fields
imeiEsn additional.fields
installedDate additional.fields
mailboxDeviceId additional.fields
mailboxManaged additional.fields
mailboxLastReportedInEpochms additional.fields
mailboxLastReported additional.fields
lastReported additional.fields
policyComplianceState additional.fields
userDomain additional.fields
maas360Environment additional.fields
encryptionStatus additional.fields
enrollmentMode additional.fields
deviceStatus additional.fields
appComplianceState additional.fields
ruleComplianceState additional.fields
selectiveWipeStatus additional.fields
jailbreakStatus additional.fields
platformName additional.fields
deviceOwner additional.fields
所有权 additional.fields
customAssetNumber additional.fields
deviceName additional.fields
modelId additional.fields
passcodeCompliance additional.fields
mdmMailboxDeviceId additional.fields
mdmPolicy additional.fields
appVersion additional.fields
deviceName entity.entity.asset.asset_id 直接复制值
deviceStatus entity.entity.asset.deployment_status 如果 deviceStatus ==“有效”,则设置为“ACTIVE”
installedDate entity.entity.asset.first_discover_time 使用日期过滤器转换,格式为 yyyy-MM-ddTHH:mm:ss
制造商 entity.entity.asset.hardware.manufacturer 直接复制值
模型 entity.entity.asset.hardware.model 直接复制值
platformSerialNumber entity.entity.asset.hardware.serial_number 直接复制值
maas360DeviceID entity.entity.asset.hostname 直接复制值
所有权 entity.entity.asset.labels 来自各个字段的合并键值对
deviceOwner entity.entity.asset.labels
imeiEsn entity.entity.asset.labels
maas360Environment entity.entity.asset.labels
installLocation entity.entity.asset.labels
maas360ManagedStatus entity.entity.asset.labels
udid entity.entity.asset.labels
mailboxDeviceId entity.entity.asset.labels
mailboxManaged entity.entity.asset.labels
phoneNumber entity.entity.asset.labels
sourceID entity.entity.asset.labels
itunesStoreAccountEnabled entity.entity.asset.labels
unifiedTravelerDeviceId entity.entity.asset.labels
enrollmentMode entity.entity.asset.labels
platformName entity.entity.asset.labels
deviceType entity.entity.asset.labels
modelId entity.entity.asset.labels
mdmPolicy entity.entity.asset.labels
policyComplianceState entity.entity.asset.labels
appComplianceState entity.entity.asset.labels
ruleComplianceState entity.entity.asset.labels
selectiveWipeStatus entity.entity.asset.labels
jailbreakStatus entity.entity.asset.labels
encryptionStatus entity.entity.asset.labels
passcodeCompliance entity.entity.asset.labels
mdmMailboxDeviceId entity.entity.asset.labels
isSupervisedDevice entity.entity.asset.labels
testDevice entity.entity.asset.labels
lastReported entity.entity.asset.last_discover_time 使用日期过滤器转换,格式为 yyyy-MM-ddTHH:mm:ss
wifiMacAddress entity.entity.asset.mac 从 wifiMacAddress 合并
userDomain entity.entity.asset.network_domain 直接复制值
osVersion entity.entity.asset.platform_software.platform_patch_level 直接复制值
osName entity.entity.asset.platform_software.platform_version 直接复制值
deviceType entity.entity.asset.type 如果 deviceType == "Smartphone",则设置为“MOBILE”
maas360DeviceID entity.metadata.entity_type 如果 maas360DeviceID 不为空,则设置为“ASSET”
maas360DeviceID entity.metadata.product_entity_id 直接复制值
entity.metadata.product_name entity.metadata.product_name 设置为“IBM MaaS360”
entity.metadata.vendor_name entity.metadata.vendor_name 设置为“IBM”
用户名 entity.relations.entity_type 如果 username 或 emailAddress 不为空,则设置为“USER”
emailAddress entity.relations.entity_type
emailAddress entity.relations.entity.user.email_addresses 从 emailAddress 合并
用户名 entity.relations.entity.user.userid 直接复制值
用户名 entity.relations.relationship 如果 username 或 emailAddress 不为空,则设置为“OWNS”
emailAddress entity.relations.relationship
maas360DeviceID metadata.event_type 如果 maas360DeviceID 不为空,则设置为 STATUS_UPDATE;否则设置为 GENERIC_EVENT
logType metadata.product_event_type 直接复制值
appVersion metadata.product_version 直接复制值
制造商 principal.asset.hardware.manufacturer 直接复制值
模型 principal.asset.hardware.model 直接复制值
platformSerialNumber principal.asset.hardware.serial_number 直接复制值
maas360DeviceID principal.asset.hostname 直接复制值
maas360DeviceID principal.hostname 直接复制值
wifiMacAddress principal.mac 如果不为空且不为“不适用”,则合并
osName principal.platform 如果匹配 (?i)windows,则设置为 WINDOWS;如果匹配 (?i)Mac,则设置为 MAC
osVersion principal.platform_version 直接复制值
deviceType principal.resource.name 直接复制值
unifiedTravelerDeviceId principal.resource.product_object_id 直接复制值
emailAddress principal.user.email_addresses 从 emailAddress 合并
用户名 principal.user.user_display_name 直接复制值
udid principal.user.userid 直接复制值
metadata.product_name metadata.product_name 设置为“IBM MaaS360”
metadata.vendor_name metadata.vendor_name 设置为“IBM”

更新日志

查看相应解析器的更改日志

需要更多帮助?获得社区成员和 Google SecOps 专业人士的解答。