我正在使用 CloudFormation 创建 EC2 实例。我要做的第一件事是签出包含 puppet 清单的 git 存储库。为此,我需要一个 SSH 密钥。
将密钥发送到服务器的最佳方法是什么?这是我考虑过的:
- 使用 KMS,但这似乎不允许您“存储密钥以供日后使用”
- 使用 EC2 密钥对,但这似乎也不允许您稍后获取私钥
- 将密钥写入
UserData
属性,但(尽管它的名字如此)这似乎不适合存储任何类型的数据,更不用说敏感数据了 - 将其存储在 S3 存储桶中,但我不确定如何设置存储桶上的权限以允许 EC2 实例使用 aws cli 工具提取数据
这似乎是一件很常见的事情,但是我一定是在寻找错误的东西,因为我找不到合理的答案。
答案1
处理此问题的直接方法是将您的机密(如 SSH 密钥)存储在专用的 S3 存储桶中,然后授予 EC2 实例对该存储桶的访问权限。
您可以先创建一个 IAM 角色:
"DeploymentRole" : {
"Type" : "AWS::IAM::Role",
"Properties" : {
"Policies" : [{
"PolicyName" : "SecretsBucketPolicy",
"PolicyDocument" : {
"Version" : "2012-10-17",
"Statement" : [{
"Resource" : "arn:aws:s3:::wherever-the-secrets-are-stored/*",
"Action" : ["s3:GetObject"],
"Effect" : "Allow"
}]
}
}],
"Path" : "/",
"AssumeRolePolicyDocument" : {
"Version" : "2012-10-17",
"Statement" : [{
"Action" : ["sts:AssumeRole"],
"Principal" : {"Service": ["ec2.amazonaws.com"]},
"Effect" : "Allow"
}]
}
}
}
该角色定义了一个策略,允许其读取秘密存储桶,并允许 EC2 承担该角色。
然后为该角色创建一个实例配置文件:
"DeploymentProfile" : {
"Type" : "AWS::IAM::InstanceProfile",
"Properties" : {
"Roles" : [{"Ref" : "DeploymentRole"}],
"Path" : "/"
}
}
对于您的 EC2 实例或启动配置,您现在可以使用该IamInstanceProfile
属性将此配置文件分配给实例。
然后秘密存储桶就应该是可读的了。
答案2
如果您需要 OpsWorks 菜谱存储库或应用程序部署的 SSH 密钥,则 S3 存储桶方法不起作用。
另一个解决方案是,您可以添加一个 SSH 密钥类型的参数CommaDelimitedList
,其中换行符用逗号替换,然后Fn::Join
在需要的地方将密钥的行重新组合在一起。
CloudFormation 模板示例:
{
"Parameters": {
"CookbooksDeployKey": {
"Type": "CommaDelimitedList",
"Description": "Enter the deploy key as CSV (replace newlines with commas)",
"NoEcho": true
}
},
"Resources": {
"myStack": {
"Type": "AWS::OpsWorks::Stack",
"Properties": {
"CustomCookbooksSource": {
"Type": "git",
"Url": "[email protected]:user/repository.git",
"Revision": "master",
"SshKey": {"Fn::Join": ["\n", {"Ref": "CookbooksDeployKey"}]}
}
}
}
}
}
要生成私钥文件的单行“CSV”格式版本,可以使用以下 sed 命令(这只是用逗号替换文件中的所有换行符,并在标准输出上返回结果):
sed ':a;N;$!ba;s/\n/,/g' /home/user/.ssh/id_rsa
结果如下所示:
-----BEGIN RSA PRIVATE KEY-----,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX,-----END RSA PRIVATE KEY-----
然后,您可以在 CloudFormation 中创建或更新堆栈时将该值粘贴到参数中。