分类目录归档:IAM

限制AccessKey使用范围的source ip

有时候我们需要限制一下某些权限较高的用户使用权限的方式,来增强安全性。
我给个方案,可使AWS管理员的权限使用起来更安全:
(1)创建用户wangfei,不要AccessKey,管理员权限,设置密码(设置了密码才能web console登录),只用作浏览器操作,然后使用MFA绑定手机,这样可以在任何地点用web console来操作,绑定了手机验证登录,而且还没有AccessKey,比较安全。
(2)创建wangfei-api用户,不设置密码,无web console登录权限,创建AccessKey, 只用作API操作, 然后授权,然后加入如下这个策略来限制AccessKey使用的来源IP

{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Deny",
    "Action": "*",
    "Resource": "*",
    "Condition": {
      "NotIpAddress": {
        "aws:SourceIp": ["114.255.34.0/24", "114.247.160.128/25"]
      }
    }
  }]
}

在白名单IP范围内正常操作
在有权限但不在IP白名单内地方执行API操作时会报错
A client error (UnauthorizedOperation) occurred when calling the DescribeVpcs operation: You are not authorized to perform this operation.

用CodeDeploy来做部署、更新和回退

一、准备工作和先决条件

(1)被部署的实例,需要安装codedeploy-agent,安装方法

git clone https://github.com/aws/aws-codedeploy-agent.git
cd aws-codedeploy-agent/bin
./install auto

或者用我从安装过程中截获的rpm包安装也行,分享给大家wget即可
http://cypay-filesharing.s3.amazonaws.com/public/wangfei/codedeploy-agent-1.0-1.643.noarch.rpm
(2)创建一个存放代码包的S3 bucket,此例中叫s3://CYPayCodeDeployBucket,美东地区
(3)创建一个Instance Profile,并使新创建的EC2实例使用该Instance Profile,这个过程中还需要创建个IAM Role,并让Role与Profile关联,该过程授权EC2实例内部的CodeDeploy-Agent能从S3下载版本包。然后再创建一个Service Role给Codedeploy服务用,该服务需要一些EC2权限

大概创建过程:创建个IAM Role,授权AssumeRole和访问版本存放S3,再创建个Instance Profile,然后将之前的Role关联到Profile上;再创建个Role给CodeDeploy服务用;
具体过程:
创建文件 CodeDeployInstanceProfile-Trust.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": "ec2.amazonaws.com"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

再创建一个文件CodeDeployInstanceProfile-Permissions.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "s3:Get*",
        "s3:List*"
      ],
      "Effect": "Allow",
      "Resource":"arn:aws:s3:::CYPayCodeDeployBucket/*"
    }
  ]
}

然后创建角色叫CodeDeployInstanceRole,并给Role加策略

aws iam create-role \
    --role-name CodeDeployInstanceRole \
    --assume-role-policy-document file://CodeDeployInstanceProfile-Trust.json
aws iam put-role-policy \
    --role-name CodeDeployInstanceRole \
    --policy-name CDInstanceRole-Permissions \
    --policy-document file://CodeDeployInstanceProfile-Permissions.json

然后创建instance profile,并把刚才创建的Role附加到instance profile上,这样使用这个instance profile创建的EC2实例,就拥有Role对应的权限和策略

aws iam create-instance-profile \
    --instance-profile-name CodeDeployInstanceProfile
aws iam add-role-to-instance-profile \
    --instance-profile-name CodeDeployInstanceProfile \
    --role-name CodeDeployInstanceRole

至此,一个具有codedeploy相关权限的Instance Profile就创建好了,创建自动部署的EC2实例的时候需要使用该Profile

接下来创建一个Service Role 这个是codedeploy服务用来在EC2实例中部署代码的
创建文件 CodeDeployServiceRole-Trust.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "",
      "Effect": "Allow",
      "Principal": {
        "Service": [
            "codedeploy.us-east-1.amazonaws.com",
            "codedeploy.us-west-2.amazonaws.com"
        ]
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

再创建一个文件CodeDeployServiceRole-Permissions.json

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Action": [
        "autoscaling:PutLifecycleHook",
        "autoscaling:DeleteLifecycleHook",
        "autoscaling:RecordLifecycleActionHeartbeat",
        "autoscaling:CompleteLifecycleAction",
        "autoscaling:DescribeAutoscalingGroups",
        "autoscaling:PutInstanceInStandby",
        "autoscaling:PutInstanceInService",
        "ec2:Describe*"
      ],
      "Effect": "Allow",
      "Resource": "*"
    }
  ]
}

创建一个角色CodeDeployServiceRole,然后将角色策略与角色相关联

