Search code examples
aws-lambdaamazon-iamfederated-identitypingfederate

How to provide selective access for lambda execution to a federated user in AWS IAM policy?


I am trying to give lambda execution access to select members within a group. Users are authenticated via PingFederate. I am having issue granting this selective access to federated user.

I have a custom IAM policy (allow-lambda-invocation-selective) attached to this role. Although the policy seems to pass validation and policy simulation shows access is allowed, when I try to execute the lambda function I get message

Calling the invoke API action failed with this message: User:arn:aws:sts::123456789012:assumed-role/role-for-grp-l2/myuser1234 is not authorized to perform: lambda:InvokeFunction on resource: arn:aws:lambda:us-east-1:123456789012:function:my-lambda-function

Here is my policy: allow-lambda-invocation-selective

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Sid": "VisualEditor0",
            "Effect": "Allow",
            "Action": [
                "lambda:InvokeFunction",
                "lambda:InvokeAsync",
                "lambda:ListVersionsByFunction",
                "lambda:GetFunction",
                "lambda:ListAliases"
            ],
            "Resource": "arn:aws:lambda:*:123456789012:function:my-lambda-function",
            "Condition": {
                "StringEquals": {
                    "aws:userid": "arn:aws:sts::123456789012:assumed-role/role-for-grp-l2/myuser1234"
                }
            }
        },
        {
            "Sid": "VisualEditor1",
            "Effect": "Allow",
            "Action": [
                "lambda:ListFunctions",
                "lambda:ListEventSourceMappings",
                "lambda:ListLayers",
                "lambda:ListLayerVersions"
            ],
            "Resource": "*",
            "Condition": {
                "StringEquals": {
                    "aws:userid": "arn:aws:sts::123456789012:assumed-role/role-for-grp-l2/myuser1234"
                }
            }
        }
    ]
}

Am i missing something?


Solution

  • I'm trying to understand your problem. Correct me if I made a wrong supposition.

    1. Every group/user already have its own role.

    When you authenticate your users, they have their assumed role. myuser1234, when authenticated, will receive arn:aws:sts::123456789012:assumed-role/role-for-grp-l2/myuser1234 role, right? Is it possible to create one role for each group and remove the conditions property (check item 2 explaining why)?

    // role-for-grp-l2
    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "lambda:InvokeFunction",
                    "lambda:InvokeAsync",
                    "lambda:ListVersionsByFunction",
                    "lambda:GetFunction",
                    "lambda:ListAliases"
                ],
                "Resource": "arn:aws:lambda:*:123456789012:function:my-lambda-function"
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": [
                    "lambda:ListFunctions",
                    "lambda:ListEventSourceMappings",
                    "lambda:ListLayers",
                    "lambda:ListLayerVersions"
                ],
                "Resource": "*"
            }
        ]
    }
    
    1. The problem with aws:userid

    Reading the docs about the key aws:userid we can find this key has the value given by role id:caller-specified-role-name,

    where role id is the unique id of the role and the caller-specified-role-name is specified by the RoleSessionName parameter passed to the AssumeRole request.

    So aws:userid has value like AIDAJQABLZS4A3QDU576Q:SomeNameYouGive. Because this, your condition never match arn:aws:sts::123456789012:assumed-role/role-for-grp-l2/myuser1234 and then user cannot assume that actions.

    1. Using conditions another way

    Assuming RoleSessionName is the user name, you can use conditions this way:

    {
        "Version": "2012-10-17",
        "Statement": [
            {
                "Sid": "VisualEditor0",
                "Effect": "Allow",
                "Action": [
                    "lambda:InvokeFunction",
                    "lambda:InvokeAsync",
                    "lambda:ListVersionsByFunction",
                    "lambda:GetFunction",
                    "lambda:ListAliases"
                ],
                "Resource": "arn:aws:lambda:*:123456789012:function:my-lambda-function",
                "Condition": {
                    "StringLike": {
                        "aws:userid": "*:myuser1234"
                    }
                }
            },
            {
                "Sid": "VisualEditor1",
                "Effect": "Allow",
                "Action": [
                    "lambda:ListFunctions",
                    "lambda:ListEventSourceMappings",
                    "lambda:ListLayers",
                    "lambda:ListLayerVersions"
                ],
                "Resource": "*",
                "Condition": {
                    "StringLike": {
                        "aws:userid": "*:myuser1234"
                    }
                }
            }
        ]
    }
    

    if you prefer, you may remove * wildcard getting role id using AWS CLI with the command:

    aws iam get-role --role-name ROLE_NAME
    

    and changing condition as follows:

    "Condition": {
       "StringEquals": {
           "aws:userid": "ROLE_ID:myuser1234"
       }
    }