收集 Oracle 雲端基礎架構記錄

支援:

本文說明如何使用下列任一方法,將 Oracle Cloud Infrastructure (OCI) 稽核記錄擷取至 Google Security Operations:

  • 方法 1:Google Cloud Storage:OCI 函式會將記錄寫入 Cloud Storage bucket,Google SecOps 動態饋給則會從 bucket 讀取記錄。
  • 方法 2:Webhook:OCI 函式會將記錄直接傳送至 Google SecOps Webhook 動態饋給。這個方法不會使用 Cloud Storage bucket。

這兩種方法都使用 OCI Connector Hub 將稽核記錄傳送至函式,因為 Oracle Cloud Infrastructure 無法直接將記錄傳送至 Cloud Storage 或 HTTPS 端點。

Oracle Cloud Infrastructure Audit 服務會自動將所有支援的 OCI 公開應用程式設計介面 (API) 端點呼叫記錄為記錄事件。稽核服務記錄的事件包括 OCI 控制台、指令列介面 (CLI)、軟體開發套件 (SDK)、自訂用戶端或其他 OCI 服務發出的 API 呼叫。記錄中的資訊包括 API 活動發生時間、活動來源和目標、動作類型和回應類型。

事前準備

請確認您符合下列必要條件:

  • Google SecOps 執行個體
  • Oracle Cloud 控制台的特殊權限,可建立及管理:
    • Connector Hub 連接器
    • Functions 應用程式和函式
    • IAM 政策
  • 可連上網際網路的 VCN 子網路,用於 OCI 函式
  • OCI Cloud Shell 或已安裝 Fn CLI 的本機環境
  • 方法 1:已啟用 Cloud Storage API 的 Google Cloud 專案,以及建立 Cloud Storage bucket、服務帳戶和 bucket Identity and Access Management (IAM) 政策的權限
  • 方法 2:在連結至 Google SecOps 執行個體的 Google Cloud 專案中,具備建立 API 金鑰的權限

方法 1:Cloud Storage

建立 Cloud Storage bucket

  1. 前往 Google Cloud 控制台。
  2. 選取專案或建立新專案。
  3. 在導覽選單中,依序前往「Cloud Storage」>「Bucket」。
  4. 按一下「建立值區」。
  5. 提供下列設定詳細資料:

    設定 值
    為 bucket 命名 輸入全域不重複的名稱 (例如 oci-audit-logs-gcs)
    位置類型 請根據需求選擇 (單一區域、雙區域、多區域)
    位置 選取位置 (例如 us-central1)
    儲存空間級別 標準 (建議用於經常存取的記錄)
    存取控管 統一 (建議)
    保護工具 選用:啟用物件版本管理或資料保留政策
  6. 點選「建立」。

設定 Oracle 雲端基礎架構,將稽核記錄匯出至 Cloud Storage

Oracle Cloud Infrastructure 不支援內建匯出至 Cloud Storage 的功能。您會搭配使用 OCI Service Connector Hub 和 OCI 函式,將稽核記錄轉送至 Cloud Storage。

