收集 Akamai SIEM 连接器日志

解析器版本:2.0

支持的平台:

本文档介绍了如何将 Akamai SIEM 连接器日志注入到 Google Security Operations。Akamai SIEM 集成通过 SIEM 集成 API 以 JSON 格式近乎实时地提供 Akamai 平台中的安全事件。这些事件包括来自 Web 应用防火墙 (WAF)、Bot Manager 和其他 Akamai 安全产品的数据。由于 Akamai SIEM Integration API 基于拉取,因此计划函数会定期提取事件并将其写入对象存储,然后 Google SecOps 会提取这些事件。您可以使用以下两种受支持的方法之一来配置提取:

  • 方法 1:Cloud Storage。Cloud Run 函数会定期从 Akamai SIEM Integration API 中拉取事件,并将其写入 Cloud Storage 存储桶,供 Google SecOps 提取。
  • 方法 2:Amazon S3。AWS Lambda 函数会定期从 Akamai SIEM 集成 API 中提取事件,并将其写入 Amazon S3 存储桶,供 Google SecOps 提取。

准备工作

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

两种方法共有的步骤:

  • Google SecOps 实例
  • 通过 Manage SIEM 用户角色对 Akamai Control Center 进行特权访问。

方法 1 的其他前提条件 (Cloud Storage):

  • 启用了 Cloud Storage、Cloud Run、Pub/Sub 和 Cloud Scheduler API 的 Google Cloud 项目。
  • 创建和管理 Cloud Storage 存储桶、服务账号、Cloud Run 函数、Pub/Sub 主题和 Cloud Scheduler 作业的权限。

方法 2 的其他前提条件 (Amazon S3):

  • 对 AWS(S3、IAM、Lambda、EventBridge)的特权访问权限。

在 Akamai Control Center 中启用 SIEM 集成

  1. 登录 Akamai 控制中心。
  2. 前往云安全部分下的安全配置。
  3. 打开要收集 SIEM 数据的安全配置(以及相应版本)。
  4. 点击高级设置,然后展开 SIEM 集成的数据收集。
  5. 点击开启以启用 SIEM。
  6. 选择要导出哪些安全政策的数据:

    • 所有安全政策 - 针对违反安全配置中任何或所有安全政策的事件发送 SIEM 数据。
    • 特定安全政策 - 从下拉列表中选择一项或多项特定安全政策。
    • 可选:如果您使用账号保护器,并希望包含未加密的用户名,请选中包含用户名复选框。
    • 可选:如果您想在 SIEM 事件中接收 JA4 指纹信息,请选中包含 JA4 客户端 TLS 指纹复选框。
    • 可选:如果您想排除属于特定保护类型和操作的事件,请点击添加例外情况。选择您不希望 SIEM 收集的保护措施和关联操作。
  7. 点击保存。

  8. 从“SIEM 集成”部分复制并保存安全配置 ID (configId)。您需要此值才能进行 Lambda 配置。

为 SIEM 集成创建 Akamai API 凭据

  1. 登录 Akamai 控制中心。
  2. 依次前往账号管理 > 身份和访问权限 > 用户和 API 客户端。
  3. 点击创建 API 客户端。
  4. 点击高级以创建高级 API 客户端。
  5. 提供以下配置详细信息:
    • 名称:输入一个描述性名称(例如 SecOps SIEM Poller)。
    • API 服务:选择 SIEM,并将访问权限级别设置为 READ-WRITE。
  6. 点击创建 API 客户端。
  7. 点击下载可将凭据保存为 .edgerc 文件,点击复制可复制凭据。请妥善保存以下值:
    • 客户端令牌
    • 客户端密钥 (Client Secret)
    • Access Token
    • 主机(例如 akab-xxxxxxxxxxxxxxxx-xxxxxxxxxxxxxxxx.luna.akamaiapis.net)

方法 1:Google Cloud Storage

使用此方法将 Akamai SIEM 连接器日志收集到 Cloud Storage 存储桶中。一个 Cloud Run 函数,由 Cloud Scheduler 通过 Pub/Sub 触发,定期从 Akamai SIEM 集成 API 中拉取事件,并将其写入存储桶,供 Google SecOps 提取。

