This repository has been archived by the owner on Feb 12, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 36
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add ReverseProxy and capture few request stats
- Loading branch information
Showing
6 changed files
with
200 additions
and
5 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,3 @@ | ||
.DS_Store | ||
/anddos/ | ||
/anddos/ | ||
*_dump.json |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
FROM alpine:3.7 | ||
COPY anddos_go /usr/bin | ||
ENTRYPOINT ["anddos_go"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,29 @@ | ||
# Standalone ANDDOS | ||
|
||
An attempt to rewrite, extend and distribute ANDDOS as a standalone service. It should rely on ReverseProxy from golang net/httputil package. | ||
An attempt to rewrite, extend and distribute ANDDOS as a standalone service. It should rely on ReverseProxy from golang net/httputil package. | ||
|
||
Under development. | ||
|
||
## Usage | ||
|
||
Command line | ||
|
||
``` | ||
$ anddos_go --help | ||
Usage of ./anddos_go: | ||
-listenPort string | ||
Port where ANDDOS should listen (default ":8080") | ||
-upstreamURL string | ||
URL to protected server (default "http://localhost:80") | ||
$ anddos_go | ||
``` | ||
|
||
### Statically linked (mostly) build | ||
|
||
``` | ||
CGO_ENABLED=0 GOOS=linux go build -a -ldflags '-extldflags "-static"' . | ||
``` | ||
|
||
### Docker Image | ||
|
||
TODO |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,42 @@ | ||
package main | ||
|
||
import ( | ||
"fmt" | ||
"flag" | ||
"log" | ||
"net/http" | ||
"net/http/httputil" | ||
"net/url" | ||
) | ||
|
||
// AnddosStatus keeps global status | ||
var AnddosStatus AnddosState | ||
|
||
// AnddosClients keeps list of all known clients based on IP | ||
var AnddosClients map[string]*AnddosClient | ||
|
||
func main() { | ||
fmt.Println("Hi ANDDOS!") | ||
//TODO | ||
// Parse CLI arguments | ||
var listenPort = flag.String("listenPort", ":8080", "Port where ANDDOS should listen") | ||
var upstreamURL = flag.String("upstreamURL", "http://localhost:80", "URL to protected server") | ||
flag.Parse() | ||
|
||
log.Println("Starting ANDDOS...") | ||
|
||
// Bootstrap Anddos | ||
AnddosStatus.Threshold = 1000 | ||
AnddosClients = make(map[string]*AnddosClient) | ||
|
||
// Setup reverse proxy | ||
remote, err := url.Parse(*upstreamURL) | ||
if err != nil { | ||
panic(err) | ||
} | ||
proxy := httputil.NewSingleHostReverseProxy(remote) | ||
http.HandleFunc("/", AnddosHandler(proxy)) | ||
|
||
// Serve the traffic | ||
err = http.ListenAndServe(*listenPort, nil) | ||
if err != nil { | ||
panic(err) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
package main | ||
|
||
import ( | ||
"encoding/json" | ||
"io/ioutil" | ||
"log" | ||
"net" | ||
"net/http" | ||
"net/http/httputil" | ||
"time" | ||
) | ||
|
||
// AnddosHandler passes HTTP connections via Reverse Proxy | ||
func AnddosHandler(p *httputil.ReverseProxy) func(http.ResponseWriter, *http.Request) { | ||
return func(w http.ResponseWriter, r *http.Request) { | ||
log.Println(r.Method) | ||
log.Println(r.URL) | ||
timeStart := time.Now() | ||
remoteIP, _, _ := net.SplitHostPort(r.RemoteAddr) | ||
|
||
log.Println(r.RemoteAddr) | ||
// find or init a client | ||
if AnddosClients[remoteIP] == nil { | ||
AnddosClients[remoteIP] = &AnddosClient{} | ||
} | ||
|
||
// decide if block or serve | ||
if AnddosClients[remoteIP].Score >= AnddosStatus.Threshold { | ||
// blocked TODO | ||
} else { | ||
// Serve the request | ||
p.ServeHTTP(w, r) | ||
|
||
// update client stats | ||
AnddosClients[remoteIP].AddRequest(200, r.Method, w.Header().Get("Content-type"), float32(time.Since(timeStart)), r.RequestURI) | ||
|
||
// TODO: print less often later.. | ||
log.Printf("%+v\n", AnddosStatus) | ||
clientsDump, _ := json.Marshal(AnddosClients) | ||
ioutil.WriteFile("/tmp/anddos_clients_dump.json", clientsDump, 0600) | ||
|
||
//log.Println(w.Header().Get("Content-length")) add to scoring? | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
package main | ||
|
||
// AnddosState keep overall server status and list of clients | ||
type AnddosState struct { | ||
Level int //(0)Normal, (10)Attack, (100)Overload; not used yet | ||
Threshold int | ||
//clients map[string]AnddosClient | ||
AvgClient AnddosClient | ||
RequestsCount int64 | ||
BlockedRequestsCount int64 | ||
//StartedAt Time | ||
} | ||
|
||
// AnddosClient keeps information about a client | ||
type AnddosClient struct { | ||
// missing: set, key | ||
ip string | ||
ua string | ||
|
||
RequestsCount int | ||
NotModCount int | ||
HTTP1Count int | ||
HTTP2Count int | ||
HTTP3Count int | ||
HTTP4Count int | ||
HTTP5Count int | ||
|
||
HTTPGetCount int | ||
HTTPPostCount int | ||
|
||
AvgTime float32 | ||
AvgHTMLTime float32 | ||
|
||
HTMLCount int | ||
CSSCount int | ||
JavasriptCount int | ||
ImageCount int | ||
OtherCount int | ||
// json etc.? | ||
|
||
HTTPCodeScore int | ||
MimeTypeScore int | ||
TimeScore int | ||
PassSeqScore int | ||
Score int | ||
|
||
PassSeq []byte | ||
|
||
// Created at | ||
// FirstRequestAt Time | ||
// LastRequestAt Time | ||
} | ||
|
||
// AddRequest adds new requests information to its client | ||
func (c *AnddosClient) AddRequest(HTTPCode int, HTTPMethod string, MimeType string, Time float32, Path string) { | ||
c.RequestsCount++ | ||
|
||
// NotModCount | ||
if HTTPCode == 304 { | ||
c.NotModCount++ | ||
} | ||
|
||
// HTTPCode | ||
if HTTPCode < 200 { | ||
c.HTTP1Count++ | ||
} else if HTTPCode < 300 { | ||
c.HTTP2Count++ | ||
} else if HTTPCode < 400 { | ||
c.HTTP3Count++ | ||
} else if HTTPCode < 500 { | ||
c.HTTP4Count++ | ||
} else { | ||
c.HTTP5Count++ | ||
} | ||
|
||
// HTTPMethod | ||
switch HTTPMethod { | ||
case "GET": | ||
c.HTTPGetCount++ | ||
case "POST": | ||
c.HTTPPostCount++ | ||
} | ||
|
||
// MimeType TODO | ||
|
||
// Time | ||
c.AvgTime = (c.AvgTime*float32(c.RequestsCount-1) + Time) / float32(c.RequestsCount) | ||
} |