Encrypt secret manager value using kms cloud formation - amazon-web-services

How I encrypt secret manager value in aws kms so that no on can see the plain text value . it basically should be decrypted in lambda using kms . is this even possible in AWS .
I could see that we can use kms to encrypt and decrypt kms values, but that happens at rest .

If you want to use lambda to either access or store data in cloud formation dynamically, you likely want to use a lambda-backed custom resource. Read more about it here: https://docs.aws.amazon.com/AWSCloudFormation/latest/UserGuide/template-custom-resources-lambda.html.
Basically, you can either pass data to the lambda function (which can be dynamic based on outputs of resources already created) and/or get dynamic data that it created through its outputs.
However, secret manager is designed to be able to let authorized users/entities (like EC2 instances, for example) to see the plaintext password. Consider instead setting up which users/entities are allowed to use. See here for more detail on access control to secret manager: https://docs.aws.amazon.com/secretsmanager/latest/userguide/auth-and-access_determining-access.html

To encrypt a given plaintext secret for inclusion within a CloudFormation template, you can use the custom secret provider. It will decrypt the secret and store it in the SSM parameter store, as shown below:
Resources:
ApiKey:
Type: Custom::Secret
Properties:
Name: /datadog/api-key
EncryptedContent: >-
AQICAHgefwksukJYA7L2AkPMZLGjZsGxHbvY9AoVs55dcju1AwEZui/8lNbnGAhv63Wh0heUAAAA3zCB
3AYJKoZIhvcNAQcGoIHOMIHLAgEAMIHFBgkqhkiG9w0BBwEwHgYJYIZIAWUDBAEuMBEEDOXKKVZ4ft75
/oZ2TQIBEICBlzf5j1M3w6OH+iphx59kFLnNoKb+u1RCLfIqEitrt6VGu13/jDlnDcPE2DfkZFkW3fnm
Nn5OXfgt1L9j4XYdIQTEwexorNqUr5pUtMfS9YX8yL9DbArH+XBv/OQPSj8VsuWRcwFP5EwZKB9O4X3l
1pZlPafp2Y/ndWXgC1o6YgfplnmjufoUUTy8wi4P5glbwnqGP/iyc7g=
ReturnSecret: true
RefreshOnUpdate: true
ServiceToken: !Sub 'arn:aws:lambda:${AWS::Region}:${AWS::AccountId}:function:binxio-cfn-secret-provider'
The value is stored encrypted, but will be available in plaintext from the parameter store for those authorised to read it. By specifying ReturnSecret, you can access the plaintext secret in the CloudFormation template itself.
If it is a static secret, storing the secret in the parameter store is way cheaper.
For a more information checkout https://binx.io/blog/2018/10/21/encrypting-secrets-in-aws-cloudformation/

Related

How to use Terraform to store a new secret in AWS Secrets Manager using already KMS-encrypted string?

I need to write Git-revisioned Terraform code to put a secret string into AWS Secrets Manager. Given a secret string in a textfile:
% cat /tmp/plaintext-password
my-super-secret-password
I am able to make an encrypted version of it using a KMS key:
# Prints base64-encoded, encrypted string.
aws kms encrypt --key-id my_kms_uuid --plaintext fileb:///tmp/plaintext-password --output text --query CiphertextBlob
# abcdef...123456789/==
What Terraform code can be written to get that base64 string in AWS Secrets Manager, such that AWS knows it was encrypted with my_kms_uuid? I have tried the following:
resource "aws_secretsmanager_secret" "testing-secrets-secret" {
name = "secret-for-testing"
kms_key_id = "<my_kms_uuid>"
}
resource "aws_secretsmanager_secret_version" "testing-secrets-version" {
secret_string = "abcdef...123456789/=="
}
The problem is I can't figure out a way to tell AWS Secrets Manager that the string is already encrypted by KMS so it doesn't have to encrypt it again. Can this be done?
If your goal is to keep secret values out of the statefile, then you have two choices:
Encrypt the secret outside of Terraform, then store the encrypted value in Secrets Manager.
This will force all consumers of the secret to decrypt it before use. Since an encrypted secret includes the CMK used to encrypt it, there's no need for you to separately track the key ID.
There are several drawbacks to this approach. For one thing, you have to do two steps to use any secret: retrieve it and decrypt it. If you use ECS, you can't provide the name of the secret and let ECS to provide the decrypted value to your container.
A bigger drawback is that it can be very easy to forget which CMK is used for which secret, and accidentally delete the CMK (at which point the secret becomes unusable). Related is knowing which permissions to grant to the consumers, especially if you have a lot of CMKs.
Create the secret inside Terraform, and set its value manually.
This keeps the actual value in Secrets Manager, so you don't need to use two steps to decrypt it.
It is possible to use local-exec to generate the secret within the Terraform configuration: write a script that generates random data and then invokes the AWS CLI to store the value. However, this technique is more frequently used for things like SSH private keys that are created outside of the Terraform provisioning process.
Better than either of these solutions is to store your statefile somewhere that it isn't generally accessible. There are a bunch of backends that can do this for you.
When encrypting a secret string with KMS, the key that was used is actually specified within the output of the resulting ciphertext. This means we can do this the following way:
Encrypt the secret string with an existing KMS key in AWS aws kms encrypt --key-id arn:aws:kms:us-east-1:<account_id>:key/mrk-blahblahblah --plaintext fileb://<(printf 'SOME_SECRET_TEXT') --output text --query CiphertextBlob --region us-east-1
Then use aws_kms_secrets to decrypt it within Terraform (something like this).
Then you push the decrypted secret up into AWS Secrets Manager using aws_secretsmanager_secret_version.
The net result is only the encoded secret is kept in version control, but the password that's actually stored in Secrets Manager is the decoded string.