建立 Oracle Cloud Infrastructure 函式,將記錄轉送至 Cloud Storage

  1. 登入 Oracle Cloud 控制台。
  2. 開啟導覽選單,依序選取「Developer Services」>「Functions」>「Applications」。
  3. 選取要建立函式應用程式的分區。
  4. 點選「Create Application」(建立應用程式)。
  5. 提供下列設定詳細資料:
    • 「Name」(名稱):輸入 audit-logs-to-gcs-app。
    • VCN:選取虛擬雲端網路。
    • 子網路:選取具有網路連線能力的子網路。
  6. 點選「建立」。
  7. 建立應用程式後,按一下「Getting Started」,然後按照操作說明,使用 Fn CLI 設定本機開發環境。
  8. 在本機電腦上建立新的函式目錄:

    mkdir oci-audit-to-gcs
    cd oci-audit-to-gcs
    
  9. 初始化 Python 函式:

    fn init --runtime python oci-audit-to-gcs
    cd oci-audit-to-gcs
    
  10. 將 func.py 的內容替換為下列程式碼:

    import io
    import json
    import logging
    import os
    from fdk import response
    from google.cloud import storage
    from google.oauth2 import service_account
    from datetime import datetime
    
    # Configure logging
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger()
    
    # Environment variables
    GCS_BUCKET = os.environ.get('GCS_BUCKET')
    GCS_PREFIX = os.environ.get('GCS_PREFIX', 'oci-audit-logs')
    GCS_CREDENTIALS_JSON = os.environ.get('GCS_CREDENTIALS_JSON')
    
    def handler(ctx, data: io.BytesIO = None):
        """
        OCI Function to forward Audit logs to GCS.
    
        Args:
            ctx: Function context
            data: Input data containing Audit log events
        """
    
        if not all([GCS_BUCKET, GCS_CREDENTIALS_JSON]):
            logger.error('Missing required environment variables: GCS_BUCKET or GCS_CREDENTIALS_JSON')
            return response.Response(
                ctx, response_data=json.dumps({"error": "Missing configuration"}),
                headers={"Content-Type": "application/json"}
            )
    
        try:
            # Parse input data
            body = json.loads(data.getvalue())
            logger.info(f"Received event: {json.dumps(body)}")
    
            # Extract log entries
            log_entries = []
            if isinstance(body, list):
                log_entries = body
            elif isinstance(body, dict):
                # Service Connector Hub sends data in specific format
                if 'data' in body:
                    log_entries = [body['data']] if isinstance(body['data'], dict) else body['data']
                else:
                    log_entries = [body]
    
            if not log_entries:
                logger.info("No log entries to process")
                return response.Response(
                    ctx, response_data=json.dumps({"status": "no_logs"}),
                    headers={"Content-Type": "application/json"}
                )
    
            # Initialize GCS client with service account credentials
            credentials_dict = json.loads(GCS_CREDENTIALS_JSON)
            credentials = service_account.Credentials.from_service_account_info(credentials_dict)
            storage_client = storage.Client(credentials=credentials, project=credentials_dict.get('project_id'))
            bucket = storage_client.bucket(GCS_BUCKET)
    
            # Write logs to GCS as NDJSON
            timestamp = datetime.utcnow().strftime('%Y%m%d_%H%M%S_%f')
            object_key = f"{GCS_PREFIX}/logs_{timestamp}.ndjson"
            blob = bucket.blob(object_key)
    
            ndjson = '\n'.join([json.dumps(entry, ensure_ascii=False) for entry in log_entries]) + '\n'
            blob.upload_from_string(ndjson, content_type='application/x-ndjson')
    
            logger.info(f"Wrote {len(log_entries)} records to gs://{GCS_BUCKET}/{object_key}")
    
            return response.Response(
                ctx, response_data=json.dumps({"status": "success", "records": len(log_entries)}),
                headers={"Content-Type": "application/json"}
            )
    
        except Exception as e:
            logger.error(f'Error processing logs: {str(e)}')
            return response.Response(
                ctx, response_data=json.dumps({"error": str(e)}),
                headers={"Content-Type": "application/json"},
                status_code=500
            )
    

  11. 使用下列依附元件更新 requirements.txt:

    fdk>=0.1.0
    google-cloud-storage>=2.0.0
    google-auth>=2.0.0
    
  12. 將函式部署至 OCI:

    fn -v deploy --app audit-logs-to-gcs-app
    
  13. 部署完成後,請記下函式 OCID。設定 Service Connector Hub 時會用到。

設定函式環境變數

  1. 在 Oracle Cloud 控制台中,依序前往「Developer Services」>「Functions」>「Applications」。
  2. 按一下應用程式 (audit-logs-to-gcs-app)。
  3. 按一下函式名稱 (oci-audit-to-gcs)。
  4. 按一下「設定」。
  5. 新增下列設定變數:

    鍵 值
    GCS_BUCKET Cloud Storage bucket 名稱 (例如 oci-audit-logs-gcs)
    GCS_PREFIX 記錄檔的前置字串 (例如 oci-audit-logs)
    GCS_CREDENTIALS_JSON Google Cloud 服務帳戶金鑰的 JSON 字串
  6. 按一下 [儲存變更]。

為 Oracle Cloud Infrastructure 函式建立 Google Cloud 服務帳戶

OCI 函式需要 Google Cloud 服務帳戶,才能寫入 Cloud Storage bucket。

  1. 在 Google Cloud 控制台中,依序前往「IAM & Admin」(IAM 與管理) >「Service Accounts」(服務帳戶)。
  2. 按一下「Create Service Account」(建立服務帳戶)。
  3. 提供下列設定詳細資料:
    • 「Service account name」(服務帳戶名稱):輸入 oci-function-gcs-writer。
    • 服務帳戶說明:輸入 Service account for OCI Function to write Audit logs to GCS。
  4. 按一下「建立並繼續」。
  5. 在「將專案存取權授予這個服務帳戶」部分,新增下列角色:
    1. 按一下「請選擇角色」。
    2. 搜尋並選取「Storage 物件管理員」。
  6. 按一下「繼續」。
  7. 按一下「完成」。
  8. 按一下新建立的服務帳戶電子郵件地址。
  9. 前往「金鑰」分頁。
  10. 依序點選「新增金鑰」>「建立新的金鑰」。
  11. 選取「JSON」做為金鑰類型。
  12. 點選「建立」。JSON 金鑰檔案會下載到您的電腦。
  13. 開啟 JSON 金鑰檔案,並複製所有內容。
  14. 返回 Oracle Cloud 控制台函式設定。
  15. 將 JSON 內容貼到 GCS_CREDENTIALS_JSON 設定變數中。

授予 Cloud Storage bucket 的 IAM 權限

