Các URL hàm AWS Lambda - Một cách khác để tạo điểm cuối HTTP cho một hàm Lambda
AWS đã phát hành các URL của hàm Lambda (hay còn gọi là Lambda FURL hoặc chỉ là các URL Lambda) , cung cấp cho bạn một khả năng khác để tạo điểm cuối HTTP cho các hàm Lambda. Trong bài đăng này, tôi sẽ tập trung vào sự khác biệt của nó với các dịch vụ hiện có cung cấp điểm cuối HTTP như vậy cho Lambda và những điều bạn cần cân nhắc khi định sử dụng nó cho dịch vụ sản xuất của mình.
Như tôi đã nói “khác”, bản thân khái niệm này không có gì mới. Bạn đã có thể gọi một hàm Lambda qua HTTPS với Lambda Invoke API mặc dù bạn sẽ không muốn khách hàng của mình làm điều đó trực tiếp.
Ví dụ, hãy tưởng tượng bạn có một hàm có tên GetItem chấp nhận itemId và lọc dưới dạng tham số. Khi bạn gọi nó trực tiếp, bạn phải sử dụng lệnh gọi kiểu RPC để đưa mọi thứ vào payload và bạn không thể tận dụng phương thức, URI tài nguyên, cũng như mã trạng thái HTTP vì chúng đã bị chiếm dụng trước. Máy khách nên sử dụng điểm cuối dịch vụ Lambda, biết tên hàm và họ phải ký yêu cầu bằng thông tin xác thực có quyền lambda: invokeFunction.

Thay vì yêu cầu người dùng cuối của bạn làm điều này, bạn sẽ muốn:
- Sử dụng một tên miền khác
- Ẩn tên chức năng
- Đóng gói và / hoặc phân quyền riêng biệt
- Yêu cầu định tuyến
- Chuyển đổi ngữ cảnh HTTP

Với cách này, máy khách có thể gọi đến một tên miền khác mà không cần biết tên hàm cũng như không có thông tin xác thực để gọi hàm Lambda. Proxy có thể xác thực / ủy quyền cho máy khách bằng một cách khác và chuyển đổi yêu cầu / phản hồi HTTP thành / từ Yêu cầu / Phản hồi gọi Lambda.
Mặc dù bạn có thể tự làm, AWS đã cung cấp một số dịch vụ được tạo sẵn như API Gateway và Lambda @ ALB. Mỗi loại có một số khác biệt về tính năng / mô hình định giá / giới hạn của chúng, nhưng về cơ bản chúng đều thực hiện các công việc giống nhau được đề cập ở trên. Lambda FURLs cũng vậy.
Tên miền điểm cuối
API Gateway cung cấp một dạng miền điểm cuối mặc định sau:
https://<apiId>.execute-api.<region>.amazonaws.com/<stage>
Đối với những người không thích điều này, nó cho phép bạn tạo miền tùy chỉnh và tạo ánh xạ đường dẫn cơ sở để ánh xạ đường dẫn trong miền tùy chỉnh để ánh xạ đến một giai đoạn của một API cụ thể. Có một số lý do giải thích tại sao bạn không nên sử dụng điểm cuối execute-api mặc định cho dịch vụ sản xuất công cộng của mình, nhưng tôi sẽ lưu nó để sử dụng sau.
Với Lambda @ ALB , bạn có thể sử dụng tên DNS ELB:
https://<elbName>-<id>.<region>.elb.amazonaws.com
URL của hàm Lambda có định dạng sau:
https://<url-id>.lambda-url.<region>.on.aws
Lambda tạo một <url-id>
phần của điểm cuối dựa trên một số yếu tố, bao gồm cả ID tài khoản AWS của bạn. Bởi vì quá trình này là xác định, bất kỳ ai cũng có thể truy xuất ID tài khoản của bạn từ <url-id>
.
Tôi không chắc "định nghĩa" chính xác ở đây là gì. Ban đầu, tôi hiểu lầm rằng url-id là xác định, nhưng nó không cung cấp cùng một url-id khi tôi xóa và tạo lại mặc dù không có gì thay đổi. Có thể dấu thời gian hoặc một giá trị ngẫu nhiên được sử dụng.