aws iam create-role \
    --role-name CodeDeployServiceRole \
    --assume-role-policy-document file://CodeDeployServiceRole-Trust.json
aws iam put-role-policy \
    --role-name CodeDeployServiceRole \
    --policy-name CDServiceRole-Permissions \
    --policy-document file://CodeDeployServiceRole-Permissions.json

获取Role的相关信息,复制下来备用

aws iam get-role --role-name CodeDeployServiceRole --query "Role.Arn" --output text
arn:aws:iam::154xxxxx7698:role/CodeDeployServiceRole # mark this down

第(3)步总结:
创建两个Role
CodeDeployInstanceRole
CodeDeployServiceRole
创建一个InstanceProfile
CodeDeployInstanceProfile
注意事项:
Instance Profile必须创建实例的时候选择,事后暂无办法让没选InstanceProfile的实例使用某个Profile
CodeDeploy-agent目前只在最新版的Amazon Linux,Ubuntu,Windows 上测试过,而且自带yum源中没有

二、用CodeDeploy部署个新项目
(1)上一步已经创建好使用CodeDeployInstanceProfile这个Profile的EC2实例,并且打好标签的(例如Name=cms.front),而且实例上已经装好了codedeploy-agent
(2)准备代码,此处用tomcat应用举例。
解压代码,在工程目录目录里添加scripts目录和一个appspec.yml,例如下面这样的目录结构,scripts里面放部署时使用的脚本,appspec.yml文件定义代码部署到哪个,用什么用户在什么阶段执行什么脚本

cms.front/
├── appspec.yml
├── css/
├── error.jsp
├── fonts/
├── html/
├── images/
├── index.jsp
├── js/
├── META-INF/
├── scripts/
├── static/
└── WEB-INF/

appspec.yml文件内容,格式要求非常严格,不允许多余空白

version: 0.0
os: linux
files:
  - source: /
    destination: /home/ec2-user/tomcat/webapps/
hooks:
  BeforeInstall:
    - location: scripts/check_dependencies.sh
      timeout: 300
      runas: root
  AfterInstall:
    - location: scripts/change_permissions.sh
      timeout: 300
      runas: root
  ApplicationStart:
    - location: scripts/start_server.sh
      timeout: 300
      runas: ec2-user
  ApplicationStop:
    - location: scripts/stop_server.sh
      timeout: 300
      runas: ec2-user

首次部署,需要安装依赖软件和环境,之后做代码更新时就不需要了
可以把启动脚本写成重启脚本
这四个脚本我就不多说了,自定义性比较强
然后重新打包,只包含内容,不要将上级目录也打进包
cd ~/version/cms.front/ && zip -r cms.front.v1.0.zip *
然后将版本包上传到上述创建的Bucket里CYPayCodeDeployBucket
官方文档中介绍用aws deploy push上传,但是报错了,也没找到解决办法,就用aws s3 cp上传的
Missing required parameter in input: "UploadId"
Unknown parameter in input: "uploadId", must be one of: Bucket, Key, UploadId

更新aws-cli也无用,有解决此错误的请告诉我
然后创建一个Application,理解成一个具有相同功能的一个代码工程,此例为cms.front

aws deploy create-application --application-name CMSTomcatApplication

然后将重新打包好的版本包push到S3上(这步报错了,所以手动传的)

aws deploy push \
  --application-name CMSTomcatApplication \
  --s3-location s3://CYPayCodeDeployBucket/cms.front.v1.0.zip \
  --ignore-hidden-files

然后创建DeploymentGroup,理解成一组需要被部署相同代码的EC2实例,此例为cms.tomcat,按Tag来区分,或则按autoscal组来区分

aws deploy create-deployment-group \
  --application-name CMSTomcatApplication \
  --deployment-group-name CMSTomcatApplication \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --ec2-tag-filters Key=Name,Value=cms.tomcat,Type=KEY_AND_VALUE \
  --service-role-arn arn:aws:iam::15xxxxxx698:role/CodeDeployServiceRole

最后一行为上面创建的ServiceRole(让吧返回值记录下来来着,现在用到了)
然后创建一个部署,首次部署,跟代码更新部署,和回滚部署,本质是一样,没区别

aws deploy create-deployment \
  --application-name CMSTomcatApplication \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --deployment-group-name CMSTomcatApplication \
  --s3-location bucket=CYPayCodeDeployBucket,bundleType=zip,key=cms.front.v1.0.zip

返回值

{
    "deploymentId": "d-ROL5D7YPZ"
}

可以查看该部署的状态

aws deploy get-deployment --deployment-id d-ROL5D7YPZ --query 'deploymentInfo.status'
"InProgress" 

