附註:
- 此教學課程需要存取 Oracle Cloud。若要註冊免費帳戶,請參閱開始使用 Oracle Cloud Infrastructure Free Tier 。
- 它使用 Oracle Cloud Infrastructure 憑證、租用戶及區間的範例值。完成實驗室時,請將這些值取代為您雲端環境特有的值。
啟用自動輪換 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 天輪換憑證。
注意: 此解決方案已經針對預設識別網域進行測試。
目標
-
透過自動化修正 Oracle Cloud Guard 問題來輪換 Oracle Cloud Infrastructure (OCI) 認證權杖、API 金鑰、客戶秘密金鑰以及 OCI 主控台密碼。下列 Oracle Cloud Guard 問題用於自動輪換 OCI IAM 證明資料。
- API 金鑰太舊
- IAM 認證權杖太舊
- IAM 客戶秘密金鑰太舊
- 密碼太舊
必要條件
OCI IAM 管理員應建立:
-
OCI Functions 的動態群組和原則,可呼叫、修正問題並傳送電子郵件通知。
-
動態群組:建立函數區間的動態群組。如需詳細資訊,請參閱建立動態群組。
# Replace OCID for function compartment All {resource.type = 'fnfunc', resource.compartment.id = '<function-compartment>'} Example: All {resource.type = 'fnfunc', resource.compartment.id = 'ocid1.compartment.oc1..aaaaaaaanovmfmmnonjjyxeq4jyghszj2eczlrkgj5svnxrt...'}
-
原則:建立原則以指定函數的動態群組。如需詳細資訊,請參閱開始使用原則。
# Replace dynamic-group-name, function-compartment and OCID of vault secret for OCI function Allow dynamic-group <dynamic-group-name> to inspect compartments in tenancy Allow dynamic-group <dynamic-group-name> to inspect users in tenancy Allow dynamic-group <dynamic-group-name> to manage users in tenancy where any {request.permission = 'USER_UPDATE', request.permission = 'USER_AUTHTOKEN_SET', request.permission = 'USER_AUTHTOKEN_REMOVE', request.permission = 'USER_SECRETKEY_ADD', request.permission = 'USER_SECRETKEY_REMOVE', request.permission = 'USER_UIPASS_SET', request.permission = 'USER_APIKEY_ADD', request.permission = 'USER_APIKEY_REMOVE'} Allow dynamic-group <dynamic-group-name> to read repos in compartment <function-compartment> Allow dynamic-group <dynamic-group-name> to use secret-family in compartment <vault-compartment> where target.secret.id='<ocid of OCI vault secret to store rotated OCI secrets>' Allow dynamic-group <dynamic-group-name> to use ons-topics in tenancy
-
-
區間、群組和原則,供開發人員建立加密密碼及部署功能。
# Replace group-name as per your tenancy Allow group <group-name> to manage repos in compartment <function-compartment> Allow group <group-name> to manage vaults in compartment <function-compartment> Allow group <group-name> to manage keys in compartment <function-compartment> Allow group <group-name> to manage secret-family in compartment <function-compartment> Allow group <group-name> to manage functions-family in compartment <function-compartment> Allow group <group-name> to use cloud-shell in tenancy Allow group <group-name> to use virtual-network-family in compartment <network-compartment> Allow group <group-name> to read objectstorage-namespaces in tenancy
-
OCI 通知主題,讓 SecOps 團隊接收電子郵件通知。
-
Oracle Cloud Infrastructure Secrets in Vault to store rotated OCI IAM credentials created by automation.
Note : Make sure you have copied existing OCI IAM credentials for Oracle Cloud Infrastructure Secrets in Vault before running this automation for the first time.
作業 1:建立及部署 OCI 函數
-
登入 OCI 主控台,然後按一下頂端導覽中的雲端 Shell 。
注意:您也可以使用自己的 IDE 工具來建立及部署函數。
-
使用 Cloud Shell 的 Fn 專案 CLI 建立函數。如需詳細資訊,請參閱建立、部署及呼叫 Helloworld 函數。
fn init --runtime python <function-name> Example: fn init --runtime python rotatecred-func
-
將目錄變更為新建立的目錄。
-
使用下列命令建立應用程式以部署函數。
# 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....."]'
-
藉由覆寫現有內容,複製並貼上
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"})
-
更新
requirements.txt
,如下所示。fdk>=0.1.59 oci pycryptodomex
-
請執行下列命令來部署函數。
fn -v deploy --app <app-name> Example: fn -v deploy --app rotatecredapp
-
執行下列命令以新增組態索引鍵和值的函數組態。
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:建立事件規則
-
前往 OCI 主控台,然後按一下可觀測性與管理。
-
在事件服務下,按一下規則與建立規則。
-
在建立規則頁面中,輸入規則的名稱和規則的描述。
-
在規則條件區段中,輸入下列資訊以指定觸發函數的事件類型和動作。
- 條件:
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 問題
-
前往 OCI 主控台,然後按一下識別與安全。
-
在雲端保全底下,按一下問題。
-
按一下支援的自動輪換問題之一。
-
按一下標示為已解決,然後依照下列格式在註解區段中輸入下列資訊。您可以依據您的組織處理程序新增其他資訊,以及下列以逗號 (,) 區隔的資訊。
-
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 的詳細資訊和其他資訊。
相關連結
認可
-
作者 - Dipesh Kumar Rathod (基礎架構首席雲端架構師)
-
貢獻者 - Bhanu Prakash Lohumi (基礎架構資深雲端工程師)
其他學習資源
瀏覽 docs.oracle.com/learn 的其他實驗室,或前往 Oracle Learning YouTube 頻道存取更多免費學習內容。此外,請造訪 education.oracle.com/learning-explorer 以成為 Oracle Learning Explorer。
如需產品文件,請造訪 Oracle Help Center 。
Enable Auto Rotation of Oracle Cloud Infrastructure Identity and Access Management Credentials
F93270-01
February 2024
Copyright © 2024, Oracle and/or its affiliates.