mirror of
https://github.com/mulgadc/spinifex.git
synced 2026-10-15 17:11:30 +00:00
fix: add request body size limit
This commit is contained in:
@@ -343,6 +343,7 @@ var (
|
||||
ErrorPendingVpcPeeringConnectionLimitExceeded = "PendingVpcPeeringConnectionLimitExceeded"
|
||||
ErrorPlacementGroupLimitExceeded = "PlacementGroupLimitExceeded"
|
||||
ErrorPrivateIpAddressLimitExceeded = "PrivateIpAddressLimitExceeded"
|
||||
ErrorRequestEntityTooLarge = "RequestEntityTooLarge"
|
||||
ErrorRequestExpired = "RequestExpired"
|
||||
ErrorRequestLimitExceeded = "RequestLimitExceeded"
|
||||
ErrorRequestResourceCountExceeded = "RequestResourceCountExceeded"
|
||||
@@ -800,6 +801,7 @@ var ErrorLookup = map[string]ErrorMessage{
|
||||
ErrorPendingVpcPeeringConnectionLimitExceeded: {HTTPCode: 409, Message: "You've reached the limit on the number of pending VPC peering connections that you can have."},
|
||||
ErrorPlacementGroupLimitExceeded: {HTTPCode: 400, Message: "You've reached the limit on the number of placement groups that you can have."},
|
||||
ErrorPrivateIpAddressLimitExceeded: {HTTPCode: 400, Message: "You've reached the limit on the number of private IP addresses that you can assign to the specified network interface for that type of instance. For more information, see IP addresses per network interface."},
|
||||
ErrorRequestEntityTooLarge: {HTTPCode: 413, Message: "Request body exceeds the maximum allowed size."},
|
||||
ErrorRequestExpired: {HTTPCode: 403, Message: "The request reached the service more than 15 minutes after the date stamp on the request or more than 15 minutes after the request expiration date (such as for presigned URLs), or the date stamp on the request is more than 15 minutes in the future. If you're using temporary security credentials, this error can also occur if the credentials have expired. For more information, see Temporary security credentials in the IAM User Guide."},
|
||||
ErrorRequestLimitExceeded: {HTTPCode: 500, Message: "The maximum request rate permitted by the Amazon EC2 APIs has been exceeded for your account. For best results, use an increasing or variable sleep interval between requests. For more information, see Query API request rate."},
|
||||
ErrorRequestResourceCountExceeded: {HTTPCode: 409, Message: "Details in your Spot request exceed the numbers allowed by the Spot service in one of the following ways, depending on the action that generated the error: \u2014If you get this error when you submitted a request for Spot Instances, check the number of Spot Instances specified in your request. The number shouldn't exceed the 3,000 maximum allowed per request. Resend your Spot Instance request and specify a number less than 3,000. If your account's regional Spot request limit is greater than 3,000 instances, you can access these instances by submitting multiple smaller requests. \u2014If you get this error when you sent Describe Spot Instance requests, check the number of requests for Spot Instance data, the amount of data you requested, and how often you sent the request. The frequency with which you requested the data combined with the amount of data exceeds the levels allowed by the Spot service. Try again and submit fewer large Describe requests over longer intervals."},
|
||||
|
||||
@@ -345,6 +345,7 @@ func TestErrorLookup(t *testing.T) {
|
||||
{code: "PendingVpcPeeringConnectionLimitExceeded", http: 409, message: "You've reached the limit on the number of pending VPC peering connections that you can have."},
|
||||
{code: "PlacementGroupLimitExceeded", http: 400, message: "You've reached the limit on the number of placement groups that you can have."},
|
||||
{code: "PrivateIpAddressLimitExceeded", http: 400, message: "You've reached the limit on the number of private IP addresses that you can assign to the specified network interface for that type of instance. For more information, see IP addresses per network interface."},
|
||||
{code: "RequestEntityTooLarge", http: 413, message: "Request body exceeds the maximum allowed size."},
|
||||
{code: "RequestExpired", http: 403, message: "The request reached the service more than 15 minutes after the date stamp on the request or more than 15 minutes after the request expiration date (such as for presigned URLs), or the date stamp on the request is more than 15 minutes in the future. If you're using temporary security credentials, this error can also occur if the credentials have expired. For more information, see Temporary security credentials in the IAM User Guide."},
|
||||
{code: "RequestLimitExceeded", http: 500, message: "The maximum request rate permitted by the Amazon EC2 APIs has been exceeded for your account. For best results, use an increasing or variable sleep interval between requests. For more information, see Query API request rate."},
|
||||
{code: "RequestResourceCountExceeded", http: 409, message: "Details in your Spot request exceed the numbers allowed by the Spot service in one of the following ways, depending on the action that generated the error: \u2014If you get this error when you submitted a request for Spot Instances, check the number of Spot Instances specified in your request. The number shouldn't exceed the 3,000 maximum allowed per request. Resend your Spot Instance request and specify a number less than 3,000. If your account's regional Spot request limit is greater than 3,000 instances, you can access these instances by submitting multiple smaller requests. \u2014If you get this error when you sent Describe Spot Instance requests, check the number of requests for Spot Instance data, the amount of data you requested, and how often you sent the request. The frequency with which you requested the data combined with the amount of data exceeds the levels allowed by the Spot service. Try again and submit fewer large Describe requests over longer intervals."},
|
||||
|
||||
@@ -4,6 +4,7 @@ import (
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/subtle"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"log/slog"
|
||||
@@ -21,6 +22,9 @@ import (
|
||||
const (
|
||||
// Maximum allowed clock skew for signature validation (5 minutes)
|
||||
maxClockSkew = 5 * time.Minute
|
||||
|
||||
// Maximum request body size for signature validation (10 MB)
|
||||
maxBodySize = 10 * 1024 * 1024
|
||||
)
|
||||
|
||||
// SigV4AuthMiddleware returns stdlib middleware that validates AWS Signature V4 authentication.
|
||||
@@ -139,9 +143,18 @@ func (gw *GatewayConfig) SigV4AuthMiddleware() func(http.Handler) http.Handler {
|
||||
return
|
||||
}
|
||||
|
||||
// Limit request body size to prevent OOM from unauthenticated requests
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxBodySize)
|
||||
|
||||
// Read body once and re-buffer for downstream handlers
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
var maxBytesErr *http.MaxBytesError
|
||||
if errors.As(err, &maxBytesErr) {
|
||||
slog.Warn("Request body too large", "limit", maxBodySize)
|
||||
gw.writeSigV4Error(w, r, awserrors.ErrorRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
slog.Error("Failed to read request body", "err", err)
|
||||
gw.writeSigV4Error(w, r, awserrors.ErrorInternalError)
|
||||
return
|
||||
|
||||
@@ -667,6 +667,34 @@ func TestSigV4Auth_DecryptFailure(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestSigV4Auth_RequestBodyTooLarge(t *testing.T) {
|
||||
handler := setupTestApp(testAccessKey, testSecretKey)
|
||||
|
||||
// Create a body that exceeds maxBodySize (10 MB + 1 byte)
|
||||
oversizedBody := strings.Repeat("x", maxBodySize+1)
|
||||
authHeader, timestamp := generateTestAuthHeader(
|
||||
"POST", "/", "", oversizedBody,
|
||||
testAccessKey, testSecretKey, testRegion, testService,
|
||||
)
|
||||
|
||||
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(oversizedBody))
|
||||
req.Host = "localhost:9999"
|
||||
req.Header.Set("Authorization", authHeader)
|
||||
req.Header.Set("X-Amz-Date", timestamp)
|
||||
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
||||
|
||||
resp := doRequest(handler, req)
|
||||
|
||||
if resp.StatusCode != http.StatusRequestEntityTooLarge {
|
||||
t.Errorf("Expected status 413, got %d", resp.StatusCode)
|
||||
}
|
||||
|
||||
body, _ := io.ReadAll(resp.Body)
|
||||
if !strings.Contains(string(body), "RequestEntityTooLarge") {
|
||||
t.Errorf("Expected RequestEntityTooLarge error, got: %s", string(body))
|
||||
}
|
||||
}
|
||||
|
||||
func TestCanonicalHeaderName(t *testing.T) {
|
||||
testCases := []struct {
|
||||
input string
|
||||
|
||||
Reference in New Issue
Block a user