按照下列步驟,將 Cloud Storage bucket 的寫入權限授予服務帳戶:

  1. 依序前往「Cloud Storage」 > 「Bucket」。
  2. 按一下 bucket 名稱 (oci-audit-logs-gcs)。
  3. 前往「權限」分頁。
  4. 按一下「授予存取權」。
  5. 提供下列設定詳細資料:
    • 新增主體:輸入服務帳戶電子郵件地址 (oci-function-gcs-writer@PROJECT_ID.iam.gserviceaccount.com)。
    • 指派角色:選取「Storage 物件管理員」。
  6. 按一下 [儲存]。

建立 OCI Service Connector Hub 連接器

  1. 登入 Oracle Cloud 控制台。
  2. 開啟導覽選單,選取「Analytics & AI」。在「Messaging」下方,選取「Connector Hub」。
  3. 選取要建立服務連接器的區間。
  4. 按一下「Create connector」(建立連接器)。
  5. 提供下列設定詳細資料:

    • 連接器資訊:

      設定 值
      連接器名稱 輸入audit-logs-to-gcs-connector人數
      說明 輸入Forward OCI Audit logs to Cloud Storage人數
      資源區間 選取隔層
    • 設定來源:

      設定 值
      來源 選取「Logging」
      Compartment 選取含有稽核記錄的分區
      記錄群組 選取「_Audit」 (稽核記錄的預設記錄群組)
  6. 按一下「+ Another Log」。

  7. 選取區塊的稽核記錄 (例如 _Audit_Include_Subcompartment)。

    • 設定目標:

      設定 值
      目標 選取「Functions」
      函式區塊 選取含有函式的區間
      函式應用 選取「audit-logs-to-gcs-app」
      功能 選取「oci-audit-to-gcs」
  8. 前往「Configure task (optional)」,並保留預設設定。

  9. 如果系統提示您建立政策 (必須建立政策,才能建立或更新服務連接器),請選取「Create」(建立)。

  10. 點選「建立」。

為 Service Connector Hub 建立 IAM 政策

Service Connector Hub 必須具備叫用函式的權限。

  1. 在 Oracle Cloud 控制台中,依序前往「Identity & Security」 >「Policies」。
  2. 選取您建立服務連接器中樞連接器的區間。
  3. 點按「建立政策」。
  4. 提供下列設定詳細資料:
    • 「Name」(名稱):輸入 service-connector-functions-policy。
    • 說明:輸入 Allow Service Connector Hub to invoke Functions。
    • Compartment:選取區間。
  5. 在「Policy Builder」部分,切換「Show manual editor」。
  6. 輸入下列政策陳述式:

    Allow any-user to use fn-function in compartment <compartment-name> where all {request.principal.type='serviceconnector'}
    Allow any-user to use fn-invocation in compartment <compartment-name> where all {request.principal.type='serviceconnector'}
    
  7. 點選「建立」。

測試整合

  1. 登入 Oracle Cloud 控制台。
  2. 執行會產生稽核記錄的操作,例如建立或修改資源。
  3. 等待 2 到 5 分鐘,讓系統處理記錄。
  4. 在 Google Cloud 控制台中,依序前往「Cloud Storage」>「Bucket」。
  5. 按一下 bucket 名稱 (oci-audit-logs-gcs)。
  6. 前往前置字元資料夾 (oci-audit-logs/)。
  7. 確認 bucket 中出現新的 .ndjson 檔案。

擷取 Google SecOps 服務帳戶

Google SecOps 會使用專屬服務帳戶,從 Cloud Storage bucket 讀取資料。您必須授予這個服務帳戶值區存取權。

取得服務帳戶電子郵件地址

  1. 依序前往 「SIEM 設定」 > 「動態饋給」。
  2. 按一下「新增動態饋給」。
  3. 按一下「設定單一動態饋給」。
  4. 在「Feed name」(動態饋給名稱) 欄位中,輸入動態饋給名稱 (例如 Oracle Cloud Audit Logs)。
  5. 選取「Google Cloud Storage V2」做為「來源類型」。
  6. 選取「Oracle Cloud Infrastructure Audit Logs」做為「記錄類型」。
  7. 按一下「取得服務帳戶」。系統會顯示專屬服務帳戶電子郵件地址,例如:

    chronicle-12345678@chronicle-gcp-prod.iam.gserviceaccount.com
    
  8. 複製這個電子郵件地址,下一個步驟將會用到。

將 IAM 權限授予 Google SecOps 服務帳戶

Google SecOps 服務帳戶需要 Cloud Storage bucket 的兩項角色:Storage 物件檢視者 (可讀取記錄物件),以及 bucket 層級角色 (可讀取 bucket 中繼資料)

  1. 依序前往「Cloud Storage」 > 「Bucket」。
  2. 按一下 bucket 名稱 (oci-audit-logs-gcs)。
  3. 前往「權限」分頁。
  4. 按一下「授予存取權」。
  5. 提供下列設定詳細資料:
    • 新增主體:貼上 Google SecOps 服務帳戶電子郵件地址。
    • 「指派角色」:選取下列兩項:
      • Storage 物件檢視者:讀取記錄物件。
      • Storage 舊版 bucket 讀取者:讀取 bucket 中繼資料。如果您選取「刪除已轉移的檔案和空白目錄」刪除選項,請改選「Storage 舊版值區寫入者」,這項角色也會授予刪除權限。
  6. 按一下 [儲存]。

