在Lambda中获取超1小时临时凭证并规避角色链问题(SAML错误)
问题描述
在Lambda中通过编程方式对用户做身份验证,调用AssumeRoleWithSAML API获取凭证,之后用该凭证扮演另一个角色,生成有效期超过1小时且无角色链问题的临时凭证。不想创建IAM用户,希望用联合用户凭证实现,但代码运行报错,请求帮助。
import requests import boto3 import base64 def authenticate_with_okta(username, password, okta_org_url, okta_app_url): okta_authn_url = f"{okta_org_url}/api/v1/authn" # Send a POST request to the Okta Authn endpoint to authenticate the user response = requests.post(okta_authn_url, json={ "username": username, "password": password }) print(response) if response.status_code == 200: # Extract the SAML assertion from the response JSON auth_response = response.json() saml_assertion = auth_response.get('sessionToken') if saml_assertion: return saml_assertion else: print(f"Authentication failed. Status code: {response.status_code}") return None def decode_saml_assertion(saml_assertion): try: decoded_saml = base64.b64decode(saml_assertion) return decoded_saml except Exception as e: print(f"Error decoding SAML Assertion: {e}") return None # Decode and print the SAML Assertion decoded_saml_assertion = decode_saml_assertion print(decoded_saml_assertion) def assume_role_with_saml(saml_assertion, role_arn, principal_arn, region_name='us-east-1'): print("Received SAML Assertion:") print(saml_assertion) client = boto3.client('sts', region_name=region_name) response = client.assume_role_with_saml( RoleArn=role_arn, PrincipalArn=principal_arn, SAMLAssertion=saml_assertion ) print("STS Response:") print(response) return response['Credentials'] def main(): # Replace with your Okta credentials and configuration okta_username = "MyOktaUser@lab.com" okta_password = "MyComplexPassword" okta_org_url = "https://mydomain.okta.com" # Replace with your Okta domain URL okta_app_url = "https://mydomain/home/amazon_appstream/0oa8alauh3v3ise4C5d7/aln1al3ek55Es08M81d8" # Replace with your Okta app URL # Replace with your AWS role ARN and principal ARN aws_role_arn = "arn:aws:iam::1234567890:role/Test_Role" aws_principal_arn = "arn:aws:iam::1234567890:saml-provider/Okta" # Step 1: Authenticate with Okta and get SAML Assertion saml_assertion = authenticate_with_okta(okta_username, okta_password, okta_org_url, okta_app_url) if not saml_assertion: print("Failed to obtain SAML Assertion.") return # Step 2: Use SAML Assertion to Assume AWS Role and get temporary credentials try: credentials = assume_role_with_saml(saml_assertion, aws_role_arn, aws_principal_arn) print("Temporary credentials fetched successfully!") print("Access Key ID:", credentials['AccessKeyId']) print("Secret Access Key:", credentials['SecretAccessKey']) print("Session Token:", credentials['SessionToken']) except Exception as e: print("Error assuming AWS role:", e) if __name__ == "__main__": main()
错误信息
Received SAML Assertion: 20111Fw2lcP1_zs-PI6z_9dF5A_-TcIbDwv-5zWaVsg4GP6SiUA_9s0 Error assuming AWS role: An error occurred (InvalidIdentityToken) when calling the AssumeRoleWithSAML operation: Invalid base64 SAMLResponse (Service: AWSOpenIdDiscoveryService; Status Code: 400; Error Code: AuthSamlInvalidSamlResponseException; Request ID: 80a016bb-17ca-4162-bf68-08f63eba32d2; Proxy: null)
问题根源与修复方案
问题根源
你把Okta身份验证返回的sessionToken当成了SAML断言传给AWS,这是核心错误。sessionToken只是用户会话令牌,不是符合AWS要求的SAML断言,所以触发了无效令牌报错。
修复后的完整代码
import requests import boto3 from bs4 import BeautifulSoup def authenticate_with_okta(username, password, okta_org_url, okta_app_url): okta_authn_url = f"{okta_org_url}/api/v1/authn" # 1. 用户身份验证获取sessionToken response = requests.post(okta_authn_url, json={ "username": username, "password": password }) if response.status_code != 200: print(f"身份验证失败,状态码: {response.status_code}") return None auth_response = response.json() session_token = auth_response.get('sessionToken') if not session_token: print("未获取到sessionToken") return None # 2. 用sessionToken访问Okta应用,提取SAML断言 app_response = requests.get(okta_app_url, params={'sessionToken': session_token}) if app_response.status_code != 200: print(f"访问Okta应用失败,状态码: {app_response.status_code}") return None # 解析HTML页面获取SAMLResponse soup = BeautifulSoup(app_response.content, 'html.parser') saml_input = soup.find('input', {'name': 'SAMLResponse'}) if not saml_input: print("页面中未找到SAMLResponse") return None return saml_input['value'] def assume_role_with_saml(saml_assertion, role_arn, principal_arn, region_name='us-east-1'): client = boto3.client('sts', region_name=region_name) response = client.assume_role_with_saml( RoleArn=role_arn, PrincipalArn=principal_arn, SAMLAssertion=saml_assertion, DurationSeconds=3600 # 可设置最长1小时,如需更长需后续调用AssumeRole ) return response['Credentials'] def assume_another_role(base_credentials, target_role_arn, region_name='us-east-1'): # 用SAML获取的凭证扮演另一个角色,最长可设置12小时有效期 sts_client = boto3.client( 'sts', region_name=region_name, aws_access_key_id=base_credentials['AccessKeyId'], aws_secret_access_key=base_credentials['SecretAccessKey'], aws_session_token=base_credentials['SessionToken'] ) response = sts_client.assume_role( RoleArn=target_role_arn, RoleSessionName="saml-federated-session", DurationSeconds=43200 # 最长12小时,需目标角色信任策略允许 ) return response['Credentials'] def main(): # 替换为你的配置信息 okta_username = "MyOktaUser@lab.com" okta_password = "MyComplexPassword" okta_org_url = "https://mydomain.okta.com" okta_app_url = "https://mydomain/home/amazon_appstream/0oa8alauh3v3ise4C5d7/aln1al3ek55Es08M81d8" aws_role_arn = "arn:aws:iam::1234567890:role/Test_Role" aws_principal_arn = "arn:aws:iam::1234567890:saml-provider/Okta" target_role_arn = "arn:aws:iam::1234567890:role/Target_Long_Lived_Role" # 获取SAML断言 saml_assertion = authenticate_with_okta(okta_username, okta_password, okta_org_url, okta_app_url) if not saml_assertion: print("获取SAML断言失败") return # 第一步:用SAML断言获取基础临时凭证 try: base_credentials = assume_role_with_saml(saml_assertion, aws_role_arn, aws_principal_arn) print("基础临时凭证获取成功!") except Exception as e: print("扮演基础角色出错:", e) return # 第二步:用基础凭证扮演目标角色,获取超1小时有效期的凭证 try: long_lived_credentials = assume_another_role(base_credentials, target_role_arn) print("长有效期临时凭证获取成功!") print("Access Key ID:", long_lived_credentials['AccessKeyId']) print("过期时间:", long_lived_credentials['Expiration']) except Exception as e: print("扮演目标角色出错:", e) if __name__ == "__main__": main()
关键说明
- SAML断言获取逻辑:新增了用
sessionToken访问Okta应用页面,解析HTML提取SAMLResponse的步骤,这才是AWS需要的合法SAML断言。 - 依赖补充:解析HTML需要
beautifulsoup4库,Lambda环境需添加该依赖层或在部署时打包。 - 超1小时凭证实现:先用SAML获取1小时内的基础凭证,再用该凭证调用
AssumeRole获取最长12小时的凭证,此方式不属于角色链(角色链指用IAM角色凭证再扮演角色),无角色链限制。 - 权限配置:目标角色的信任策略需允许基础角色扮演它,同时需在角色配置中开启允许最长12小时的会话有效期。
内容的提问来源于stack exchange,提问作者Piyush Pandey
相关产品推荐
相关产品推荐

