serverless-application-model icon indicating copy to clipboard operation
serverless-application-model copied to clipboard

Unable to use cross-account token lambda authorizer with SAM template

Open xiananfan opened this issue 4 years ago • 7 comments

Description:

We have 2 AWS accounts, let's say account A and account B. Account A contains a lambda authorizer we want to use in Account B with API Gateway as a token authorizer. Using the classic cloud formation template, we will be able to create the authorizer reference in account B, and it will not attempt to modify the authorizer in account A to update its resource-based policy to allow cross-account access. The CFT deployment will be able to complete without problems.

However, using SAM syntax like below will cause the CFT deployment to fail:

# template.yaml, intended to be deployed in Account B
    TestAuthServiceGateway:
        Type: AWS::Serverless::Api
        Properties:
            StageName: !Ref Environment
            OpenApiVersion: '3.0.0'
            Auth:
                DefaultAuthorizer: MyAuthorizerInAccountA
                Authorizers:
                    MyAuthorizerInAccountA:
                        FunctionArn: arn:aws:lambda:us-west-2:<AccountA>:function:dev-us-west-2-StandardAuthorizer
                        FunctionPayloadType: 'TOKEN'
                        Identity:
                            ReauthorizeEvery: 0 # disable cache
                            ValidationExpress: ^Bearer [a-zA-Z0-9\-_]+?\.[a-zA-Z0-9\-_]+?\.([a-zA-Z0-9\-_]+)?$

The reason is the user who triggered this deployment is currently in Account B and does not have permission to modify the authorizer in Account A.

The error CFT gives looks like below:

User: arn:aws:iam::<AccountB>:user/CICD is not authorized to perform: lambda:AddPermission on resource: arn:aws:lambda:us-west-2:<AccountA>:function:dev-us-west-2-StandardAuthorizer (Service: AWSLambda; Status Code: 403; Error Code: AccessDeniedException; Request ID: 964a7be1-fb95-4759-8c2d-11476c74630b)

My questions are:

  1. Is there a way to setup IAM role/policy/trust relationship, such that user in Account B, at cloudformation deployment time, can have the permission to modify cross-account authorizer's resource-based policy? This wiki does not suggest "lambda:AddPermission" is a supported API in resourced-based policy. I also gave it a try, and could not get it to work.
  2. Is it possible to suggest a feature request that do not set the authorizer's resource policy at deployment time? And assume it's already done outside out cloud formation? (This is a one-time thing either way). Something similar to this "AddDefaultAuthorizerToCorsPreflight" flag?

Steps to reproduce the issue:

  1. Create an lambda authorizer in Account A
  2. Trying to deploy a SAM template in Account B referencing the token authorizer in Account A
  3. See the failure in cloud formation saying it does not have permission to modify the resource-based policy of authorizer

Observed result: Unable to modify authorizer resource-based policy in Account A

Expected result: Able to modify given appropriate cross-account permission setup, or allow disable this step of updating the authorizer's resource policy.

Update: Looked into code here, can we update the logic here such that we only attempt to update authorizer's resource based policy when the authorizer lives in the same AWS account? Maybe just update this logic here that also skip when the AWS account of the authorizer.function_arn does not contain the current AWS account ID?

xiananfan avatar Jun 30 '20 04:06 xiananfan

Just stumbled across the same problem.

~~The workaround is to mix the SAM Serverless::Api with the classic cloudformation ApiGateway::Authorizer resource.~~

Edit: After further testing this seems to also not be possible. You can add a Authorizer to the API (via the ApiGateway::Authorizer resource) but you are not able to attach it to the Serverless::Api (via DefaultAuthorizer) nor the Serverless::Function (as ApiAuth)

imo modifying the policy should be optional. Looking forward to the discussion here

denniskribl avatar Jul 24 '20 11:07 denniskribl

If the outcome of this issue results in code changes, I'll happily contribute as this issue is really bothering me and my team :smile:

denniskribl avatar Jul 28 '20 14:07 denniskribl

Also running into this issue

icj217 avatar Oct 05 '20 19:10 icj217

Ended up creating a very simple custom macro to do this for me.

Macro stack:

Description: Remove Authorizer Lambda Permissions
AWSTemplateFormatVersion: "2010-09-09"
Transform: [AWS::Serverless-2016-10-31]

Resources:
  RemoveAuthorizerLambdaPermissionsFunction:
    Type: AWS::Serverless::Function
    Properties:
      Description: CFN Macro function that removes AWS::Lambda::Permission resources associated with lambda authorizers
      Runtime: python3.7
      Handler: index.handler
      InlineCode: |
        def handler(event, context):
          print("Got a request", str(event))
          # Get list of resources to be deleted
          resources = [name for name in event['fragment']['Resources'] if name.endswith('AuthorizerPermission')]
          # Delete them from template
          for resource in resources:
            print('Deleting authorizer permission:', resource)
            event['fragment']['Resources'].pop(resource)
          # Return template to CFN
          return { "requestId": event["requestId"], "status": "success", "fragment": event["fragment"] }
  RemoveAuthorizerLambdaPermissionsMacro:
    Type: AWS::CloudFormation::Macro
    Properties:
      Name: RemoveAuthorizerLambdaPermissions
      Description: Removes AWS::Lambda::Permission resources associated with lambda authorizers
      FunctionName:
        Ref: RemoveAuthorizerLambdaPermissionsFunction

Once the above stack is deployed, you can simply add the macro to to your API stack's Transform:

AWSTemplateFormatVersion: "2010-09-09"
Transform: [AWS::Serverless-2016-10-31, RemoveAuthorizerLambdaPermissions]

  DeidApi:
    Type: AWS::Serverless::Api
    Properties:
      Name: my-api
      Auth:
        DefaultAuthorizer: MyFn
        Authorizers:
          MyTokenFn:
            ...

Not ideal but it works for our use case 🤷

If you happen to have multiple authorizers and one of them is in the sample account, you'll need to explicitly create the permission resource rather than rely on SAM's generated resource (which the macro will delete). As long as the naming convention doesn't match what the macro looks for it should work fine.

icj217 avatar Oct 06 '20 14:10 icj217

+1 It would be beneficial for orgs where there is a centrally managed authorizer to be able to deploy stacks that aren't trying to also create the permission.

brysontyrrell avatar Nov 11 '20 15:11 brysontyrrell

@sriram-mv I'm having this exact same issue. Any updates on this subject?

santiperone avatar Jul 05 '22 21:07 santiperone

I thik it should be possible to add an attribute like "AddPermissions" which defaults to true (to maintain current behaviour) here: https://github.com/aws/serverless-application-model/blob/e62dcfc6ed2ea3b4f3473e7797ee0663f4e06302/samtranslator/model/api/api_generator.py#L1113-L1133

santiperone avatar Jul 05 '22 22:07 santiperone