Search code examples
amazon-web-servicesaws-lambdaamazon-iamserverless-frameworkaws-secrets-manager

How do I grant a rotation Lambda access to AWS Secrets Manager


Using the serverless framework, I am trying to build a Lambda function that periodically rotates a secret stored in AWS Secrets Manager.

I am having trouble configuring the roles needed for the Secret Manager to execute the Lambda. In my serverless.yml I have defined the following resources:

resources:
  Resources:
    RotateKeysRole:
      Type: AWS::IAM::Role
      Properties:
        RoleName: rotate-keys-role
        ManagedPolicyArns:
          - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
        AssumeRolePolicyDocument:
          Version: '2012-10-17'
          Statement:
            - Effect: Allow
              Principal:
                Service:
                  - lambda.amazonaws.com
                  - secretsmanager.amazonaws.com
              Action: sts:AssumeRole

and attach this role to the rotation Lambda like this:

functions:
  rotateKeys:
    handler: lambdas.rotate_keys.handler
    role: RotateKeysRole

Yet, when I try to set up Secrets Manager to use this Lambda for rotating secrets I will get the following error message:

Secrets Manager cannot invoke the specified Lambda function. Ensure that the function policy grants access to the principal secretsmanager.amazonaws.com

which puzzles me as this principal is specified. Inspecting the role in the IAM console did not reveal anything that seemed wrong to me.

How do I correctly configure the role setup in this scenario?


Solution

  • The procedure of setting up permissions for a lambda function which rotates AWS Secrets Manager secrets is explained in the docs. [1]

    To put it in a nutshell, you need two steps:

    • Add a trust policy to the lambda function. This can be achieved using the CloudFormation resource AWS::Lambda::Permission in the serverless.yml file. However, it is a little bit tricky to set this up, because you need to depend on the function being created. That is why the DependsOn is necessary and its value must be structured as follows: <function-name-with-first-letter-uppercase>LambdaFunction.
    • Add statements for the lambda function to call the AWS Secrets Manager API to update the secret. In the following example, I added these statements (for the Single user rotation case - see docs [1]) to the customer managed policy called rotateKeysPolicy.

    Note: The function name is referenced in the DependsOn attribute. It is also referenced in the condition StringEquals and the attribute FunctionName as: arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:${self:service}-${self:provider.stage}-rotateKeys. Keep in mind to change them if you change your function name.

    Here is how the serverless.yml file should look like:

    service:
      name: <your-service-name>
    
    provider:
      name: aws
      region: '<your-region>'
    
    custom:
      region: ${self:provider.region}
      accountId: <your-account-id>
    
    resources:
      Resources:
        FunctionRole:
          Type: AWS::IAM::Role
          Properties:
            RoleName: basic-function-role
            ManagedPolicyArns:
              - arn:aws:iam::aws:policy/service-role/AWSLambdaBasicExecutionRole
            Policies:
              - PolicyName: rotateKeysPolicy
                PolicyDocument:
                  Version: '2012-10-17'
                  Statement:
                    - Effect: Allow
                      Action:
                        - secretsmanager:DescribeSecret
                        - secretsmanager:GetSecretValue
                        - secretsmanager:PutSecretValue
                        - secretsmanager:UpdateSecretVersionStage
                      Resource: '*'
                      Condition:
                        StringEquals:
                          'secretsmanager:resource/AllowRotationLambdaArn': "arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:${self:service}-${self:provider.stage}-rotateKeys"
                    - Effect: Allow
                      Action:
                      - secretsmanager:GetRandomPassword
                      Resource: '*'
                    - Effect: Allow
                      Action:
                        - ec2:CreateNetworkInterface
                        - ec2:DeleteNetworkInterface
                        - ec2:DescribeNetworkInterfaces
                      Resource: '*'
            AssumeRolePolicyDocument:
              Version: '2012-10-17'
              Statement:
                - Effect: Allow
                  Principal:
                    Service:
                      - lambda.amazonaws.com
                  Action: sts:AssumeRole         
        LambdaInvokePermission:
          Type: AWS::Lambda::Permission
          DependsOn: RotateKeysLambdaFunction
          Properties:
            FunctionName: "arn:aws:lambda:${self:custom.region}:${self:custom.accountId}:function:${self:service}-${self:provider.stage}-rotateKeys"
            Action: lambda:InvokeFunction
            Principal: 'secretsmanager.amazonaws.com'
    
    functions:
      rotateKeys:
        handler: lambdas.rotate_keys.handler
        role: FunctionRole
    

    You have to replace <your-service-name>, <your-region>, <your-account-id> and upload your rotation code using e.g. the package -> include attributes.

    Note: There are templates for the lambda function which update the secrets. [2][3]

    Please also keep in mind to configure your VPC correctly for the lambda function being able to access the AWS Secrets Manager service over the network. [4]

    References

    [1] https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-required-permissions.html
    [2] https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotating-secrets-create-generic-template.html
    [3] https://github.com/aws-samples/aws-secrets-manager-rotation-lambdas
    [4] https://docs.aws.amazon.com/secretsmanager/latest/userguide/rotation-network-rqmts.html