创建 Cloud Storage 存储桶

  1. 前往 Google Cloud 控制台。
  2. 选择您的项目或创建一个新项目。
  3. 前往 Cloud Storage > 存储桶。
  4. 点击创建存储分区。
  5. 提供以下配置详细信息:
    • 指定存储桶的名称:输入一个全局唯一的名称(例如 akamai-siem-logs)。
    • 位置类型:根据您的需求进行选择(区域、双区域或多区域)。
    • 位置:选择位置(例如 us-central1)。
    • 存储类别:选择 Standard。
    • 访问权限控制:选择统一。
  6. 点击创建。

为 Cloud Run 函数创建服务账号

  1. 前往 IAM 和管理 > 服务账号。
  2. 点击创建服务账号。
  3. 提供以下配置详细信息:
    • 服务账号名称:输入 akamai-siem-collector-sa。
    • 服务账号说明:输入 Service account for Cloud Run function to collect Akamai SIEM Connector logs。
  4. 点击创建并继续。
  5. 授予以下角色:
    • Storage Object Admin:将事件写入存储桶并管理状态文件。
    • Cloud Run Invoker:允许 Pub/Sub 调用该函数。
  6. 依次点击继续 > 完成。

向服务账号授予对相应存储桶的访问权限

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

创建 Pub/Sub 主题

  1. 依次前往 Pub/Sub > 主题。
  2. 点击创建主题。
  3. 在主题 ID 字段中,输入 akamai-siem-trigger。
  4. 点击创建。

创建 Cloud Run 函数以收集日志

Cloud Run 函数由 Cloud Scheduler 中的 Pub/Sub 消息触发。它使用 Akamai EdgeGrid 凭据对请求进行签名,从 SIEM 集成 API 中提取事件,并将这些事件写入存储桶。

  1. 前往 Cloud Run。
  2. 点击编写函数。
  3. 提供以下配置详细信息:
    • 服务名称:输入 akamai-siem-collector。
    • 区域:选择与您的存储桶匹配的区域(例如 us-central1)。
    • 运行时:选择 Python 3.12 或更高版本。
  4. 在触发器部分中,点击添加触发器,然后选择 Pub/Sub 触发器。
  5. 在 Eventarc 触发器窗格中,提供以下配置详细信息:
    • 触发器名称:保留生成的名称,或输入触发器的名称。
    • 触发器类型:选择 Google 来源。
    • 事件提供方:选择 Pub/Sub。
    • 事件类型:选择 google.cloud.pubsub.topic.v1.messagePublished。
    • 选择 Cloud Pub/Sub 主题:选择主题 akamai-siem-trigger。
    • 区域:选择与函数相同的区域。
    • 服务账号:选择您为函数创建的服务账号。
  6. 点击保存触发器。

  7. 在身份验证部分中,选择需要进行身份验证。

  8. 展开容器、网络、安全性,然后在安全性标签页上将服务账号设置为 akamai-siem-collector-sa。

  9. 在容器 > 变量和密钥标签页中,添加以下环境变量:

    变量名称 示例值
    GCS_BUCKET akamai-siem-logs
    GCS_PREFIX akamai-siem/
    STATE_KEY akamai-siem-state.json
    AKAMAI_HOST akab-xxxxxxxx.luna.akamaiapis.net
    AKAMAI_CLIENT_TOKEN akab-client-token-xxxx
    AKAMAI_CLIENT_SECRET your-client-secret
    AKAMAI_ACCESS_TOKEN akab-access-token-xxxx
    AKAMAI_CONFIG_ID 12345
    LIMIT 10000
    WINDOW_SECONDS 3600
    HTTP_TIMEOUT 110
    MAX_PAGES 20
  10. 将请求超时设置为 600 秒,并将内存设置为 512 MiB 或更高值。

  11. 点击创建。服务创建完成后,系统会打开内嵌式代码编辑器。

