Accessing LambdaURL with AWS_IAM auth from EC2 using IAM role - amazon-web-services

I have a Lambda that does some things and needs to be trigered from Jenkins build. Jenkins uses EC2 agents with a specific "jenkins" role attached to them to execute the build. To access lambda we use LambdaURL, when we set auth type to NONE, everything works fine, but we want to have this endpoint secure, so we switched to AWS_IAM auth type. We set up lambda permissions to give "jenkins" role lambda:InvokeFunctionUrl rights for this lambda, I also attached full lambda permissions policy for this role during testing. But still we get "Forbidden" from that LambdaURL. I found this blogpost that states that in case of AWS_IAM auth type we also need AWS Sigv4 signature. But I don't understand how we can sign it in jenkins using that "jenkins" role. Am I missing something? What may be the best way to limit access to that FunctionURL- instead of auth type?
Thanks!

After some time I realised that in this case it is mush better to invoke lambda with API or awc-cli, not via FunctionURL. I used boto3 and it worked! More on how: https://hands-on.cloud/working-with-aws-lambda-in-python-using-boto3/#How-to-invoke-Lambda-function-using-Boto3

Related

Why is iam:PassRole required in this project?

I cloned this solution azure-devops-on-aws and used dotnet lambda deploy-serverless ... to deploy the MyLizardApp to my personal AWS account.
During the learning curve, I created an S3 bucket my-lizard-test, IAM user group MyLizardGroup with user lizard-user and group policy MyLizardApp-Policy. Included in the policy are these services:
API Gateway (full access, all resources)
CloudFormation (full access, all resources)
Lambda (full access, all resources)
S3 (full access, all resources)
(Eventually) the deployment succeeded and I had a Lambda application serving the simple razor page showing the time.
I then copied the LambdaEntryPoint.cs, aws-lambda-tools-defaults.json and serverless.template files to my own dotnet core webapp (also a razor project) and attempted to deploy it to the same AWS account with the same command. The only changes made were the namespace of the LambdaEntryPoint class (reflected in the serverless.template file) and the .csproj file to include:
<AWSProjectType>Lambda</AWSProjectType>
and:
<PackageReference Include="Amazon.Lambda.AspNetCoreServer" Version="5.0.0" />
The dotnet lambda deploy-serverless ... command failed with the message:
User: arn:aws:iam::123456789120:user/lizard-user is not authorized to perform: iam:PassRole on resource: arn:aws:iam::123456789120:role/MyLizardAppServiceRole (Service: AWSLambdaInternal; Status Code: 403; Error Code: AccessDeniedException; Request ID: 12345678-1234-1234-1234-123456789012; Proxy: null)
I got the command to succeed by adding the IAM service to the MyLizardApp-Policy with the PassRole (all resources).
Why was this necessary for my personal app and not the demo solution from github? If the answer is not clear, what should I be looking for as differences? My personal app is not significantly different from the demo solution and I don't think the functional differences (in C#) would matter.
Whenever an AWS Service assumes (uses) an IAM Role, the service must have iam:PassRole permission to grant permission to use the Role. This required to prevent users from gaining too much permission.
For example, imagine a normal (non-Admin) user who launches an Amazon EC2 instance. When launching the instance, they can nominate an IAM Role to be assigned to the instance. If this user was permitted to select any IAM Role, they could select an Admin role and assign it to the EC2 instance. They could then login to the instance and use the credentials to make API calls as an Admin. This is an unwanted "privilege escalation".
Similarly, when an AWS Lambda function executes, it uses an IAM Role to obtain permissions. The iam:PassRole permission is used to control which roles a user can assign to the Lambda function.
So, there is something in that project that is trying to use an IAM Role and needs appropriate permissions.
First of all, we need to know what PassRole is:
iam:PassRole is the permission that controls which users can delegate an IAM role to an AWS resource.
As I can see in the repo, there is a file for CodeDeploy which already have credentials so maybe you are using CodeDeploy.
But btw, you are using an instances to deploy a Lambda function, and you need to pass the role to that Lambda so that is what PassRole do
AWS Services cannot directly assume service-linked roles. The role must be passed to the service by a user with the iam::PassRole permission.
The role-passing needs to be done only once, when a resource (e.g. EC2 instance) is created. After that the resource can assume the role repeatedly.
EC2 Instance profile is implemented this way. When a user launches an instance, it passes a role to the instance to act as an instance profile (it in addition needs iam:AddRoleToInstanceProfile for this case).
Other service-linked roles are also passed in this way.
Do not confuse it with the iam::CreateRole permission. A user may freely create service-linked roles, but is unable to pass the role to a service when needed.
In the management console, and to some extent in the CLI commands, role-passing is implicit, so you may encounter it without clear error messages when using non-root accounts.
As for why sometimes you need this permission, ands sometimes you don't, that is because when you use the root user, it will have AdministratorAccess which basically allows all actions on all resources.
If you create a new IAM user or account with blank permissions then you will need to add this permission manually.

An error occurred (AuthFailure) AWS was not able to validate the provided access credentials: ClientError from lambda function

I am trying to run the below command from lambda function but I keep getting the error AWS was not able to validate the provided access credentials. I am very sure that the credentials are correct because the access credentials are the set of credentials I use in my local AWS CLI. Does anyone have any idea?
ec2 = boto3.client('ec2', region_name=str(REGION_NAME), aws_access_key_id=str(ACCESS_KEY), aws_secret_access_key=str(SECRET_KEY))
When you are using lambda function, its permissions should be provided using AWS Lambda execution role:
An AWS Lambda function's execution role grants it permission to access AWS services and resources. You provide this role when you create a function, and Lambda assumes the role when your function is invoked.
I know it does not answer directly to your question, but this is good practice and you should not hard code any IAM credentials in your lambda function or its environmental variables.

