多云身份管控平台构建——第二篇 基于动态策略实现 Auth0 与亚马逊云控制台的权限集成

0
0
{"value":"越来越多的企业在迁移上云旅程中都会面临多云的选择。不管是出于主动原因希望融合多云的服务优势,还是出于被动原因需要适应公司的业务并购,抑或是因为上云过程中需要实现混合云架构的打通,构建多云的统一身份权限管控平台往往是这些企业需要解决的问题。\n\n构建这一平台,不仅要实现单点登录,还要考虑统一身份管控、统一权限管控和云上用户行为审计;并且企业内人员的多组织管理模型通常与云上基于角色的IAM管理模型存在较大差异,这进一步增加了构建跨云身份管理平台的难度。\n\n本博客从企业客户的实际需求和技术难点出发,分别阐述了使用\n\n- RBAC — 基于角色的权限管控\n- ABAC — 基于属性的权限管控\n- 动态策略生成\n\n这三种思路实现企业统一身份管控平台,并与公有云控制台 (Web console) 实现身份和权限的打通;以及三种思路各自的适用场景;并在此基础上给出了基于 Auth0 构建该平台,完成与亚马逊云控制台集成,实现统一登录与授权的具体实现代码样例。\n\n受篇幅所限,我们把博客分成两部分,上一篇讲述 RBAC 和 ABAC 的实现方法;本篇主要讲述动态策略生成的实现方法。\n\n[多云身份管控平台构建——第一篇 基于RBAC&ABAC实现Auth0与亚马逊云控制台的权限集成](https://aws.amazon.com/cn/blogs/china/security-identity-compliance-construction-of-multi-cloud-identity-management-and-control-platform-the-first-article-realizes-the-permission-integration-of-auth0-and-amazon-cloud-console-ba/)\n\n### **1. 方案概述**\n在第一篇,我们讲述的 RBAC 思路用于实现 IDP 中的用户属性(项目与角色的组合)与 Amazon IAM 角色的一对一映射。集成起来简单快速,适合用户的项目x角色组合数量较少的使用场景;而 ABAC 则适用于多租户模式下对资源类型使用相对固定的场景。我们使用一个 policy,通过资源标签和主体标签的匹配规则就可以覆盖所有权限管控需求,并可以扩展至任意数量的项目\n\n但实际的企业环境中,如果项目的数量很多,而且每个项目中用到的资源类型又不尽相同, RBAC 和 APBAC 都很难满足需求。为了提升环境的可管理性和敏捷性,避免可能产生的策略数量和尺寸的爆炸性增长,基于动态策略生成的权限管理模型将是一个更为通用的解决方案。基本思路如下图:\n\n![image.png](https://dev-media.amazoncloud.cn/c57909c2e8614ed6a847ec6645f4a411_image.png)\n\n- 首先,构建策略模版库,用于保存各种云资源的动态权限模版\n模版库可以保存在 DynamoDB 或 S3 中。为便于后续的维护和管理,建议对每个资源的不同角色分别维护一个独立的模版。下面的策略模版库演示了允许查询和启动 EC2 实例的一个策略模版,其中 {{region}}、{{accountid}} 和 {{project}} 都为模版中的 placeholder,以备后续动态生成策略。\n\n```\\n{\\n \\"Version\\": \\"2012-10-17\\",\\n \\"Statement\\": [\\n {\\n \\"Effect\\": \\"Allow\\",\\n \\"Action\\": [\\n \\"ec2:Describe*\\",\\n \\"ec2:List*\\"\\n ],\\n \\"Resource\\": \\"*\\"\\n },\\n {\\n \\"Effect\\": \\"Allow\\",\\n \\"Action\\": [\\n \\"ec2:StartInstances\\"\\n ],\\n \\"Resource\\": \\"arn:aws:ec2:{{region}}:{{accountid}}:instance/*\\",\\n \\"Condition\\": {\\n \\"StringEquals\\": {\\n \\"aws:ResourceTag/environment\\": \\"{{project}}\\"\\n }\\n }\\n }\\n ]\\n}\\n```\n\n- 之后,维护用户属性与策略模版间的对应关系\n在实际项目中,该对应关系可以通过开发配置界面完成,便于管理员进行动态配置。\n\n![image.png](https://dev-media.amazoncloud.cn/83c5815e9e3b452a9acbbb7c4c3f902f_image.png)\n\n- 最后,构建策略生成器,用于根据用户登录后的上下文信息动态生成 IAM 策略\n用户在 IDP 完成身份认证后,通过 OpenID 或 SAML 断言携带用户的上下文信息。我们构建的策略动态生成器需要先验证用户的合法身份,然后根据上下文信息获取对应的策略模版,并生成所需的策略,之后通过 Amazon STS 服务完成 AssumeRole,获得临时的 AKSK、SessionToken 等信息,生成访问亚马逊云控制台的 URL,以保证资源访问的隔离。\n\n这一方案的好处显而易见,它不拘于使用 RBAC 或 ABAC 模型,也不依赖于标签的使用,我们可以在模版中任何需要的地方放置 placeholder,构建所需策略,非常灵活。\n\n下面我们对几个技术关键点的实现做详细展开:\n\n### **2. 配置 Auth0 实现基于 OIDC 的身份认证**\n本博客,我们使用 Auth0 作为统一身份管控平台的 IDP。验证中,我们使用了 OIDC 的身份层协议,在 Auth0 完成身份认证后将向管控平台返回 IDToken 和 Access Token。\n\n#### **2.1 IDToken or AccessToken?**\n当前的场景是企业的身份管控平台需要从 Auth0 获得 IDToken,表明已完成身份认证,具体的资源授权由 API GW 和后端的 Lambda 根据 Token 中携带的用户属性自行完成,因此应向后端的 APIGateway 传递 IDtoken。在其他的场景,如果是访问 Auth0 获得对资源的访问授权,携带允许访问资源的 scope 信息到 resource server 获取相应资源的场景(如常见的使用微信认证,并允许获得微信头像的场景),则应使用 access token。因此在后面的方案展示中,我们将使把从 Auth0 中获得的 IDToken 传递给 APIGateway。\n\n![image.png](https://dev-media.amazoncloud.cn/2ef0d6c4e99946cfa2ca588ad1adab75_image.png)\n\n#### **2.2 配置 Auth0 获取 IDToken**\n在这个场景中,企业的身份管控平台需要依赖 Auth0 完成身份认证,再将从 Auth0 获得的包含用户属性信息的 ID token 携带在 http header 中,请求 API Gateway 调用。在 Auth0 中我们需要完成如下的配置,以获得包含完整用户属性信息的 ID token:\n\n- 首先,在 Auth0 中创建 API application \n- 创建成功后,将获得 application 对应的 clientID 和 client secret。clientID 后面会包含在 ID token 的 aud 中,用于表明该 token 是合法颁发给该 application 的。\n- 然后,在创建的 application 中选择认证使用的工作流\n- 在 application ->新创建的 Application -> Advanced Settings 中选择 Implicit 的授权模式。该模式为 0 中的 RFC6749 中的隐式授权模式,授权过程安全且较为简单,不需授权码可直接获得 IDToken。\n\n![image.png](https://dev-media.amazoncloud.cn/810aca0ab2604abd90cab09bf0282f74_image.png)\n\n- 最后,修改 application 的登录流程,加入客户化的 action“ appProject2Token ”,该 Action 用于把用户的 user_metadata 中的 project 和 role 的属性信息加入到 IDToken 中。\n\n![image.png](https://dev-media.amazoncloud.cn/40d7161843e844f1819491f5427e7e11_image.png)\n\n![image.png](https://dev-media.amazoncloud.cn/d693547e424248dfaf45fba9d10ec1cb_image.png)\n\n- Action 代码如下:\n\n```\\nexports.onExecutePostLogin = async (event, api) => {\\n const namespace = 'https://mytesttest.com';\\n const project = event.user.user_metadata.project;\\n const role = event.user.user_metadata.role;\\n\\n //if (event.authorization) { 这里为简便起见忽略了验证部分,实际项目中可根据安全需要对applications ->api -> permission中限定的scope进行验证,允许访问相应的内容时,才将用户的相关信息放入IDToken\\n // Set claims \\n api.idToken.setCustomClaim(`\${namespace}/project`, event.user.user_metadata.project);\\n api.idToken.setCustomClaim(`\${namespace}/role`, event.user.user_metadata.role);\\n //}\\n};\\n```\n\n#### **2.3 IDToken获取验证**\n\n下面我们验证从 Auth0 中获取的 IDToken\n\nAuth0 提供了 Authentication API Debugger 可以方便地查看获取到的 IDToken。对创建的 API application 使用如下的 authorizer 接口进行访问(下面的{{}}部分用 API application 产生的 domain 和 clientid 替换),rediect_url 为 Authentication API Debugger 的 URL。\n\nhttps://{{ApplicationDomain}}/authorize?responsetype=idtoken token&responsemode=formpost&clientid={{CLINTID}}&redirecturi=https://dev-gzu7esjn.us.webtask.run/auth0-authentication-api-debugger&scope=openid profile&state=test&nonce=test&audience=[https://oauth0api.com](https://oauth0api.com/)\n\n可以看到返回的 IDToken 部分包含我们之前定义的用户属性信息( project 和role )。\n\n![image.png](https://dev-media.amazoncloud.cn/90be455c172f4acdb2ba08c12bb15246_image.png)\n\n### **3.动态策略生成器实现**\n企业身份管控平台在 Auth0 完成身份验证并获得 IDToken 后,我们使用 [Amazon API Gateway](https://aws.amazon.com/cn/api-gateway/?trk=cndc-detail) 的 Lambda Authorizer 构建策略生成器,由于请求处理和策略生成。Lambda Authorizer 是 API Gateway 的一个功能,该功能使用 Lambda 函数来控制对 API Gateway 的访问。在该 Lambda 函数中我们主要完成了两项工作:首先,根据持有者令牌(如 OAuth 或 SAML)验证用户身份的合法性,验证通过即可允许 API Gateway 的调用;同时对合法用户,根据用户的上下文信息结合策略模版动态生成访问策略,并将生成的策略传递给后端的业务 Lambda,业务 Lambda 根据这些 policy 通过 AssumeRole 获得临时凭证来生成 Amazon console URL;也可以构建业务 Lambda 通过临时凭证完成对后端资源的安全访问。\n\n![image.png](https://dev-media.amazoncloud.cn/15e5fff932d94858a4dfc2e73b1e3fd3_image.png)\n\n#### **3.1构建具有 Authorizer Lambda 的 APIGateway**\n实现中,我们构建了名为“ auth0api ”的 Rest APIGateway,调用后端名为 “mytest” 的业务 Lambda,业务 Lambda 生成访问亚马逊云控制台的 URL 通过 APIGateway 返回给前端的 client app/browser。\n\n![image.png](https://dev-media.amazoncloud.cn/99580c0ee9694497b311a737b0c95223_image.png)\n\n下图展示了为该 APIGateway 添加 Authorizer Lambda,选择 Token 作为 Lambda Event 负载,选择认证后的结果信息在 APIGateway 中保留 3s,期间不需要再次通过 Authorizer 做认证。\n\n![image.png](https://dev-media.amazoncloud.cn/4573627db81e430da2cd409692605beb_image.png)\n\n#### **3.2 Authorizer Lambda 的实现**\n该函数是动态策略生成器的核心部分。它负责获取 APIGateway 中的 IDToken 信息,校验 IDToken 的合法性。\n\n完整的代码可以参考 https://github.com/1559550282/AWS/tree/main/MultiCloud-identityPrivControl\n\n```\\ndef lambda_handler(event, context):\\n #从Event中获取APIGateway中传来的IDToken\\n token = event['authorizationToken'].split(\\" \\")\\n if (token[0] != 'Bearer'):\\n raise Exception('Authorization header should have a format Bearer <JWT> Token')\\n jwt_bearer_token = token[1] \\n unauthorized_claims = jwt.get_unverified_claims(jwt_bearer_token) \\n #并从IDToken中获得project和role的属性\\n project_attri = unauthorized_claims['https://mytesttest.com/project']\\n accessrole_attri = unauthorized_claims['https://mytesttest.com/role']\\n \\n #从Auth0中创建的API Application中获得jwt签名的公共密钥\\n keys_url = 'https://dev-gzu7esjn.us.auth0.com/.well-known/jwks.json' \\n with urllib.request.urlopen(keys_url) as f:\\n response = f.read()\\n keys = json.loads(response.decode('utf-8'))['keys']\\n\\n #用公共密钥校验IDToen中的签名部分,并核对IDToken中的aud是否为Auth0中创建的API Application对应的ClientID,是则认为用户校验通过。该部分代码篇幅原因删略,如需要请参见github链接\\n response = validateJWT(jwt_bearer_token, appclientid, keys)\\n \\n #get authenticated claims\\n if (response == False):\\n raise Exception('Unauthorized')\\n else:\\n principal_id = response[\\"sub\\"]\\n \\n #生成APIGateway的policy--authResponse,通过Authorizer lambda 返回给apigw,决定是否允许调用APIGateway\\n tmp = event['methodArn'].split(':')\\n api_gateway_arn_tmp = tmp[5].split('/')\\n aws_account_id = tmp[4] \\n policy = AuthPolicy(principal_id, aws_account_id)\\n policy.restApiId = api_gateway_arn_tmp[0]\\n policy.region = tmp[3]\\n policy.stage = api_gateway_arn_tmp[1]\\n policy.allowAllMethods() \\n authResponse = policy.build()\\n \\n #同时,获取IDToken中的用户属性信息,结合策略模版为后端的业务lambda生成动态policy:deliverpolicy,通过上下文传给后端业务lambda\\n deliverpolicy = policyGenerator(project_attri, accessrole_attri)\\n ##将生成的动态policy向后端业务传递\\n context = {\\n 'policy': deliverpolicy\\n }\\n authResponse['context'] = context\\n return authResponse\\n \\n## 根据用户的属性信息从DynamoDB中取出策略模版,使用策略模版动态生成policy。模版替换使用mastache的python版本实现。 \\ndef policyGenerator(project, projrole):\\n dynamodb = boto3.client('dynamodb')\\n response = dynamodb.get_item(\\n TableName=\\"policytemplate\\",\\n Key={\\n 'project': {'S': 'project'},\\n 'role': {'S': 'projrole'}\\n }\\n )\\n thecontext = {'region':REGIONID,'accountid':ACCOUNTID,'project':project}\\n temppolicy = pystache.render(response['Item']['policy']['S'],thecontext)\\n return temppolicy\\n```\n\n#### **3.3 构建业务\nLambda\n生成符合权限管控的亚马逊云控制台**\nAthorizer Lambda\n生成了 APIGateway 的访问策略和后端业务所需的动态策略,返回给 APIGateway。后端业务 Lambda 与 APIGateway 的集成应注意使用 “Lambda proxy Integration” ,以便业务 Lambda 可以从 event 参数中获取 APIGateway 传递的从 Athorizer Lambda 获得的动态 policy。\n\n![image.png](https://dev-media.amazoncloud.cn/4b7b194957434b21b71074337921653b_image.png)\n\n业务 Lambda 生成亚马逊云控制台 URL 的代码片段:\n\n```\\ndef lambda_handler(event, context):\\n ##由于Amazon console不支持角色串联(参见https://aws.amazon.com/cn/premiumsupport/knowledge-center/iam-role-chaining-limit/),因此示例中使用了IAM user的AKSK再通过assumerole的方式挂载policy\\n session = boto3.session.Session(aws_access_key_id=\\"xxxxx\\", aws_secret_access_key=\\"xxxx\\")\\n sts_connection = session.client('sts')\\n ##在APIGateway传递来的event中获取由authorizer lambda生成的动态policy\\n thepolicy = event['requestContext']['authorizer']['policy']\\n \\n ##由于assume_role最终生成的临时凭证是基础role_arn和policy的合集,因此示例中选用的较大权限。实际生产中可以根据需要选择合理范围。同时注意对role_arn创建对指定aksk的用户的信任策略,以便assumerole具有权限。\\n role_arn = \\"arn:aws:iam::{}:role/adminrole\\".format(aws_account_id)\\n assumed_role = sts_connection.assume_role(\\n RoleArn=role_arn,\\n RoleSessionName=\\"console-session\\",\\n Policy=thepolicy\\n )\\n credentials = assumed_role[\\"Credentials\\"]\\n # 获得临时凭证的AKSK和session Token\\n url_credentials = {}\\n url_credentials['sessionId'] = credentials['AccessKeyId']\\n url_credentials['sessionKey'] = credentials['SecretAccessKey']\\n url_credentials['sessionToken'] = credentials[\\"SessionToken\\"]\\n json_string_with_temp_credentials = json.dumps(url_credentials)\\n \\n #用临时凭证构建Amazon console\\n request_parameters = \\"?Action=getSigninToken\\"\\n request_parameters += \\"&SessionDuration=43200\\"\\n if sys.version_info[0] < 3:\\n def quote_plus_function(s):\\n return urllib.quote_plus(s)\\n else:\\n def quote_plus_function(s):\\n return urllib.parse.quote_plus(s)\\n \\n request_parameters += \\"&Session=\\" + quote_plus_function(json_string_with_temp_credentials)\\n request_url = \\"https://signin.aws.amazon.com/federation\\" + request_parameters\\n r = requests.get(request_url)\\n signin_token = json.loads(r.text)\\n\\n request_parameters = \\"?Action=login\\"\\n request_parameters += \\"&Issuer=Example.org\\"\\n request_parameters += \\"&Destination=\\" + quote_plus_function(\\"https://console.aws.amazon.com/\\")\\n request_parameters += \\"&SigninToken=\\" + signin_token[\\"SigninToken\\"]\\n request_url = \\"https://signin.aws.amazon.com/federation\\" + request_parameters\\n \\n return {\\n 'statusCode': 200,\\n 'body': json.dumps(request_url)\\n }\\n```\n\n#### **4. 验证**\n使用2.3节获得的 IDToken 调用 APIGateway\n\ncurl –request GET –url https://{{APIID}}.execute-api.ap-southeast-1.amazonaws.com/default/mytest –header ‘authorization: Bearer {{IDToken}}’\n\n即可获得访问 Amazon console 的 url 如下:\n\n[https://signin.aws.amazon.com/federation?Action=login&Issuer=Example.org&Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&SigninToken=xxxx](https://signin.aws.amazon.com/federation?Action=login&Issuer=Example.org&Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&SigninToken=xxxx)\n\n该 URL 的有效期可以在 assume_role 中设定从 15min 到12小时的时长。\n\n### **总结**\n至此,我们介绍了基于 RBAC、ABAC 和动态策略生成这三种方式实现统一身份认证平台,完成用户属性与 IAM 角色的对应,从而构建统一身份管控平台与 Amazon 控制台的 SSO 和统一授权。\n\n实际场景中,如果用户多种属性的组合数量非常有限,推荐使用 RBAC 实现属性与 IAM 角色的直接映射,简单快捷;如果不同属性的用户使用的资源类型整齐划一,则可以使用 ABAC 通过会话标签和资源标签实现 IAM 角色的限定;但如果用户多种属性的组合过于复杂,而且不同类型的用户使用的资源需求不尽相同,则可以使用动态策略生成的方式,通过策略模版生成动态策略,实现灵活的权限隔离。\n\n### **本篇作者**\n\n![image.png](https://dev-media.amazoncloud.cn/cb1d722d177c454ca615eaeac20b2a68_image.png)\n\n#### **倪惠青**\n\nAmazon 解决方案架构师,负责基于 Amazon 云计算方案架构的咨询和设计,在国内推广 Amazon 云平台技术和各种解决方案。在加入 Amazon 之前曾在 Oracle,Microsoft 工作多年,负责企业公有云方案咨询和架构设计,在基础架构及大数据方面有丰富经验。\n\n![image.png](https://dev-media.amazoncloud.cn/4e620f2fa9424cccb1b133538022146e_image.png)\n\n#### **任耀洲**\n\nAmazon 解决方案架构师,负责企业客户应用在 Amazon 的架构咨询和设计。在微服务架构设计、数据库等领域有丰富的经验。","render":"<p>越来越多的企业在迁移上云旅程中都会面临多云的选择。不管是出于主动原因希望融合多云的服务优势,还是出于被动原因需要适应公司的业务并购,抑或是因为上云过程中需要实现混合云架构的打通,构建多云的统一身份权限管控平台往往是这些企业需要解决的问题。</p>\n<p>构建这一平台,不仅要实现单点登录,还要考虑统一身份管控、统一权限管控和云上用户行为审计;并且企业内人员的多组织管理模型通常与云上基于角色的IAM管理模型存在较大差异,这进一步增加了构建跨云身份管理平台的难度。</p>\n<p>本博客从企业客户的实际需求和技术难点出发,分别阐述了使用</p>\n<ul>\\n<li>RBAC — 基于角色的权限管控</li>\n<li>ABAC — 基于属性的权限管控</li>\n<li>动态策略生成</li>\n</ul>\\n<p>这三种思路实现企业统一身份管控平台,并与公有云控制台 (Web console) 实现身份和权限的打通;以及三种思路各自的适用场景;并在此基础上给出了基于 Auth0 构建该平台,完成与亚马逊云控制台集成,实现统一登录与授权的具体实现代码样例。</p>\n<p>受篇幅所限,我们把博客分成两部分,上一篇讲述 RBAC 和 ABAC 的实现方法;本篇主要讲述动态策略生成的实现方法。</p>\n<p><a href=\\"https://aws.amazon.com/cn/blogs/china/security-identity-compliance-construction-of-multi-cloud-identity-management-and-control-platform-the-first-article-realizes-the-permission-integration-of-auth0-and-amazon-cloud-console-ba/\\" target=\\"_blank\\">多云身份管控平台构建——第一篇 基于RBAC&amp;ABAC实现Auth0与亚马逊云控制台的权限集成</a></p>\\n<h3><a id=\\"1__16\\"></a><strong>1. 方案概述</strong></h3>\\n<p>在第一篇,我们讲述的 RBAC 思路用于实现 IDP 中的用户属性(项目与角色的组合)与 Amazon IAM 角色的一对一映射。集成起来简单快速,适合用户的项目x角色组合数量较少的使用场景;而 ABAC 则适用于多租户模式下对资源类型使用相对固定的场景。我们使用一个 policy,通过资源标签和主体标签的匹配规则就可以覆盖所有权限管控需求,并可以扩展至任意数量的项目</p>\n<p>但实际的企业环境中,如果项目的数量很多,而且每个项目中用到的资源类型又不尽相同, RBAC 和 APBAC 都很难满足需求。为了提升环境的可管理性和敏捷性,避免可能产生的策略数量和尺寸的爆炸性增长,基于动态策略生成的权限管理模型将是一个更为通用的解决方案。基本思路如下图:</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/c57909c2e8614ed6a847ec6645f4a411_image.png\\" alt=\\"image.png\\" /></p>\n<ul>\\n<li>首先,构建策略模版库,用于保存各种云资源的动态权限模版<br />\\n模版库可以保存在 DynamoDB 或 S3 中。为便于后续的维护和管理,建议对每个资源的不同角色分别维护一个独立的模版。下面的策略模版库演示了允许查询和启动 EC2 实例的一个策略模版,其中 {{region}}、{{accountid}} 和 {{project}} 都为模版中的 placeholder,以备后续动态生成策略。</li>\n</ul>\\n<pre><code class=\\"lang-\\">{\\n &quot;Version&quot;: &quot;2012-10-17&quot;,\\n &quot;Statement&quot;: [\\n {\\n &quot;Effect&quot;: &quot;Allow&quot;,\\n &quot;Action&quot;: [\\n &quot;ec2:Describe*&quot;,\\n &quot;ec2:List*&quot;\\n ],\\n &quot;Resource&quot;: &quot;*&quot;\\n },\\n {\\n &quot;Effect&quot;: &quot;Allow&quot;,\\n &quot;Action&quot;: [\\n &quot;ec2:StartInstances&quot;\\n ],\\n &quot;Resource&quot;: &quot;arn:aws:ec2:{{region}}:{{accountid}}:instance/*&quot;,\\n &quot;Condition&quot;: {\\n &quot;StringEquals&quot;: {\\n &quot;aws:ResourceTag/environment&quot;: &quot;{{project}}&quot;\\n }\\n }\\n }\\n ]\\n}\\n</code></pre>\\n<ul>\\n<li>之后,维护用户属性与策略模版间的对应关系<br />\\n在实际项目中,该对应关系可以通过开发配置界面完成,便于管理员进行动态配置。</li>\n</ul>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/83c5815e9e3b452a9acbbb7c4c3f902f_image.png\\" alt=\\"image.png\\" /></p>\n<ul>\\n<li>最后,构建策略生成器,用于根据用户登录后的上下文信息动态生成 IAM 策略<br />\\n用户在 IDP 完成身份认证后,通过 OpenID 或 SAML 断言携带用户的上下文信息。我们构建的策略动态生成器需要先验证用户的合法身份,然后根据上下文信息获取对应的策略模版,并生成所需的策略,之后通过 Amazon STS 服务完成 AssumeRole,获得临时的 AKSK、SessionToken 等信息,生成访问亚马逊云控制台的 URL,以保证资源访问的隔离。</li>\n</ul>\\n<p>这一方案的好处显而易见,它不拘于使用 RBAC 或 ABAC 模型,也不依赖于标签的使用,我们可以在模版中任何需要的地方放置 placeholder,构建所需策略,非常灵活。</p>\n<p>下面我们对几个技术关键点的实现做详细展开:</p>\n<h3><a id=\\"2__Auth0__OIDC__66\\"></a><strong>2. 配置 Auth0 实现基于 OIDC 的身份认证</strong></h3>\\n<p>本博客,我们使用 Auth0 作为统一身份管控平台的 IDP。验证中,我们使用了 OIDC 的身份层协议,在 Auth0 完成身份认证后将向管控平台返回 IDToken 和 Access Token。</p>\n<h4><a id=\\"21_IDToken_or_AccessToken_69\\"></a><strong>2.1 IDToken or AccessToken?</strong></h4>\\n<p>当前的场景是企业的身份管控平台需要从 Auth0 获得 IDToken,表明已完成身份认证,具体的资源授权由 API GW 和后端的 Lambda 根据 Token 中携带的用户属性自行完成,因此应向后端的 APIGateway 传递 IDtoken。在其他的场景,如果是访问 Auth0 获得对资源的访问授权,携带允许访问资源的 scope 信息到 resource server 获取相应资源的场景(如常见的使用微信认证,并允许获得微信头像的场景),则应使用 access token。因此在后面的方案展示中,我们将使把从 Auth0 中获得的 IDToken 传递给 APIGateway。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/2ef0d6c4e99946cfa2ca588ad1adab75_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"22__Auth0__IDToken_74\\"></a><strong>2.2 配置 Auth0 获取 IDToken</strong></h4>\\n<p>在这个场景中,企业的身份管控平台需要依赖 Auth0 完成身份认证,再将从 Auth0 获得的包含用户属性信息的 ID token 携带在 http header 中,请求 API Gateway 调用。在 Auth0 中我们需要完成如下的配置,以获得包含完整用户属性信息的 ID token:</p>\n<ul>\\n<li>首先,在 Auth0 中创建 API application</li>\n<li>创建成功后,将获得 application 对应的 clientID 和 client secret。clientID 后面会包含在 ID token 的 aud 中,用于表明该 token 是合法颁发给该 application 的。</li>\n<li>然后,在创建的 application 中选择认证使用的工作流</li>\n<li>在 application -&gt;新创建的 Application -&gt; Advanced Settings 中选择 Implicit 的授权模式。该模式为 0 中的 RFC6749 中的隐式授权模式,授权过程安全且较为简单,不需授权码可直接获得 IDToken。</li>\n</ul>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/810aca0ab2604abd90cab09bf0282f74_image.png\\" alt=\\"image.png\\" /></p>\n<ul>\\n<li>最后,修改 application 的登录流程,加入客户化的 action“ appProject2Token ”,该 Action 用于把用户的 user_metadata 中的 project 和 role 的属性信息加入到 IDToken 中。</li>\n</ul>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/40d7161843e844f1819491f5427e7e11_image.png\\" alt=\\"image.png\\" /></p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/d693547e424248dfaf45fba9d10ec1cb_image.png\\" alt=\\"image.png\\" /></p>\n<ul>\\n<li>Action 代码如下:</li>\n</ul>\\n<pre><code class=\\"lang-\\">exports.onExecutePostLogin = async (event, api) =&gt; {\\n const namespace = 'https://mytesttest.com';\\n const project = event.user.user_metadata.project;\\n const role = event.user.user_metadata.role;\\n\\n //if (event.authorization) { 这里为简便起见忽略了验证部分,实际项目中可根据安全需要对applications -&gt;api -&gt; permission中限定的scope进行验证,允许访问相应的内容时,才将用户的相关信息放入IDToken\\n // Set claims \\n api.idToken.setCustomClaim(`\${namespace}/project`, event.user.user_metadata.project);\\n api.idToken.setCustomClaim(`\${namespace}/role`, event.user.user_metadata.role);\\n //}\\n};\\n</code></pre>\\n<h4><a id=\\"23_IDToken_106\\"></a><strong>2.3 IDToken获取验证</strong></h4>\\n<p>下面我们验证从 Auth0 中获取的 IDToken</p>\n<p>Auth0 提供了 Authentication API Debugger 可以方便地查看获取到的 IDToken。对创建的 API application 使用如下的 authorizer 接口进行访问(下面的{{}}部分用 API application 产生的 domain 和 clientid 替换),rediect_url 为 Authentication API Debugger 的 URL。</p>\n<p>https://{{ApplicationDomain}}/authorize?responsetype=idtoken token&amp;responsemode=formpost&amp;clientid={{CLINTID}}&amp;redirecturi=https://dev-gzu7esjn.us.webtask.run/auth0-authentication-api-debugger&amp;scope=openid profile&amp;state=test&amp;nonce=test&amp;audience=<a href=\\"https://oauth0api.com/\\" target=\\"_blank\\">https://oauth0api.com</a></p>\\n<p>可以看到返回的 IDToken 部分包含我们之前定义的用户属性信息( project 和role )。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/90be455c172f4acdb2ba08c12bb15246_image.png\\" alt=\\"image.png\\" /></p>\n<h3><a id=\\"3_118\\"></a><strong>3.动态策略生成器实现</strong></h3>\\n<p>企业身份管控平台在 Auth0 完成身份验证并获得 IDToken 后,我们使用 Amazon API Gateway 的 Lambda Authorizer 构建策略生成器,由于请求处理和策略生成。Lambda Authorizer 是 API Gateway 的一个功能,该功能使用 Lambda 函数来控制对 API Gateway 的访问。在该 Lambda 函数中我们主要完成了两项工作:首先,根据持有者令牌(如 OAuth 或 SAML)验证用户身份的合法性,验证通过即可允许 API Gateway 的调用;同时对合法用户,根据用户的上下文信息结合策略模版动态生成访问策略,并将生成的策略传递给后端的业务 Lambda,业务 Lambda 根据这些 policy 通过 AssumeRole 获得临时凭证来生成 Amazon console URL;也可以构建业务 Lambda 通过临时凭证完成对后端资源的安全访问。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/15e5fff932d94858a4dfc2e73b1e3fd3_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"31_Authorizer_Lambda__APIGateway_123\\"></a><strong>3.1构建具有 Authorizer Lambda 的 APIGateway</strong></h4>\\n<p>实现中,我们构建了名为“ auth0api ”的 Rest APIGateway,调用后端名为 “mytest” 的业务 Lambda,业务 Lambda 生成访问亚马逊云控制台的 URL 通过 APIGateway 返回给前端的 client app/browser。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/99580c0ee9694497b311a737b0c95223_image.png\\" alt=\\"image.png\\" /></p>\n<p>下图展示了为该 APIGateway 添加 Authorizer Lambda,选择 Token 作为 Lambda Event 负载,选择认证后的结果信息在 APIGateway 中保留 3s,期间不需要再次通过 Authorizer 做认证。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/4573627db81e430da2cd409692605beb_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"32_Authorizer_Lambda__132\\"></a><strong>3.2 Authorizer Lambda 的实现</strong></h4>\\n<p>该函数是动态策略生成器的核心部分。它负责获取 APIGateway 中的 IDToken 信息,校验 IDToken 的合法性。</p>\n<p>完整的代码可以参考 https://github.com/1559550282/AWS/tree/main/MultiCloud-identityPrivControl</p>\n<pre><code class=\\"lang-\\">def lambda_handler(event, context):\\n #从Event中获取APIGateway中传来的IDToken\\n token = event['authorizationToken'].split(&quot; &quot;)\\n if (token[0] != 'Bearer'):\\n raise Exception('Authorization header should have a format Bearer &lt;JWT&gt; Token')\\n jwt_bearer_token = token[1] \\n unauthorized_claims = jwt.get_unverified_claims(jwt_bearer_token) \\n #并从IDToken中获得project和role的属性\\n project_attri = unauthorized_claims['https://mytesttest.com/project']\\n accessrole_attri = unauthorized_claims['https://mytesttest.com/role']\\n \\n #从Auth0中创建的API Application中获得jwt签名的公共密钥\\n keys_url = 'https://dev-gzu7esjn.us.auth0.com/.well-known/jwks.json' \\n with urllib.request.urlopen(keys_url) as f:\\n response = f.read()\\n keys = json.loads(response.decode('utf-8'))['keys']\\n\\n #用公共密钥校验IDToen中的签名部分,并核对IDToken中的aud是否为Auth0中创建的API Application对应的ClientID,是则认为用户校验通过。该部分代码篇幅原因删略,如需要请参见github链接\\n response = validateJWT(jwt_bearer_token, appclientid, keys)\\n \\n #get authenticated claims\\n if (response == False):\\n raise Exception('Unauthorized')\\n else:\\n principal_id = response[&quot;sub&quot;]\\n \\n #生成APIGateway的policy--authResponse,通过Authorizer lambda 返回给apigw,决定是否允许调用APIGateway\\n tmp = event['methodArn'].split(':')\\n api_gateway_arn_tmp = tmp[5].split('/')\\n aws_account_id = tmp[4] \\n policy = AuthPolicy(principal_id, aws_account_id)\\n policy.restApiId = api_gateway_arn_tmp[0]\\n policy.region = tmp[3]\\n policy.stage = api_gateway_arn_tmp[1]\\n policy.allowAllMethods() \\n authResponse = policy.build()\\n \\n #同时,获取IDToken中的用户属性信息,结合策略模版为后端的业务lambda生成动态policy:deliverpolicy,通过上下文传给后端业务lambda\\n deliverpolicy = policyGenerator(project_attri, accessrole_attri)\\n ##将生成的动态policy向后端业务传递\\n context = {\\n 'policy': deliverpolicy\\n }\\n authResponse['context'] = context\\n return authResponse\\n \\n## 根据用户的属性信息从DynamoDB中取出策略模版,使用策略模版动态生成policy。模版替换使用mastache的python版本实现。 \\ndef policyGenerator(project, projrole):\\n dynamodb = boto3.client('dynamodb')\\n response = dynamodb.get_item(\\n TableName=&quot;policytemplate&quot;,\\n Key={\\n 'project': {'S': 'project'},\\n 'role': {'S': 'projrole'}\\n }\\n )\\n thecontext = {'region':REGIONID,'accountid':ACCOUNTID,'project':project}\\n temppolicy = pystache.render(response['Item']['policy']['S'],thecontext)\\n return temppolicy\\n</code></pre>\\n<h4><a id=\\"33__199\\"></a>**3.3 构建业务</h4>\\n<p>Lambda<br />\\n生成符合权限管控的亚马逊云控制台**<br />\\nAthorizer Lambda<br />\\n生成了 APIGateway 的访问策略和后端业务所需的动态策略,返回给 APIGateway。后端业务 Lambda 与 APIGateway 的集成应注意使用 “Lambda proxy Integration” ,以便业务 Lambda 可以从 event 参数中获取 APIGateway 传递的从 Athorizer Lambda 获得的动态 policy。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/4b7b194957434b21b71074337921653b_image.png\\" alt=\\"image.png\\" /></p>\n<p>业务 Lambda 生成亚马逊云控制台 URL 的代码片段:</p>\n<pre><code class=\\"lang-\\">def lambda_handler(event, context):\\n ##由于Amazon console不支持角色串联(参见https://aws.amazon.com/cn/premiumsupport/knowledge-center/iam-role-chaining-limit/),因此示例中使用了IAM user的AKSK再通过assumerole的方式挂载policy\\n session = boto3.session.Session(aws_access_key_id=&quot;xxxxx&quot;, aws_secret_access_key=&quot;xxxx&quot;)\\n sts_connection = session.client('sts')\\n ##在APIGateway传递来的event中获取由authorizer lambda生成的动态policy\\n thepolicy = event['requestContext']['authorizer']['policy']\\n \\n ##由于assume_role最终生成的临时凭证是基础role_arn和policy的合集,因此示例中选用的较大权限。实际生产中可以根据需要选择合理范围。同时注意对role_arn创建对指定aksk的用户的信任策略,以便assumerole具有权限。\\n role_arn = &quot;arn:aws:iam::{}:role/adminrole&quot;.format(aws_account_id)\\n assumed_role = sts_connection.assume_role(\\n RoleArn=role_arn,\\n RoleSessionName=&quot;console-session&quot;,\\n Policy=thepolicy\\n )\\n credentials = assumed_role[&quot;Credentials&quot;]\\n # 获得临时凭证的AKSK和session Token\\n url_credentials = {}\\n url_credentials['sessionId'] = credentials['AccessKeyId']\\n url_credentials['sessionKey'] = credentials['SecretAccessKey']\\n url_credentials['sessionToken'] = credentials[&quot;SessionToken&quot;]\\n json_string_with_temp_credentials = json.dumps(url_credentials)\\n \\n #用临时凭证构建Amazon console\\n request_parameters = &quot;?Action=getSigninToken&quot;\\n request_parameters += &quot;&amp;SessionDuration=43200&quot;\\n if sys.version_info[0] &lt; 3:\\n def quote_plus_function(s):\\n return urllib.quote_plus(s)\\n else:\\n def quote_plus_function(s):\\n return urllib.parse.quote_plus(s)\\n \\n request_parameters += &quot;&amp;Session=&quot; + quote_plus_function(json_string_with_temp_credentials)\\n request_url = &quot;https://signin.aws.amazon.com/federation&quot; + request_parameters\\n r = requests.get(request_url)\\n signin_token = json.loads(r.text)\\n\\n request_parameters = &quot;?Action=login&quot;\\n request_parameters += &quot;&amp;Issuer=Example.org&quot;\\n request_parameters += &quot;&amp;Destination=&quot; + quote_plus_function(&quot;https://console.aws.amazon.com/&quot;)\\n request_parameters += &quot;&amp;SigninToken=&quot; + signin_token[&quot;SigninToken&quot;]\\n request_url = &quot;https://signin.aws.amazon.com/federation&quot; + request_parameters\\n \\n return {\\n 'statusCode': 200,\\n 'body': json.dumps(request_url)\\n }\\n</code></pre>\\n<h4><a id=\\"4__259\\"></a><strong>4. 验证</strong></h4>\\n<p>使用2.3节获得的 IDToken 调用 APIGateway</p>\n<p>curl –request GET –url https://{{APIID}}.execute-api.ap-southeast-1.amazonaws.com/default/mytest –header ‘authorization: Bearer {{IDToken}}’</p>\n<p>即可获得访问 Amazon console 的 url 如下:</p>\n<p><a href=\\"https://signin.aws.amazon.com/federation?Action=login&amp;Issuer=Example.org&amp;Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&amp;SigninToken=xxxx\\" target=\\"_blank\\">https://signin.aws.amazon.com/federation?Action=login&amp;Issuer=Example.org&amp;Destination=https%3A%2F%2Fconsole.aws.amazon.com%2F&amp;SigninToken=xxxx</a></p>\\n<p>该 URL 的有效期可以在 assume_role 中设定从 15min 到12小时的时长。</p>\n<h3><a id=\\"_270\\"></a><strong>总结</strong></h3>\\n<p>至此,我们介绍了基于 RBAC、ABAC 和动态策略生成这三种方式实现统一身份认证平台,完成用户属性与 IAM 角色的对应,从而构建统一身份管控平台与 Amazon 控制台的 SSO 和统一授权。</p>\n<p>实际场景中,如果用户多种属性的组合数量非常有限,推荐使用 RBAC 实现属性与 IAM 角色的直接映射,简单快捷;如果不同属性的用户使用的资源类型整齐划一,则可以使用 ABAC 通过会话标签和资源标签实现 IAM 角色的限定;但如果用户多种属性的组合过于复杂,而且不同类型的用户使用的资源需求不尽相同,则可以使用动态策略生成的方式,通过策略模版生成动态策略,实现灵活的权限隔离。</p>\n<h3><a id=\\"_275\\"></a><strong>本篇作者</strong></h3>\\n<p><img src=\\"https://dev-media.amazoncloud.cn/cb1d722d177c454ca615eaeac20b2a68_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"_279\\"></a><strong>倪惠青</strong></h4>\\n<p>Amazon 解决方案架构师,负责基于 Amazon 云计算方案架构的咨询和设计,在国内推广 Amazon 云平台技术和各种解决方案。在加入 Amazon 之前曾在 Oracle,Microsoft 工作多年,负责企业公有云方案咨询和架构设计,在基础架构及大数据方面有丰富经验。</p>\n<p><img src=\\"https://dev-media.amazoncloud.cn/4e620f2fa9424cccb1b133538022146e_image.png\\" alt=\\"image.png\\" /></p>\n<h4><a id=\\"_285\\"></a><strong>任耀洲</strong></h4>\\n<p>Amazon 解决方案架构师,负责企业客户应用在 Amazon 的架构咨询和设计。在微服务架构设计、数据库等领域有丰富的经验。</p>\n"}
目录
亚马逊云科技解决方案 基于行业客户应用场景及技术领域的解决方案
联系亚马逊云科技专家
亚马逊云科技解决方案
基于行业客户应用场景及技术领域的解决方案
联系专家
0
目录
关闭