AWS系列:深入了解IAM和访问控制
2017-09-26 17:05
555 查看
写在前面:访问控制,换句话说,谁能在什么情况下访问哪些资源或者操作,是绝大部分应用程序需要仔细斟酌的问题。作为一个志存高远的云服务提供者,AWS自然也在访问控制上下了很大的力气,一步步完善,才有了今日的IAM:Identity and Access Management。如果你要想能够游刃有余地使用AWS的各种服务,在安全上的纰漏尽可能地少,那么,首先需要先深入了解 IAM。
IAM enables you to control who can do what in your AWS account.
它提供了用户(users)管理、群组(groups)管理、角色(roles)管理和权限(permissions)管理等供AWS的客户来管理自己账号下面的资源。
1、首先说用户(users)。在AWS里,一个IAM user和unix下的一个用户几乎等价。你可以创建任意数量的用户,为其分配登录AWS management console所需要的密码,以及使用AWS CLI(或其他使用AWS SDK的应用)所需要的密钥。你可以赋予用户管理员的权限,使其能够任意操作AWS的所有服务,也可以依照Principle of least privilege,只授权合适的权限。下面是使用AWS CLI创建一个用户的示例:
当然,这样创建的用户是没有任何权限的,甚至无法登录,你可以用下面的命令进一步为用户关联群组,设置密码和密钥:
2、群组(groups)也等同于常见的unix group。将一个用户添加到一个群组里,可以自动获得这个群组所具有的权限。在一家小的创业公司里,其AWS账号下可能会建立这些群组:
Admins:拥有全部资源的访问权限
Devs:拥有大部分资源的访问权限,但可能不具备一些关键性的权限,如创建用户
Ops:拥有部署的权限
Stakeholders:拥有只读权限,一般给manager查看信息之用
创建一个群组很简单:
然而,这样的群组没有任何权限,我们还需要为其添加policy:
在前面的例子和这个例子里,我们都看到了ARN这个关键字。ARN是Amazon Resource Names的缩写,在AWS里,创建的任何资源有其全局唯一的ARN。ARN是一个很重要的概念,它是访问控制可以到达的最小粒度。在使用AWS SDK时,我们也需要ARN来操作对应的资源。
policy是描述权限的一段JSON文本,比如AdministratorAccess这个policy,其内容如下:
用户或者群组只有添加了相关的policy,才会有相应的权限。
3、角色(roles)类似于用户,但没有任何访问凭证(密码或者密钥),它一般被赋予某个资源(包括用户),使其临时具备某些权限。比如说一个EC2实例需要访问DynamoDB,我们可以创建一个具有访问DynamoDB权限的角色,允许其被EC2 Service代入(AssumeRule),然后创建EC2的instance-profile使用这个角色。这样,这个EC2实例就可以访问DynamoDB了。当然,这样的权限控制也可以通过在EC2的文件系统里添加AWS配置文件设置某个用户的密钥(AccessKey)来获得,但使用角色更安全更灵活。角色的密钥是动态创建的,更新和失效都无须特别处理。想象一下如果你有成百上千个EC2实例,如果使用某个用户的密钥来访问AWS
SDK,那么,只要某台机器的密钥泄漏,这个用户的密钥就不得不手动更新,进而手动更新所有机器的密钥。这是很多使用AWS多年的老手也会犯下的严重错误。
4、最后是权限(permissions)。AWS下的权限都通过policy document描述,就是上面我们给出的那个例子。policy是IAM的核心内容,我们稍后详细介绍。
每年的AWS re:invent大会,都会有一个session:Top 10 AWS IAM Best Practices,感兴趣的读者可以去YouTube搜索。2015年的top
10(top 11)如下:
users: create individual users
permissions: Grant least priviledge
groups: manage permissions with groups
conditions: restrict priviledged access further with conditions
auditing: enable cloudTrail to get logs of API calls
password: configure a strong password policy
rotate: rotate security credentials regularly.
MFA: enable MFA (Multi-Factor Authentication) for priviledged users
sharing: use IAM roles to share access
roles: use IAM roles for EC2 instances
root: reduce or remove use of root
这11条best practices很清晰,我就不详细解释了。
按照上面的原则,如果一个用户只需要访问AWS management console,那么不要为其创建密钥;反之,如果一个用户只使用AWS CLI,则不要为其创建密码。一切不必要的,都是多余的——这就是安全之道。
前面我们看到,policy是用JSON来描述的,主要包含Statement,也就是这个policy拥有的权限的陈述,一言以蔽之,即:谁在什么条件下能对哪些资源的哪些操作进行处理。也就是所谓的撰写policy的PARCE原则:
Principal:谁
Action:哪些操作
Resource:哪些资源
Condition:什么条件
Effect:怎么处理(Allow/Deny)
我们看一个允许对S3进行只读操作的policy:
其中,Effect是Allow,允许policy中所有列出的权限,Resource是
在这个policy里,Principal和Condition都没有出现。如果对资源的访问没有任何附加条件,是不需要Condition的;而这条policy的使用者是用户相关的principal(users, groups, roles),当其被添加到某个用户身上时,自然获得了principal的属性,所以这里不必指明,也不能指明。
所有的IAM managed policy是不需要指明Principal的。这种policy可以单独创建,在需要的时候可以被添加到用户、群组或者角色身上。另一大类policy是Resource policy,它们不能单独创建,只能依附在某个资源之上(所以也叫inline policy),这时候,需要指明Principal。比如,当我希望对一个S3 bucket使能Web hosting时,这个bucket里面的对象自然是要允许外界访问的,所以需要如下的inline policy:
这里,我们对于
有时候,我们希望能更加精细地控制用户究竟能访问资源下的哪些数据,这个时候,可以使用Condition。比如对一个叫tyrchen的用户,只允许他访问personal-files这个S3 bucket下和他有关的目录:
这里我们用到了
在一条Condition下并列的若干个条件间是and的关系,这里IPAddress和StringEquals这两个条件必须同时成立;在某个条件内部则是or的关系,这里10.0.0.0/8和4.4.4.4/32任意一个源IP都可以访问。这个条件最终的意思是:对于一个EC2实例,如果其department标签是dev,且访问的源IP是10网段的内网地址或者4.4.4.4/32这个外网地址,则Condition成立。
讲完了Condition,我们再回到之前的policy。
这条policy里有两个statement,前一个允许列出
因为这样的话,用户在AWS management console连在personal-files下列出
上面的policy可以被添加到用户tyrchen身上,这样他就可以访问自己的私人目录;如果我们创建一个新用户叫lindsey,也想做类似处理,则需要再创建一个几乎一样的policy,非常不符合DRY(Don’t Repeat Yourself)原则。好在AWS也考虑到了这一点,它支持policy variable,允许用户在policy里使用AWS预置的一些变量,比如
以上是policy的一些基础用法,下面讲讲policy的执行规则,它也是几乎所有访问控制方案的通用规则:
默认情况下,一切资源的一切行为的访问都是Deny
如果在policy里显式Deny,则最终的结果是Deny
否则,如果在policy里是Allow,则最终结果是Allow
否则,最终结果是Deny
如下图:
什么情况下我们会用到显式Deny呢?请看下面的例子:
在这个例子里,我们只允许用户访问DynamoDB和S3中的特定资源,除此之外一律不允许访问。我们知道一个用户可以有多重权限,属于多个群组。所以上述policy里的第一个statement虽然规定了用户只能访问的资源,但别的policy可能赋予用户其他资源的访问权限。所以,我们需要第二条statement封死其他的可能。按照之前的policy enforcement的规则,只要看见Deny,就是最终结果,不会考虑其他policy是否有Allow,这样杜绝了一些隐性的后门,符合Principle of least
privilege。
我们再看一个生产环境中可能用得着的例子,来证明IAM不仅「攘内」,还能「安外」。假设我们是一个手游公司,使用AWS Cognito来管理游戏用户。每个游戏用户的私人数据放置于S3之中。我们希望congito user只能访问他们各自的目录,IAM policy可以定义如下:
最后,讲一下如何创建policy。很简单:
其中policy-document就是如上所示的一个个JSON文件。
policy variables
policy evaluation logic
基本概念
按照 AWS 的定义:IAM enables you to control who can do what in your AWS account.
它提供了用户(users)管理、群组(groups)管理、角色(roles)管理和权限(permissions)管理等供AWS的客户来管理自己账号下面的资源。
1、首先说用户(users)。在AWS里,一个IAM user和unix下的一个用户几乎等价。你可以创建任意数量的用户,为其分配登录AWS management console所需要的密码,以及使用AWS CLI(或其他使用AWS SDK的应用)所需要的密钥。你可以赋予用户管理员的权限,使其能够任意操作AWS的所有服务,也可以依照Principle of least privilege,只授权合适的权限。下面是使用AWS CLI创建一个用户的示例:
saws> aws iam create-user --user-name tyrchen { "User": { "CreateDate": "2015-11-03T23:05:05.353Z", "Arn": "arn:aws:iam::<ACCOUNT-ID>:user/tyrchen", "UserName": "tyrchen", "UserId": "AIDAISBVIGXYRRQLDDC3A", "Path": "/" } }
当然,这样创建的用户是没有任何权限的,甚至无法登录,你可以用下面的命令进一步为用户关联群组,设置密码和密钥:
saws> aws iam add-user-to-group --user-name --group-name saws> aws iam create-login-profile --user-name --password saws> aws iam create-access-key --user-name
2、群组(groups)也等同于常见的unix group。将一个用户添加到一个群组里,可以自动获得这个群组所具有的权限。在一家小的创业公司里,其AWS账号下可能会建立这些群组:
Admins:拥有全部资源的访问权限
Devs:拥有大部分资源的访问权限,但可能不具备一些关键性的权限,如创建用户
Ops:拥有部署的权限
Stakeholders:拥有只读权限,一般给manager查看信息之用
创建一个群组很简单:
saws> aws iam create-group --group-name stakeholders { "Group": { "GroupName": "stakeholders", "GroupId": "AGPAIVGNNEGMEPLHXY6JU", "Arn": "arn:aws:iam::<ACCOUNT-ID>:group/stakeholders", "Path": "/", "CreateDate": "2015-11-03T23:15:47.021Z" } }
然而,这样的群组没有任何权限,我们还需要为其添加policy:
saws> aws iam attach-group-policy --group-name --policy-arn
在前面的例子和这个例子里,我们都看到了ARN这个关键字。ARN是Amazon Resource Names的缩写,在AWS里,创建的任何资源有其全局唯一的ARN。ARN是一个很重要的概念,它是访问控制可以到达的最小粒度。在使用AWS SDK时,我们也需要ARN来操作对应的资源。
policy是描述权限的一段JSON文本,比如AdministratorAccess这个policy,其内容如下:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": "*", "Resource": "*" } ] }
用户或者群组只有添加了相关的policy,才会有相应的权限。
3、角色(roles)类似于用户,但没有任何访问凭证(密码或者密钥),它一般被赋予某个资源(包括用户),使其临时具备某些权限。比如说一个EC2实例需要访问DynamoDB,我们可以创建一个具有访问DynamoDB权限的角色,允许其被EC2 Service代入(AssumeRule),然后创建EC2的instance-profile使用这个角色。这样,这个EC2实例就可以访问DynamoDB了。当然,这样的权限控制也可以通过在EC2的文件系统里添加AWS配置文件设置某个用户的密钥(AccessKey)来获得,但使用角色更安全更灵活。角色的密钥是动态创建的,更新和失效都无须特别处理。想象一下如果你有成百上千个EC2实例,如果使用某个用户的密钥来访问AWS
SDK,那么,只要某台机器的密钥泄漏,这个用户的密钥就不得不手动更新,进而手动更新所有机器的密钥。这是很多使用AWS多年的老手也会犯下的严重错误。
4、最后是权限(permissions)。AWS下的权限都通过policy document描述,就是上面我们给出的那个例子。policy是IAM的核心内容,我们稍后详细介绍。
每年的AWS re:invent大会,都会有一个session:Top 10 AWS IAM Best Practices,感兴趣的读者可以去YouTube搜索。2015年的top
10(top 11)如下:
users: create individual users
permissions: Grant least priviledge
groups: manage permissions with groups
conditions: restrict priviledged access further with conditions
auditing: enable cloudTrail to get logs of API calls
password: configure a strong password policy
rotate: rotate security credentials regularly.
MFA: enable MFA (Multi-Factor Authentication) for priviledged users
sharing: use IAM roles to share access
roles: use IAM roles for EC2 instances
root: reduce or remove use of root
这11条best practices很清晰,我就不详细解释了。
按照上面的原则,如果一个用户只需要访问AWS management console,那么不要为其创建密钥;反之,如果一个用户只使用AWS CLI,则不要为其创建密码。一切不必要的,都是多余的——这就是安全之道。
使用 policy 做访问控制
上述内容你若理顺,IAM 就算入了门。但真要把握好IAM的精髓,需要深入了解policy,以及如何撰写policy。前面我们看到,policy是用JSON来描述的,主要包含Statement,也就是这个policy拥有的权限的陈述,一言以蔽之,即:谁在什么条件下能对哪些资源的哪些操作进行处理。也就是所谓的撰写policy的PARCE原则:
Principal:谁
Action:哪些操作
Resource:哪些资源
Condition:什么条件
Effect:怎么处理(Allow/Deny)
我们看一个允许对S3进行只读操作的policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "s3:Get*", "s3:List*" ], "Resource": "*" } ] }
其中,Effect是Allow,允许policy中所有列出的权限,Resource是
*,代表任意S3的资源,Action有两个:
s3:Get*和
s3:List*,允许列出S3下的资源目录,及获取某个具体的S3 Object。
在这个policy里,Principal和Condition都没有出现。如果对资源的访问没有任何附加条件,是不需要Condition的;而这条policy的使用者是用户相关的principal(users, groups, roles),当其被添加到某个用户身上时,自然获得了principal的属性,所以这里不必指明,也不能指明。
所有的IAM managed policy是不需要指明Principal的。这种policy可以单独创建,在需要的时候可以被添加到用户、群组或者角色身上。另一大类policy是Resource policy,它们不能单独创建,只能依附在某个资源之上(所以也叫inline policy),这时候,需要指明Principal。比如,当我希望对一个S3 bucket使能Web hosting时,这个bucket里面的对象自然是要允许外界访问的,所以需要如下的inline policy:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Principal": "*", "Action": "s3:GetObject", "Resource": "arn:aws:s3:::corp-fs-web-bucket/*" } ] }
这里,我们对于
arn:aws:s3:::corp-fs-web-bucket/*这个资源的
s3:GetObject,允许任何人访问(
Principal: *)。
有时候,我们希望能更加精细地控制用户究竟能访问资源下的哪些数据,这个时候,可以使用Condition。比如对一个叫tyrchen的用户,只允许他访问personal-files这个S3 bucket下和他有关的目录:
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListBucket" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::personal-files" ], "Condition": { "StringLike": { "s3:prefix": [ "tyrchen/*" ] } } }, { "Action": [ "s3:GetObject", "s3:PutObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::personal-files/tyrchen/*" ] } ] }
这里我们用到了
StringLike这个Condition,只有当访问的
s3:prefix满足
tyrchen/*时,才为真。我们还可以使用非常复杂的Condition:
"Condition": { "IPAddress": {"aws:SourceIP": ["10.0.0.0/8", "4.4.4.4/32"]}, "StringEquals": {"ec2:ResourceTag/department": "dev"} }
在一条Condition下并列的若干个条件间是and的关系,这里IPAddress和StringEquals这两个条件必须同时成立;在某个条件内部则是or的关系,这里10.0.0.0/8和4.4.4.4/32任意一个源IP都可以访问。这个条件最终的意思是:对于一个EC2实例,如果其department标签是dev,且访问的源IP是10网段的内网地址或者4.4.4.4/32这个外网地址,则Condition成立。
讲完了Condition,我们再回到之前的policy。
这条policy里有两个statement,前一个允许列出
arn:aws:s3:::personal-files下prefix是
tyrchen/*里的任何对象;后一个允许读写
arn:aws:s3:::personal-files/tyrchen/*里的对象。注意这个policy不能写成:
{ "Version": "2012-10-17", "Statement": [ { "Action": [ "s3:ListObject", "s3:GetObject", "s3:PutObject" ], "Effect": "Allow", "Resource": [ "arn:aws:s3:::personal-files/tyrchen/*" ] } ] }
因为这样的话,用户在AWS management console连在personal-files下列出
/tyrchen这个根目录的机会都没有了,自然也无法进行后续的操作。这样的policy其权限是不完备的。
上面的policy可以被添加到用户tyrchen身上,这样他就可以访问自己的私人目录;如果我们创建一个新用户叫lindsey,也想做类似处理,则需要再创建一个几乎一样的policy,非常不符合DRY(Don’t Repeat Yourself)原则。好在AWS也考虑到了这一点,它支持policy variable,允许用户在policy里使用AWS预置的一些变量,比如
${aws:username}。上述的policy里,把
tyrchen替换为
${aws:username},就变得通用多了。
以上是policy的一些基础用法,下面讲讲policy的执行规则,它也是几乎所有访问控制方案的通用规则:
默认情况下,一切资源的一切行为的访问都是Deny
如果在policy里显式Deny,则最终的结果是Deny
否则,如果在policy里是Allow,则最终结果是Allow
否则,最终结果是Deny
如下图:
什么情况下我们会用到显式Deny呢?请看下面的例子:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": [ "dynamodb:*", "s3:*" ], "Resource": [ "arn:aws:dynamodb:AWS-REGION-IDENTIFIER:ACCOUNT-ID-WITHOUT-HYPHENS:table/EXAMPLE-TABLE-NAME", "arn:aws:s3:::EXAMPLE-BUCKET-NAME", "arn:aws:s3:::EXAMPLE-BUCKET-NAME/*" ] }, { "Effect": "Deny", "NotAction": [ "dynamodb:*", "s3:*" ], "NotResource": [ "arn:aws:dynamodb:AWS-REGION-IDENTIFIER:ACCOUNT-ID-WITHOUT-HYPHENS:table/EXAMPLE-TABLE-NAME", "arn:aws:s3:::EXAMPLE-BUCKET-NAME", "arn:aws:s3:::EXAMPLE-BUCKET-NAME/*" ] } ] }
在这个例子里,我们只允许用户访问DynamoDB和S3中的特定资源,除此之外一律不允许访问。我们知道一个用户可以有多重权限,属于多个群组。所以上述policy里的第一个statement虽然规定了用户只能访问的资源,但别的policy可能赋予用户其他资源的访问权限。所以,我们需要第二条statement封死其他的可能。按照之前的policy enforcement的规则,只要看见Deny,就是最终结果,不会考虑其他policy是否有Allow,这样杜绝了一些隐性的后门,符合Principle of least
privilege。
我们再看一个生产环境中可能用得着的例子,来证明IAM不仅「攘内」,还能「安外」。假设我们是一个手游公司,使用AWS Cognito来管理游戏用户。每个游戏用户的私人数据放置于S3之中。我们希望congito user只能访问他们各自的目录,IAM policy可以定义如下:
{ "Version": "2012-10-17", "Statement": [ { "Effect": "Allow", "Action": ["s3:ListBucket"], "Resource": ["arn:aws:s3:::awesome-game"], "Condition": {"StringLike": {"s3:prefix": ["cognito/*"]}} }, { "Effect": "Allow", "Action": [ "s3:GetObject", "s3:PutObject", "s3:DeleteObject" ], "Resource": [ "arn:aws:s3:::awesome-game/cognito/${cognito-identity.amazonaws.com:sub}", "arn:aws:s3:::awesome-gamecognito/${cognito-identity.amazonaws.com:sub}/*" ] } ] }
最后,讲一下如何创建policy。很简单:
$ aws iam create-policy --policy-name --policy-document
其中policy-document就是如上所示的一个个JSON文件。
参考文档
IAM policy overviewpolicy variables
policy evaluation logic
相关文章推荐
- AWS系列:深入了解IAM和访问控制
- Windows Server入门系列之十五 深入了解ping命令
- 【转载】深入了解scanf()/getchar()和gets()等函数,C++系列教程,C++实例教程,C++
- 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制
- 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制
- 深入了解ApusicAS服务器配置系列之——配置虚拟主机
- Spark入门实战系列--6.SparkSQL(中)--深入了解运行计划及调优
- http协议学习和总结系列——深入了解篇
- 深入了解C#系列:谈谈C#中垃圾回收与内存管理机制
- http协议学习系列之三——深入了解篇
- Selenium私房菜系列6 -- 深入了解Selenium RC工作原理(1)
- Android源码解析Handler系列第(三)篇---深入了解Android的消息机制
- 深入了解ApusicAS服务器配置系列之——SSL配置
- JNI学习系列——深入了解JNI
- RabbitMQ系列(二)深入了解RabbitMQ工作原理及简单使用
- [jvm解析系列][十二]分派,重载和重写,查看字节码带你深入了解分派的过程。
- (转)蜜果私塾:http协议学习和总结系列--深入了解篇
- Android知识点剖析系列:深入了解layout_weight属性
- Android知识点剖析系列:深入了解layout_weight属性
- 深入了解ApusicAS服务器配置系列之——配置Web上下文根