如何在不创建数百个IAM用户的情况下授予对AWS资源的访问权限?

2020-08-19 02:51:29

假设您是一家拥有100多名销售员工的公司的解决方案架构师,您正在从内部部署迁移到AWS云。您希望使用现有的员工身份验证系统,并希望将员工在日常工作中使用的文件存储在S3上。您不想将S3存储桶公开,这会将所有文件公开给每个人。您有两个选项:

为所有员工创建具有S3存储桶访问权限和登录凭据的单一角色。有了这个,用户必须使用2个不同的登录。一个用于访问其现有系统,另一个用于访问S3文件。

使用AWS安全令牌服务(STS)承担具有S3访问权限的角色,并使用该角色授予对文件的访问权限。用户仍将使用其现有系统进行身份验证。

在这篇文章中,我们将探索并实现选项2。请注意,我们是在上一篇文章的基础上构建这个示例的。

AWS安全令牌服务(AWS STS)是一项Web服务,使您可以为AWS Identity and Access Management(IAM)用户或您验证的用户请求临时、有限权限凭据。您可以对STS使用AssumeRole操作,该操作返回一组临时安全凭据,您可以使用这些凭据来访问您通常可能无权访问的AWS资源。这些临时凭据由访问密钥ID、秘密访问密钥和安全令牌组成。通常,您在帐户内使用AssumeRole或用于跨帐户访问。在我们的示例中,我们在同一帐户中使用AssumeRole。

在我们的示例中,我们将创建一个名为S3ReadOnlyAccessAssumeRole的角色。顾名思义,它只有S3读访问策略。我们还将创建信任策略并附加到此S3角色。信任策略将允许此角色由我们的lambda执行角色承担。下面是我们的SAM在这个角色中的样子。

IamS3Role:Type:aws::iam::Role Properties:AssumeRolePolicyDocument:Version:2012-10-17语句:-Effect:允许主体:aws:!GetAtt ShowFilesFunctionRole.Arn操作:-';sts:AssumeRole';描述:';Lamdba在运行时承担的只读S3角色';ManagedPolicyArns:-arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess角色名称。

在上面的代码片断中,AssumeRolePolicyDocument属性指定信任策略,该信任策略允许由原则aws:!GetAtt ShowFilesFunctionRole.Arn标识的Lamdba执行角色到此策略附加到的AssumeRole,即S3ReadOnlyAccessAssumeRole。ManagedPolicyArns属性指定S3ReadOnlyAccessAssumeRole的策略,该策略允许对S3存储桶进行只读访问。

现在,让我们编写将使用此角色的Lamdba处理程序。这是SAM的认证书。

ApiGatewayShowFilesApi:type:aws::Serverless::API Properties:Stagename:Prod Auth:UsagePlan:CreateUsagePlan:Per_API Description:此API的使用计划配额:Limit:500 Period:Month Throttle:BurstLimit:100 RateLimit:50 ShowFilesFunction:Type:Aws::Serverless::Function Properties:Environment:Variables:UserTable:!ref myDynamoDBTable s。:!ref myDynamoDBTable事件:getCounter:Type:API属性:Path:/showFiles方法:获取RestApiId:!ref ApiGatewayShowFilesApi。

我们在这里定义了几个参数,它们将被设置为Lambda的环境变量。使用Origin,我们指定CORS的原始域。这是我们的S3存储桶的虚拟托管风格的URL。FilesBucket是存储文件的存储桶。在无服务器函数定义中,它使用showfiles.py作为lambda处理程序。函数具有使用DB的权限。我们还使用path/showFiles为这个Lamdba创建了API。

让我们看看我们在Lambda处理程序中做了什么。我们修改了上一篇文章中的login.py lambda处理程序。一旦用户通过身份验证,我们将设置Cookie。这是完全可选的,并且确实不是STS工作所必需的,但是您可能需要某种系统来识别该用户是否已经通过身份验证。

