주:

Oracle Cloud Infrastructure Identity and Access Management 인증서 자동 교체 사용

소개

이 사용지침서에서는 API 키, 인증 토큰, 고객 암호 키 및 OCI 콘솔 로그인 비밀번호인 OCI IAM(Oracle Cloud Infrastructure Identity and Access Management) 인증서를 자동 교체하는 데 도움이 됩니다. Oracle Cloud Guard 문제 해결 및 OCI 통지 서비스를 기반으로 합니다. 또한 기본 응답기 규칙의 차이를 충족하여 OCI IAM 인증서를 교체합니다.

Oracle Cloud Guard는 정책에 정의된 규칙을 기반으로 문제를 감지합니다. 규칙이 충족되면 문제가 발생합니다.

보안 모범 사례이며 CIS OCI Foundations(Center for Internet Security Oracle Cloud Infrastructure Foundations) 벤치마크에서 90일마다 인증서를 교체하는 것이 좋습니다.

주: 이 솔루션은 기본 ID 도메인에 대해 테스트되었습니다.

목표

필요 조건

OCI IAM 관리자는 다음을 생성해야 합니다.

작업 1: OCI 함수 생성 및 배치

  1. OCI 콘솔에 로그인하고 위쪽 탐색에서 Cloud Shell을 누릅니다.

    주: 고유의 IDE 툴을 사용하여 함수를 생성하고 배치할 수도 있습니다.

  2. Cloud Shell에서 Fn 프로젝트 CLI를 사용하여 함수를 생성합니다. 자세한 내용은 Creating, Deploying, and Invoking a Helloworld Function를 참조하십시오.

    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: 전자메일 통지 수신을 위해 통지 토픽의 OCID(Oracle Cloud 식별자)를 사용합니다.
    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 콘솔로 이동하여 ID 및 보안을 누릅니다.

  2. Cloud Guard에서 문제를 누릅니다.

  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를 참조하십시오.