Skip to content
This repository has been archived by the owner on Feb 12, 2024. It is now read-only.

Commit

Permalink
Add ReverseProxy and capture few request stats
Browse files Browse the repository at this point in the history
  • Loading branch information
aufi committed Jan 16, 2019
1 parent 411831b commit 14bfd1f
Show file tree
Hide file tree
Showing 6 changed files with 200 additions and 5 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
.DS_Store
/anddos/
/anddos/
*_dump.json
3 changes: 3 additions & 0 deletions anddos_go/Dockerfile
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"]
28 changes: 27 additions & 1 deletion anddos_go/README.md
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
38 changes: 35 additions & 3 deletions anddos_go/anddos.go
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)
}
}
45 changes: 45 additions & 0 deletions anddos_go/anddos_handler.go
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?
}
}
}
88 changes: 88 additions & 0 deletions anddos_go/anddos_structs.go
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)
}

0 comments on commit 14bfd1f

Please sign in to comment.