From b7b2a5947f482b06085e59c1643e2ca20d3c60f6 Mon Sep 17 00:00:00 2001 From: Chris Lu Date: Mon, 2 Nov 2026 11:32:03 -0800 Subject: [PATCH] admin: configurable file browser upload size limit (#12348) * admin: carry a file browser upload size limit The admin server keeps the total size limit of one file browser upload, 500 MB by default as the UI enforced before, and hands it to the file browser page. Co-Authored-By: Claude Opus 5.5 * admin: reject file browser uploads over the size limit The 500 MB limit was only checked in the browser, so any client could post larger uploads and fill the temp dir with spooled parts. Cap the request body at the limit plus room for the multipart framing, and answer 413 when the files add up to more than the limit. Parse the form before reading the path field. FormValue used to parse it first with the default 32 MB memory, which made the later 1 GB ParseMultipartForm a no-op; keep 32 MB explicitly. Co-Authored-By: Claude Opus 5.5 * admin: show and check the configured upload limit in the file browser The upload dialog reads the limit from the page instead of a hard-coded 500 MB, lists it under the file picker, and skips the check when uploads are unlimited. Co-Authored-By: Claude Opus 5.5 * admin: add -uploadMaxSizeMB to set the file browser upload limit weed admin -uploadMaxSizeMB and weed mini -admin.uploadMaxSizeMB set the total size limit of one file browser upload; 0 removes it. Co-Authored-By: Claude Opus 5.5 * admin: note why the upload framing allowance is enough Co-Authored-By: Claude Opus 5.5 * admin: test an upload whose files only exceed the limit together Co-Authored-By: Claude Opus 5.5 * admin: remove spooled upload parts when the upload handler returns Parts over 32 MB spool to temp files. The router passes the handler a copy of the request, so net/http never sees the parsed form and never removes those files. Every large upload, and every one rejected for size after parsing, left them on disk. Co-Authored-By: Claude Opus 5.5 --------- Co-authored-by: Claude Opus 5.5 --- weed/admin/dash/admin_server.go | 19 +++++ weed/admin/dash/file_browser_data.go | 4 + weed/admin/handlers/file_browser_handlers.go | 47 +++++++++-- .../handlers/file_browser_handlers_test.go | 73 ++++++++++++++++++ weed/admin/static/js/admin.js | 7 +- weed/admin/static_gz/js/admin.js.gz | Bin 15636 -> 15540 bytes weed/admin/view/app/file_browser.templ | 5 +- weed/admin/view/app/file_browser_templ.go | 44 ++++++++++- weed/command/admin.go | 5 ++ weed/command/mini.go | 2 + 10 files changed, 189 insertions(+), 17 deletions(-) diff --git a/weed/admin/dash/admin_server.go b/weed/admin/dash/admin_server.go index eadd89b3c9..fd9abe2e0d 100644 --- a/weed/admin/dash/admin_server.go +++ b/weed/admin/dash/admin_server.go @@ -175,8 +175,14 @@ type AdminServer struct { // s3PublicEndpoint is the client-facing S3 address from admin.toml // (s3.public_endpoint); discovered S3 servers are the fallback. s3PublicEndpoint string + + // uploadMaxSize caps the total bytes of one file browser upload; 0 means no limit. + uploadMaxSize int64 } +// DefaultUploadMaxSizeMB is the default total size limit of one file browser upload. +const DefaultUploadMaxSizeMB = 500 + // Type definitions moved to types.go func NewAdminServer(masters string, filerGroup string, templateFS http.FileSystem, dataDir string, icebergPort, lancePort int, s3PublicEndpoint string) *AdminServer { @@ -217,6 +223,7 @@ func NewAdminServer(masters string, filerGroup string, templateFS http.FileSyste icebergPort: icebergPort, lancePort: lancePort, s3PublicEndpoint: normalizeS3PublicEndpoint(s3PublicEndpoint), + uploadMaxSize: DefaultUploadMaxSizeMB << 20, pluginLock: lockManager, adminPresenceLock: presenceLock, bgCancel: bgCancel, @@ -1574,6 +1581,18 @@ func (s *AdminServer) GetS3Endpoint() string { return s.s3PublicEndpoint } +// SetUploadMaxSize sets the total size limit in bytes of one file browser +// upload; zero or less removes the limit. +func (s *AdminServer) SetUploadMaxSize(maxBytes int64) { + s.uploadMaxSize = max(maxBytes, 0) +} + +// GetUploadMaxSize returns the total size limit in bytes of one file browser +// upload, or 0 when uploads are unlimited. +func (s *AdminServer) GetUploadMaxSize() int64 { + return s.uploadMaxSize +} + // normalizeS3PublicEndpoint trims a trailing slash and drops, with a warning, // a value that is not a plain absolute http or https URL, so the file browser // hides its URL actions instead of copying broken links. Checking the string diff --git a/weed/admin/dash/file_browser_data.go b/weed/admin/dash/file_browser_data.go index 37385f5fb4..7fd14a3e6b 100644 --- a/weed/admin/dash/file_browser_data.go +++ b/weed/admin/dash/file_browser_data.go @@ -53,6 +53,8 @@ type FileBrowserData struct { HasNextPage bool `json:"has_next_page"` LastFileName string `json:"last_file_name"` // Cursor for next page CurrentLastFileName string `json:"current_last_file_name"` // Cursor from current request (for page size changes) + + UploadMaxSize int64 `json:"upload_max_size"` } // GetFileBrowser retrieves file browser data for a given path with cursor-based @@ -240,6 +242,8 @@ func (s *AdminServer) GetFileBrowser(dir string, prefix string, lastFileName str HasNextPage: hasNextPage, LastFileName: lastEntryName, // Store for next page navigation CurrentLastFileName: lastFileName, // Store input cursor for page size changes + + UploadMaxSize: s.uploadMaxSize, }, nil } diff --git a/weed/admin/handlers/file_browser_handlers.go b/weed/admin/handlers/file_browser_handlers.go index 085679b768..fb5f122b72 100644 --- a/weed/admin/handlers/file_browser_handlers.go +++ b/weed/admin/handlers/file_browser_handlers.go @@ -4,6 +4,7 @@ import ( "bufio" "compress/gzip" "context" + "errors" "fmt" "io" "mime" @@ -247,25 +248,46 @@ func (h *FileBrowserHandlers) CreateFolder(w http.ResponseWriter, r *http.Reques // UploadFile handles file upload requests func (h *FileBrowserHandlers) UploadFile(w http.ResponseWriter, r *http.Request) { - // Get the current path + maxSize := h.adminServer.GetUploadMaxSize() + if maxSize > 0 { + r.Body = http.MaxBytesReader(w, r.Body, maxSize+uploadFormOverhead) + } + + // Parts beyond 32MB spill to temporary files instead of memory + err := r.ParseMultipartForm(32 << 20) + if err != nil { + var maxBytesErr *http.MaxBytesError + if errors.As(err, &maxBytesErr) { + writeJSONError(w, http.StatusRequestEntityTooLarge, uploadTooLargeMessage(maxSize)) + return + } + writeJSONError(w, http.StatusBadRequest, "Failed to parse multipart form: "+err.Error()) + return + } + // The router hands us a copy of the request, so the server cannot remove the spooled parts itself + defer r.MultipartForm.RemoveAll() + currentPath := r.FormValue("path") if currentPath == "" { currentPath = "/" } - // Parse multipart form - err := r.ParseMultipartForm(1 << 30) // 1GB max memory for large file uploads - if err != nil { - writeJSONError(w, http.StatusBadRequest, "Failed to parse multipart form: "+err.Error()) - return - } - // Get uploaded files (supports multiple files) files := r.MultipartForm.File["files"] if len(files) == 0 { writeJSONError(w, http.StatusBadRequest, "No files uploaded") return } + if maxSize > 0 { + var totalSize int64 + for _, fileHeader := range files { + totalSize += fileHeader.Size + } + if totalSize > maxSize { + writeJSONError(w, http.StatusRequestEntityTooLarge, uploadTooLargeMessage(maxSize)) + return + } + } var uploadResults []map[string]interface{} var failedUploads []string @@ -327,6 +349,15 @@ func (h *FileBrowserHandlers) UploadFile(w http.ResponseWriter, r *http.Request) } } +// uploadFormOverhead allows for the multipart headers and the path field on +// top of the file bytes an upload may carry. Go's multipart parser accepts at +// most 1000 parts, whose headers take about half of this. +const uploadFormOverhead = 1 << 20 + +func uploadTooLargeMessage(maxSize int64) string { + return fmt.Sprintf("Total upload size exceeds the %d MB limit", maxSize>>20) +} + // uploadFileToFiler uploads a file to the cluster via filer gRPC + volume // HTTP. This works whether or not the filer is running with -disableHttp=true, // since the bytes never traverse the filer's HTTP listener. The multipart diff --git a/weed/admin/handlers/file_browser_handlers_test.go b/weed/admin/handlers/file_browser_handlers_test.go index f02fead273..179be2a6a0 100644 --- a/weed/admin/handlers/file_browser_handlers_test.go +++ b/weed/admin/handlers/file_browser_handlers_test.go @@ -1,8 +1,12 @@ package handlers import ( + "bytes" + "fmt" + "mime/multipart" "net/http" "net/http/httptest" + "os" "slices" "strings" "testing" @@ -143,3 +147,72 @@ func TestListFolders_RejectsPathOutsideBuckets(t *testing.T) { } } } + +func TestUploadFile_RejectsOversizedUpload(t *testing.T) { + adminServer := &dash.AdminServer{} + adminServer.SetUploadMaxSize(1 << 20) + h := &FileBrowserHandlers{adminServer: adminServer} + + // 1.5MB and two 600KB files fit the body allowance but exceed the file total; + // 3MB trips the body cap. + for _, sizes := range [][]int{{3 << 19}, {600 << 10, 600 << 10}, {3 << 20}} { + var body bytes.Buffer + mw := multipart.NewWriter(&body) + for i, size := range sizes { + part, err := mw.CreateFormFile("files", fmt.Sprintf("big%d.bin", i)) + if err != nil { + t.Fatal(err) + } + part.Write(make([]byte, size)) + } + mw.WriteField("path", "/buckets/b") + mw.Close() + + req := httptest.NewRequest(http.MethodPost, "/api/files/upload", &body) + req.Header.Set("Content-Type", mw.FormDataContentType()) + w := httptest.NewRecorder() + + h.UploadFile(w, req) + + if w.Code != http.StatusRequestEntityTooLarge { + t.Errorf("sizes %v: expected 413, got %d (body: %s)", sizes, w.Code, w.Body.String()) + } + if !strings.Contains(w.Body.String(), "1 MB limit") { + t.Errorf("sizes %v: expected the limit in the error, got %s", sizes, w.Body.String()) + } + } +} + +func TestUploadFile_RemovesSpooledParts(t *testing.T) { + tmpDir := t.TempDir() + t.Setenv("TMPDIR", tmpDir) + + adminServer := &dash.AdminServer{} + adminServer.SetUploadMaxSize(34 << 20) + h := &FileBrowserHandlers{adminServer: adminServer} + + // The 33MB part spills to disk; together the parts exceed the limit, so the upload stops there. + var body bytes.Buffer + mw := multipart.NewWriter(&body) + for i, size := range []int{33 << 20, 3 << 19} { + part, err := mw.CreateFormFile("files", fmt.Sprintf("part%d.bin", i)) + if err != nil { + t.Fatal(err) + } + part.Write(make([]byte, size)) + } + mw.Close() + + req := httptest.NewRequest(http.MethodPost, "/api/files/upload", &body) + req.Header.Set("Content-Type", mw.FormDataContentType()) + w := httptest.NewRecorder() + + h.UploadFile(w, req.WithContext(req.Context())) + + if w.Code != http.StatusRequestEntityTooLarge { + t.Fatalf("expected 413, got %d (body: %s)", w.Code, w.Body.String()) + } + if left, _ := os.ReadDir(tmpDir); len(left) != 0 { + t.Errorf("spooled parts left behind: %v", left) + } +} diff --git a/weed/admin/static/js/admin.js b/weed/admin/static/js/admin.js index 5de9b8bd64..77681f26fc 100644 --- a/weed/admin/static/js/admin.js +++ b/weed/admin/static/js/admin.js @@ -1180,10 +1180,9 @@ async function submitUploadFile() { const files = Array.from(fileInput.files); const totalSize = files.reduce((sum, file) => sum + file.size, 0); - // Validate total file size (limit to 500MB for admin interface) - const maxSize = 500 * 1024 * 1024; // 500MB total - if (totalSize > maxSize) { - showErrorMessage('Total file size exceeds 500MB limit. Please select fewer or smaller files.'); + const maxSize = Number(document.getElementById('uploadFileForm').dataset.maxSize) || 0; + if (maxSize > 0 && totalSize > maxSize) { + showErrorMessage(`Total file size exceeds the ${formatBytes(maxSize)} limit. Please select fewer or smaller files.`); return; } diff --git a/weed/admin/static_gz/js/admin.js.gz b/weed/admin/static_gz/js/admin.js.gz index a4ebc6fd53e1c820ea2b4eb2d59903db97544b99..d6a6220718b75962a8d0838a051246e1eb26a8bb 100644 GIT binary patch literal 15540 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 Cmr9-h literal 15636 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

Upload Files
Choose one or more files to upload to the current directory. You can select multiple files by holding Ctrl (Cmd on Mac) while clicking.
Upload Files
0%
Preparing upload...
") + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 94, "\">
Choose one or more files to upload to the current directory. You can select multiple files by holding Ctrl (Cmd on Mac) while clicking. ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + if data.UploadMaxSize > 0 { + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 95, "Total upload size limit: ") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + var templ_7745c5c3_Var41 string + templ_7745c5c3_Var41, templ_7745c5c3_Err = templ.JoinStringErrs(formatBytes(data.UploadMaxSize)) + if templ_7745c5c3_Err != nil { + return templ.Error{Err: templ_7745c5c3_Err, FileName: `view/app/file_browser.templ`, Line: 320, Col: 67} + } + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var41)) + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 96, ".") + if templ_7745c5c3_Err != nil { + return templ_7745c5c3_Err + } + } + templ_7745c5c3_Err = templruntime.WriteString(templ_7745c5c3_Buffer, 97, "
0%
Preparing upload...
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/weed/command/admin.go b/weed/command/admin.go index a4844f1b8f..59ac89bdb0 100644 --- a/weed/command/admin.go +++ b/weed/command/admin.go @@ -64,6 +64,7 @@ type AdminOptions struct { debugPort *int cpuProfile *string memProfile *string + uploadMaxSizeMB *int // workerGrpcListener, when set, is a listener already bound to grpcPort by // the caller. `weed mini` reserves the port this way because the admin @@ -125,6 +126,7 @@ func init() { a.debugPort = cmdAdmin.Flag.Int("debug.port", 6060, "http port for debugging") a.cpuProfile = cmdAdmin.Flag.String("cpuprofile", "", "cpu profile output file") a.memProfile = cmdAdmin.Flag.String("memprofile", "", "memory profile output file") + a.uploadMaxSizeMB = cmdAdmin.Flag.Int("uploadMaxSizeMB", dash.DefaultUploadMaxSizeMB, "maximum total size in MB of one file browser upload (0 for no limit)") } var cmdAdmin = &Command{ @@ -500,6 +502,9 @@ func startAdminServer(ctx context.Context, options AdminOptions, enableUI bool, s3PublicEndpoint = options.defaultS3PublicEndpoint } adminServer := dash.NewAdminServer(*options.master, *options.filerGroup, nil, dataDir, icebergPort, lancePort, s3PublicEndpoint) + if options.uploadMaxSizeMB != nil { + adminServer.SetUploadMaxSize(int64(*options.uploadMaxSizeMB) << 20) + } if err := adminServer.ApplyPluginConfigFromToml(util.GetViper()); err != nil { return fmt.Errorf("apply admin.toml to plugin config: %w", err) diff --git a/weed/command/mini.go b/weed/command/mini.go index 498635d302..2d963f7bda 100644 --- a/weed/command/mini.go +++ b/weed/command/mini.go @@ -17,6 +17,7 @@ import ( "sync" "time" + "github.com/seaweedfs/seaweedfs/weed/admin/dash" "github.com/seaweedfs/seaweedfs/weed/glog" "github.com/seaweedfs/seaweedfs/weed/pb" "github.com/seaweedfs/seaweedfs/weed/pb/filer_pb" @@ -598,6 +599,7 @@ func initMiniAdminFlags() { miniAdminOptions.allowInsecureBind = cmdMini.Flag.Bool("admin.allowInsecureBind", false, "INSECURE: allow the unauthenticated admin interface to bind to -ip.bind instead of loopback") miniAdminWorkerBindIP = cmdMini.Flag.String("admin.worker.ip", "127.0.0.1", "worker gRPC bind address; expose only with grpc.admin mTLS configured") miniAdminOptions.urlPrefix = cmdMini.Flag.String("admin.urlPrefix", "", "URL path prefix when running the admin UI behind a reverse proxy under a subdirectory (e.g. /seaweedfs)") + miniAdminOptions.uploadMaxSizeMB = cmdMini.Flag.Int("admin.uploadMaxSizeMB", dash.DefaultUploadMaxSizeMB, "maximum total size in MB of one file browser upload (0 for no limit)") } func init() {