添加函数代码

  1. 在函数入口点字段中,输入 main。
  2. 在内嵌代码编辑器中,将 main.py 的内容替换为以下代码:

    ```python
    import functions_framework
    from google.cloud import storage
    import json
    import os
    import time
    import uuid
    import requests
    from akamai.edgegrid import EdgeGridAuth
    
    storage_client = storage.Client()
    
    GCS_BUCKET = os.environ.get('GCS_BUCKET')
    GCS_PREFIX = os.environ.get('GCS_PREFIX', 'akamai-siem/')
    STATE_KEY = os.environ.get('STATE_KEY', 'akamai-siem-state.json')
    AKAMAI_HOST = os.environ.get('AKAMAI_HOST')
    CLIENT_TOKEN = os.environ.get('AKAMAI_CLIENT_TOKEN')
    CLIENT_SECRET = os.environ.get('AKAMAI_CLIENT_SECRET')
    ACCESS_TOKEN = os.environ.get('AKAMAI_ACCESS_TOKEN')
    CONFIG_ID = os.environ.get('AKAMAI_CONFIG_ID')
    LIMIT = int(os.environ.get('LIMIT', '10000'))
    WINDOW_SEC = int(os.environ.get('WINDOW_SECONDS', '3600'))
    HTTP_TIMEOUT = int(os.environ.get('HTTP_TIMEOUT', '110'))
    MAX_PAGES = int(os.environ.get('MAX_PAGES', '20'))
    
    def _session():
            """Create a requests session that signs requests with Akamai EdgeGrid."""
            session = requests.Session()
            session.auth = EdgeGridAuth(
                    client_token=CLIENT_TOKEN,
                    client_secret=CLIENT_SECRET,
                    access_token=ACCESS_TOKEN,
            )
            return session
    
    def _load_state(bucket):
            """Load the saved offset 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, state):
            """Persist the offset to GCS for the next run."""
            try:
                    bucket.blob(STATE_KEY).upload_from_string(
                            json.dumps(state, separators=(',', ':')),
                            content_type='application/json',
                    )
            except Exception as e:
                    print(f'Warning: could not save state: {e}')
    
    def _fetch(session, offset, from_ts):
            """Fetch one batch of events. Use offset when available, otherwise a time window.
    
            The Akamai SIEM API does not allow combining the offset parameter with from/to.
            """
            url = f'https://{AKAMAI_HOST}/siem/v1/configs/{CONFIG_ID}'
            params = {'limit': LIMIT}
            if offset:
                    params['offset'] = offset
            else:
                    params['from'] = int(from_ts)
            resp = session.get(
                    url, params=params, timeout=HTTP_TIMEOUT,
                    headers={'Accept': 'application/json'},
            )
            resp.raise_for_status()
            return resp.text
    
    def _parse_ndjson(text):
            """Split the NDJSON response into event lines and the trailing context object.
    
            Each line is a JSON security event; the last line is a context object that
            contains the offset to use for the next request.
            """
            lines = [ln for ln in text.splitlines() if ln.strip()]
            if not lines:
                    return [], {}
            try:
                    context = json.loads(lines[-1])
            except ValueError:
                    return lines, {}
            if isinstance(context, dict) and 'offset' in context:
                    return lines[:-1], context
            return lines, {}
    
    def _write_events(bucket, event_lines, page_num):
            """Write a batch of raw JSON events to GCS, one event per line."""
            ts_path = time.strftime('%Y/%m/%d', time.gmtime())
            uniq = f'{int(time.time() * 1e6)}_{uuid.uuid4().hex[:8]}'
            key = f'{GCS_PREFIX}{ts_path}/akamai_siem_p{page_num:03d}_{uniq}.json'
            bucket.blob(key).upload_from_string(
                    '\n'.join(event_lines),
                    content_type='application/x-ndjson',
            )
            return key
    
    @functions_framework.cloud_event
    def main(cloud_event):
            """Triggered by Pub/Sub to pull Akamai SIEM events and write them to GCS."""
            if not all([GCS_BUCKET, AKAMAI_HOST, CLIENT_TOKEN, CLIENT_SECRET,
                                    ACCESS_TOKEN, CONFIG_ID]):
                    print('Error: missing required environment variables')
                    return
    
            bucket = storage_client.bucket(GCS_BUCKET)
            state = _load_state(bucket)
            offset = state.get('offset')
            from_ts = float(state.get('last_to_ts') or (time.time() - WINDOW_SEC))
    
            session = _session()
            pages = 0
            total = 0
            try:
                    while pages < MAX_PAGES:
                            text = _fetch(session, offset, from_ts)
                            events, context = _parse_ndjson(text)
                            if events:
                                    _write_events(bucket, events, pages + 1)
                                    total += len(events)
                            new_offset = context.get('offset')
                            if new_offset:
                                    offset = new_offset
                            pages += 1
                            # Stop when this window is drained (fewer than LIMIT events returned).
                            if not new_offset or len(events) < LIMIT:
                                    break
    
                    state['offset'] = offset
                    state['last_to_ts'] = time.time()
                    state['last_successful_run'] = time.time()
                    _save_state(bucket, state)
                    print(f'Retrieved {total} events across {pages} pages')
            except Exception as e:
                    print(f'Error collecting Akamai SIEM logs: {e}')
                    raise
    ```
    

  3. 创建一个包含以下内容的 requirements.txt 文件:

    ```none
    functions-framework==3.*
    google-cloud-storage==2.*
    requests>=2.31.0
    edgegrid-python>=1.3.1
    ```
    
  4. 点击部署,然后等待部署完成。

