Add maximum sync size enforcement

This commit is contained in:
Maurizio Porrato 2021-05-16 16:46:05 +01:00
parent 664c6fd4fa
commit 62cc02cffe
Signed by: guru
GPG Key ID: C622977DF024AC24
3 changed files with 52 additions and 25 deletions

View File

@ -37,18 +37,22 @@ To run the binary you just built:
`~/go/bin/ubsserver`
It will start the API server listening on port 8090 and using the `data` directory under $PWD as the datastore.
You can change the listening address using the `-listen` flag (e.g.: `-listen :9999` will listen on port 9999 on all
addresses).
Similarly, you can change the datastore using the `-store` flag. The store is defined in the format
`driver_name:driver_args` where `driver_name` is the name of the store driver while `driver_args` contains
driver-specific settings.
There are a few command line flags that can be used to change the behaviour of the API service:
Currently, only two drivers are implemented:
- `-listen $address:$port`: changes the listening address of the built in HTTP server, for example `-listen :9999` will
listen on port 9999 on all addresses, while `-listen 127.0.0.1:9876` will only listen on port 9876 on the loopback
interface
- `-store $driver_name:$driver_args`: changes the datastore driver and its configuration. `$driver_name` is the name of
the store driver while `$driver_args` contains driver-specific settings. Currently, only two drivers are implemented:
- **mem**: a volatile RAM-backed datastore only useful for testing and debugging. It does not take any arguments.
- **fs**: the default driver: it stores sync data in json files in the directory specified as the argument (default is
`data`)
- `mem`: a volatile RAM-backed datastore only useful for testing and debugging. It does not take any arguments
(i.e.: it can be only used by specifying `-store mem`).
- `fs`: the default driver: it stores sync data in json files in the directory specified as the argument (default is
`data`)
- `-maxsize $size`: changes the maximum size of a sync (in bytes) that can be accepted by the API. The default is set
to 512000.
If you built the container image, you can run the containerised version:
@ -62,11 +66,14 @@ where you want to expose the API service.
There are a few missing features that I would like to add.
Hig priority:
- **Enforce storage limit**: it's missing in the current implementation, but it must be implemented to prevent abuse
Nice to have:
- **Rate limiting**: currently it can be enforced by a reverse proxy in front of the API server but would be nice to
- **Enforce storage limits**: only the limit on the sync size is currently enforced: more checks must be implemented to
prevent abuse
Medium priority:
- **Rate limiting**: this can be partially enforced by a reverse proxy in front of the API server but would be nice to
have it as part of the server itself
Nice to have:
- **Front page**: serving a static HTML page on `/`, perhaps with some simple JS to show the service status, should be
fairly straightforward and would improve the end user experience. The main blocker here is my lack of design skills
to craft a half decent looking front page

View File

@ -13,10 +13,14 @@ import (
"gitlab.com/mporrato/uBrowserSync/syncstore"
)
const apiVersion = "1.1.13"
const infoMessage = "Powered by uBrowserSync"
const defaultMaxSyncSize = 512000
type serviceInfoResp struct {
Version string `json:"version"`
Message string `json:"message"`
MaxSyncSize int32 `json:"maxSyncSize"`
MaxSyncSize int `json:"maxSyncSize"`
Status int `json:"status"`
}
@ -41,6 +45,13 @@ type GetSyncVerResp struct {
Version string `json:"version"`
}
var store syncstore.Store
var maxSyncSize = defaultMaxSyncSize
var serviceStatus = 1
var listen string
var sidRe = regexp.MustCompile("^[[:xdigit:]]{32}$")
func sendJSON(w http.ResponseWriter, status int, data interface{}) {
body, err := json.Marshal(data)
if err != nil {
@ -76,16 +87,15 @@ func info(w http.ResponseWriter, req *http.Request) {
sendJSONError(w, syncstore.NotImplementedError)
} else {
log.Println("info()")
sendJSONOk(w, serviceInfoResp{
Version: "1.1.13",
Message: "Powered by uBrowserSync",
MaxSyncSize: 2 * 1024 * 1024,
Status: 1})
serviceInfo := serviceInfoResp{
Version: apiVersion,
Message: infoMessage,
Status: serviceStatus,
MaxSyncSize: maxSyncSize}
sendJSONOk(w, serviceInfo)
}
}
var store syncstore.Store
var invalidRequestError = syncstore.NewSyncError(
"Invalid request",
"Malformed request body",
@ -98,6 +108,10 @@ func createSync(w http.ResponseWriter, req *http.Request) {
sendJSONError(w, invalidRequestError)
return
}
if len(body.Version) > 20 {
sendJSONError(w, invalidRequestError)
return
}
resp, err := store.Create(body.Version)
if err != nil {
sendJSONError(w, err)
@ -140,6 +154,10 @@ func updateSync(syncId string, w http.ResponseWriter, req *http.Request) {
sendJSONError(w, invalidRequestError)
return
}
if len(body.Bookmarks) > maxSyncSize {
sendJSONError(w, syncstore.SyncDataLimitExceededError)
return
}
resp, err := store.Update(syncId, body.Bookmarks, body.LastUpdated)
if err != nil {
sendJSONError(w, err)
@ -148,8 +166,6 @@ func updateSync(syncId string, w http.ResponseWriter, req *http.Request) {
sendJSONOk(w, UpdateResp{LastUpdated: resp})
}
var sidRe = regexp.MustCompile("^[[:xdigit:]]{32}$")
func bookmarks(w http.ResponseWriter, req *http.Request) {
elements := strings.Split(strings.Trim(req.URL.Path, "/"), "/")
@ -202,8 +218,6 @@ func bookmarks(w http.ResponseWriter, req *http.Request) {
sendJSONError(w, syncstore.NotImplementedError)
}
var listen string
func init() {
var err error
var storeFlag string
@ -211,6 +225,7 @@ func init() {
flag.StringVar(&listen, "listen", ":8090", "listen address and port")
flag.StringVar(&storeFlag, "store", "fs:data", "blob store driver")
flag.IntVar(&maxSyncSize, "maxsize", defaultMaxSyncSize, "maximum size of a sync in bytes")
flag.Parse()
storeFlagTokens := strings.Split(storeFlag, ":")

View File

@ -56,3 +56,8 @@ var SyncConflictError = NewSyncError(
"SyncConflictException",
"A sync conflict was detected",
http.StatusConflict)
var SyncDataLimitExceededError = NewSyncError(
"SyncDataLimitExceededException",
"Sync data limit exceeded",
http.StatusRequestEntityTooLarge)