附註:

啟用自動輪換 Oracle Cloud Infrastructure Identity and Access Management 證明資料

簡介

此教學課程可協助自動輪換 API 金鑰、認證權杖、客戶秘密金鑰以及 OCI 主控台登入密碼的 Oracle Cloud Infrastructure Identity and Access Management (OCI IAM) 證明資料。此服務以 Oracle Cloud Guard 問題修正和 OCI 通知服務為基礎。它也滿足預設回應器規則的間隙,以旋轉 OCI IAM 證明資料。

Oracle Cloud Guard 會根據原則中定義的規則偵測問題。滿足規則之後,就會產生問題。

這是一個安全最佳做法,由 Center for Internet Security Oracle Cloud Infrastructure Foundations (CIS OCI Foundations) Benchmark 建議,每 90 天輪換憑證。

注意: 此解決方案已經針對預設識別網域進行測試。

目標

必要條件

OCI IAM 管理員應建立:

作業 1:建立及部署 OCI 函數

  1. 登入 OCI 主控台,然後按一下頂端導覽中的雲端 Shell

    注意:您也可以使用自己的 IDE 工具來建立及部署函數。

  2. 使用 Cloud Shell 的 Fn 專案 CLI 建立函數。如需詳細資訊,請參閱建立、部署及呼叫 Helloworld 函數

    fn init --runtime python <function-name>
    
    Example: fn init --runtime python rotatecred-func
    
  3. 將目錄變更為新建立的目錄。

  4. 使用下列命令建立應用程式以部署函數。

    # Specify the OCID of subnet
    fn create app <app-name> --annotation oracle.com/oci/subnetIds='["<subnet OCID>"]'
    
    Example:  fn create app rotatecredapp --annotation oracle.com/oci/subnetIds='["ocid1.subnet.oc1.ap-mumbai-1.aaaaaaaabitp32dkyox37qa3tk3evl2nxivwb....."]'
    
  5. 藉由覆寫現有內容,複製並貼上 func.py 中的下列指令碼。

    # python script for auto rotation of OCI IAM credentials
    import io
    import json
    import logging
    import oci
    import base64
    from fdk import response
    from Cryptodome.PublicKey import RSA
    
    # Get Resource Principal Credentials
    signer = oci.auth.signers.get_resource_principals_signer()
    
    # Initialize client
    identity_client = oci.identity.IdentityClient(config={}, signer=signer)
    onsclient = oci.ons.NotificationDataPlaneClient(config={}, signer=signer)
    vault_client = oci.vault.VaultsClient(config={}, signer=signer)
    
    # Get tenancy id and name
    tenancy_data = identity_client.get_tenancy(tenancy_id=signer.tenancy_id).data
    t_name = str(tenancy_data.name)
    t_id = signer.tenancy_id
    
    # Get secret OCID from comments
    def get_secret_ocids(comments_items,find_name):
       secret_ocid = ""
       for comment in comments_items:
          if comment.split(":")[0] == find_name:
                secret_ocid = comment.split(":")[1]
       return secret_ocid
    
    # Function to store secret in OCI vault
    def update_secret(vault_client,secret_id,new_value):
       # Base64 encode
       new_token_ascii = new_value.encode("ascii")
       base64_bytes = base64.b64encode(new_token_ascii)
       base64_string = base64_bytes.decode("ascii")
    
       # Create new version of secret
       vault_client.update_secret(secret_id=secret_id,update_secret_details=oci.vault.models.UpdateSecretDetails(secret_content=oci.vault.models.Base64SecretContentDetails(content_type="BASE64", content=base64_string)))
    
    def handler(ctx, data: io.BytesIO=None):
       try:
          cfg = ctx.Config()
          ons_topic = cfg["ons_topic"]
          body = json.loads(data.getvalue())
    
          # Get common parameters values
          e_time = str(body["eventTime"]).lstrip()
          problem_name = str(body["data"]["additionalDetails"]["problemName"]).lstrip()
          status = "NOT RESOLVED"
          resource_name = str(body["data"]["resourceName"]).lstrip()
          user_ocid = str(body["data"]["additionalDetails"]["problemAdditionalDetails"]["User OCID"]).lstrip()
          target_resource_name = str(body["data"]["additionalDetails"]["resourceName"]).lstrip()
          target_resource_id = str(body["data"]["additionalDetails"]["resourceId"]).lstrip()
          risk_level = str(body["data"]["additionalDetails"]["riskLevel"]).lstrip()
          comments = str(body["data"]["additionalDetails"]["problemAdditionalDetails"]["comments"]).lstrip()
          comments_items = comments.split(",")
          additional_details = "\r\r\nAction : Closure comments was not in required format hence, no action by automation."
    
          try:
                # Check Problem Type
                if problem_name == "PASSWORD_TOO_OLD":
                   identity_client.create_or_reset_ui_password(user_id=user_ocid)
                   additional_details = "\r\r\nAction : Your password has been reset by the System Administrator as per password policy rotation. Please set new password by clicking on forgot password from OCI console. "
                   status = "RESOLVED"
    
                elif problem_name == "AUTH_TOKEN_TOO_OLD":
                   auth_secret_ocid = get_secret_ocids(comments_items,"auth_secret_ocid")
                   if auth_secret_ocid != "":
                      # Delete existing auth token
                      identity_client.delete_auth_token(user_id=user_ocid, auth_token_id=target_resource_id)
                      # Create new auth token
                      create_auth_token_response = identity_client.create_auth_token(
                      create_auth_token_details=oci.identity.models.CreateAuthTokenDetails(description=target_resource_name),user_id=user_ocid).data
                      new_value = create_auth_token_response.token
                      # Store new auth token in vault secret
                      update_secret(vault_client,auth_secret_ocid,new_value)
                      additional_details = '\r\nAuth Token - Secret OCID : ' + auth_secret_ocid
                      status = "RESOLVED"
    
                elif problem_name == "SECRET_KEY_TOO_OLD":
                   access_id_secret_ocid = get_secret_ocids(comments_items, "accesskey_secret_ocid")
                   secret_key_secret_ocid = get_secret_ocids(comments_items, "secretkey_secret_ocid")
                   if access_id_secret_ocid != "" and secret_key_secret_ocid != "":
                      # Delete existing customer secrete key
                      delete_secret_key_response = identity_client.delete_customer_secret_key(user_ocid, target_resource_id).data
                      # Create new customer secret key
                      create_customer_secret_key_response = identity_client.create_customer_secret_key(create_customer_secret_key_details=oci.identity.models.CreateCustomerSecretKeyDetails(display_name=target_resource_name),user_id=user_ocid).data
                      new_secret_key = str(create_customer_secret_key_response.key)
                      new_access_key_id = str(create_customer_secret_key_response.id)
                      # Store new customer secret key in vault secret
                      update_secret(vault_client,secret_key_secret_ocid,new_secret_key)
                      update_secret(vault_client,access_id_secret_ocid,new_access_key_id)
                      additional_details = '\r\nAccess Key - Secret OCID : ' + access_id_secret_ocid + \
                               '\r\nSecret Key - Secret OCID : ' + secret_key_secret_ocid
                      status = "RESOLVED"
    
                elif problem_name == "API_KEY_TOO_OLD":
                   key_fingerprint = target_resource_id.split("/")[2]
                   api_secret_ocid = get_secret_ocids(comments_items,"api_secret_ocid")
                   if api_secret_ocid != "":
                      key = RSA.generate(2048)
                      key_private = key.exportKey()
                      pubkey = key.publickey()
                      key_public = pubkey.exportKey()
                      # Delete existing API key
                      delete_api_key_response = identity_client.delete_api_key(user_id=user_ocid,fingerprint=key_fingerprint)
                      # Upload new public API key in OCI for the user
                      upload_api_key_response = identity_client.upload_api_key(user_id=user_ocid,
                                                                               create_api_key_details=oci.identity.models.CreateApiKeyDetails(
                                                                                     key=key_public.decode()))
                      # Store content of new private key in vault secret
                      update_secret(vault_client,api_secret_ocid,key_private.decode())
                      additional_details = '\r\nSecret OCID for private API key : ' + api_secret_ocid
                      status = "RESOLVED"
          except Exception as e:
                additional_details = '\r\r\n Error: '+ str(e)
    
          # Message Body Customization, it can be updated as per need
          line_head = 'Oracle Cloud Notification' + '\n====================='
          message_body = line_head + \
                         '\r\r\nProblem Name : ' + problem_name + \
                         '\r\r\nRisk Level : ' + risk_level + \
                         '\r\nEvent Time : ' + e_time + \
                         '\r\nTenancy Name : ' + t_name + \
                         '\r\nTenancy ID : ' + t_id + \
                         '\r\r\nAdditional Details : ' + '\n-------------------------' \
                         '\r\nResource Name : ' + target_resource_name + \
                         '\r\nResource ID : ' + target_resource_id + \
                         '\r\nResource User OCID : ' + user_ocid + ' ' + additional_details
    
          # Message Title
          message_title = 'Problem : ' + resource_name + ' | ' + status +' by automation '
    
          # Message Detail
          message_details = oci.ons.models.MessageDetails(body=message_body, title=message_title)
    
          # Publish message to ONS
          onsclient.publish_message(ons_topic, message_details)
    
       except (Exception, ValueError) as ex:
          logging.getLogger().info('error parsing json payload: ' + str(ex))
    
       return response.Response(ctx, response_data=json.dumps({"message": "success"}),headers={"Content-Type": "application/json"})
    
  6. 更新 requirements.txt,如下所示。

    fdk>=0.1.59
    oci
    pycryptodomex
    
  7. 請執行下列命令來部署函數。

    fn -v deploy --app <app-name>
    
    Example: fn -v deploy --app rotatecredapp
    
  8. 執行下列命令以新增組態索引鍵和值的函數組態。

    • ons_topic:使用通知主題的 Oracle Cloud Identifier (OCID) 來接收電子郵件通知。
    fn config function <app-name> <function-name> <config-key> <config-value>
    
    Example: fn config function rotatecredapp rotatecred-func ons_topic ocid1.onstopic.oc1.ap-mumbai-1.aaaaaaaau3onlufcfz.......kae26637zovwd7q
    