创建 Cloud Scheduler 作业

  1. 前往 Cloud Scheduler。
  2. 点击创建作业。
  3. 提供以下配置详细信息:
    • 名称:输入 akamai-siem-collector-5min。
    • 区域:选择与 Cloud Run 函数相同的区域。
    • 频次:输入 */5 * * * *(每 5 分钟一次)。
    • 时区:选择 UTC。
    • 目标类型:选择 Pub/Sub。
    • 主题:选择 akamai-siem-trigger。
    • 消息正文:输入 {}。
  4. 点击创建。
  5. 如需验证设置,请点击作业中的强制运行,然后检查 Cloud Run 日志和存储桶,确认事件已写入。

在 Google SecOps 中配置 Feed 以从 Cloud Storage 注入数据

  1. 前往 SIEM 设置 > Feed。
  2. 点击添加新 Feed。
  3. 在下一页上,点击配置单个 Feed。
  4. 在 Feed 名称字段中,输入 Feed 的名称(例如 Akamai SIEM Connector GCS)。
  5. 选择 Google Cloud Storage V2 作为来源类型。
  6. 选择 Akamai SIEM 连接器作为日志类型。
  7. 点击编年史服务账号字段旁边的获取服务账号,然后复制服务账号电子邮件地址。
  8. 点击下一步。
  9. 为以下输入参数指定值:
    • 存储桶 URI:输入 gs://akamai-siem-logs/akamai-siem/。此网址必须以尾随正斜杠 (/) 结尾。
    • 源删除选项:根据您的偏好选择删除选项。
    • 文件存在时间上限:包含在过去指定天数内修改过的文件。默认值为 180 天。
  10. 点击下一步。
  11. 在最终确定界面中查看新的 Feed 配置,然后点击提交。

向 Google SecOps 服务账号授予对相应存储桶的访问权限

  1. 前往 Cloud Storage > 存储桶。
  2. 点击您的存储桶名称。
  3. 前往权限标签页。
  4. 点击授予访问权限。
  5. 提供以下配置详细信息:
    • 添加主账号:粘贴从 Feed 设置中复制的 Google SecOps 服务账号电子邮件地址。
    • 分配角色:选择以下两个角色:
      • Storage Object Viewer:读取日志对象。
      • Storage Legacy Bucket Reader:读取存储桶元数据。如果您选择了删除已转移的文件和空目录删除选项,请改为选择存储旧版存储桶写入者,该角色也会授予删除权限。
  6. 点击保存。

方法 2:Amazon S3

使用此方法可使用 AWS Lambda 和 EventBridge 将 Akamai SIEM 连接器日志收集到 Amazon S3 存储桶中。

