added rate limiting
This commit is contained in:
@@ -1,5 +1,3 @@
|
|||||||
Reseed server for I2P
|
Reseed server for I2P
|
||||||
=========================================
|
=========================================
|
||||||
|
|
||||||
* Author: Matt Drollette <matt@drollette.com>
|
|
||||||
|
|
||||||
|
|||||||
17
cli.go
17
cli.go
@@ -48,11 +48,26 @@ func main() {
|
|||||||
Value: 300 * time.Second,
|
Value: 300 * time.Second,
|
||||||
Usage: "Period to refresh routerInfo lists in time duration format (200ns, 1s, 5m)",
|
Usage: "Period to refresh routerInfo lists in time duration format (200ns, 1s, 5m)",
|
||||||
},
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "proxy",
|
||||||
|
Usage: "Trust the IP supplied in the X-Forwarded-For header",
|
||||||
|
},
|
||||||
|
cli.BoolFlag{
|
||||||
|
Name: "verbose",
|
||||||
|
Usage: "Display all access logs",
|
||||||
|
},
|
||||||
|
cli.IntFlag{
|
||||||
|
Name: "rateLimit",
|
||||||
|
Usage: "Maximum number of requests per minute per IP",
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Action: func(c *cli.Context) {
|
Action: func(c *cli.Context) {
|
||||||
server := NewReseeder()
|
server := NewReseeder()
|
||||||
server.NetDBDir = c.String("netdb")
|
server.NetDBDir = c.String("netdb")
|
||||||
server.RefreshInterval = c.Duration("refresh")
|
server.RefreshInterval = c.Duration("refresh")
|
||||||
|
server.Proxy = c.Bool("proxy")
|
||||||
|
server.Verbose = c.Bool("verbose")
|
||||||
|
server.RateLimit = c.Int("rateLimit")
|
||||||
server.Start(c.String("addr"), c.String("port"), c.String("cert"), c.String("key"))
|
server.Start(c.String("addr"), c.String("port"), c.String("cert"), c.String("key"))
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
@@ -71,7 +86,7 @@ func main() {
|
|||||||
},
|
},
|
||||||
cli.DurationFlag{
|
cli.DurationFlag{
|
||||||
Name: "validFor",
|
Name: "validFor",
|
||||||
Value: 365 * 24 * time.Hour,
|
Value: 2 * 365 * 24 * time.Hour,
|
||||||
Usage: "Duration that certificate is valid for",
|
Usage: "Duration that certificate is valid for",
|
||||||
},
|
},
|
||||||
cli.BoolFlag{
|
cli.BoolFlag{
|
||||||
|
|||||||
136
reseeder.go
136
reseeder.go
@@ -15,7 +15,8 @@ package main
|
|||||||
// https://geti2p.net/en/docs/spec/updates
|
// https://geti2p.net/en/docs/spec/updates
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"github.com/PuerkitoBio/throttled"
|
||||||
|
"github.com/PuerkitoBio/throttled/store"
|
||||||
"github.com/gorilla/handlers"
|
"github.com/gorilla/handlers"
|
||||||
"github.com/gorilla/mux"
|
"github.com/gorilla/mux"
|
||||||
"html/template"
|
"html/template"
|
||||||
@@ -28,51 +29,6 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reseeder struct {
|
|
||||||
NetDBDir string
|
|
||||||
nextMap chan []string
|
|
||||||
RefreshInterval time.Duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func (rs *Reseeder) Start(addr, port, cert, key string) {
|
|
||||||
log.Println("Starting reseed server on " + addr + ":" + port)
|
|
||||||
|
|
||||||
r := mux.NewRouter()
|
|
||||||
s := r.PathPrefix("/netdb").Subrouter()
|
|
||||||
s.HandleFunc("/", rs.listHandler)
|
|
||||||
//s.HandleFunc("/i2pseeds.su3", rs.su3Handler)
|
|
||||||
s.HandleFunc(`/routerInfo-{hash:[A-Za-z0-9+/\-=~]+}.dat`, rs.routerInfoHandler)
|
|
||||||
|
|
||||||
http.Handle("/", handlers.CombinedLoggingHandler(os.Stdout, proxiedHandler(r)))
|
|
||||||
|
|
||||||
go rs.runMap()
|
|
||||||
rs.Refresh()
|
|
||||||
|
|
||||||
// sample function to update routerInfo map every minute
|
|
||||||
go func() {
|
|
||||||
for {
|
|
||||||
time.Sleep(rs.RefreshInterval)
|
|
||||||
log.Println("Updating routerInfos")
|
|
||||||
rs.Refresh()
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
if _, err := os.Stat(cert); err == nil {
|
|
||||||
if _, err := os.Stat(key); err == nil {
|
|
||||||
err := http.ListenAndServeTLS(addr+":"+port, cert, key, nil)
|
|
||||||
if nil != err {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
err := http.ListenAndServe(addr+":"+port, nil)
|
|
||||||
if nil != err {
|
|
||||||
log.Fatalln(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func proxiedHandler(h http.Handler) http.Handler {
|
func proxiedHandler(h http.Handler) http.Handler {
|
||||||
return remoteAddrFixup{h}
|
return remoteAddrFixup{h}
|
||||||
}
|
}
|
||||||
@@ -88,6 +44,72 @@ func (h remoteAddrFixup) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
|||||||
h.h.ServeHTTP(w, r)
|
h.h.ServeHTTP(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Reseeder struct {
|
||||||
|
NetDBDir string
|
||||||
|
nextMap chan []string
|
||||||
|
RefreshInterval time.Duration
|
||||||
|
Proxy bool
|
||||||
|
Verbose bool
|
||||||
|
RateLimit int
|
||||||
|
|
||||||
|
listTemplate *template.Template
|
||||||
|
}
|
||||||
|
|
||||||
|
func (rs *Reseeder) Start(addr, port, cert, key string) {
|
||||||
|
var err error
|
||||||
|
|
||||||
|
go rs.runMap()
|
||||||
|
go rs.refresher()
|
||||||
|
|
||||||
|
// parse the template for routerInfo lists
|
||||||
|
rs.listTemplate, err = template.New("routerinfos").Parse(`<html><head><title>NetDB</title></head><body><ul>{{ range . }}<li><a href="{{ . }}">{{ . }}</a></li>{{ end }}</ul></body></html>`)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatalln("error parsing routerInfo list template", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
r := mux.NewRouter()
|
||||||
|
s := r.PathPrefix("/netdb").Subrouter()
|
||||||
|
|
||||||
|
s.HandleFunc("/", rs.listHandler)
|
||||||
|
s.HandleFunc("/i2pseeds.su3", rs.su3Handler)
|
||||||
|
s.HandleFunc(`/routerInfo-{hash:[A-Za-z0-9+/\-=~]+}.dat`, rs.routerInfoHandler)
|
||||||
|
|
||||||
|
// timeout
|
||||||
|
muxWithMiddlewares := http.TimeoutHandler(r, time.Second*5, "Timeout!")
|
||||||
|
|
||||||
|
if rs.Proxy {
|
||||||
|
muxWithMiddlewares = proxiedHandler(muxWithMiddlewares)
|
||||||
|
}
|
||||||
|
|
||||||
|
th := throttled.RateLimit(throttled.PerMin(rs.RateLimit), &throttled.VaryBy{RemoteAddr: true}, store.NewMemStore(0))
|
||||||
|
muxWithMiddlewares = th.Throttle(muxWithMiddlewares)
|
||||||
|
|
||||||
|
if rs.Verbose {
|
||||||
|
muxWithMiddlewares = handlers.CombinedLoggingHandler(os.Stdout, muxWithMiddlewares)
|
||||||
|
}
|
||||||
|
|
||||||
|
// try to start tls server
|
||||||
|
if _, err = os.Stat(cert); err == nil {
|
||||||
|
if _, err = os.Stat(key); err == nil {
|
||||||
|
log.Println("Starting TLS reseed server on " + addr + ":" + port)
|
||||||
|
err := http.ListenAndServeTLS(addr+":"+port, cert, key, muxWithMiddlewares)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// fall back to regular http server
|
||||||
|
log.Println("Starting reseed server on " + addr + ":" + port)
|
||||||
|
err = http.ListenAndServe(addr+":"+port, muxWithMiddlewares)
|
||||||
|
if nil != err {
|
||||||
|
log.Fatalln(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func NewReseeder() *Reseeder {
|
func NewReseeder() *Reseeder {
|
||||||
return &Reseeder{nextMap: make(chan []string)}
|
return &Reseeder{nextMap: make(chan []string)}
|
||||||
}
|
}
|
||||||
@@ -102,6 +124,14 @@ func (r *Reseeder) runMap() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (r *Reseeder) refresher() {
|
||||||
|
for {
|
||||||
|
log.Println("Updating routerInfos")
|
||||||
|
r.Refresh()
|
||||||
|
time.Sleep(r.RefreshInterval)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (r *Reseeder) Refresh() {
|
func (r *Reseeder) Refresh() {
|
||||||
var m []string
|
var m []string
|
||||||
|
|
||||||
@@ -133,19 +163,14 @@ func (r *Reseeder) Refresh() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Reseeder) listHandler(w http.ResponseWriter, r *http.Request) {
|
func (rs *Reseeder) listHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
tmpl, err := template.New("foo").Parse(`<html><head><title>NetDB</title></head><body><ul>{{ range . }}<li><a href="{{ . }}">{{ . }}</a></li>{{ end }}</ul></body></html>`)
|
err := rs.listTemplate.Execute(w, <-rs.nextMap)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
panic(err)
|
log.Fatalln("error rending list template", err)
|
||||||
}
|
|
||||||
|
|
||||||
err = tmpl.Execute(w, <-rs.nextMap)
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Reseeder) su3Handler(w http.ResponseWriter, r *http.Request) {
|
func (rs *Reseeder) su3Handler(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Fprintf(w, "")
|
http.NotFound(w, r)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rs *Reseeder) routerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
func (rs *Reseeder) routerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
||||||
@@ -153,7 +178,8 @@ func (rs *Reseeder) routerInfoHandler(w http.ResponseWriter, r *http.Request) {
|
|||||||
fileName := "routerInfo-" + vars["hash"] + ".dat"
|
fileName := "routerInfo-" + vars["hash"] + ".dat"
|
||||||
f, err := os.Open(rs.NetDBDir + "/" + fileName)
|
f, err := os.Open(rs.NetDBDir + "/" + fileName)
|
||||||
if nil != err {
|
if nil != err {
|
||||||
log.Fatalln("error sending file", err)
|
http.NotFound(w, r)
|
||||||
|
log.Println("error sending file", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
io.Copy(w, f)
|
io.Copy(w, f)
|
||||||
|
|||||||
Reference in New Issue
Block a user