Pass db credentials to Lambda using Parameter Store

If you are connecting to a RDS server from Lambda using native authentication method then you have to store user and password somewhere in the code or pass it as an environment variable to the Lambda. In both cases, the RDS admin ends up having to give the information to developers.

However, there is a secure way to pass this information to the Lambda and still keep it a secret. Using AWS Parameter Store an admin can securely store the password and not have to give it out to the developers.

RDS admin gives the developer a string which corresponds to a database and what kind of access it provides and the developer uses the string in Lambda function to lookup information from Parameter Store, and connect to the RDS instance. The information is stored in Parameter Store as a SecureString which is encrypted.

The String given to the developer could be something like this /dev/mysql/somedb/write implying that this lookup is for a development MySQL database called somedb and it provides write access. The Lookup String is set as a Lambda environment variable and used by the code at execution time.

Procedure:

Note: The code descried here is pseudo code and not the complete listing. Please add error/exception handling as needed.

  • First, let us create an IAM policy for our Lambda Function that allows it to access the parameter store.
    {       
"Sid": "VisualEditor0",
"Effect": "Allow",
"Action": [
"ssm:GetParametersByPath",
"ssm:GetParameters",
"ssm:GetParameter"
],
"Resource": "arn:aws:ssm:aws-region:account-number:parameter/*"
},
{
"Sid": "VisualEditor1",
"Effect": "Allow",
"Action": "ssm:DescribeParameters",
"Resource": "*"
}
  • Then using the AWS console navigate to Systems Manager and select Parameter Store.
  • Select the Create Parameter button and set the name as /dev/mysql/somedb/write.
  • I store the information in this format, which makes it easy to read and set using ConfigParser
{"dbname": "somedb","userid": "rwsomedb","password": "xxxxxxxxx", "host": "RDS DNS"}
  • The above gives all the required information for your lambda function to connect to the database. The user, password, dns or ip of db and dbname.
  • You can also set up a read-only string similar as shown below
/dev/mysql/somedb/readonly corresponds to
{"dbname": "somedb","userid": "rwsomedb","password": "xxxxxxxxx", "host": "RDS DNS"}
  • In your Lambda function definition set an Environment Variable for the SSM Parameter Path.
  • In the Lambda code, initialize the boto client and set the lookup path
#ssm    
ssm_ppath = os.environ['SSM_PARAMETER_PATH']
ssm = boto3.client('ssm')
lookup = ssm_ppath + '/write'
  • Return the configuration after populating it using a ConfigParser
def get_config(ssm_parameter_path):
configuration = configparser.ConfigParser()

# Get all parameters by path
param_details = ssm.get_parameters_by_path(
Path=ssm_parameter_path,
Recursive=False,
WithDecryption=True
)
# Populate the ConfigParser
if 'Parameters' in param_details and len(param_details.get('Parameters')) > 0:
for param in param_details.get('Parameters'):
section_name = param.get('Name')
config_values = json.loads(param.get('Value'))
config_dict = {section_name: config_values}
configuration.read_dict(config_dict)
return configuration
  • The lookup here is /dev/mysql/somedb/write
def handler(event, context):
config = get_config(ssm_ppath)

rds_host = config[lookup]['host']
db_name = config[lookup]['dbname']
name = config[lookup]['userid']
password = config[lookup]['password']
  • Make sure the developers are not printing the secure information to CloudWatch logs 🙂

Further reading and improvements:

Leave a Reply