VYPR
High severity8.2GHSA Advisory· Published Sep 2, 2025· Updated Apr 15, 2026

CVE-2024-58259

CVE-2024-58259

Description

A vulnerability has been identified within Rancher Manager in which it did not enforce request body size limits on certain public (unauthenticated) and authenticated API endpoints. This allows a malicious user to exploit this by sending excessively large payloads, which are fully loaded into memory during processing, leading to Denial of Service (DoS).

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
github.com/rancher/rancherGo
>= 2.12.0, < 2.12.12.12.1
github.com/rancher/rancherGo
>= 2.11.0, < 2.11.52.11.5
github.com/rancher/rancherGo
>= 2.10.0, < 2.10.92.10.9
github.com/rancher/rancherGo
>= 2.9.0, < 2.9.112.9.11
github.com/rancher/rancherGo
< 0.0.0-20250813072957-aee95d4e2a410.0.0-20250813072957-aee95d4e2a41

Affected products

1

Patches

1
aee95d4e2a41

Public API body request limiting (#51422)

https://github.com/rancher/rancherKevin McDermottAug 13, 2025via ghsa
7 files changed · +168 5
  • hack/airgap/main.go+2 2 modified
    @@ -55,13 +55,13 @@ func main() {
     	f.StringVar(&output, "output", "", "target file to be generated")
     	err := f.Parse(os.Args[1:])
     	if err != nil {
    -		fmt.Println("failed to parse args %q: %v", os.Args, err)
    +		fmt.Printf("failed to parse args %q: %v\n", os.Args, err)
     		os.Exit(1)
     	}
     
     	err = Save(version, output)
     	if err != nil {
    -		fmt.Println("failed to save tarball: %v", err)
    +		fmt.Printf("failed to save tarball: %v\n", err)
     		os.Exit(1)
     	}
     }
    
  • pkg/auth/server.go+35 2 modified
    @@ -4,6 +4,7 @@ import (
     	"context"
     	"fmt"
     	"net/http"
    +	"os"
     
     	"github.com/gorilla/mux"
     	"github.com/rancher/rancher/pkg/api/norman"
    @@ -17,9 +18,11 @@ import (
     	"github.com/rancher/rancher/pkg/clusterrouter"
     	"github.com/rancher/rancher/pkg/features"
     	"github.com/rancher/rancher/pkg/types/config"
    +	"github.com/rancher/rancher/pkg/utils"
     	"github.com/rancher/rancher/pkg/wrangler"
     	steveauth "github.com/rancher/steve/pkg/auth"
     	"github.com/sirupsen/logrus"
    +	"k8s.io/apimachinery/pkg/api/resource"
     	"k8s.io/apiserver/pkg/endpoints/request"
     )
     
    @@ -75,8 +78,16 @@ func newAPIManagement(ctx context.Context, scaledContext *config.ScaledContext)
     
     	root := mux.NewRouter()
     	root.UseEncodedPath()
    -	root.PathPrefix("/v3-public").Handler(publicAPI)
    -	root.PathPrefix("/v1-saml").Handler(saml)
    +
    +	apiLimit, err := quantityAsInt64(getEnvWithDefault("CATTLE_AUTH_API_BODY_LIMIT", "1Mi"), 1024*1024)
    +	if err != nil {
    +		return nil, err
    +	}
    +	logrus.Infof("Configuring auth server API body limit to %v bytes", apiLimit)
    +
    +	limitingHandler := utils.APIBodyLimitingHandler(apiLimit)
    +	root.PathPrefix("/v3-public").Handler(limitingHandler(publicAPI))
    +	root.PathPrefix("/v1-saml").Handler(limitingHandler(saml))
     	root.NotFoundHandler = privateAPI
     
     	return func(next http.Handler) http.Handler {
    @@ -162,3 +173,25 @@ func SetXAPICattleAuthHeader(next http.Handler) http.Handler {
     		next.ServeHTTP(rw, req)
     	})
     }
    +
    +func quantityAsInt64(s string, d int64) (int64, error) {
    +	i, err := resource.ParseQuantity(s)
    +	if err != nil {
    +		return 0, fmt.Errorf("parsing setting: %w", err)
    +	}
    +
    +	q, ok := i.AsInt64()
    +	if ok {
    +		return q, nil
    +	}
    +
    +	return d, nil
    +}
    +
    +func getEnvWithDefault(key, defaultValue string) string {
    +	if v := os.Getenv(key); v != "" {
    +		return v
    +	}
    +
    +	return defaultValue
    +}
    
  • pkg/multiclustermanager/routes.go+12 1 modified
    @@ -2,6 +2,7 @@ package multiclustermanager
     
     import (
     	"context"
    +	"fmt"
     	"net/http"
     
     	"github.com/gorilla/mux"
    @@ -30,10 +31,13 @@ import (
     	"github.com/rancher/rancher/pkg/metrics"
     	"github.com/rancher/rancher/pkg/multiclustermanager/whitelist"
     	"github.com/rancher/rancher/pkg/rbac"
    +	"github.com/rancher/rancher/pkg/settings"
     	"github.com/rancher/rancher/pkg/tunnelserver/mcmauthorizer"
     	"github.com/rancher/rancher/pkg/types/config"
    +	"github.com/rancher/rancher/pkg/utils"
     	"github.com/rancher/rancher/pkg/version"
     	"github.com/rancher/steve/pkg/auth"
    +	"github.com/sirupsen/logrus"
     )
     
     func router(ctx context.Context, localClusterEnabled bool, tunnelAuthorizer *mcmauthorizer.Authorizer, scaledContext *config.ScaledContext, clusterManager *clustermanager.Manager) (func(http.Handler) http.Handler, error) {
    @@ -70,6 +74,13 @@ func router(ctx context.Context, localClusterEnabled bool, tunnelAuthorizer *mcm
     	unauthed := mux.NewRouter()
     	unauthed.UseEncodedPath()
     
    +	publicLimit, err := settings.APIBodyLimit.GetQuantityAsInt64(1024 * 1024)
    +	if err != nil {
    +		return nil, fmt.Errorf("parsing the public API body limit: %w", err)
    +	}
    +	logrus.Infof("Configuring public API body limit to %v bytes", publicLimit)
    +	limitingHandler := utils.APIBodyLimitingHandler(publicLimit)
    +
     	unauthed.Path("/").MatcherFunc(parse.MatchNotBrowser).Handler(managementAPI)
     	unauthed.Handle("/v3/connect", connectHandler)
     	unauthed.Handle("/v3/connect/register", connectHandler)
    @@ -134,7 +145,7 @@ func router(ctx context.Context, localClusterEnabled bool, tunnelAuthorizer *mcm
     
     	return func(next http.Handler) http.Handler {
     		metricsAuthed.NotFoundHandler = next
    -		return unauthed
    +		return limitingHandler(unauthed)
     	}, nil
     }
     
    
  • pkg/settings/setting.go+28 0 modified
    @@ -16,6 +16,7 @@ import (
     	fleetconst "github.com/rancher/rancher/pkg/fleet"
     	"github.com/sirupsen/logrus"
     	v1 "k8s.io/api/core/v1"
    +	"k8s.io/apimachinery/pkg/api/resource"
     )
     
     const (
    @@ -380,6 +381,11 @@ var (
     	SQLCacheGCKeepCount = NewSetting("sql-cache-gc-keep-count", "1000")
     
     	SCCOperatorImage = NewSetting("scc-operator-image", buildconfig.DefaultSccOperatorImage)
    +
    +	// This is the limit for request bodies sent to /v3-public/* endpoints in
    +	// bytes.
    +	// The default = 1MiB
    +	APIBodyLimit = NewSetting("public-api-body-limit", "1Mi")
     )
     
     // FullShellImage returns the full private registry name of the rancher shell image.
    @@ -519,6 +525,28 @@ func (s Setting) GetInt() int {
     	return i
     }
     
    +// GetQuantityAsInt64 will return the currently stored value of the setting as an int64
    +// parsed from a Kubernetes Quantity format string.
    +//
    +// See https://pkg.go.dev/k8s.io/apimachinery/pkg/api/resource#ParseQuantity for
    +// format details.
    +//
    +// If the quantity cannot be expressed as an int64 d will be returned.
    +func (s Setting) GetQuantityAsInt64(d int64) (int64, error) {
    +	v := s.Get()
    +	i, err := resource.ParseQuantity(v)
    +	if err != nil {
    +		return 0, fmt.Errorf("parsing setting: %w", err)
    +	}
    +
    +	q, ok := i.AsInt64()
    +	if ok {
    +		return q, nil
    +	}
    +
    +	return d, nil
    +}
    +
     // SetProvider will set the given provider as the global provider for all settings.
     func SetProvider(p Provider) error {
     	if err := p.SetAll(settings); err != nil {
    
  • pkg/settings/setting_test.go+19 0 modified
    @@ -9,6 +9,7 @@ import (
     
     	"github.com/sirupsen/logrus"
     	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
     	v1 "k8s.io/api/core/v1"
     )
     
    @@ -170,6 +171,24 @@ func TestGetInt(t *testing.T) {
     	assert.Equal(t, 0, fakeStringSetting.GetInt())
     }
     
    +func TestGetQuantityAsInt64(t *testing.T) {
    +	t.Parallel()
    +	fakeLimitSetting := NewSetting("limit", "1Mi")
    +
    +	val, err := fakeLimitSetting.GetQuantityAsInt64(1000)
    +	require.NoError(t, err)
    +	assert.Equal(t, int64(1024*1024), val)
    +
    +	badQuantity := NewSetting("bad-quantity", "9223372036854775807")
    +	val, err = badQuantity.GetQuantityAsInt64(1000)
    +	require.NoError(t, err)
    +	assert.Equal(t, int64(1000), val)
    +
    +	errorQuantity := NewSetting("error-quantity", "error")
    +	val, err = errorQuantity.GetQuantityAsInt64(1000)
    +	require.ErrorContains(t, err, "parsing setting: quantities must match")
    +}
    +
     func TestGetRancherVersion(t *testing.T) {
     	inputs := map[string]string{
     		"dev-version":    RancherVersionDev,
    
  • pkg/utils/handler.go+13 0 added
    @@ -0,0 +1,13 @@
    +package utils
    +
    +import (
    +	"net/http"
    +)
    +
    +// APIBodyLimitingHandler returns a middleware that can be applied to
    +// http.Handlers to restrict the number of bytes that can be read in a handler.
    +func APIBodyLimitingHandler(limit int64) func(http.Handler) http.Handler {
    +	return func(h http.Handler) http.Handler {
    +		return http.MaxBytesHandler(h, limit)
    +	}
    +}
    
  • pkg/utils/handler_test.go+59 0 added
    @@ -0,0 +1,59 @@
    +package utils
    +
    +import (
    +	"bytes"
    +	"encoding/json"
    +	"fmt"
    +	"io"
    +	"net/http"
    +	"net/http/httptest"
    +	"testing"
    +
    +	"github.com/stretchr/testify/assert"
    +	"github.com/stretchr/testify/require"
    +)
    +
    +func TestAPIBodyLimitingHandler(t *testing.T) {
    +	b, err := json.Marshal(map[string]any{
    +		"testing": "value",
    +		"multiple": []string{
    +			"test1",
    +			"test2",
    +			"test3",
    +		},
    +	})
    +	require.NoError(t, err)
    +
    +	handler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
    +		_, err := io.ReadAll(r.Body)
    +		if err != nil {
    +			http.Error(w, fmt.Sprintf("reading body: %s", err), http.StatusBadRequest)
    +		}
    +	})
    +
    +	t.Run("when the limit is smaller than the body", func(t *testing.T) {
    +		limitingHandler := APIBodyLimitingHandler(int64(len(b) - 2))
    +		require.NoError(t, err)
    +
    +		srv := httptest.NewServer(limitingHandler(handler))
    +		defer srv.Close()
    +
    +		resp, err := srv.Client().Post(srv.URL, "application/json", bytes.NewReader(b))
    +		require.NoError(t, err)
    +
    +		assert.Equal(t, http.StatusBadRequest, resp.StatusCode)
    +	})
    +
    +	t.Run("when the limit is larger than the body", func(t *testing.T) {
    +		limitingHandler := APIBodyLimitingHandler(int64(len(b)))
    +		require.NoError(t, err)
    +
    +		srv := httptest.NewServer(limitingHandler(handler))
    +		defer srv.Close()
    +
    +		resp, err := srv.Client().Post(srv.URL, "application/json", bytes.NewReader(b))
    +		require.NoError(t, err)
    +
    +		assert.Equal(t, http.StatusOK, resp.StatusCode)
    +	})
    +}
    

Vulnerability mechanics

Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

8

News mentions

0

No linked articles in our index yet.