mirror of
https://github.com/netbirdio/netbird.git
synced 2026-10-10 15:39:07 +02:00
[misc] Add upload URL signing and rate limiting (#7502)
This commit is contained in:
@@ -8,9 +8,12 @@ import (
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/netbirdio/netbird/management/server/http/middleware"
|
||||
|
||||
"github.com/netbirdio/netbird/upload-server/types"
|
||||
)
|
||||
|
||||
@@ -20,11 +23,12 @@ const (
|
||||
)
|
||||
|
||||
type local struct {
|
||||
url string
|
||||
dir string
|
||||
url string
|
||||
dir string
|
||||
signer *signer
|
||||
}
|
||||
|
||||
func configureLocalHandlers(mux *http.ServeMux) error {
|
||||
func configureLocalHandlers(mux *http.ServeMux, limiter *middleware.APIRateLimiter) error {
|
||||
envURL, ok := os.LookupEnv("SERVER_URL")
|
||||
if !ok {
|
||||
return fmt.Errorf("SERVER_URL environment variable is required")
|
||||
@@ -44,11 +48,17 @@ func configureLocalHandlers(mux *http.ServeMux) error {
|
||||
dir = envDir
|
||||
}
|
||||
|
||||
l := &local{
|
||||
url: envURL,
|
||||
dir: dir,
|
||||
uploadSigner, err := newSigner()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
mux.HandleFunc(types.GetURLPath, l.handlerGetUploadURL)
|
||||
|
||||
l := &local{
|
||||
url: envURL,
|
||||
dir: dir,
|
||||
signer: uploadSigner,
|
||||
}
|
||||
mux.Handle(types.GetURLPath, limiter.Middleware(http.HandlerFunc(l.handlerGetUploadURL)))
|
||||
mux.HandleFunc(putURLPath+putHandler, l.handlePutRequest)
|
||||
|
||||
return nil
|
||||
@@ -80,10 +90,11 @@ func (l *local) getUploadURL(objectKey string) (string, error) {
|
||||
return "", fmt.Errorf("failed to parse upload URL: %w", err)
|
||||
}
|
||||
newURL := parsedUploadURL.JoinPath(parsedUploadURL.Path, putURLPath, objectKey)
|
||||
newURL.RawQuery = l.signer.sign(objectKey, time.Now()).Encode()
|
||||
return newURL.String(), nil
|
||||
}
|
||||
|
||||
const maxUploadSize = 150 << 20
|
||||
const maxUploadSize = 50 << 20
|
||||
|
||||
func (l *local) handlePutRequest(w http.ResponseWriter, r *http.Request) {
|
||||
if r.Method != http.MethodPut {
|
||||
@@ -91,13 +102,6 @@ func (l *local) handlePutRequest(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "request body too large or failed to read", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
uploadDir := r.PathValue("dir")
|
||||
if uploadDir == "" {
|
||||
http.Error(w, "missing dir path", http.StatusBadRequest)
|
||||
@@ -109,6 +113,19 @@ func (l *local) handlePutRequest(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := l.signer.verify(uploadDir+"/"+uploadFile, r.URL.Query(), time.Now()); err != nil {
|
||||
http.Error(w, "unauthorized", http.StatusUnauthorized)
|
||||
log.Warnf("Rejected upload of %s/%s: %v", uploadDir, uploadFile, err)
|
||||
return
|
||||
}
|
||||
|
||||
r.Body = http.MaxBytesReader(w, r.Body, maxUploadSize)
|
||||
body, err := io.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
http.Error(w, "request body too large or failed to read", http.StatusRequestEntityTooLarge)
|
||||
return
|
||||
}
|
||||
|
||||
cleanBase := filepath.Clean(l.dir) + string(filepath.Separator)
|
||||
|
||||
dirPath := filepath.Clean(filepath.Join(l.dir, uploadDir))
|
||||
@@ -125,14 +142,14 @@ func (l *local) handlePutRequest(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err = os.MkdirAll(dirPath, 0750); err != nil {
|
||||
if err = os.MkdirAll(dirPath, 0o750); err != nil {
|
||||
http.Error(w, "failed to create upload dir", http.StatusInternalServerError)
|
||||
log.Errorf("Failed to create upload dir: %v", err)
|
||||
return
|
||||
}
|
||||
|
||||
flags := os.O_WRONLY | os.O_CREATE | os.O_EXCL
|
||||
f, err := os.OpenFile(filePath, flags, 0600)
|
||||
f, err := os.OpenFile(filePath, flags, 0o600)
|
||||
if err != nil {
|
||||
if os.IsExist(err) {
|
||||
http.Error(w, "file already exists", http.StatusConflict)
|
||||
|
||||
Reference in New Issue
Block a user