Skip to content

Commit d95da9b

Browse files
committed
gateway: rate limiting
Signed-off-by: Sander Pick <sanderpick@gmail.com>
1 parent b25c856 commit d95da9b

File tree

6 files changed

+84
-7
lines changed

6 files changed

+84
-7
lines changed

cmd/buckd/main.go

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,14 @@ var (
9898
Key: "gateway.subdomains",
9999
DefValue: false,
100100
},
101+
"gatewayMaxEventsPerSec": {
102+
Key: "gateway.max_events_per_sec",
103+
DefValue: 1000,
104+
},
105+
"gatewayMaxBurstSize": {
106+
Key: "gateway.max_burst_size",
107+
DefValue: 20,
108+
},
101109

102110
// Cloudflare
103111
// @todo: Change these to cloudflareDnsDomain, etc.
@@ -204,6 +212,14 @@ func init() {
204212
"gatewaySubdomains",
205213
config.Flags["gatewaySubdomains"].DefValue.(bool),
206214
"Enable gateway namespace redirects to subdomains")
215+
rootCmd.PersistentFlags().Int(
216+
"gatewayMaxEventsPerSec",
217+
config.Flags["gatewayMaxEventsPerSec"].DefValue.(int),
218+
"Gateway max events per second")
219+
rootCmd.PersistentFlags().Int(
220+
"gatewayMaxBurstSize",
221+
config.Flags["gatewayMaxBurstSize"].DefValue.(int),
222+
"Gateway gateway max burst size")
207223