Is it possible to assume a role from a running task in ECS?

I'm looking for an example of how to assume a role from within a running application within ECS.
I have a role setup, and I've added the roleARN to the task definition, using the setup here: https://docs.aws.amazon.com/en_us/AmazonECS/latest/developerguide/task-iam-roles.html
Can I then assume the role from the application logic as usual? Or is there something special required?
I have this setup working, but with a IAM User which has a Secret Key and Access Key used as credentials within my application. Would switching this auth to the assume role be possible with the setup above?
The AWS SDK and CLI will automatically handle using the credentials from the assigned IAM task role.
Note that when you specify an IAM role for a task, the AWS CLI or other SDKs in the containers for that task use the AWS credentials provided by the task role exclusively and they no longer inherit any IAM permissions from the container instance.

Run AWS lambda as an IAM user

I want to run a python project on AWS lambda, but the API (STS:AssumeRole to generate federation URL) which I am trying to call only supports as an IAM user with long-term credentials. It is working on my machine with my IAM keys, but how can I make it run on AWS lambda?
You need to create a role that has similar permissions like your local user. And you can attach the role to your lambda to utilize the STS service.

AWS Execution failed due to configuration error: Authorizer error

I created 2 Authorizers to an API Gateway Endpoint. One manually using console and the other one using boto3.
The one created manually works great, however the one created using script gives error mentioned in the subject line. If you check the contents of these 2 authorizers, they are the same.
What can be the missing part? I dont think this is Invalid permission on lambda as it is working on one authorizer when configured manually.
The code for the same is as below:
response = client.create_authorizer(
restApiId=apiid,
name=authName,
type='TOKEN',
authType='custom',
authorizerUri=authorizerUri,
##arn:aws:apigateway:us-west-2:lambda:path/2015-03-31/functions/arn:aws:lambda:us-west-2:<AcctId>:function:CustomAuthorizer/invocations
identitySource='method.request.header.Authorization',
identityValidationExpression= '.*',
authorizerResultTtlInSeconds=300
)
Getting error:
Mon Jul 18 11:53:04 UTC 2016 : Execution failed due to configuration error: Invalid permissions on Lambda function
Mon Jul 18 11:53:04 UTC 2016 : Execution failed due to configuration error: Authorizer error
Mon Jul 18 11:53:04 UTC 2016 : AuthorizerConfigurationException
Just went down this rabbit hole, and came across a solution (worked with boto3 and python, hope it is similar across different sdk's).
The issue is that when you create it manually through the console, there is a popup that asks to enable api gateway (this specific authorizer to be exact) as a trigger to the lambda function you are using as an authorizer. When doing it via sdk, there obviously is no popup, so the authorizer is not authorized to invoke that lambda.
You can enable permissions two ways:
In lambda, create a new trigger for your authorizer in the Designer panel. Specifying an api and stage should do the trick
Create a role that will handle this permission. Give it permission to invoke lambda (you can use the AWSLambdaRole template role) and make sure to add api-gateway as a trusted entity to use this role in the Trust Relationships tab. your policy should look something like this:
{"Version": "2012-10-17","Statement": [{"Effect": "Allow","Principal": {"Service": "apigateway.amazonaws.com"},"Action": "sts:AssumeRole"}]}
Now, when creating a custom authorizer using the sdk, you have to provide an authorizerCredentials parameter, which is described as "(string) Specifies the required credentials as an IAM role for API Gateway to invoke the authorizer. To specify an IAM role for API Gateway to assume, use the role's Amazon Resource Name (ARN). To use resource-based permissions on the Lambda function, specify null.".
Copy your newly-created role's arn and use this as the value for the authorizerCredentials param.
Viola! you now have a custom authorizer that is allowed to use the role that allows it to invoke the lambda function!
(sorry for bad formatting, I don't comment often :D)
First, use the test button in API Gateway to confirm if you can call your Lambda function from within it. This will ensure that the API Gateway-to-Lambda connection is working.
To assess your resource policies, you need to call the GetPolicy API; the execution role controls what your code an do in Lambda, not who can call it. That would be a good next check.
You can also turn on logging in API Gateway, which is a good way to gain additional insight into what it's doing on your behalf. These logs then show up in Amazon CloudWatch Logs, where you can check out the flow of your request.
If you're using CORS, it's possible to miss a setting (or two :), so I would double check that as well. CURLing directly to your endpoint (you can easily test from Lambda using its HTTP endpoint blueprint) is a good first step before you "wire up" your API to a website or other app directly.
It would be helpful if you check your CloudWatch Logs Insights.
Go to your lambda function, open "Monitor" section. Read through the last logs received.
In my case, I made a typo on lambda methods handler address. That is why the error thrown.
You need to add a resource-based statement to your Lambda's permissions, in order to allow it to be called by your gateway. As #MichaelJoy points out, this is taken care of in the console when you click "Create" on the popup. Doing this programmatically requires taking a second step after your authorizer has been created.
To do this via CLI, you can do the following (presumably boto3 has all of the corresponding commands):
aws lambda add-permission --function-name 'arn:aws:lambda:us-west-2:<AcctId>:function:CustomAuthorizer' \
--action lambda:InvokeFunction --statement-id 'api_gateway' \
--principal apigateway.amazonaws.com --output text \
--source-arn "AUTHORIZER_ARN"
where AUTHORIZER_ARN is the ARN of the authorizer you just created. Note that the statement id of 'api_gateway' is arbitrary.
You can see all resource-based policies on your Lambda via the following. This may be helpful esp if you want to inspect the permissions added by you via the console as a working example of what you need to effect programmatically to get the same result:
aws lambda get-policy --function-name XXXX
If you're updating an existing resource-based permission, you'll need to remove it first via the remove-policy command