Recently I’ve been working on a project that involved using an AWS Lambda to interact with resources that required authentication using secrets. This is a fairly typical problem in cloud engineering and the "modern" approach is to use a secrets management engine to make sure only resources that should have access to specific secrets can get access to them.
This environment was using Hashicorp’s Vault as a secrets engine, this seemed relatively straight forward and in the end was but some slightly outdated documentation made it much harder than it ought to have been so I’m writing this up to help anybody who has to do this in the future not have to go through this process.
Thankfully Vault supports AWS’s excellent IAM authentication mechanism which allows individual AWS resources to access other resources without having to do awkward things like store secrets as environment variables.
This has a multitude of benefits, I particularly like from a cloud engineering perspective that it makes it very easy to explicitly state in an infrastructure as code repository what cloud resources have access to.
Vault’s IAM authentication works by your cloud resource (in this case a Lambda) making a call to Amazon’s Security Token Service (STS) to make sure you are who you are saying you are. STS then returns a key which can be used with your resource’s IAM credentials to authenticate against Vault.
Authentication with Python
That’s the theory, the practise for a Python user who doesn’t want to craft the several api calls required there’s a library called hvac.
This code is adapted from the usage documentation, the only exception being I removed the reference to an ec2 login method that isn’t needed if like me, you know you are going to be running in Lambda:
import hvac, os client = hvac.Client(url=your_vault_url) def infer_credentials_from_iam_role(iam_role): on_lambda = 'AWS_LAMBDA_FUNCTION_NAME' in os.environ if on_lambda: return os.environ['AWS_ACCESS_KEY_ID'], os.environ['AWS_SECRET_ACCESS_KEY'], os.environ['AWS_SESSION_TOKEN'] else: print("error not running in Lambda") access_key_id, secret_access_key, token = infer_credentials_from_iam_role('your_iam_rolet') client.auth.aws.iam_login(access_key_id, secret_access_key, token)
With this all you need to do is replace the placeholder values
your_iam_role with the ones relevant to your environment and you can then use the
client object to interact with whatever Vault secrets you need.