Introduction to AWS Secrets Manager
Servers and applications require passwords and secrets for authentication reasons. When we migrated our application to AWS cloud a few years ago, we took the opportunity to add more security to our code repository by storing our secrets in AWS Secret Manager.
What is Secrets Manager
Secrets Manager is an AWS managed service that provides secure storage for your secrets (passwords, API tokens, Oauth tokens etc.). Its focus is to make applications more secure by shifting credentials storage from the application’s source code to an API call to retrieve those credentials at runtime. That way, the risk of compromise is reduced if the application’s source code is examined/leaked, and it makes rotating credentials a lot easier without the need to redeploy new versions of the application.
The advantages of using Secrets Manager are:
- Centralized management of secrets
- Secrets are always encrypted
- Secrets can be shared between accounts
- Credential rotation can be automated on customer-defined schedule
- Credential rotation can be applied automatically for RDS, DocumentDB or Redshift databases. Otherwise, custom lambda functions can be created to perform the rotation for other type of credentials
Based on the environment (production, pre-production, ...), we pull the secrets stored in AWS Secret Manager into our CI/CD systems and inject them in our application configuration file. This way we do not store any secret in our code repository. Thanks to the IAM Policy, we can narrow the visibility to the secret to the application (EC2 and Lambda) and only a few administrators that are allowed to create/read secrets.
As we are migrating some existing applications to a serverless architecture based on AWS Lambda, we are currently changing these application’s behavior in order to dynamically access Secret Manager's secrets from the Lambda. This way the secrets are only stored in AWS Secret Manager and in the lambda cache.
To create a secret using Secrets Manager, we’ll simply look for Secrets Manager in the dashboard search bar and select it from the results:
Next, we’ll choose to store a new secret. You could also see the pricing information displayed on the same page.
We can see that Secrets Manager is geared towards storing database credentials. In our case, we’ll choose “Other type of secrets”.
Then, we’ll enter a database connection string (as an example), and select what encryption key Secrets Manager will use to encrypt the secret. we can use any key created in AWS KMS (Key Management Service) that we have the permission to access.
After that, we can give the secret a name and optionally a description. We can associate tags to this secret for billing reasons for example.
On the next page, we can select to enable automatic secret rotation. This will allow us to change our secret periodically.
If we’re using an AWS database like RDS, DocumentDB or Redshift, Secrets Manager will automatically handle the rotation. In other cases, we’ll need to create a lambda function to:
- Create a new version of the secret.
- Store the secret in Secrets Manager.
- Configure the protected service to use the new version.
- Verify the new version.
- Mark the new version as production ready.
When enabling rotation, the first rotation will happen immediately after storing the secret and then, it will continue to happen by the defined interval.
Finally, we’ll reach a review page where we can examine our configured secret.
Once the secret is created, we can find down below the success page, code samples for many programming languages describing how we can access the created secret.
To access our created secret, we can use the aws cli for instance with the “get-secret-value” command while specifying the id of our created secret:
To view all the details of our secret except the encrypted text, we can use “describe-secret”:
To give access to our secret we will use policies. Two types of policies can be used to ensure we are only allowing people who are authorized to access our secrets (technically there are more than two but for simplicity’s sake we will only consider these two).
Identity based policies
Also known as IAM policies, identity-based policies as their name imply, have principles as main subject. Identity-based policies can be managed or inline, attached to IAM identities.
They specify which identity has access to which resource. For our case, they can be used to:
- Grant an identity access to multiple secrets.
- Control who can create new secrets, and who can access secrets that haven't been created yet.
- Grant an IAM group access to secrets.
In the next example we’ll create a policy that will give permission to the user johndoe to access the “describe-secret”, “get-secret-value” and deny “get-resource-policy” actions for the secret that we created (dev/MyTestConnectionString/SqlServer):
And we can see that our user clearly doesn’t have the authorization to execute the “get-resource-policy” command.
Resource based policies
Resource based policies are inline policies directly attached to AWS resources. Resource-based policies grant permissions to the principal specified in the policy. This could be useful for example to ensure that only one IAM identity has access to a secret.
In the following example, we are giving the user ‘johndoe’ access to the “describe-secret”, “get-secret-value” and “get-resource-policy” actions for the secret that we created (dev/MyTestConnectionString/SqlServer):
We can see in the next screenshot that our user was denied access to the “get-resource-policy” action even though it is explicitly allowed on our resource-based policy. This is due to the fact that we’re explicitly denying johndoe this action in the identity-based policy.
Identity-based and resource-based policies are evaluated together to determine if the principal making the request is authorized or not. One caveat to remember is that if either of them explicitly deny the statement, the request will be denied. If one of the policy types allows the request and the other doesn't (explicitly), the request is still allowed.