为 Google SecOps 配置 AWS S3 存储桶和 IAM

  1. 按照以下用户指南创建 Amazon S3 存储桶:创建存储桶
  2. 保存存储桶名称和区域以供日后参考(例如,akamai-siem-logs)。
  3. 按照以下用户指南创建用户:创建 IAM 用户。
  4. 选择创建的用户。
  5. 选择安全凭据标签页。
  6. 在访问密钥部分中,点击创建访问密钥。
  7. 选择第三方服务作为使用情形。
  8. 点击下一步。
    • 可选:添加说明标记。
  9. 点击创建访问密钥。
  10. 点击下载 .csv 文件以保存访问密钥和密钥,供日后参考。
  11. 点击完成。
  12. 选择权限标签页。
  13. 在权限政策部分中,点击添加权限。
  14. 选择添加权限。
  15. 选择直接附加政策。
  16. 搜索 AmazonS3FullAccess 政策。
  17. 选择相应政策。
  18. 点击下一步。
  19. 点击添加权限。

为 S3 上传配置 IAM 政策和角色

  1. 在 AWS 控制台中,依次前往 IAM > 政策 > 创建政策 > JSON 标签页。
  2. 复制并粘贴以下政策。

    • 政策 JSON(将 akamai-siem-logs 替换为您的存储桶名称):

      {
          "Version": "2012-10-17",
          "Statement": [
              {
                  "Sid": "AllowPutObjects",
                  "Effect": "Allow",
                  "Action": "s3:PutObject",
                  "Resource": "arn:aws:s3:::akamai-siem-logs/*"
              },
              {
                  "Sid": "AllowGetStateObject",
                  "Effect": "Allow",
                  "Action": "s3:GetObject",
                  "Resource": "arn:aws:s3:::akamai-siem-logs/akamai-siem-state.json"
              }
          ]
      }
      
  3. 点击下一步。

  4. 输入政策名称 AkamaiSIEMtoS3Policy,然后点击创建政策。

  5. 依次前往 IAM > 角色 > 创建角色。

  6. 选择 AWS 服务。

  7. 选择 Lambda 作为用例。

  8. 点击下一步。

  9. 搜索并选择政策 AkamaiSIEMtoS3Policy。

  10. 点击下一步。

  11. 输入角色名称 AkamaiSIEMtoS3Role,然后点击创建角色。