208224
// Cloudflare
209225
rootCmd.PersistentFlags().String(
@@ -266,6 +282,10 @@ var rootCmd = &cobra.Command{
266282
addrIpfsApi := cmd.AddrFromStr(config.Viper.GetString("addr.ipfs.api"))
267283
addrPowergateApi := config.Viper.GetString("addr.powergate.api")
268284

285+
gatewaySubdomains := config.Viper.GetBool("gateway.subdomains")
286+
gatewayMaxEventsPerSec := config.Viper.GetInt("gateway.max_events_per_sec")
287+
gatewayMaxBurstSize := config.Viper.GetInt("gateway.max_burst_size")
288+
269289
dnsDomain := config.Viper.GetString("dns.domain")
270290
dnsZoneID := config.Viper.GetString("dns.zone_id")
271291
dnsToken := config.Viper.GetString("dns.token")
@@ -296,7 +316,9 @@ var rootCmd = &cobra.Command{
296316
AddrPowergateAPI: addrPowergateApi,
297317
IPNSRepublishSchedule: ipnsRepublishSchedule,
298318
IPNSRepublishConcurrency: ipnsRepublishConcurrency,
299-
UseSubdomains: config.Viper.GetBool("gateway.subdomains"),
319+
UseSubdomains: gatewaySubdomains,
320+
MaxEventsPerSec: gatewayMaxEventsPerSec,
321+
MaxBurstSize: gatewayMaxBurstSize,
300322

301323
DNSDomain: dnsDomain,
302324
DNSZoneID: dnsZoneID,

cmd/hubd/main.go

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -144,6 +144,14 @@ var (
144144
Key: "gateway.subdomains",
145145
DefValue: false,
146146
},
147+
"gatewayMaxEventsPerSec": {
148+
Key: "gateway.max_events_per_sec",
149+
DefValue: 1000,
150+
},
151+
"gatewayMaxBurstSize": {
152+
Key: "gateway.max_burst_size",
153+
DefValue: 20,
154+
},
147155

148156
// Cloudflare
149157
"dnsDomain": {
@@ -308,6 +316,14 @@ func init() {
308316
"gatewaySubdomains",
309317
config.Flags["gatewaySubdomains"].DefValue.(bool),
310318
"Enable gateway namespace redirects to subdomains")
319+
rootCmd.PersistentFlags().Int(
320+
"gatewayMaxEventsPerSec",
321+
config.Flags["gatewayMaxEventsPerSec"].DefValue.(int),
322+
"Gateway max events per second")
323+
rootCmd.PersistentFlags().Int(
324+
"gatewayMaxBurstSize",
325+
config.Flags["gatewayMaxBurstSize"].DefValue.(int),
326+
"Gateway gateway max burst size")
311327

312328
// Cloudflare
313329
// @todo: Change these to cloudflareDnsDomain, etc.
@@ -411,6 +427,8 @@ var rootCmd = &cobra.Command{
411427

412428
// Gateway
413429
gatewaySubdomains := config.Viper.GetBool("gateway.subdomains")
430+
gatewayMaxEventsPerSec := config.Viper.GetInt("gateway.max_events_per_sec")
431+
gatewayMaxBurstSize := config.Viper.GetInt("gateway.max_burst_size")
414432

415433
// Cloudflare
416434
dnsDomain := config.Viper.GetString("dns.domain")
@@ -464,7 +482,9 @@ var rootCmd = &cobra.Command{
464482
IPNSRepublishSchedule: ipnsRepublishSchedule,
465483
IPNSRepublishConcurrency: ipnsRepublishConcurrency,
466484
// Gateway
467-
UseSubdomains: gatewaySubdomains,
485+
UseSubdomains: gatewaySubdomains,
486+
MaxEventsPerSec: gatewayMaxEventsPerSec,
487+
MaxBurstSize: gatewayMaxBurstSize,
468488
// Cloudflare
469489
DNSDomain: dnsDomain,
470490
DNSZoneID: dnsZoneID,

core/core.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,9 @@ type Config struct {
198198
ArchiveJobPollIntervalFast time.Duration
199199

200200
// Gateway
201-
UseSubdomains bool
201+
UseSubdomains bool
202+
MaxEventsPerSec int
203+
MaxBurstSize int
202204

203205
// Cloudflare
204206
DNSDomain string
@@ -547,6 +549,8 @@ func NewTextile(ctx context.Context, conf Config, opts ...Option) (*Textile, err
547549
Collections: t.collections,
548550
IPFSClient: ic,
549551
EmailSessionBus: t.emailSessionBus,
552+
MaxEventsPerSec: conf.MaxEventsPerSec,
553+
MaxBurstSize: conf.MaxBurstSize,
550554
Hub: conf.Hub,
551555
Debug: conf.Debug,
552556
})

gateway/gateway.go

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import (
3232
"github.com/textileio/textile/v2/api/common"
3333
mdb "github.com/textileio/textile/v2/mongodb"
3434
"go.mongodb.org/mongo-driver/mongo"
35+
"golang.org/x/time/rate"
3536
"google.golang.org/grpc"
3637
)
3738

@@ -68,6 +69,9 @@ type Gateway struct {
6869
ipfs iface.CoreAPI
6970

7071
emailSessionBus *broadcast.Broadcaster
72+
73+
maxEventsPerSec int
74+
maxBurstSize int
7175
}
7276

7377
// Config defines the gateway configuration.
@@ -81,6 +85,8 @@ type Config struct {
8185
Collections *mdb.Collections
8286
IPFSClient iface.CoreAPI
8387
EmailSessionBus *broadcast.Broadcaster
88+
MaxEventsPerSec int
89+
MaxBurstSize int
8490
Hub bool
8591
Debug bool
8692
}
@@ -111,6 +117,14 @@ func NewGateway(conf Config) (*Gateway, error) {
111117
if err != nil {
112118
return nil, err
113119
}
120+
121+
if conf.MaxEventsPerSec < 1 {
122+
conf.MaxEventsPerSec = 1000
123+
}
124+
if conf.MaxBurstSize < 1 {
125+
conf.MaxBurstSize = 20
126+
}
127+
114128
return &Gateway{
115129
addr: conf.Addr,
116130
url: conf.URL,
@@ -123,6 +137,8 @@ func NewGateway(conf Config) (*Gateway, error) {
123137
hub: conf.Hub,
124138
ipfs: conf.IPFSClient,
125139
emailSessionBus: conf.EmailSessionBus,
140+
maxEventsPerSec: conf.MaxEventsPerSec,
141+
maxBurstSize: conf.MaxBurstSize,
126142
}, nil
127143
}
128144

@@ -140,6 +156,7 @@ func (g *Gateway) Start() {
140156
}
141157
router.SetHTMLTemplate(temp)
142158

159+
router.Use(throttle(g.maxEventsPerSec, g.maxBurstSize))
143160
router.Use(location.Default())
144161
router.Use(static.Serve("", &fileSystem{Assets}))
145162
router.Use(serveBucket(&bucketFS{
@@ -499,3 +516,17 @@ func (g *Gateway) toSubdomainURL(r *http.Request) (redirURL string, ok bool) {
499516
host := urlparts[1]
500517
return safeRedirectURL(fmt.Sprintf("%s://%s.%s.%s/%s%s", scheme, rootID, ns, host, rest, query))
501518
}
519+
520+
func throttle(maxEventsPerSec int, maxBurstSize int) gin.HandlerFunc {
521+
limiter := rate.NewLimiter(rate.Limit(maxEventsPerSec), maxBurstSize)
522+
523+
return func(context *gin.Context) {
524+
if limiter.Allow() {
525+
context.Next()
526+
return
527+
}
528+
529+
context.Error(errors.New("limit exceeded"))
530+
context.AbortWithStatus(http.StatusTooManyRequests)
531+
}
532+
}

go.mod

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ require (
7777
github.com/textileio/dcrypto v0.0.1
7878
github.com/textileio/go-assets v0.0.0-20200430191519-b341e634e2b7
7979
github.com/textileio/go-ds-mongo v0.1.5
80-
github.com/textileio/go-threads v1.1.5
80+
github.com/textileio/go-threads v1.1.6-0.20220406044848-cdd032536e1f
8181
github.com/textileio/powergate/v2 v2.3.0
8282
github.com/textileio/swagger-ui v0.3.29-0.20210224180244-7d73a7a32fe7
8383
github.com/xakep666/mongo-migrate v0.2.1
@@ -86,7 +86,7 @@ require (
8686
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d
8787
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c
8888
golang.org/x/text v0.3.6
89-
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1 // indirect
89+
golang.org/x/time v0.0.0-20200416051211-89c76fbcd5d1
9090
google.golang.org/genproto v0.0.0-20210207032614-bba0dbe2a9ea
9191
google.golang.org/grpc v1.39.0
9292
google.golang.org/protobuf v1.27.1

go.sum

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1892,8 +1892,8 @@ github.com/textileio/go-libp2p-pubsub-rpc v0.0.5 h1:De54sqNpQocJebf7P+4RrwtuUw8s
18921892
github.com/textileio/go-libp2p-pubsub-rpc v0.0.5/go.mod h1:MlOMOz3KZxexobvUuFXT/QY9Vjh9eKJpZPr48hDUdVo=
18931893
github.com/textileio/go-log/v2 v2.1.3-gke-1 h1:7e3xSUXQB8hn4uUe5fp41kLThW1o9T65gSM7qjS323g=
18941894
github.com/textileio/go-log/v2 v2.1.3-gke-1/go.mod h1:DwACkjFS3kjZZR/4Spx3aPfSsciyslwUe5bxV8CEU2w=
1895-
github.com/textileio/go-threads v1.1.5 h1:+1R+maX4mSJy3wRTFxdG1f/y319xR24v20xaPUEU1m4=
1896-
github.com/textileio/go-threads v1.1.5/go.mod h1:yPnVPdm8mpCq+fEDw5AamqZy4cQ+wE9wOCaDm3BVEmQ=
1895+
github.com/textileio/go-threads v1.1.6-0.20220406044848-cdd032536e1f h1:E6Cshg8EsNVluIB6Q6XjVSL0QRm2+w5e5K5Ze1vDIhE=
1896+
github.com/textileio/go-threads v1.1.6-0.20220406044848-cdd032536e1f/go.mod h1:yPnVPdm8mpCq+fEDw5AamqZy4cQ+wE9wOCaDm3BVEmQ=
18971897
github.com/textileio/powergate/v2 v2.3.0 h1:kelYh+ZWDQao1rL5YiMznQscd6CsDjgt6P/D1S5UYwQ=
18981898
github.com/textileio/powergate/v2 v2.3.0/go.mod h1:2j2NL1oevaVdrI6MpKfHnfgUOy1D4L7eP3I+1czxDjw=
18991899
github.com/textileio/swagger-ui v0.3.29-0.20210224180244-7d73a7a32fe7 h1:qUEurT6kJF+nFkiNjUPMJJ7hgg9OIDnb8iLn6VtBukE=

0 commit comments

Comments
 (0)