I have some ECS tasks deployed in the subnet of a VPC. All traffic from the private subnet is routed to a NAT in the public subnet, and all traffic from the public subnet is routed to an internet gateway. In this way, the ECS tasks may access the internet, including invoking some of my Lambda functions that are NOT in a VPC.
I learned that using a VPC interface endpoint can avoid traffic via the internet, but the resources in VPC call AWS services via the endpoint. So I created one following this documentation. In particular, I associate the endpoint with my private subnet and the default security group of the VPC.
It seems to me that the ECS tasks are still invoking the Lambda function via the old way. To examine it, I created the following policy in the endpoint that deny all invocations of the lambda, but the ECS task can still invoke the lambda, so I assume they are doing so via the NAT-igw way.
{
"Statement":[
{
"Principal":"*",
"Effect":"Deny",
"Action":[
"lambda:InvokeFunction"
],
"Resource": [
"arn:aws:lambda:us-east-2:123456789012:function:my-function",
"arn:aws:lambda:us-east-2:123456789012:function:my-function:*"
]
}
]
}
Do I need to somehow explicitly add a route to my private subnet's route table that routes traffic targeting the public Lambda endpoints to my VPC endpoint? This is how a VPC gateway endpoint for S3 service does (it adds the route automatically to the route tables associated with the VPC gateway endpoint). To configure an interface endpoint, I'm asked to associate it with subnets/security groups instead of route tables though. And I don't see anyone saying this is needed.
To answer myself (thanks for @mark-b pointing me in the right direction):
Different from a gateway endpoint, an interface endpoint is just an ENI per subnet and DNS pointing them. To use it to invoke an outside Lambda, the inside resource can specify this DSN as the Lambda service endpoint when invoking, which replaces the default service endpoints. For example, my ECS task uses Python SDK, so I can do something like
lambda_client = boto3.client("lambda", endpoint_url="vpce-123456abcde-abcdef.lambda.us-east-2.vpce.amazonaws.com")
lambda_client.invoke(FunctionName="my_lambda_function")
If I don't specify endpoint_url="xxxxxxxx"
, the SDK selects the default one for us, and it was why my ECS task still used the public internet path to invoke the function.
Alternatively, as this answer pointed out, we can also turn on "private DNS name" for the endpoint (which requires to turn on "DNS hostname" for the VPC). This makes your inside resource resolve the default DNS (e.g., lambda.us-east-2.amazonaws.com
) as your VPC endpoint instead of the public endpoint. So you don't need to specify a special endpoint URL.