86 lines
1.8 KiB
Go
86 lines
1.8 KiB
Go
package httpserver
|
|
|
|
import (
|
|
"bytes"
|
|
"io"
|
|
"net"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type loginBucket struct {
|
|
Start time.Time
|
|
Count int
|
|
}
|
|
|
|
func (s *Server) allowLogin(address string) bool {
|
|
ip, _, err := net.SplitHostPort(address)
|
|
if err != nil {
|
|
ip = address
|
|
}
|
|
s.loginMu.Lock()
|
|
defer s.loginMu.Unlock()
|
|
if s.logins == nil {
|
|
s.logins = map[string]loginBucket{}
|
|
}
|
|
now := time.Now()
|
|
for k, v := range s.logins {
|
|
if now.Sub(v.Start) > time.Minute {
|
|
delete(s.logins, k)
|
|
}
|
|
}
|
|
b := s.logins[ip]
|
|
if b.Start.IsZero() {
|
|
if len(s.logins) >= 1024 {
|
|
return false
|
|
}
|
|
b.Start = now
|
|
}
|
|
b.Count++
|
|
s.logins[ip] = b
|
|
return b.Count <= 10
|
|
}
|
|
|
|
func requestProtection(next http.Handler) http.Handler {
|
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
// No proxy headers are trusted. Configure the proxy to preserve the Host.
|
|
admin := strings.HasPrefix(r.URL.Path, "/ui/") || r.URL.Path == "/login" || r.URL.Path == "/logout"
|
|
if admin {
|
|
w.Header().Set("Cache-Control", "no-store")
|
|
}
|
|
if admin && r.Method != "GET" && r.Method != "HEAD" {
|
|
if origin := r.Header.Get("Origin"); origin != "" {
|
|
u, err := url.Parse(origin)
|
|
if err != nil || u.Host != r.Host || (u.Scheme != "http" && u.Scheme != "https") {
|
|
http.Error(w, "cross-origin request rejected", 403)
|
|
return
|
|
}
|
|
}
|
|
if r.Header.Get("Sec-Fetch-Site") == "cross-site" {
|
|
http.Error(w, "cross-site request rejected", 403)
|
|
return
|
|
}
|
|
}
|
|
if r.Body != nil {
|
|
limit := int64(1 << 20)
|
|
if admin {
|
|
limit = 2 << 20
|
|
}
|
|
b, err := io.ReadAll(io.LimitReader(r.Body, limit+1))
|
|
r.Body.Close()
|
|
if err != nil {
|
|
http.Error(w, "invalid body", 400)
|
|
return
|
|
}
|
|
if int64(len(b)) > limit {
|
|
http.Error(w, "request too large", 413)
|
|
return
|
|
}
|
|
r.Body = io.NopCloser(bytes.NewReader(b))
|
|
}
|
|
next.ServeHTTP(w, r)
|
|
})
|
|
}
|