创建 Lambda 函数

  1. 在 AWS 控制台中,依次前往 Lambda > 函数 > 创建函数。
  2. 点击从头开始创作。
  3. 提供以下配置详细信息:

    设置 值
    名称 AkamaiSIEMtoS3Function
    运行时 Python 3.13
    架构 x86_64
    执行角色 使用现有角色
    现有角色 AkamaiSIEMtoS3Role
  4. 点击创建函数。

  5. 创建函数后,打开代码标签页,删除桩代码并粘贴以下代码:

    ```python
    import json
    import boto3
    import os
    import urllib3
    import hmac
    import hashlib
    import base64
    from datetime import datetime
    from urllib.parse import urlparse, urljoin
    
    # Configuration from environment variables
    S3_BUCKET = os.environ['S3_BUCKET']
    S3_PREFIX = os.environ.get('S3_PREFIX', 'akamai-siem/')
    STATE_KEY = os.environ.get('STATE_KEY', 'akamai-siem-state.json')
    
    AKAMAI_HOST = os.environ['AKAMAI_HOST']
    AKAMAI_CLIENT_TOKEN = os.environ['AKAMAI_CLIENT_TOKEN']
    AKAMAI_CLIENT_SECRET = os.environ['AKAMAI_CLIENT_SECRET']
    AKAMAI_ACCESS_TOKEN = os.environ['AKAMAI_ACCESS_TOKEN']
    AKAMAI_CONFIG_IDS = os.environ['AKAMAI_CONFIG_IDS'].split(',')
    
    LIMIT = int(os.environ.get('LIMIT', '10000'))
    
    s3_client = boto3.client('s3')
    http = urllib3.PoolManager()
    
    def load_state():
            """Load offset state from S3"""
            try:
                    response = s3_client.get_object(Bucket=S3_BUCKET, Key=STATE_KEY)
                    return json.loads(response['Body'].read().decode('utf-8'))
            except s3_client.exceptions.NoSuchKey:
                    # Only a missing object is a cold start. Any other error is raised:
                    # swallowing it would drop the saved offset and restart collection.
                    return {}
    
    def save_state(state):
            """Save offset state to S3.
    
            Failures are raised, not logged. If the state write fails after the
            events were uploaded, the next run repeats the same batch and duplicates it.
            """
            s3_client.put_object(
                    Bucket=S3_BUCKET,
                    Key=STATE_KEY,
                    Body=json.dumps(state, indent=2).encode('utf-8'),
                    ContentType='application/json'
            )
    
    def make_edgegrid_auth_header(url, method='GET'):
            """Create EdgeGrid authentication header"""
            timestamp = datetime.utcnow().strftime('%Y%m%dT%H:%M:%S+0000')
            nonce = base64.b64encode(os.urandom(16)).decode('utf-8')
    
            parsed_url = urlparse(url)
            relative_url = parsed_url.path
            if parsed_url.query:
                    relative_url += '?' + parsed_url.query
    
            auth_header = f'EG1-HMAC-SHA256 ' \
                                    f'client_token={AKAMAI_CLIENT_TOKEN};' \
                                    f'access_token={AKAMAI_ACCESS_TOKEN};' \
                                    f'timestamp={timestamp};' \
                                    f'nonce={nonce};'
    
            data_to_sign = '\t'.join([
                    method,
                    parsed_url.scheme,
                    parsed_url.netloc,
                    relative_url,
                    '',  # Request body for GET
                    '',  # No additional headers
            ])
    
            signing_key = hmac.new(
                    AKAMAI_CLIENT_SECRET.encode('utf-8'),
                    timestamp.encode('utf-8'),
                    hashlib.sha256
            ).digest()
    
            auth_signature = base64.b64encode(
                    hmac.new(
                            signing_key,
                            (data_to_sign + auth_header).encode('utf-8'),
                            hashlib.sha256
                    ).digest()
            ).decode('utf-8')
    
            return auth_header + f'signature={auth_signature}'
    
    def fetch_akamai_events(config_id, offset=None):
            """Fetch events from Akamai SIEM API"""
            base_url = f'https://{AKAMAI_HOST}'
            endpoint = f'/siem/v1/configs/{config_id}'
    
            params = f'limit={LIMIT}'
            if offset:
                    params += f'&offset={offset}'
    
            url = f'{base_url}{endpoint}?{params}'
    
            headers = {
                    'Authorization': make_edgegrid_auth_header(url)
            }
    
            # Any transport or HTTP failure is raised so the invocation fails visibly
            # and the saved offset is left untouched for the next run.
            response = http.request('GET', url, headers=headers, timeout=120)
    
            if response.status != 200:
                    raise RuntimeError(
                            f"Akamai SIEM API returned {response.status} for config {config_id}: "
                            f"{response.data.decode('utf-8', 'replace')}"
                    )
    
            # Parse multi-JSON response (newline-delimited JSON)
            lines = response.data.decode('utf-8').strip().split('\n')
            events = []
            new_offset = offset
    
            for line in lines:
                    if not line.strip():
                            continue
                    try:
                            obj = json.loads(line)
                    except json.JSONDecodeError as e:
                            print(f"Warning: Failed to parse line: {e}")
                            continue
    
                    # Check if this is offset context (metadata object with offset)
                    if 'offset' in obj and ('total' in obj or 'responseContext' in obj):
                            new_offset = obj.get('offset')
                            continue
    
                    # This is an event
                    events.append(obj)
    
            return events, new_offset
    
    def lambda_handler(event, context):
            """Lambda handler - fetches Akamai events and writes to S3"""
            print(f"Starting Akamai SIEM fetch at {datetime.utcnow().isoformat()}Z")
    
            state = load_state()
            total_events = 0
    
            for config_id in AKAMAI_CONFIG_IDS:
                    config_id = config_id.strip()
                    if not config_id:
                            continue
    
                    print(f"Fetching events for config: {config_id}")
    
                    current_offset = state.get(config_id)
                    events, new_offset = fetch_akamai_events(config_id, current_offset)
    
                    if events:
                            print(f"Fetched {len(events)} events for config {config_id}")
    
                            # Write events to S3 as newline-delimited JSON
                            timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S')
                            s3_key = f'{S3_PREFIX}{config_id}/{timestamp}.json'
    
                            payload = '\n'.join(json.dumps(event) for event in events)
    
                            # A failed upload raises: the offset stays where it was and the
                            # next run fetches the same batch again.
                            s3_client.put_object(
                                    Bucket=S3_BUCKET,
                                    Key=s3_key,
                                    Body=payload.encode('utf-8'),
                                    ContentType='application/json'
                            )
                            print(f"Wrote {len(events)} events to s3://{S3_BUCKET}/{s3_key}")
    
                            # Update offset only after successful write
                            if new_offset:
                                    state[config_id] = new_offset
                                    total_events += len(events)
                    else:
                            print(f"No new events for config {config_id}")
    
            # Save updated state
            save_state(state)
    
            return {
                    'statusCode': 200,
                    'body': json.dumps({
                            'message': f'Successfully processed {total_events} events',
                            'configs_processed': len(AKAMAI_CONFIG_IDS)
                    })
            }
    ```
    
  6. 点击部署以保存代码。

  7. 依次前往配置 > 环境变量 > 修改。

  8. 针对以下各项,点击添加环境变量:

    键 示例值
    S3_BUCKET akamai-siem-logs
    S3_PREFIX akamai-siem/
    STATE_KEY akamai-siem-state.json
    AKAMAI_HOST akab-xxxxxxxx.luna.akamaiapis.net
    AKAMAI_CLIENT_TOKEN your-client-token
    AKAMAI_CLIENT_SECRET your-client-secret
    AKAMAI_ACCESS_TOKEN your-access-token
    AKAMAI_CONFIG_IDS 12345,67890
    LIMIT 10000
  9. 点击保存。

  10. 选择配置标签页。

  11. 在常规配置面板中,点击修改。

  12. 将超时更改为 5 分钟(300 秒),然后点击保存。