如果deccryptedPass==pwd:Token=Secrets.Token_hex(16)Response=table.update_item(key={';userid';:uname},AttributeUpdate={';Token';:{';value';:Token,}})返回{';statusCode';200,';Header';:{';Set-Cookie。&;+Token+';;Secure;SameSite=None;HttpOnly;Domain=.amazonaws.com;Path=/';,';内容类型';:';Text/html';},';Body';:';<;html>;<;head>;<;script>;window.location.href=\';';+os.environ[';showFilesUrl';]+';\';<;/script>;<;/head>;<;body>;Hello<;/body>;<;/html>;';}否则:响应[';状态';]=';登录失败';返回{';状态代码';:200,';主体';:json.dump(响应)}。

当用户提交用户名和密码时,Login lambda处理程序将对用户进行身份验证,将唯一ID存储在数据库中,设置cookie以响应位置以显示S3存储桶中的showFiles url html页面。在页面加载时,浏览器会将位置更改为showfiles url,这将触发showFiles.py中定义的showFiles Lambda处理程序。

文件访问系统文件正在加载..。FETCH(";https://9nimlkmz74.execute-api.us-east-2.amazonaws.com/Prod/showFiles/";,{Credentials:';Include';}).Then(Response=&>;response.text()).Then((Body)=>;{var files=";;var obj=JSON.parse(Body)for(i=0;i<;obj.length;i++){FILES=FILES+";<;I class=';>;<;a href=';#';>;&;nbsp;&;nbsp;";+obj[i]+";<;/a>;&34;}Document.getElementById(";Files";).innerHTML=FILES}).catch(函数(错误){控制台日志(错误);});

我们调用showFiles API,它从S3存储桶中获取文件列表并显示在页面上。

Import json import Logging import boto3 import oslog=logging.getLogger()log.setLevel(logging.info)#返回登录cookie信息userid和唯一令牌def(Cookies):cookie中x的data={}if keyValue[0].strie()==';tkn';:cookieValue=keyValue[1]t。]=tKnValues[0]DATA[';TKN';]=tKNALUES[1]ELSE:cookieValue=';';return data#验证保存在数据库中的唯一令牌与请求def(Data)中的令牌:DynamoDB=boto3.resource(';DynamoDB';)table=dynamic odb.Table(os.environ[';userTable';])response=table.get_item(。]})json_str=json.dump(Response[';Item';])resp_dict=json.loads(Json_Str)内标识=resp_didic.get(";Token";)return bool(Token==data[';TKN';])#使用STS def()返回存储桶中的文件列表:sts_client=boto3.client(';STS'。)#调用STSConnection对象的Asmise_Role方法,并传递角色#ARN和角色会话名称。Assumed_Role_Object=STS_client.Assumed_Role(RoleArn=os.environ[';s3ole';],RoleSessionName=";AssumeRoleSession1";)#从包含假定角色的响应中获取可用于进行后续API调用的临时#凭据Credentials=AssumeRole_Object[';Credentials';]#使用AssumeRole返回到的临时凭据。AccessKeyId';],AWS_SECRET_ACCESS_KEY=Credentials[';SecretAccessKey';],AWS_SESSION_TOKEN=Credentials[';SessionToken';],)bucket=s3_resource ce.Bucket(os.environ[';filesBucket';])bucket中obj的files=[]。objects.all():files.append(obj.key。Cookie';].Split(";;";)Data=getLoginCookie(Cookie)isVerify=verifyLogin(Data)if(IsVerify):Response=getFilesList()Return{';statusCode';:200,';Header';:{';Content-Type&39;:';Application/json&39;,';Access-Control-Allow-Origin';:os.environ[';Origin&。],';Access-Control-Allow-Credentials';:';true';},';body';:json.dump(Response)}。

这里关注lamdba_handler函数。我们首先获得cookie并验证登录。如果经过验证,我们将调用getFilesList函数,STS的魔力就是在这里发生的。我们从Lambda环境变量中获得要假定的角色的ARN。ASSUME_ROLE函数返回包含访问密钥ID、秘密访问密钥和会话令牌的凭据。您可以使用此凭据获取S3资源并访问存储桶。我们以数组的形式创建文件列表,并将其作为响应发送。

在运行SAM Deploy之前,创建一个S3存储桶来存储html文件。在我们的例子中,它是stsexamplebucket。我们在SAM模板中使用此存储桶中的URL。在SAM Deploy上,它将生成带有3个API URL的输出。修改html文件以指向这些URL并上传到S3存储桶。一定要把那些文件公之于众。

您需要使用SAM模板中FilesBucket参数中指定的名称创建一个Bucket。此存储桶将存储文件以供显示。

总之,我们使用了一个自定义身份代理来对用户进行身份验证,然后使用AWS STS来允许这些用户访问AWS资源。您可能会想,我们是否可以将访问S3的权限授予Lambda执行角色,而不是使用STS。你当然可以,而且它会奏效的。但这里的想法是使用sts,您可以使用sts而不考虑lambda,即在您的其他应用程序中使用sts,如Spring boot、java、python应用程序等。在下一篇文章中,我们将进一步扩展这一点,使用s3签名的url来访问存储在s3中的文件。