AWS Crossaccount - Parameters Store / Secrets Manager access to parameters in AWS CDK

I'm wondering if something is possible at all, or I'm trying to build something that is not possible from the start.
Let's say within Account A there is an RDS DB Password, (can be any AWS resource ID or value) that I have stored in Secrets Manager or Parameter Store.
Now I want to use that value in AWS CDK in Account B, is this possible?
It is possible to retrieve the value based on ARN, see: https://bobbyhadz.com/blog/get-secrets-manager-values-aws-cdk#get-secrets-manager-value-by-arn---alternative but would this work cross-account?
You can attach a policy to your secret granting access to other AWS account. Check https://aws.amazon.com/premiumsupport/knowledge-center/secrets-manager-share-between-accounts/

How to store password in AWS Parameter Store

I am planning to use AWS parameter store to store config for one of the project I am working on it. We are using cloud formation (or CDK) to deploy all the components. That includes parameter store as well.
I have some config which has password and other sensitive fields which I can't put to in version control. How to handle this scenario?
I would use AWS Secrets Manager to generate the secrets randomly.
#This is a Secret resource with a randomly generated password in its SecretString JSON.
MyRDSInstanceRotationSecret:
Type: AWS::SecretsManager::Secret
Properties:
Description: 'This is my rds instance secret'
GenerateSecretString:
SecretStringTemplate: '{"username": "admin"}'
GenerateStringKey: 'password'
PasswordLength: 16
ExcludeCharacters: '"#/\'
Tags:
-
Key: AppName
Value: MyApp
And would further export the same into AWS Parameter Store using a policy attached and later on access them using static or dyanmic reference.
The best would be to take your secrets management out of Cloudformation as suggested by #jordanm.
Take a look at AWS Secrets Manage for this use case. If you are implementing your solution in Java, see this Github URL:
https://github.com/awsdocs/aws-doc-sdk-examples/tree/master/javav2/example_code/secretsmanager
Provisioning SecureString parameter type is not possible in clouldforamtion
AWS CloudFormation doesn't support creating a SecureString parameter
type
See the following link: This
But you can reference it securely, using dynamic references which provide a compact, powerful way for you to specify external values that are stored and managed in other services, such as the Systems Manager Parameter Store, in your stack template.
Use the ssm-secure dynamic reference pattern to specify AWS Systems
Manager SecureString type parameters in your templates. For ssm-secure
dynamic references, AWS CloudFormation never stores the actual
parameter value. AWS CloudFormation accesses the parameter value
during create and update operations for stacks and change sets.
Check the following link:This

secret manager - hide secret in AWS console

I am creating a new secret in AWS secret manager from the AWS console
I used default encryption key to encrypt it.
When I retrieve the secret from console I see it as plain text.
Can the secret be encrypted (not see it as plain text) and saved in AWS console
By default access to AWS secret manager is private and not allow to any IAM users or roles. The fact that you or someone else can view them, means they have been explicitly allowed to access the secrets.
Thus, if you have any users who should not see the secrets, deny them secretsmanager:GetSecretValue permissions or any actions that you don't want them to be able to perform.

What is the best possible way to pass API key for AWS EC2 user data script

I have bash script to run as user data script when launching EC2 instance. For that I need to pass external API access key id and secret key. I don't want to store these keys in my user data scripts as it is visible in plaintext. Is there any way that I can store this keys in somewhere such as AWS Secret Manager and use that in user data scripts?
I would suggest either storing it in Secrets Manager or SSM Parameter Store.
You would need to use the CLI in your userdata script to retrieve the value.
For SSM you would retrieve the secret by using the get-parameter function.
secret=$(aws ssm get-parameter --name "MyStringParameter")
For Secrets Manager you would retrieve the secret using the get-secret-value function.
secret=$(aws secretsmanager get-secret-value --secret-id MyTestDatabaseSecret)
Then in your bash script when you want to reference it you would just need to use the variable $secret to actually replace with your secret.
If you decide to use either of these you will need to ensure EC2 instance has an IAM role attached to the instance with the correct policy to apply the permissions you require.
Alternatively if this is a process that happens frequently (autoscaled instance for example) then you should take a look at configuring the base server image (AMI) ahead of time and then referencing this as the source AMI.
With tools such as Ansible, Chef and Puppet you could provision the base image with your secret which would replace any need to do anything in the UserData as it would be available ahead of time.
Usually you can store such secrets in AWS Systems Manager Parameter Store which is free, unlike AWS Secret Manager:
AWS Systems Manager Parameter Store provides secure, hierarchical storage for configuration data management and secrets management. You can store data such as passwords, database strings, Amazon Machine Image (AMI) IDs, and license codes as parameter values.
To use that in your UserData, the instance role has to be set with permissions to access the Parameter Store. Then in your UserData you can use aws cli get-parameter to get the value of your secrets.