创建 EventBridge 计划

  1. 依次前往 Amazon EventBridge > 调度器 > 创建调度。
  2. 提供以下配置详细信息:
    • 日程名称:输入 AkamaiSIEMtoS3-5min。
    • 时间表模式:选择周期性时间表。
    • 时间表类型:选择基于费率的时间表。
    • 费率表达式:输入 5,然后选择分钟。
  3. 点击下一步。
  4. 提供以下配置详细信息:
    • 目标:选择 AWS Lambda 调用。
    • Lambda 函数:选择 AkamaiSIEMtoS3Function。
  5. 点击下一步。
  6. 点击下一步(跳过可选设置)。
  7. 查看并点击创建时间表。

在 Google SecOps 中配置 Feed 以注入 Akamai SIEM 连接器日志

  1. 前往 SIEM 设置 > Feed。
  2. 点击添加新 Feed。
  3. 在下一页上,点击配置单个 Feed。
  4. 为 Feed 名称输入一个唯一名称(例如 Akamai SIEM Connector)。
  5. 选择 Amazon S3 V2 作为来源类型。
  6. 选择 Akamai SIEM 连接器作为日志类型。
  7. 点击下一步,然后点击提交。
  8. 为以下输入参数指定值:

    • S3 URI:s3://akamai-siem-logs/akamai-siem/
    • 源删除选项:根据您的偏好选择删除选项。
    • 文件存在时间上限:包含在过去指定天数内修改过的文件。默认值为 180 天。
    • 访问密钥 ID:具有 S3 存储桶访问权限的用户访问密钥。
    • 私有访问密钥:具有 S3 存储桶访问权限的用户私有密钥。
    • 资源命名空间:资源命名空间。
    • 注入标签:应用于此 Feed 中事件的标签。
  9. 点击下一步,然后点击提交。

UDM 映射表