作業 2:建立事件規則

  1. 前往 OCI 主控台,然後按一下可觀測性與管理

  2. 事件服務下,按一下規則建立規則

  3. 建立規則頁面中,輸入規則的名稱和規則的描述

  4. 規則條件區段中,輸入下列資訊以指定觸發函數的事件類型和動作。

    • 條件:Event Type
    • 服務名稱:Cloud Guard
    • 事件類型:Remediated-Problem
    • 屬性名稱: ProblemRecommendation
    • 屬性值:
      • Rotate IAM Console password regularly, at least every 90 days.,
      • Rotate IAM Auth token regularly, at least every 90 days.,
      • Rotate IAM Customer secret keys regularly, at least every 90 days.,
      • Rotate API keys regularly, at least every 90 days.

    下列影像是 Oracle Cloud Guard 問題修正事件的範例。

    圖像

作業 3:修正 Oracle Cloud Guard 問題

  1. 前往 OCI 主控台,然後按一下識別與安全

  2. 雲端保全底下,按一下問題

  3. 按一下支援的自動輪換問題之一。

  4. 按一下標示為已解決,然後依照下列格式在註解區段中輸入下列資訊。您可以依據您的組織處理程序新增其他資訊,以及下列以逗號 (,) 區隔的資訊。

    • IAM 客戶秘密金鑰太舊

      accesskey_secret_ocid:<OCID of access key secret>,secretkey_secret_ocid:<OCID of secret key secret>,<additional comments>

      圖像

    • API 金鑰太舊

      api_secret_ocid:<OCID of api key secret>,<additional comments>

      圖像

    • IAM 認證權杖太舊

      auth_secret_ocid:<OCID of auth token secret>,<additional comments>

      圖像

    • 密碼太舊

      注意:

      • 此問題的註解中沒有必要的資訊。
      • 請在下次登入時按一下忘記密碼,讓個別使用者重設密碼。使用者必須要有有效的電子郵件地址,才能接收電子郵件通知。

工作 4:驗證新的 OCI IAM 證明資料和電子郵件通知

您將會收到問題解決電子郵件通知,其中包含循環加密密碼 OCID 的詳細資訊和其他資訊。

圖像

認可

其他學習資源

瀏覽 docs.oracle.com/learn 的其他實驗室,或前往 Oracle Learning YouTube 頻道存取更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。

如需產品文件,請造訪 Oracle Help Center