在 Google SecOps 設定動態饋給,擷取 Oracle Cloud Infrastructure 稽核記錄

  1. 依序前往 「SIEM 設定」 > 「動態饋給」。
  2. 按一下「新增動態饋給」。
  3. 按一下「設定單一動態饋給」。
  4. 在「Feed name」(動態饋給名稱) 欄位中,輸入動態饋給名稱 (例如 Oracle Cloud Audit Logs)。
  5. 選取「Google Cloud Storage V2」做為「來源類型」。
  6. 選取「Oracle Cloud Infrastructure Audit Logs」做為「記錄類型」。
  7. 點選「下一步」。
  8. 指定下列輸入參數的值:

    • Storage bucket URL:輸入 Cloud Storage bucket URI,並加上前置字元路徑:

      gs://oci-audit-logs-gcs/oci-audit-logs/
      
    • 更改項目:

      • oci-audit-logs-gcs:Cloud Storage bucket 名稱。
      • oci-audit-logs:儲存記錄的選用前置字元或路徑 (留空則為根目錄)。

      範例:

      • 根值區:gs://company-logs/
      • 前置字元:gs://company-logs/oci-audit-logs/
      • 子資料夾:gs://company-logs/oracle/audit/
    • 來源刪除選項:根據偏好選取刪除選項:

      • 「Never delete files」(一律不刪除檔案):轉移後一律不刪除任何檔案 (建議用於測試)。
      • 刪除已轉移的檔案和空目錄:成功轉移後,刪除檔案和空目錄。
    • 檔案存在時間上限 (天數):包含過去幾天內修改過的檔案。預設為 180 天。

    • Asset namespace:資產命名空間。

    • 擷取標籤:要套用至這個動態饋給事件的標籤。

  9. 點選「下一步」。

  10. 在「Finalize」(完成) 畫面中檢查新的新聞動態消息設定,然後按一下「Submit」(提交)。

方法 2:Webhook

採用這個方法時,Connector Hub 會將稽核記錄項目批次傳送至 OCI 函式,再由函式透過 HTTPS 傳送至 Google SecOps Webhook 動態饋給。不涉及 Cloud Storage bucket。

在 Google SecOps 建立 Webhook 動態饋給

建立動態饋給

  1. 依序前往 「SIEM 設定」 > 「動態饋給」。
  2. 按一下「新增動態饋給」。
  3. 在下一頁,點按「設定單一動態饋給」。
  4. 在「Feed name」(動態饋給名稱) 欄位中,輸入動態饋給名稱 (例如 OCI Audit Logs Webhook)。
  5. 「Source type」(來源類型) 請選取「Webhook」。
  6. 選取「Oracle Cloud Infrastructure Audit Logs」做為「記錄類型」。
  7. 點選「下一步」。
  8. 指定下列輸入參數的值:
    • 「Split delimiter」(分割分隔符號):輸入 \n。函式會將每個記錄項目傳送至其專屬行。
    • Asset namespace:資產命名空間。
    • 擷取標籤:要套用至這個動態饋給事件的標籤。
  9. 點選「下一步」。
  10. 在「Finalize」(完成) 畫面中檢查新的新聞動態消息設定,然後按一下「Submit」(提交)。

產生並儲存密鑰

  1. 在動態饋給詳細資料頁面,按一下「產生 Secret Key」。
  2. 複製並妥善儲存密鑰。

取得動態饋給端點網址

  1. 前往動態饋給的「詳細資料」分頁。
  2. 在「Endpoint Information」部分,複製「Feed endpoint URL」。網址格式如下:

    https://<REGION>-chronicle.googleapis.com/v1alpha/projects/<PROJECT_NUMBER>/locations/<REGION>/instances/<INSTANCE_ID>/feeds/<FEED_ID>:importPushLogs
    
  3. 按一下「完成」。

建立 Google Cloud API 金鑰

  1. 前往Google Cloud 控制台的「Credentials」(憑證) 頁面。
  2. 選取連結至 Google SecOps 執行個體的專案。
  3. 依序點選「建立憑證」 > 「API 金鑰」。
  4. 按一下「編輯 API 金鑰」,即可限制金鑰。
  5. 在「Name」(名稱) 欄位輸入描述性名稱,例如 oci-audit-webhook。
  6. 在「API 限制」下方,選取「限制金鑰」,然後在「選取 API」清單中選取「Chronicle API」。
  7. 按一下 [儲存]。
  8. 複製 API 金鑰值並妥善保存。

