问题
Is there anyway we can pass dynamic references to Secret Manager to AWS Launch Config User Data?
Here is the code snippet I tried:
"SampleLaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
"AMI"
]
},
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"yum update -y\n",
"useradd -p <<pwd>>{{resolve:secretsmanager:Credentials:SecretString:userName}}\n",
"\n"
]
]
}
}
}
}
Seems error in getting the useradd: invalid user name '{{resolve:secretsmanager:Credentials:SecretString:userName}}'
How can I pass Secret Manager secret value to cloudformation user data ?
回答1:
I am not sure why this is not expanded correctly for you. However, you probably do not want CFN to expand your secret in the user data because the password would be embedded in the base64 encoded user data script which is visible in the EC2 console.
Instead you should take advantage of the fact that you have a script that executes on the host and call secrets manager at script execution time (warning untested):
"SampleLaunchConfig": {
"Type": "AWS::AutoScaling::LaunchConfiguration",
"Properties": {
"ImageId": {
"Fn::FindInMap": [
"AWSRegionArch2AMI",
{
"Ref": "AWS::Region"
},
"AMI"
]
},
"UserData": {
"Fn::Base64": {
"Fn::Join": [
"",
[
"#!/bin/bash -xe\n",
"yum update -y\n",
"yum install -y jq\n",
!Sub "useradd -p `aws --region ${AWS::Region} secretsmanager get-secret-value --secret-id Credentials --query SecretString --output text | jq -r .passwordKey` `aws --region ${AWS::Region} secretsmanager get-secret-value --secret-id Credentials --query SecretString --output text | jq -r .userName`\n",
"\n"
]
]
}
}
}
}
This is not ideal since it expands the password on the command line. It might be made more secure by putting the password in a file first and reading it from there and then shredding the file.
回答2:
It seems that {{resolve:...}}
dynamic references are only expanded in certain contexts within a template.
There is no precise information in the AWS docs about exactly where in a template you can use these references. The current wording with regard to {{resolve:secretsmanager:...}}
says:
"The secretsmanager dynamic reference can be used in all resource properties"
However this is contradicted by your example, and I've also observed dynamic references failing to resolve inside of CloudFormation::Init data.
I have an active Support case open with AWS about this, they have agreed that the behaviour of dynamic references is inadequately documented. I'll update this answer as I learn more.
https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/dynamic-references.html#dynamic-references-secretsmanager
回答3:
I can confirm that @JoeB's "warning untested" answer works, with the caveat that the machine in question must have permission to read the secret. You'll need something like
MyInstancePolicy:
Type: AWS::IAM::Policy
Properties:
PolicyName: MyPolicy
PolicyDocument:
Version: 2012-10-17
Statement:
-
Effect: Allow
Action:
- secretsmanager:GetSecretValue
Resource: !Join
- ''
- - !Sub "arn:aws:secretsmanager:${AWS::Region}:"
- !Sub "${AWS::AccountId}:secret:Credentials-??????"
Note a couple of things:
Unlike S3 buckets, you can't do arn:aws:secretsmanager:::secret...
. If you don't want to explicitly declare the region and account, you need to use a wildcard. Buried at the bottom of Using Identity-based Policies (IAM Policies) for Secrets Manager
If you don't care about the region or account that owns a secret, you must specify a wildcard character * (not an empty field) for the region and account ID number fields of the ARN.
Perhaps less important, and less likely to cause unexpected failures, but still worth note:
Using '??????' as a wildcard to match the 6 random characters that are assigned by Secrets Manager avoids a problem that occurs if you use the '*' wildcard instead. If you use the syntax "another_secret_name-*", it matches not just the intended secret with the 6 random characters, but it also matches "another_secret_name-a1b2c3". Using the '??????' syntax enables you to securely grant permissions to a secret that doesn't yet exist.
回答4:
Variant on @JoeB's answer:
Resources:
SampleLaunchConfig:
Type: AWS::AutoScaling::LaunchConfiguration
Properties:
ImageId: !FindInMap [ AWSRegionArch2AMI, !Ref: 'AWS::Region', AMI ]
UserData:
Fn::Base64: !Sub |
#!/bin/bash -xe
exec > >(tee /var/log/user-data.log | logger -t user-data) 2>&1
yum update -y
yum install -y jq
username=$(aws secretsmanager get-secret-value --secret-id Credentials \
--query SecretString \
--region ${AWS::Region} --output text | jq -r .userName)
password=$(aws secretsmanager get-secret-value --secret-id Credentials \
--query SecretString \
--region ${AWS::Region} --output text | jq -r .passwordKey)
useradd -p "$password" $username
UserData in JSON is painful to watch these days.
I've also added a technique to split out the UserData logic to it's own log file, as otherwise it goes in cloud-init.log, which is also painful to read.
来源:https://stackoverflow.com/questions/53589880/dynamic-references-to-specify-secret-manager-values-in-aws-cloudformation