等进度从InProgress变成Succeeded就成功了,然后浏览器访问下自己的代码是否OK

三、 做次代码更新和回滚
本质是一样的,只不过版本包不同而已,可以吧首次部署的一些无用脚本删除,回滚的话,会删除就得部署新的
命令行的话,还是这样

aws deploy create-deployment \
  --application-name CMSTomcatApplication \
  --deployment-config-name CodeDeployDefault.OneAtATime \
  --deployment-group-name CMSTomcatApplication \
  --s3-location bucket=CYPayCodeDeployBucket,bundleType=zip,key=cms.front.v1.1.zip

区别在于使用的版本包版本不同
版本更新和回退,使用web console用浏览器来做最爽
选择你的Application,进到DeploymentGroup里,然后选择Deploy New Revision
此时Application和DeploymentGroup的下拉菜单应该已经都默认选好了,还可以改
选择Revision Type,可选为S3或者github,此例为S3 bucket
Revision Location里写上版本包的全路径,s3://CYPayCodeDeployBucket/cms.front.v1.1.zip
File Type会自动识别,支持zip,tar,tar.gz,别的格式没试过,war跟zip一样的吧
Deployment Description写上版本说明
Deployment Config有三种可选
CodeDeployDefault.AllAtOnce, CodeDeployDefault.HalfAtATime, CodeDeployDefault.AllAtATime.
AllAtOnce:组中所有实例一次部署完(同时并发?),有成功的实例就算此次部署成功,都部署失败就算此次部署失败
HalfAtATime:一次部署一半实例,分两次部署完,一半以上实例成功才算部署成功,一半以上失败就算部署失败
AllAtATime:一次部署一台,直至全部完成,所有实例都部署成功才算成功,有失败的就算部署失败(这个最符合我的需求,可以在起服脚本中加上sleep来实现一台一台来更新,并间隔几百秒)
点击Deploy Now来完成部署,等到刷新状态为Succeeded就成功了,接下来就是测试

总结:
创建好Application和DeploymentGroup之后,跟配置管理工程师协定好往版本中添加的文件,然后按时间+项目+版本号的方式打包,并上传到S3上,然后代码的更新回退,由测试和开发同学在浏览器上就能完成,省的自己写代码更新平台了
上新项目的时候,也可以直接利用这个服务来实现,首次部署脚本是得写的复杂点,装上依赖软件和设置环境
用好之后,甚至能不用碰ssh工具就能在浏览器或者用代码把活干完

AWS跨账号授权访问对方资源

假设A账号要控制B账号资源,但是B账号不想给A账号创建专用IAM用户,这是就可以用role来给A账号中的用户受临时权限。

具体方案为:
(1)    在B账号(被控制账号)中创建个角色,选择Role for Cross-Account Access,然后选择Provide access between AWS accounts you own,然后在Account ID里写上被授权账号,也就是A账号的AccountID
(2)    然后在B账号中给这个角色授权,这个跟给IAM用户授权方法一样
(3)    然后在A账号选择允许执行跨账号权限的组或者用户,然后授权给这些组或者用户,授权json如下:

{
  "Version": "2012-10-17",
  "Statement": [{
      "Effect": "Allow",
      "Action": "sts:AssumeRole",
      "Resource": "arn:aws:iam::AccounId-A:role/role-name"
    }]
}

这个是给A账号中的用户或者组执行AssumeRole(成为某角色)的权限
(4)    然后A账号中的用户,就可以通过AssumeRole的API来执行B账号中这个角色的权限
(5)    A中的用户控制A中的资源,还是原来的方法,A中的用户控制B中的资源时,需要先使用AssumeRole API获取角色的临时权限

给个python调用的例子

#!/usr/bin/env python
import boto
from boto.sts import STSConnection
from boto.s3.connection import S3Connection

## 需要提前写好A账号中用户的boto的授权配置文件~/.boto
sts_connection = STSConnection()
assumedRoleObject = sts_connection.assume_role(
    role_arn="arn:aws:iam::account-of-role-to-assume:role/name-of-role",
    role_session_name="AssumeRoleSession1"
)
# Use the temporary credentials returned by AssumeRole to call Amazon S3 
# and list all buckets in the account that owns the role (the trusting account)

## AssumeRole API 返回角色提供的临时AccessKey Pairs
s3_connection = S3Connection(
    aws_access_key_id=assumedRoleObject.credentials.access_key,
    aws_secret_access_key=assumedRoleObject.credentials.secret_key,
    security_token=assumedRoleObject.credentials.session_token
)
bucketList = s3_connection.get_all_buckets()
for bucket in bucketList:
    print bucket.name