日志字段 UDM 映射 逻辑
accept_language additional.fields 已合并
additional_policy_key additional.fields 已合并
attackData_label additional.fields 已合并
attackData_ruleActions_label additional.fields 已合并
attackData_ruleData_label additional.fields 已合并
attackData_ruleSelectors_label additional.fields 已合并
attackdata_ruleMessages_label additional.fields 已合并
attackdata_ruleTags_label additional.fields 已合并
attackdata_ruleVersions_label additional.fields 已合并
botData_label additional.fields 已合并
continent_label additional.fields 已合并
dur_label additional.fields 已合并
etag_label additional.fields 已合并
format_label additional.fields 已合并
httpMessage_query_label additional.fields 已合并
httpMessage_response_headers_label additional.fields 已合并
identity_label additional.fields 已合并
identityv2_label additional.fields 已合并
mime_version_label additional.fields 已合并
regionCode_label additional.fields 已合并
req_id_label additional.fields 已合并
requestHeaders_label additional.fields 已合并
responseSegment_label additional.fields 已合并
ruleAction_list additional.fields 已合并
ruleData_list additional.fields 已合并
ruleMessages_list additional.fields 已合并
ruleSelectors_list additional.fields 已合并
ruleTags_list additional.fields 已合并
ruleVersions_list additional.fields 已合并
rules_list additional.fields 已合并
same_site additional.fields 已合并
telemetryType_label additional.fields 已合并
Date metadata.event_timestamp 解析为 EEE, dd MMM yyyy HH:mm:ss ZZZ
expires metadata.event_timestamp 解析为 EEE, dd-MMM-yyyy HH:mm:ss ZZZ
httpMessage.start metadata.event_timestamp 解析为 UNIX
event_type metadata.event_type 直接映射
attackData.configId metadata.product_log_id 直接映射
version metadata.product_version 直接映射
protocol network.application_protocol 直接映射
MS-ASProtocolVersion network.application_protocol_version 直接映射
httpMessage.method network.http.method 直接映射
httpMessage.status network.http.response_code 已重命名/已映射
User-Agent network.http.user_agent 直接映射
httpMessage.bytes network.sent_bytes 已重命名/已映射
ASP.NET_SessionId network.session_id 直接映射
httpMessage.requestId network.session_id 直接映射
httpMessage.tls network.tls.version 直接映射
attackData.clientIP principal.asset.ip 已合并
attackData.clientIP principal.ip 已合并
geo.city principal.location.city 直接映射
geo.country principal.location.country_or_region 直接映射
geo.asn principal.location.name 直接映射
additional_content_Security_Policy principal.resource.attribute.labels 已合并
DeviceType principal.resource.name 直接映射
DeviceId principal.resource.product_object_id 直接映射
cache_label security_result.about.labels 已合并
accept_encoding_label security_result.about.resource.attribute.labels 已合并
accept_label security_result.about.resource.attribute.labels 已合并
connection_label security_result.about.resource.attribute.labels 已合并
content_encoding security_result.about.resource.attribute.labels 已合并
content_length_label security_result.about.resource.attribute.labels 已合并
content_type_label security_result.about.resource.attribute.labels 已合并
req_content_length_label security_result.about.resource.attribute.labels 已合并
req_content_type_label security_result.about.resource.attribute.labels 已合并
desc security_result.description 直接映射
CP_IsMobile_label security_result.detection_fields 已合并
X-BEServer_label security_result.detection_fields 已合并
X-CalculatedBETarget_label security_result.detection_fields 已合并
X-Content-Type-Options_label security_result.detection_fields 已合并
X-DiagInfo_label security_result.detection_fields 已合并
X-FEServer_label security_result.detection_fields 已合并
X-MS-BackOffDuration_label security_result.detection_fields 已合并
apiId_label security_result.detection_fields 已合并
backend_label security_result.detection_fields 已合并
p3p_label security_result.detection_fields 已合并
type_label security_result.detection_fields 已合并
vary_label security_result.detection_fields 已合并
attackData.policyId security_result.summary 直接映射
Host target.asset.hostname 直接映射
httpMessage.host target.asset.hostname 直接映射
path target.file.full_path 直接映射
Host target.hostname 直接映射
httpMessage.host target.hostname 直接映射
httpMessage.port target.port 直接映射
Cmd target.process.command_line 直接映射
httpMessage.path target.url 直接映射
User target.user.userid 直接映射
不适用 metadata.event_type 常量:GENERIC_EVENT
不适用 metadata.product_name 常量:AKAMAI_SIEM_CONNECTOR
不适用 metadata.vendor_name 常量:AKAMAI_SIEM_CONNECTOR
不适用 principal.resource.resource_type 常量:DEVICE

更新日志

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

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