建立 OCI 函式,將記錄轉送至 Webhook

建立應用程式

  1. 登入 Oracle Cloud 控制台。
  2. 開啟導覽選單,依序選取「Developer Services」>「Functions」>「Applications」。
  3. 選取要建立函式應用程式的分區。
  4. 點選「Create Application」(建立應用程式)。
  5. 提供下列設定詳細資料:
    • 「Name」(名稱):輸入 audit-logs-to-secops-app。
    • VCN:選取虛擬雲端網路。
    • 子網路:選取具有網路連線能力的子網路,例如有網際網路閘道的公開子網路,或有網路位址轉譯閘道的私人子網路。函式必須透過 HTTPS (通訊埠 443) 連線至 Google SecOps 端點。
  6. 點選「建立」。

建立及部署函式

  1. 建立應用程式後,按一下「Getting Started」(開始使用),然後按照操作說明,使用 Fn CLI 設定 OCI Cloud Shell 或本機開發環境。
  2. 初始化 Python 函式:

    fn init --runtime python oci-audit-to-secops
    cd oci-audit-to-secops
    
  3. 將 func.py 的內容替換為下列程式碼:

    import io
    import json
    import logging
    import os
    import random
    import time
    from email.utils import parsedate_to_datetime
    
    import requests
    from fdk import response
    
    logging.basicConfig(level=logging.INFO)
    logger = logging.getLogger()
    
    # The webhook accepts at most 4 MB per request and 1 MB per log line.
    # Connector Hub delivers batches of up to 6 MB, so a batch is sent as several
    # requests, each made of whole log entries.
    MAX_REQUEST_BYTES = 3 * 1024 * 1024
    MAX_LINE_BYTES = 1024 * 1024
    
    MAX_ATTEMPTS = 5
    RETRYABLE_STATUS = {429, 500, 502, 503, 504}
    # OCI stops the function at its configured timeout (300 seconds). Stop retrying
    # early enough to fail cleanly so that Connector Hub retries the whole batch.
    DEADLINE_SECONDS = 240
    
    def to_lines(body):
        """Serialize each log entry from the Connector Hub batch as one JSON line."""
        entries = body if isinstance(body, list) else [body]
        lines = []
        for entry in entries:
            line = json.dumps(entry, ensure_ascii=False, separators=(',', ':')).encode('utf-8')
            if len(line) > MAX_LINE_BYTES:
                entry_id = entry.get('id') if isinstance(entry, dict) else None
                logger.error(f'Skipping log entry {entry_id}: {len(line)} bytes exceeds the 1 MB line limit')
                continue
            lines.append(line)
        return lines
    
    def to_requests(lines):
        """Group whole lines into newline-delimited request bodies below MAX_REQUEST_BYTES."""
        chunk, size = [], 0
        for line in lines:
            if chunk and size + len(line) + 1 > MAX_REQUEST_BYTES:
                yield b'\n'.join(chunk), len(chunk)
                chunk, size = [], 0
            chunk.append(line)
            size += len(line) + 1
        if chunk:
            yield b'\n'.join(chunk), len(chunk)
    
    def retry_after_seconds(value):
        """Parse a Retry-After header given either in seconds or as an HTTP date."""
        if not value:
            return None
        try:
            return max(0.0, float(value))
        except ValueError:
            pass
        try:
            return max(0.0, parsedate_to_datetime(value).timestamp() - time.time())
        except (TypeError, ValueError, IndexError, OverflowError):
            return None
    
    def post(session, url, headers, payload, deadline):
        """Send one request, retrying throttling and server errors until the deadline."""
        status, detail = None, ''
        for attempt in range(1, MAX_ATTEMPTS + 1):
            remaining = deadline - time.monotonic()
            if remaining < 5:
                break
            wait = None
            try:
                resp = session.post(url, data=payload, headers=headers, timeout=(10, min(60, remaining)))
            except requests.RequestException as e:
                status, detail = None, str(e)
            else:
                if 200 <= resp.status_code < 300:
                    return
                status, detail = resp.status_code, resp.text[:500]
                if status not in RETRYABLE_STATUS:
                    raise RuntimeError(f'Webhook rejected the request with HTTP {status}: {detail}')
                wait = retry_after_seconds(resp.headers.get('Retry-After'))
            if wait is None:
                wait = min(30, 2 ** attempt) + random.uniform(0, 1)
            if attempt == MAX_ATTEMPTS or time.monotonic() + wait >= deadline:
                break
            logger.warning(f'Attempt {attempt} failed (HTTP {status}): {detail}. Retrying in {wait:.1f} seconds')
            time.sleep(wait)
        raise RuntimeError(f'Webhook request failed (HTTP {status}): {detail}')
    
    def handler(ctx, data: io.BytesIO = None):
        """Forward a Connector Hub batch of OCI Audit log entries to the webhook feed.
    
        Any exception fails the invocation, and Connector Hub then retries the batch.
        """
        deadline = time.monotonic() + DEADLINE_SECONDS
    
        url = os.environ.get('SECOPS_WEBHOOK_URL')
        api_key = os.environ.get('SECOPS_API_KEY')
        secret_key = os.environ.get('SECOPS_SECRET_KEY')
        if not all([url, api_key, secret_key]):
            raise RuntimeError('Missing configuration: SECOPS_WEBHOOK_URL, SECOPS_API_KEY and SECOPS_SECRET_KEY are required')
    
        raw = data.getvalue() if data else b''
        if not raw.strip():
            logger.info('Received an empty batch')
            return response.Response(
                ctx, response_data=json.dumps({'records': 0}),
                headers={'Content-Type': 'application/json'}
            )
    
        lines = to_lines(json.loads(raw))
        headers = {
            'Content-Type': 'application/json',
            'X-goog-api-key': api_key,
            'X-Webhook-Access-Key': secret_key,
        }
    
        sent = 0
        with requests.Session() as session:
            for payload, count in to_requests(lines):
                post(session, url, headers, payload, deadline)
                sent += count
    
        logger.info(f'Forwarded {sent} log entries')
        return response.Response(
            ctx, response_data=json.dumps({'records': sent}),
            headers={'Content-Type': 'application/json'}
        )
    

  4. 將 requirements.txt 的內容替換為下列依附元件:

    fdk>=0.1.0
    requests>=2.32.0
    
  5. 開啟 func.yaml 並新增下列程式碼,將函式逾時時間從預設的 30 秒提高至上限 300 秒:

    timeout: 300
    
  6. 將函式部署至 OCI:

    fn -v deploy --app audit-logs-to-secops-app
    

