forked from wgroeneveld/go-jamming
add ip logging to jail pingback spammers
This commit is contained in:
parent
e6bd0ef669
commit
54018ecc42
|
@ -6,7 +6,6 @@ import (
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"golang.org/x/time/rate"
|
"golang.org/x/time/rate"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -77,7 +76,7 @@ func (rl *RateLimiter) cleanupVisitors() {
|
||||||
// with the help of https://www.alexedwards.net/blog/how-to-rate-limit-http-requests, TY!
|
// with the help of https://www.alexedwards.net/blog/how-to-rate-limit-http-requests, TY!
|
||||||
func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
ip := rl.guessIp(r)
|
ip := ipFrom(r)
|
||||||
limiter := rl.getVisitor(ip)
|
limiter := rl.getVisitor(ip)
|
||||||
|
|
||||||
if limiter.Allow() == false {
|
if limiter.Allow() == false {
|
||||||
|
@ -89,15 +88,3 @@ func (rl *RateLimiter) limiterMiddleware(next http.Handler) http.Handler {
|
||||||
next.ServeHTTP(w, r)
|
next.ServeHTTP(w, r)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
func (rl *RateLimiter) guessIp(r *http.Request) string {
|
|
||||||
realIp := r.Header.Get("X-Real-IP")
|
|
||||||
forwardedFor := r.Header.Get("X-Forwarded-For")
|
|
||||||
if realIp != "" { // in case of proxy. is IP itself
|
|
||||||
return realIp
|
|
||||||
}
|
|
||||||
if forwardedFor != "" { // in case of proxy. Could be: clientip, proxy1, proxy2, ...
|
|
||||||
return strings.Split(forwardedFor, ",")[0]
|
|
||||||
}
|
|
||||||
return r.RemoteAddr // also contains port, but don't care
|
|
||||||
}
|
|
||||||
|
|
|
@ -17,12 +17,14 @@ func (lrw *loggingResponseWriter) WriteHeader(code int) {
|
||||||
lrw.ResponseWriter.WriteHeader(code)
|
lrw.ResponseWriter.WriteHeader(code)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Logs ip (for those pingback bad boys to put in jail), request url, method, and response status code.
|
||||||
func LoggingMiddleware(next http.Handler) http.Handler {
|
func LoggingMiddleware(next http.Handler) http.Handler {
|
||||||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
logWriter := &loggingResponseWriter{w, http.StatusOK}
|
logWriter := &loggingResponseWriter{w, http.StatusOK}
|
||||||
next.ServeHTTP(logWriter, r)
|
next.ServeHTTP(logWriter, r)
|
||||||
log.Info().
|
log.Info().
|
||||||
Str("url", r.RequestURI).
|
Str("url", r.RequestURI).
|
||||||
|
Str("ip", ipFrom(r)).
|
||||||
Str("method", r.Method).
|
Str("method", r.Method).
|
||||||
Int("status", logWriter.statusCode).
|
Int("status", logWriter.statusCode).
|
||||||
Msg("handled")
|
Msg("handled")
|
||||||
|
|
|
@ -6,6 +6,7 @@ import (
|
||||||
"brainbaking.com/go-jamming/common"
|
"brainbaking.com/go-jamming/common"
|
||||||
"brainbaking.com/go-jamming/rest"
|
"brainbaking.com/go-jamming/rest"
|
||||||
"encoding/xml"
|
"encoding/xml"
|
||||||
|
"fmt"
|
||||||
"github.com/rs/zerolog/log"
|
"github.com/rs/zerolog/log"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
@ -15,18 +16,18 @@ func HandlePost(conf *common.Config) http.HandlerFunc {
|
||||||
return func(w http.ResponseWriter, r *http.Request) {
|
return func(w http.ResponseWriter, r *http.Request) {
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
body, err := ioutil.ReadAll(r.Body)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pingbackError(w, "Unable to read request body", []byte("<not-parsaeble>"))
|
pingbackError(w, fmt.Errorf("pingback POST: Unable to read body: %v", err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
rpc := &XmlRPCMethodCall{}
|
rpc := &XmlRPCMethodCall{}
|
||||||
err = xml.Unmarshal(body, rpc)
|
err = xml.Unmarshal(body, rpc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
pingbackError(w, "Unable to unmarshal XMLRPC request body", body)
|
pingbackError(w, fmt.Errorf("pingback POST: Unable to unmarshal XMLRPC %s: %v", body, err))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if !validate(rpc, conf) {
|
if !validate(rpc, conf) {
|
||||||
pingbackError(w, "malformed pingback request", body)
|
pingbackError(w, fmt.Errorf("pingback POST: malformed pingback request: %s", body))
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -61,7 +62,7 @@ func pingbackSuccess(w http.ResponseWriter) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// according to the XML-RPC spec, always return a 200, but encode it into the XML.
|
// according to the XML-RPC spec, always return a 200, but encode it into the XML.
|
||||||
func pingbackError(w http.ResponseWriter, msg string, body []byte) {
|
func pingbackError(w http.ResponseWriter, err error) {
|
||||||
xml := `<?xml version="1.0" encoding="UTF-8"?>
|
xml := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<methodResponse>
|
<methodResponse>
|
||||||
<fault>
|
<fault>
|
||||||
|
@ -91,7 +92,7 @@ func pingbackError(w http.ResponseWriter, msg string, body []byte) {
|
||||||
</value>
|
</value>
|
||||||
</fault>
|
</fault>
|
||||||
</methodResponse>`
|
</methodResponse>`
|
||||||
log.Error().Str("msg", msg).Str("xml", string(body)).Msg("Pingback receive went wrong")
|
log.Error().Err(err).Msg("Pingback receive went wrong")
|
||||||
w.WriteHeader(200)
|
w.WriteHeader(200)
|
||||||
w.Write([]byte(xml))
|
w.Write([]byte(xml))
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,14 @@ import (
|
||||||
"testing"
|
"testing"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func TestMarshallXmlSpamFromProductionWithMissingDecoderCharset(t *testing.T) {
|
||||||
|
xmlString := `<?xml version="1.0" encoding="utf-16" standalone="yes"?><methodCall><methodName>pingback.ping</methodName><params><param><value><string>https://teramassage.com/gwangju/</string></value></param><param><value><string>https://brainbaking.com/projects/</string></value></param></params></methodCall>`
|
||||||
|
var rpc XmlRPCMethodCall
|
||||||
|
err := xml.Unmarshal([]byte(xmlString), &rpc)
|
||||||
|
|
||||||
|
assert.EqualError(t, err, `xml: encoding "utf-16" declared but Decoder.CharsetReader is nil`)
|
||||||
|
}
|
||||||
|
|
||||||
// See https://www.hixie.ch/specs/pingback/pingback#refsXMLRPC
|
// See https://www.hixie.ch/specs/pingback/pingback#refsXMLRPC
|
||||||
func TestMarshallValidXMLRPC(t *testing.T) {
|
func TestMarshallValidXMLRPC(t *testing.T) {
|
||||||
xmlString := `<?xml version="1.0" encoding="UTF-8"?>
|
xmlString := `<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"github.com/MagnusFrater/helmet"
|
"github.com/MagnusFrater/helmet"
|
||||||
"net/http"
|
"net/http"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"brainbaking.com/go-jamming/common"
|
"brainbaking.com/go-jamming/common"
|
||||||
|
|
||||||
|
@ -28,6 +29,18 @@ func (s *server) authorizedOnly(h http.HandlerFunc) http.HandlerFunc {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func ipFrom(r *http.Request) string {
|
||||||
|
realIp := r.Header.Get("X-Real-IP")
|
||||||
|
forwardedFor := r.Header.Get("X-Forwarded-For")
|
||||||
|
if realIp != "" { // in case of proxy. is IP itself
|
||||||
|
return realIp
|
||||||
|
}
|
||||||
|
if forwardedFor != "" { // in case of proxy. Could be: clientip, proxy1, proxy2, ...
|
||||||
|
return strings.Split(forwardedFor, ",")[0]
|
||||||
|
}
|
||||||
|
return r.RemoteAddr // also contains port, but don't care
|
||||||
|
}
|
||||||
|
|
||||||
func Start() {
|
func Start() {
|
||||||
r := mux.NewRouter()
|
r := mux.NewRouter()
|
||||||
config := common.Configure()
|
config := common.Configure()
|
||||||
|
|
Loading…
Reference in New Issue