From 7ee4a8667fd6ff531d65afd8427d3ccb0822a3f4 Mon Sep 17 00:00:00 2001 From: John Lee Date: Wed, 21 Jun 2023 09:25:07 -0400 Subject: [PATCH 1/2] Bump buildnumber.dat --- buildnumber.dat | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/buildnumber.dat b/buildnumber.dat index 0cfbf08886..00750edc07 100644 --- a/buildnumber.dat +++ b/buildnumber.dat @@ -1 +1 @@ -2 +3 From 796ac9f5795a02c941e010205334d6ca50d9b6dd Mon Sep 17 00:00:00 2001 From: Will Winder Date: Thu, 22 Jun 2023 11:34:18 -0400 Subject: [PATCH 2/2] api: Disable body limit middleware for admin endpoints. (#5486) --- daemon/algod/api/server/router.go | 32 +++++++++------- daemon/algod/api/server/router_test.go | 37 +++++++------------ .../algod/api/server/v2/test/handlers_test.go | 34 ++++++++++++++++- daemon/algod/api/server/v2/test/helpers.go | 2 +- 4 files changed, 66 insertions(+), 39 deletions(-) diff --git a/daemon/algod/api/server/router.go b/daemon/algod/api/server/router.go index 6797bd988c..b599194ab3 100644 --- a/daemon/algod/api/server/router.go +++ b/daemon/algod/api/server/router.go @@ -51,8 +51,8 @@ const ( apiV1Tag = "/v1" // TokenHeader is the header where we put the token. TokenHeader = "X-Algo-API-Token" - // maxRequestBodyBytes is the maximum request body size that we allow in our APIs. - maxRequestBodyBytes = "10MB" + // MaxRequestBodyBytes is the maximum request body size that we allow in our APIs. + MaxRequestBodyBytes = "10MB" ) // wrapCtx passes a common context to each request without a global variable. @@ -79,8 +79,13 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str if err := tokens.ValidateAPIToken(adminAPIToken); err != nil { logger.Errorf("Invalid adminAPIToken was passed to NewRouter ('%s'): %v", adminAPIToken, err) } - adminAuthenticator := middlewares.MakeAuth(TokenHeader, []string{adminAPIToken}) - apiAuthenticator := middlewares.MakeAuth(TokenHeader, []string{adminAPIToken, apiToken}) + adminMiddleware := []echo.MiddlewareFunc{ + middlewares.MakeAuth(TokenHeader, []string{adminAPIToken}), + } + publicMiddleware := []echo.MiddlewareFunc{ + middleware.BodyLimit(MaxRequestBodyBytes), + middlewares.MakeAuth(TokenHeader, []string{adminAPIToken, apiToken}), + } e := echo.New() @@ -93,7 +98,6 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str e.Use( middlewares.MakeLogger(logger), middlewares.MakeCORS(TokenHeader), - middleware.BodyLimit(maxRequestBodyBytes), ) // Request Context @@ -104,14 +108,14 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str // Route pprof requests to DefaultServeMux. // The auth middleware removes /urlAuth/:token so that it can be routed correctly. if node.Config().EnableProfiler { - e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux), adminAuthenticator) - e.GET(fmt.Sprintf("%s/debug/pprof/*", middlewares.URLAuthPrefix), echo.WrapHandler(http.DefaultServeMux), adminAuthenticator) + e.GET("/debug/pprof/*", echo.WrapHandler(http.DefaultServeMux), adminMiddleware...) + e.GET(fmt.Sprintf("%s/debug/pprof/*", middlewares.URLAuthPrefix), echo.WrapHandler(http.DefaultServeMux), adminMiddleware...) } // Registering common routes (no auth) registerHandlers(e, "", common.Routes, ctx) // Registering v1 routes - registerHandlers(e, apiV1Tag, routes.V1Routes, ctx, apiAuthenticator) + registerHandlers(e, apiV1Tag, routes.V1Routes, ctx, publicMiddleware...) // Registering v2 routes v2Handler := v2.Handlers{ @@ -119,17 +123,17 @@ func NewRouter(logger logging.Logger, node APINodeInterface, shutdown <-chan str Log: logger, Shutdown: shutdown, } - nppublic.RegisterHandlers(e, &v2Handler, apiAuthenticator) - npprivate.RegisterHandlers(e, &v2Handler, adminAuthenticator) - ppublic.RegisterHandlers(e, &v2Handler, apiAuthenticator) - pprivate.RegisterHandlers(e, &v2Handler, adminAuthenticator) + nppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...) + npprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...) + ppublic.RegisterHandlers(e, &v2Handler, publicMiddleware...) + pprivate.RegisterHandlers(e, &v2Handler, adminMiddleware...) if node.Config().EnableFollowMode { - data.RegisterHandlers(e, &v2Handler, apiAuthenticator) + data.RegisterHandlers(e, &v2Handler, publicMiddleware...) } if node.Config().EnableExperimentalAPI { - experimental.RegisterHandlers(e, &v2Handler, apiAuthenticator) + experimental.RegisterHandlers(e, &v2Handler, publicMiddleware...) } return e diff --git a/daemon/algod/api/server/router_test.go b/daemon/algod/api/server/router_test.go index 98e772f276..37fc3be74a 100644 --- a/daemon/algod/api/server/router_test.go +++ b/daemon/algod/api/server/router_test.go @@ -22,22 +22,15 @@ import ( "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" - "github.com/stretchr/testify/suite" "github.com/algorand/go-algorand/daemon/algod/api/server/lib" "github.com/algorand/go-algorand/daemon/algod/api/server/v1/routes" - "github.com/algorand/go-algorand/logging" "github.com/algorand/go-algorand/test/partitiontest" ) -type TestSuite struct { - suite.Suite - e *echo.Echo -} - -func (s *TestSuite) SetupSuite() { - s.e = echo.New() +func setupRouter() *echo.Echo { + e := echo.New() // Make a deep copy of the routes array with handlers. v1RoutesCopy := make([]lib.Route, len(routes.V1Routes)) for _, route := range routes.V1Routes { @@ -51,10 +44,13 @@ func (s *TestSuite) SetupSuite() { // Make a ReqContext with an initialized logger to prevent nil dereferencing. reqCtx := lib.ReqContext{Log: logging.NewLogger()} // Registering v1 routes - registerHandlers(s.e, apiV1Tag, v1RoutesCopy, reqCtx) + registerHandlers(e, apiV1Tag, v1RoutesCopy, reqCtx) + return e } -func (s *TestSuite) TestGetTransactionV1Sunset() { +func TestGetTransactionV1Sunset(t *testing.T) { + partitiontest.PartitionTest(t) + testCases := []struct { path string route string @@ -67,21 +63,16 @@ func (s *TestSuite) TestGetTransactionV1Sunset() { } rec := httptest.NewRecorder() - ctx := s.e.NewContext(nil, rec) + e := setupRouter() + ctx := e.NewContext(nil, rec) for _, testCase := range testCases { - s.e.Router().Find(http.MethodGet, testCase.path, ctx) - assert.Equal(s.T(), testCase.route, ctx.Path()) + e.Router().Find(http.MethodGet, testCase.path, ctx) + assert.Equal(t, testCase.route, ctx.Path()) // Check that router correctly routes to the v1Sunset handler. - assert.Equal(s.T(), nil, ctx.Handler()(ctx)) - assert.NotNil(s.T(), rec.Body) - assert.Equal(s.T(), http.StatusGone, rec.Code) + assert.Equal(t, nil, ctx.Handler()(ctx)) + assert.NotNil(t, rec.Body) + assert.Equal(t, http.StatusGone, rec.Code) } - -} - -func TestTestSuite(t *testing.T) { - partitiontest.PartitionTest(t) - suite.Run(t, new(TestSuite)) } diff --git a/daemon/algod/api/server/v2/test/handlers_test.go b/daemon/algod/api/server/v2/test/handlers_test.go index e30169b4ad..a4625d41c0 100644 --- a/daemon/algod/api/server/v2/test/handlers_test.go +++ b/daemon/algod/api/server/v2/test/handlers_test.go @@ -24,15 +24,16 @@ import ( "fmt" "io" "math" + "net" "net/http" "net/http/httptest" "strings" "testing" "time" + "github.com/algorand/go-algorand/daemon/algod/api/server" "github.com/algorand/go-algorand/ledger/eval" "github.com/algorand/go-algorand/ledger/ledgercore" - "github.com/labstack/echo/v4" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" @@ -2203,3 +2204,34 @@ func TestDeltasForTxnGroup(t *testing.T) { require.NoError(t, err) require.Equal(t, 501, rec.Code) } + +func TestRouterRequestBody(t *testing.T) { + partitiontest.PartitionTest(t) + t.Parallel() + + mockLedger, _, _, _, _ := testingenv(t, 1, 1, true) + mockNode := makeMockNode(mockLedger, t.Name(), nil, cannedStatusReportGolden, false) + dummyShutdownChan := make(chan struct{}) + l, err := net.Listen("tcp", ":0") // create listener so requests are buffered + e := server.NewRouter(logging.TestingLog(t), mockNode, dummyShutdownChan, "", "", l, 1000) + go e.Start(":0") + defer e.Close() + + // Admin API call greater than max body bytes should succeed + assert.Equal(t, "10MB", server.MaxRequestBodyBytes) + stringReader := strings.NewReader(strings.Repeat("a", 50_000_000)) + req, err := http.NewRequest(http.MethodPost, fmt.Sprintf("https://%s/v2/participation", e.Listener.Addr().String()), stringReader) + assert.NoError(t, err) + rec := httptest.NewRecorder() + e.ServeHTTP(rec, req) + assert.Equal(t, http.StatusOK, rec.Code) + + // Public API call greater than max body bytes fails + assert.Equal(t, "10MB", server.MaxRequestBodyBytes) + stringReader = strings.NewReader(strings.Repeat("a", 50_000_000)) + req, err = http.NewRequest(http.MethodPost, fmt.Sprintf("https://%s/v2/transactions", e.Listener.Addr().String()), stringReader) + assert.NoError(t, err) + rec = httptest.NewRecorder() + e.ServeHTTP(rec, req) + assert.Equal(t, http.StatusRequestEntityTooLarge, rec.Code) +} diff --git a/daemon/algod/api/server/v2/test/helpers.go b/daemon/algod/api/server/v2/test/helpers.go index bcc1e15d88..75ee461ca0 100644 --- a/daemon/algod/api/server/v2/test/helpers.go +++ b/daemon/algod/api/server/v2/test/helpers.go @@ -103,7 +103,7 @@ type mockNode struct { } func (m *mockNode) InstallParticipationKey(partKeyBinary []byte) (account.ParticipationID, error) { - panic("implement me") + return account.ParticipationID{}, nil } func (m *mockNode) ListParticipationKeys() ([]account.ParticipationRecord, error) {