設定函式環境變數

  1. 在 Oracle Cloud 控制台中,依序前往「Developer Services」 > 「Functions」 > 「Applications」。
  2. 按一下應用程式 (audit-logs-to-secops-app)。
  3. 按一下函式名稱 (oci-audit-to-secops)。
  4. 按一下「設定」。
  5. 新增下列設定變數:

    鍵 值
    SECOPS_WEBHOOK_URL 動態饋給端點網址
    SECOPS_API_KEY Google Cloud API 金鑰
    SECOPS_SECRET_KEY 動態饋給密鑰
  6. 按一下 [儲存變更]。

啟用函式記錄

  1. 在「Applications」(應用程式) 清單頁面中,選取 audit-logs-to-secops-app。
  2. 選取「監控」分頁標籤,然後前往「記錄」部分。
  3. 在「函式叫用記錄」類別的「動作」選單 (三點圖示) 中,選取「啟用記錄」。
  4. 保留預設的區間、記錄群組和記錄名稱,或視需要變更。
  5. 選取「啟用記錄」。

建立 OCI Connector Hub 連接器

  1. 開啟導覽選單,選取「Analytics & AI」。在「Messaging」下方,選取「Connector Hub」。
  2. 選取要建立連接器的區間。
  3. 按一下「Create connector」(建立連接器)。
  4. 提供下列設定詳細資料:

    • 連接器資訊:

      設定 值
      連接器名稱 輸入audit-logs-to-secops-connector人數
      說明 輸入Forward OCI Audit logs to a webhook feed人數
      資源區間 選取隔層
    • 設定來源:

      設定 值
      來源 選取「Logging」
      Compartment 選取含有稽核記錄的分區
      記錄群組 選取「_Audit」 (稽核記錄的預設記錄群組)
  5. 按一下「+ Another Log」。

  6. 選取區塊的稽核記錄 (例如 _Audit_Include_Subcompartment)。

    • 設定目標:

      設定 值
      目標 選取「Functions」
      函式區塊 選取含有函式的區間
      函式應用 選取「audit-logs-to-secops-app」
      功能 選取「oci-audit-to-secops」
  7. 前往「Configure task (optional)」,並保留預設設定。

  8. 如果系統提示您建立政策 (必須建立政策,才能建立或更新服務連接器),請選取「Create」(建立)。

  9. 點選「建立」。

建立 Connector Hub 的 IAM 政策

Connector Hub 必須具備叫用函式的權限。

  1. 在 Oracle Cloud 控制台中,依序前往「Identity & Security」 >「Policies」。
  2. 選取您建立連接器的區間。
  3. 點按「建立政策」。
  4. 提供下列設定詳細資料:
    • 「Name」(名稱):輸入 connector-hub-secops-function-policy。
    • 說明:輸入 Allow Connector Hub to invoke the webhook forwarding function。
    • Compartment:選取區間。
  5. 在「Policy Builder」部分,切換「Show manual editor」。
  6. 輸入下列政策陳述式:

    Allow any-user to use fn-function in compartment <compartment-name> where all {request.principal.type='serviceconnector'}
    Allow any-user to use fn-invocation in compartment <compartment-name> where all {request.principal.type='serviceconnector'}
    
  7. 點選「建立」。