Miền dường như cho khách hàng của bạn biết rằng bạn đang sử dụng Lambda FURL. Bản thân Lambda FURL không hỗ trợ miền tùy chỉnh. Bạn có thể đặt CloudFront trước hàm Lambda nếu muốn, mặc dù nó có thể làm mất đi lợi ích của Lambda FURL. Lambda FURL có thể chạy tối đa 15 phút, trong khi CloudFront có thời gian chờ yêu cầu 30 giây. Nó có thể được tăng lên theo yêu cầu, nhưng không chắc nó có thể tăng lên đến 15 phút.
Ẩn tên hàm
Mỗi dịch vụ cho phép cấu hình hàm đích Arn theo một kiểu khác nhau. Đây là tài nguyên CloudFormation cho Lambda FURL. Tôi chưa thấy AWS :: Lambda :: Url trong tài liệu CloudFormation và tôi mong đợi nhận được URL điểm cuối trong Kết quả đầu ra.
{
"Type" : "AWS::Lambda::Url",
"Properties" : {
"AuthType" : String,
"Cors" : Cors,
"Qualifier" : String,
"TargetFunctionArn" : String
}
}
Trong nhiều trường hợp, bạn muốn tách ủy quyền chống lại khách hàng của mình khỏi ủy quyền chống lại hàm Lambda . Người trung gian phải có chứng chỉ để gọi hàm Lambda, trong khi người dùng cuối không cần thiết phải có.
API Gateway cung cấp các giải pháp toàn diện cho trường hợp này. Để ủy quyền từ API Gateway cho Lambda, bạn có thể sử dụng chính sách dựa trên danh tính hoặc chính sách tài nguyên, mặc dù tôi thực sự khuyên bạn nên sử dụng chính sách tài nguyên để giảm độ trễ nếu có thể. Để ủy quyền từ người dùng cuối tới API Gateway, bạn có thể sử dụng trình ủy quyền IAM / Lambda. API REST cũng hỗ trợ thêm trình ủy quyền Cognito, chính sách tài nguyên và tích hợp WAF. API HTTP hỗ trợ thêm trình ủy quyền JWT.
Lambda @ ALB sử dụng ủy quyền dựa trên chính sách Tài nguyên từ ALB cho Lambda. Để bảo vệ khỏi vấn đề cấp phó nhầm lẫn, hàm Lambda nên có chính sách cho phép lệnh gọi từ nguồn arn, phải là ALB Arn và sourceAccount. Tuy nhiên, họ không cung cấp bất kỳ hỗ trợ nào để ủy quyền cho người dùng cuối. Mặc dù bạn có thể thực hiện một ủy quyền trong hàm Lambda của mình, nhưng điều đó có nghĩa là tất cả các yêu cầu trái phép sẽ đến với hàm Lambda của bạn. Tất nhiên, WAF có thể được sử dụng để giảm rủi ro.
URL hàm Lambda có một cách tiếp cận duy nhất. Không có ủy quyền cụ thể nào giữa Lambda FURL và Lambda Function. Thay vào đó, người gọi phải được ủy quyền đối với chính hàm, nhưng đối với một hành động khác được gọi là lambda: InvokeFunctionUrl.
{
"Sid": "test123",
"Effect": "Allow",
"Principal": {
"AWS": "<user/role>"
},
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:<region>:<account>:function:<function_name>",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "AWS_IAM"
}
}
}
{
"Sid": "test123",
"Effect": "Allow",
"Principal": {
"AWS": "<user/role>"
},
"Action": "lambda:InvokeFunctionUrl",
"Resource": "arn:aws:lambda:<region>:<account>:url:<url-id>",
"Condition": {
"StringEquals": {
"lambda:FunctionUrlAuthType": "AWS_IAM"
}
}
}
Dù sao, trong Lambda FURL, người gọi phải thông qua ủy quyền dựa trên chính sách nhận dạng và / hoặc ủy quyền dựa trên chính sách tài nguyên. Chờ đã, không có NONE AuthType? Tôi biết nó là khó hiểu cho bạn. AuthType trong Lambda Function thực sự chỉ ra AuthN (xác thực), không phải ủy quyền. Khi AWS_IAM được sử dụng, nó sẽ thực hiện xác thực IAM. Sau khi một danh tính được xác thực, nó cho phép chống lại cả chính sách danh tính và chính sách tài nguyên. Khi NONE được sử dụng, nó không thực hiện xác thực vì vậy nó không thể cho phép chống lại chính sách nhận dạng, nhưng nó vẫn cho phép chống lại chính sách tài nguyên. Nếu bạn thực sự muốn cho phép bất kỳ ai gọi hàm Lambda của mình, bạn cần định cấu hình chính sách tài nguyên để cho phép nó.
Tuy nhiên, hãy suy nghĩ kỹ trước khi sử dụng NONE. Ví dụ về bài đăng trên blog AWS sử dụng NONE và thực hiện ủy quyền tùy chỉnh trong hàm Lambda. Tôi thực sự không khuyến khích kiểu này vì bất kỳ người dùng trái phép nào cũng có thể nhấn vào hàm Lambda của bạn và nó sẽ sử dụng đồng thời thực thi Lambda và bạn sẽ bị tính phí cho thời gian thực thi để thực hiện ủy quyền. Không có bất kỳ cách nào để giảm thiểu.
Nó không giống với việc sử dụng trình ủy quyền Lambda của API Gateway phải không? Không hẳn vậy. Trình ủy quyền Lambda của API Gateway kiểm tra xem (các) nguồn nhận dạng có được cung cấp hay không và cung cấp bộ đệm ẩn của trình ủy quyền để bảo vệ chức năng ủy quyền khỏi brownout. API Gateway cũng yêu cầu yêu cầu truy cập vào phương thức / tài nguyên hiện có. Bạn cũng có thể thu hẹp khả năng truy cập với API Riêng tư, sử dụng WAF hoặc thậm chí MTLS.
Điều này giống như khi bạn định cấu hình loại ủy quyền API Gateway thành NONE. Tôi đã thấy nhiều khách hàng rời khỏi API của họ với kiểu ủy quyền NONE, sau đó nhận được một hóa đơn khổng lồ do các yêu cầu từ một nơi nào đó trên internet. Vì nó được hiển thị trên internet công cộng, bất kỳ ai cũng có thể gọi Lambda của bạn.
Yêu cầu định tuyến
Điểm cuối HTTP phải cung cấp một cách để định tuyến một yêu cầu HTTP đến hàm Lambda. API Gateway cung cấp ngữ nghĩa REST (phương thức / tài nguyên) để ánh xạ thành các hàm Lambda khác nhau. Lambda @ ALB cung cấp định tuyến dựa trên quy tắc để ánh xạ vào các hàm Lambda khác nhau.
Ngược lại, URL Hàm Lambda luôn định tuyến đến một hàm Lambda. Bản thân hàm lambda của bạn sẽ hoạt động như một bộ định tuyến để thực hiện một hành động khác dựa trên yêu cầu. Mặc dù mẫu Mono Lambda này rất đơn giản và thuận tiện, nhưng điều trở lại là gói triển khai của bạn có thể trở nên lớn hơn và bạn đang mất tất cả tính chi tiết về quyền, ghi nhật ký / số liệu và đồng thời.
Ngoài ra, bạn có thể sử dụng tạo nhiều hàm sau đó yêu cầu khách hàng của bạn sử dụng nhiều Lambda FURL nếu nó được chấp nhận đối với chúng. Tuy nhiên, thông thường khách hàng sẽ muốn sử dụng một điểm cuối tích hợp duy nhất, sau đó cuối cùng bạn sẽ phải đặt một proxy khác trước các Lambda FURL đó để tổng hợp thành một miền duy nhất.
Chuyển đổi ngữ cảnh HTTP
Khi một yêu cầu đến điểm cuối HTTP, một yêu cầu HTTP phải được chuyển đổi thành một định dạng để được chuyển làm đối số đầu vào của hàm Lambda. Theo cách tương tự, đầu ra hàm phải được chuyển đổi thành phản hồi HTTP.
Khi API Gateway khởi chạy API REST, ban đầu nó có tích hợp Lambda (không proxy). Nó có nghĩa là bạn phải sử dụng VTL để định cấu hình tải trọng được chuyển. Bạn phải dựa vào RegEx để kích hoạt mã trạng thái khác cho phản hồi.
Sau đó, tích hợp Lambda Proxy được giới thiệu. Nó cung cấp một định dạng trọng tải cụ thể cho yêu cầu và phản hồi. Nó tiết kiệm rất nhiều thời gian cho bạn khỏi việc gỡ lỗi các ánh xạ và ánh xạ VTL xấu xí. Nếu bạn vẫn đang sử dụng tích hợp không proxy Lambda, tôi thực sự khuyên bạn nên ngừng sử dụng nó. Nếu bạn mới sử dụng API Gateway, đừng xem xét tích hợp không proxy. Định dạng tải trọng, hay còn gọi là định dạng tải trọng Lambda Proxy v1 đã được áp dụng cho Lambda @ ALB sau đó.
Sau đó, trong khi nhóm API Gateway đang xây dựng API HTTP, Lambda Payload Version 2 đã được giới thiệu để kết hợp tất cả các bài học kinh nghiệm từ REST và API WebSocket . Để tương thích ngược, Tích hợp API HTTP có một tùy chọn để chỉ định phiên bản định dạng tải trọng .
URL hàm Lambda đã thông qua định dạng Lambda Payload của API Gateway phiên bản 2.0, có nghĩa là bạn có thể sử dụng cùng một hàm Lambda giữa API HTTP và URL hàm Lambda. Tuy nhiên, vì nó không hỗ trợ định dạng Payload 1.0, bạn không thể chia sẻ hàm lambda với REST API. Điều này có thể được hỗ trợ trong tương lai.
Có gì thiếu trong URL của hàm Lambda?
Đối với quan điểm tính năng, URL hàm Lambda gần như nằm giữa API Gateway và Lambda @ ALB. Nó thiếu nhiều tính năng trong API Gateway, trong khi nó cung cấp xác thực IAM và CORS bên ngoài so với Lambda @ ALB.
So sánh các tính năng với API Gateway là không có ý nghĩa. Rõ ràng API Gateway có nhiều tính năng hơn trong khi tốn thêm tiền. Có cái gì thiếu nó khi nói đến một dịch vụ sản xuất?
Mối quan tâm hàng đầu trong tâm trí tôi là không có biện pháp giảm thiểu bảo mật nào tốt ngoài việc ủy quyền. Nếu không có IAM Auth, biện pháp giảm thiểu bảo mật duy nhất là điều chỉnh đồng thời Lambda để bảo vệ bạn khỏi bị tính phí quá mức hoặc ảnh hưởng đến các chức năng khác của Lambda. Không có cách nào khác để tự bảo vệ tính khả dụng của chức năng.
Một điểm thiếu khác là nhật ký truy cập. Bạn có số liệu 4xx / 5xx từ nó, nhưng bạn không có cách nào để xác định yêu cầu khi yêu cầu bị từ chối trước khi đến hàm Lambda của bạn. Vì yêu cầu không bao giờ đến được hàm Lambda của bạn nên bạn không có bất kỳ nhật ký nào. Đây là một lá cờ đỏ lớn đối với tôi về khả năng hiển thị hoạt động.
Ví dụ: URL của hàm Lambda sẽ trả về 403 Forbidden mà không cung cấp thêm thông tin nào. Ví dụ dưới đây không phải do vấn đề ủy quyền gây ra mặc dù đó là 403. FWIW, tôi đã có quyền truy cập vào phản hồi, nhưng nếu khách hàng của bạn phàn nàn về điều này thì sao?
HTTP/1.1 403 Forbidden
x-amzn-ErrorType: AccessDeniedException
x-amzn-RequestId: xxxxxxx-ef5a-4cc4-b6bf-7c971d3188d1
{"Message":"Forbidden"}
Rõ ràng, các URL của hàm Lambda có một số điểm hấp dẫn. Nó không tính thêm chi phí cho bạn (bao gồm trong yêu cầu / thực thi Lambda) và bạn có thể chạy nó lên đến 15 phút. Điều này giống như một chiến thắng lớn trước API Gateway khiến bạn mất thêm tiền và thời gian chờ tích hợp là 29 giây.
Đầu tiên, bạn sẽ ổn với việc tên miền thể hiện sự thật rằng bạn đang sử dụng Lambda FURLs. Bạn có thể cân nhắc đặt CloudFront trước nó để sử dụng miền tùy chỉnh trong khi nó có thể tốn thêm tiền (mặc dù bạn thích Lambda FURL vì nó miễn phí) và bạn cần phải xử lý thời gian chờ yêu cầu CloudFront làm mất đi lợi ích thực thi 15 phút.
Tôi sẽ không thể sử dụng điều này mà không có xác thực IAM như tôi đã chỉ ra. Sử dụng IAM Auth hạn chế khách hàng có thể ký yêu cầu bằng thông tin xác thực có quyền. Tài khoản phải nằm trong tài khoản hoặc có thể quản lý trong chính sách tài nguyên. Trong một số trường hợp, bạn có thể đặt một số điều kiện với các khóa điều kiện toàn cầu AWS trong chính sách Tài nguyên khi đang sử dụng loại xác thực NONE. Một lần nữa, bạn có thể đặt CloudFront trước nó, sau đó liên kết WAF ở đó, miễn là bạn có cách hạn chế quyền truy cập vào Lambda FURL chỉ từ CloudFront.
Nó hoạt động rất tốt khi bạn chỉ có một khách hàng duy nhất. Bạn có thể sử dụng đồng thời hàm Lambda trực tiếp để điều chỉnh ứng dụng khách. Không phải lo lắng về việc điều chỉnh giới hạn điều chỉnh trong API Gateway chống lại đồng thời Lambda. Tuy nhiên, nếu nhiều máy khách chia sẻ chức năng, một máy khách có thể sử dụng hết giới hạn đồng thời vì không có cách nào để giảm tốc cho mỗi máy khách. nó làm nảy sinh vấn đề hàng xóm ồn ào và có thể dẫn đến điều chỉnh không công bằng. Nếu khách hàng của bạn có thể đồng ý với sự không công bằng, điều đó không quan trọng.
Tóm lại, điều này rất hiệu quả đối với một số lượng tương đối nhỏ khách hàng đáng tin cậy có thể sử dụng IAM Auth mà không tính thêm phí cho tôi.