函式如何傳送記錄

  • 批次大小:Connector Hub 傳送至函式的批次大小上限為 6 MB,Webhook 接受的每項要求大小上限為 4 MB。函式會將每個批次分割成最多 3 MB 的要求。只會在記錄項目之間分割,不會在項目內分割,且每個項目都會以單一事件的形式傳送至 Google SecOps。
  • 重試:函式會以指數輪詢方式重試 HTTP 429 和 5xx 回應,並遵循 Retry-After 標頭。如果發生任何其他錯誤,或要求在嘗試五次後仍失敗,叫用就會失敗。Connector Hub 接著會重試整個批次,並在成功後才移動後續批次。
  • 保留:Connector Hub 的 Logging 來源會保留未傳送的資料 24 小時。如果傳送失敗的時間過長,最舊的未傳送記錄會遺失,因此請盡快修正函式記錄中回報的錯誤。
  • 重複:Connector Hub 至少會傳送每批資料一次。如果批次資料在部分傳送後重試,Google SecOps 可能會收到重複的項目。
  • 項目過大:記錄項目超過 1 MB,超出 Webhook 行數限制。函式會略過該項目,並將其 ID 寫入函式記錄。

驗證整合

如要驗證整合,請按照下列步驟操作:

  1. 在 Oracle Cloud 控制台中執行一些動作,產生稽核記錄 (例如建立或修改資源)。
  2. 等待 2 到 5 分鐘,讓 Connector Hub 傳送記錄。
  3. 開啟您啟用的函式叫用記錄,確認其中包含 Forwarded N log entries 訊息。錯誤訊息包含 Google SecOps 傳回的 HTTP 狀態。
  4. 在 Google SecOps 中,對 OCI_AUDIT 記錄類型執行原始記錄搜尋,確認稽核事件已送達。

UDM 對應表

記錄欄位 UDM 對應 邏輯
accept_encoding_field additional.fields 已合併
accept_field additional.fields 已合併
additional_app additional.fields 已合併
additional_content_type additional.fields 已合併
additional_sort_by additional.fields 已合併
additionaldetails_data additional.fields 已合併
allow_methods_field additional.fields 已合併
auth_type_label additional.fields 已合併
authorization_field additional.fields 已合併
backend_connect_time_field additional.fields 已合併
backend_processing_time_field additional.fields 已合併
caller_id_label additional.fields 已合併
caller_name_label additional.fields 已合併
cloud_event_versiom_field additional.fields 已合併
compartment_id_field additional.fields 已合併
compartment_name_field additional.fields 已合併
connection_field additional.fields 已合併
content_length_field additional.fields 已合併
content_type_field additional.fields 已合併
event_grouping_id_field additional.fields 已合併
event_name_label additional.fields 已合併
event_resource_label additional.fields 已合併
event_source_label additional.fields 已合併
event_type_label additional.fields 已合併
event_type_versiom_field additional.fields 已合併
has_compariment_id additional.fields 對應:true → additional_app
has_sort_by additional.fields 對應:true → additional_sort_by
listener_name_field additional.fields 已合併
log_group_id_field additional.fields 已合併
log_id_field additional.fields 已合併
message additional.fields 對應值 (共 41 個,例如 \/ → tenant_id_label、\/ → auth_type_label、\/ → `ca...
opc_request_id_field additional.fields 已合併
origin_field additional.fields 已合併
param0_field additional.fields 已合併
param1_field additional.fields 已合併
referer_field additional.fields 已合併
request_processing_time_field additional.fields 已合併
resource_id_field additional.fields 已合併
routing_rules_engine_errors_field additional.fields 已合併
routing_rules_matched_rule_field additional.fields 已合併
routing_rules_rule_hits_field additional.fields 已合併
routing_rules_rule_misses_field additional.fields 已合併
source_field additional.fields 已合併
tenant_id_label additional.fields 已合併
column3 extensions.auth.type 對應:login successful → AUTHTYPE_UNSPECIFIED
message extensions.auth.type 對應:\/ → AUTHTYPE_UNSPECIFIED
column3 metadata.description 直接對應
data.message metadata.description 直接對應
column1 metadata.event_timestamp 剖析為 yyyy-MM-dd HH:mm:ss
data_event_time metadata.event_timestamp 剖析為 yyyy-MM-ddTHH:mm:ss.SSSSSSZ
time metadata.event_timestamp 剖析為 ISO8601
metadata_event_type metadata.event_type 直接對應
type metadata.product_event_type 直接對應
data_event_id metadata.product_log_id 直接對應
id metadata.product_log_id 直接對應
message metadata.product_name 對應:\/ → OCI_AUDIT
specversion metadata.product_version 直接對應
message metadata.vendor_name 對應:\/ → Oracle
message network.application_protocol 對應:\/ → HTTPS,\/ → HTTP
protocol network.application_protocol 對應:(?i)HTTPS → HTTPS,(?i)HTTP → HTTP
version network.application_protocol_version 直接對應
data_request_action network.http.method 直接對應
method network.http.method 直接對應
data_identity_userAgent network.http.parsed_user_agent 已重新命名/對應
data_request_agent network.http.parsed_user_agent 已重新命名/對應
url network.http.referral_url 直接對應
data.backendStatusCode network.http.response_code 直接對應
data_response_status network.http.response_code 已重新命名/對應
data.userAgent network.http.user_agent 直接對應
data_identity_userAgent network.http.user_agent 直接對應
data_request_agent network.http.user_agent 直接對應
data.receivedBytes network.received_bytes 直接對應
message network.received_bytes 對應:\/ → uinteger
data.sentBytes network.sent_bytes 直接對應
message network.sent_bytes 對應:\/ → uinteger
data_identity_consoleSessionId network.session_id 已重新命名/對應
data.sslCipher network.tls.cipher 直接對應
data.sslProtocol network.tls.version 直接對應
data.host principal.asset.hostname 直接對應
hostname principal.asset.hostname 直接對應
column6 principal.asset.ip 已合併
data.forwardedForAddr principal.asset.ip 已合併
data_identity_ipAddress principal.asset.ip 對應:^(?:[0-9]{1,3}[.]){3}[0-9]{1,3}$ → data_identity_ipAddress
data_request_origin principal.asset.ip 已合併
ip1 principal.asset.ip 已合併
ip2 principal.asset.ip 已合併
message principal.asset.ip 對應值 (共 7 個,例如 \/ → column6、\/ → data_request_origin、\/ → ip2)
src_ip principal.asset.ip 已合併
data.host principal.hostname 直接對應
hostname principal.hostname 直接對應
column6 principal.ip 已合併
data.forwardedForAddr principal.ip 已合併
data_identity_ipAddress principal.ip 對應:^(?:[0-9]{1,3}[.]){3}[0-9]{1,3}$ → data_identity_ipAddress
data_request_origin principal.ip 已合併
ip1 principal.ip 已合併
ip2 principal.ip 已合併
message principal.ip 對應值 (共 7 個,例如 \/ → column6、\/ → data_request_origin、\/ → ip2)
src_ip principal.ip 已合併
data_request_headers_sec-ch-ua-platform_0 principal.platform 對應:(?i)Linux → LINUX、(?i)windows → WINDOWS、(?i)mac/ios → MAC
message principal.platform 對應:\/ → LINUX、\/ → WINDOWS、\/ → MAC
originalConnection.sourcePort principal.port 直接對應
src_port principal.port 直接對應
data_request_headers_oci-original-url_0 principal.url 直接對應
credentials_label principal.user.attribute.labels 已合併
message principal.user.attribute.labels 對應:\/ → credentials_label
data.identity.principalName principal.user.user_display_name 直接對應
data.username principal.user.user_display_name 直接對應
data.identity.principalId principal.user.userid 直接對應
data.principalId principal.user.userid 直接對應
column3 security_result 對應:login successful → security_result
message security_result 對應:\/ → security_result
allow_action security_result.action 已合併
column3 security_result.action 對應:login successful → allow_action
message security_result.action 對應:\/ → allow_action
credential_type_field security_result.detection_fields 已合併
message security_result.detection_fields 對應:\/ → credential_type_field
data.response.message security_result.summary 直接對應
message target.asset.ip 對應:\/ → originalConnection.destinationIp,\/ → tar_ip
originalConnection.destinationIp target.asset.ip 已合併
tar_ip target.asset.ip 已合併
message target.ip 對應:\/ → originalConnection.destinationIp,\/ → tar_ip
originalConnection.destinationIp target.ip 已合併
tar_ip target.ip 已合併
originalConnection.destinationPort target.port 直接對應
tar_port target.port 直接對應
message target.resource.attribute.labels 對應:\/ → namespace_label
namespace_label target.resource.attribute.labels 已合併
type target.resource.attribute.labels 對應:listretentionrules → namespace_label
data.resourceId target.resource.name 直接對應
data.resourceName target.resource.name 直接對應
bucketId target.resource.product_object_id 直接對應
data.resourceId target.resource.product_object_id 直接對應
data_request_id target.resource.product_object_id 直接對應
message target.resource.resource_type 對應:\/ → STORAGE_BUCKET
type target.resource.resource_type 對應:listretentionrules → STORAGE_BUCKET
data_request_path target.url 直接對應
column2 target.user.email_addresses 已合併
message target.user.email_addresses 對應:\/ → column2
不適用 extensions.auth.type 常數:AUTHTYPE_UNSPECIFIED
不適用 metadata.product_name 常數:OCI_AUDIT
不適用 metadata.vendor_name 常數:Oracle
不適用 network.application_protocol 常數:HTTPS
不適用 principal.platform 常數:LINUX
不適用 target.resource.resource_type 常數:STORAGE_BUCKET

變更記錄

查看這個剖析器的變更記錄

還有其他問題嗎?向社群成員和 Google SecOps 專家尋求解答。