rewrite redis communication

This commit is contained in:
Max Lerebourg
2022-11-11 00:09:06 +01:00
parent 4a5f1eca6a
commit 18f68de196
4 changed files with 73 additions and 164 deletions
@@ -45,16 +45,18 @@ services:
- "traefik.http.middlewares.crowdsec1.plugin.bouncer.rediscacheenabled=true" - "traefik.http.middlewares.crowdsec1.plugin.bouncer.rediscacheenabled=true"
whoami2: whoami2:
image: traefik/whoami image: 4206969/spiderfoot
container_name: "simple-service2" container_name: "simple-service2"
volumes:
- ./spiderfoot-data:/var/lib/spiderfoot
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
# Definition of the router # Definition of the router
- "traefik.http.routers.router2.rule=Path(`/bar`)" - "traefik.http.routers.router2.rule=PathPrefix(`/`)"
- "traefik.http.routers.router2.entrypoints=web" - "traefik.http.routers.router2.entrypoints=web"
- "traefik.http.routers.router2.middlewares=crowdsec1@docker" - "traefik.http.routers.router2.middlewares=crowdsec1@docker"
# Definition of the service # Definition of the service
- "traefik.http.services.service2.loadbalancer.server.port=80" - "traefik.http.services.service2.loadbalancer.server.port=5001"
# Definition of the middleware # Definition of the middleware
- "traefik.http.middlewares.crowdsec2.plugin.bouncer.enabled=true" - "traefik.http.middlewares.crowdsec2.plugin.bouncer.enabled=true"
# crowdseclapikey must be uniq to the middleware attached to the service # crowdseclapikey must be uniq to the middleware attached to the service
+6 -1
View File
@@ -19,6 +19,8 @@ var redis simpleredis.SimpleRedis
var redisEnabled = false var redisEnabled = false
// CLASSIC
func getDecisionLocalCache(clientIP string) (bool, error) { func getDecisionLocalCache(clientIP string) (bool, error) {
banned, isCached := cache.Get(clientIP) banned, isCached := cache.Get(clientIP)
bannedString, isValid := banned.(string) bannedString, isValid := banned.(string)
@@ -36,6 +38,8 @@ func deleteDecisionLocalCache(clientIP string) {
cache.Del(clientIP) cache.Del(clientIP)
} }
// REDIS
func getDecisionRedisCache(clientIP string) (bool, error) { func getDecisionRedisCache(clientIP string) (bool, error) {
banned, err := redis.Get(clientIP) banned, err := redis.Get(clientIP)
bannedString := string(banned) bannedString := string(banned)
@@ -53,6 +57,7 @@ func deleteDecisionRedisCache(clientIP string) {
redis.Del(clientIP) redis.Del(clientIP)
} }
// DeleteDecision delete decision in cache
func DeleteDecision(clientIP string) { func DeleteDecision(clientIP string) {
if redisEnabled { if redisEnabled {
deleteDecisionRedisCache(clientIP) deleteDecisionRedisCache(clientIP)
@@ -89,5 +94,5 @@ func SetDecision(clientIP string, isBanned bool, duration int64) {
func InitRedisClient(host string) { func InitRedisClient(host string) {
redisEnabled = true redisEnabled = true
redis.Init(host) redis.Init(host)
logger.Debug("connect to redis") logger.Debug("Redis initialized")
} }
+7 -7
View File
@@ -14,13 +14,13 @@ var (
// Init Set Default log level to info in case log level to defined // Init Set Default log level to info in case log level to defined
func Init(logLevel string) { func Init(logLevel string) {
switch logLevel { switch logLevel {
case "INFO": case "INFO":
loggerInfo.SetOutput(os.Stdout) loggerInfo.SetOutput(os.Stdout)
case "DEBUG": case "DEBUG":
loggerInfo.SetOutput(os.Stdout) loggerInfo.SetOutput(os.Stdout)
loggerDebug.SetOutput(os.Stdout) loggerDebug.SetOutput(os.Stdout)
default: default:
loggerInfo.SetOutput(os.Stdout) loggerInfo.SetOutput(os.Stdout)
} }
} }
+55 -153
View File
@@ -1,13 +1,14 @@
package simpleredis package simpleredis
import ( import (
"bufio"
"fmt" "fmt"
"net" "net"
"strconv" "net/textproto"
"strings" "strings"
"time" "time"
"github.com/tehnerd/goUtils/netutils" logger "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/logger"
) )
type RedisCmd struct { type RedisCmd struct {
@@ -19,10 +20,7 @@ type RedisCmd struct {
} }
type SimpleRedis struct { type SimpleRedis struct {
redisChanRead chan RedisCmd redisHost string
redisChanWrite chan RedisCmd
redisHost string
redisCmd RedisCmd
} }
func genRedisArray(params ...[]byte) []byte { func genRedisArray(params ...[]byte) []byte {
@@ -35,135 +33,43 @@ func genRedisArray(params ...[]byte) []byte {
return []byte(MSG) return []byte(MSG)
} }
func parseResponse(response []byte, dataBuf []byte, Len *int) ([]byte, []byte, error) { func askRedis(hostnamePort string, cmd RedisCmd, channel chan RedisCmd) {
dataBuf = append(dataBuf, response...) conn, err := net.Dial("tcp", hostnamePort)
lenCRLF := 2
if *Len != 0 {
if len(dataBuf) < *Len {
return nil, dataBuf, nil
} else {
return dataBuf[:*Len], dataBuf[*Len:], nil
}
}
for {
switch string(dataBuf[0]) {
case "+", "-", ":":
//simple strings, error,int. usually ther are in format (+|-|:)DATA\r\n"
if len(dataBuf) < 3 {
return nil, dataBuf, nil
}
cntr := 1
for ; cntr < len(dataBuf); cntr++ {
if dataBuf[cntr] == '\r' {
break
}
}
if cntr == len(dataBuf) {
return nil, dataBuf, nil
}
response = dataBuf[1:cntr]
return response, dataBuf[cntr+2:], nil
case "$":
//bulk string. format $LEN\r\nDATA\r\n. up to 512MB
cntr := 1
for ; cntr < len(dataBuf); cntr++ {
if string(dataBuf[cntr]) == "\r" {
break
}
}
if cntr == len(dataBuf) || cntr+lenCRLF > len(dataBuf) {
return nil, dataBuf, nil
}
dataLen, err := strconv.Atoi(string(dataBuf[1:cntr]))
if err != nil {
return nil, dataBuf[cntr:], nil
}
if dataLen == -1 {
return nil, dataBuf[cntr:], fmt.Errorf("NOT FOUND")
}
if cntr+lenCRLF > len(dataBuf)-lenCRLF {
*Len = dataLen
return nil, dataBuf[cntr+lenCRLF:], nil
}
if len(dataBuf[cntr+lenCRLF:len(dataBuf)-lenCRLF]) < dataLen {
*Len = dataLen
return nil, dataBuf[cntr+lenCRLF:], nil
} else {
return dataBuf[cntr+lenCRLF : cntr+dataLen+lenCRLF], dataBuf[cntr+dataLen+lenCRLF:], nil
}
case "*":
panic("array")
default:
if len(dataBuf) > 1 {
dataBuf = dataBuf[1:]
} else {
return nil, dataBuf, nil
}
}
}
}
func initContext(hostnamePort string, redisCmdWrite, redisCmdRead chan RedisCmd) {
tcpRemoteAddress, err := net.ResolveTCPAddr("tcp", hostnamePort)
if err != nil { if err != nil {
panic("cant resolve remote redis address") return
} }
var ladr *net.TCPAddr defer conn.Close()
msgBuf := make([]byte, 65000)
initMsg := []byte("*1\r\n$4\r\nPING\r\n") writer := textproto.NewWriter(bufio.NewWriter(conn))
writeChan := make(chan []byte) reader := textproto.NewReader(bufio.NewReader(conn))
readChan := make(chan []byte)
flushChan := make(chan int) switch cmd.Command {
go netutils.AutoRecoonectedTCP(ladr, tcpRemoteAddress, msgBuf, initMsg, writeChan, readChan, flushChan) case "SET":
<-readChan data := genRedisArray([]byte("SET"), []byte(cmd.Name), []byte(cmd.Data), []byte("EX"), []byte(fmt.Sprintf("%v", cmd.Duration)))
dataBuf := make([]byte, 0) writer.PrintfLine(string(data))
dataLen := 0 logger.Info("set")
for { case "DEL":
select { data := genRedisArray([]byte("DEL"), []byte(cmd.Name))
case cmd := <-redisCmdWrite: writer.PrintfLine(string(data))
switch cmd.Command { logger.Info("del")
case "SET": case "GET":
data := genRedisArray([]byte("SET"), []byte(cmd.Name), cmd.Data, []byte("EX"), []byte(fmt.Sprintf("%v", cmd.Duration))) data := genRedisArray([]byte("GET"), []byte(cmd.Name))
writeChan <- data writer.PrintfLine(string(data))
case "GET": logger.Info("get")
data := genRedisArray([]byte("GET"), []byte(cmd.Name)) for {
writeChan <- data
case "DEL":
data := genRedisArray([]byte("DEL"), []byte(cmd.Name))
writeChan <- data
}
case response := <-readChan:
data, dataBuf, err := parseResponse(response, dataBuf, &dataLen)
if dataLen != 0 {
for data == nil {
response = <-readChan
data, dataBuf, err = parseResponse(response, dataBuf, &dataLen)
}
}
if err != nil {
select {
case redisCmdRead <- RedisCmd{
Error: err,
}:
case <-time.After(time.Second * 5):
}
}
if data != nil && string(data) != "PONG" {
select {
case redisCmdRead <- RedisCmd{
Data: data,
}:
case <-time.After(time.Second * 5):
}
dataLen = 0
}
case <-flushChan:
dataBuf = dataBuf[:]
dataLen = 0
select { select {
case redisCmdRead <- RedisCmd{}: case <-time.After(time.Second * 1):
case <-time.After(time.Second * 5): channel <- RedisCmd{Error: fmt.Errorf("timeout")}
return
default:
read, _ := reader.ReadLineBytes()
if string(read) != "$1" {
channel <- RedisCmd{Error: fmt.Errorf("miss")}
return
}
read, _ = reader.ReadLineBytes()
channel <- RedisCmd{Data: read}
return
} }
} }
} }
@@ -171,16 +77,16 @@ func initContext(hostnamePort string, redisCmdWrite, redisCmdRead chan RedisCmd)
func (sr *SimpleRedis) Init(redisHost string) { func (sr *SimpleRedis) Init(redisHost string) {
sr.redisHost = redisHost sr.redisHost = redisHost
sr.redisChanWrite = make(chan RedisCmd)
sr.redisChanRead = make(chan RedisCmd)
go initContext(sr.redisHost, sr.redisChanWrite, sr.redisChanRead)
} }
func (sr *SimpleRedis) Get(name string) ([]byte, error) { func (sr *SimpleRedis) Get(name string) ([]byte, error) {
sr.redisCmd.Command = "GET" redisCmd := RedisCmd{
sr.redisCmd.Name = name Command: "GET",
sr.redisChanWrite <- sr.redisCmd Name: name,
resp := <-sr.redisChanRead }
channel := make(chan RedisCmd)
go askRedis(sr.redisHost, redisCmd, channel)
resp := <-channel
if resp.Error != nil { if resp.Error != nil {
return nil, resp.Error return nil, resp.Error
} }
@@ -188,25 +94,21 @@ func (sr *SimpleRedis) Get(name string) ([]byte, error) {
} }
func (sr *SimpleRedis) Set(name string, data []byte, duration int64) error { func (sr *SimpleRedis) Set(name string, data []byte, duration int64) error {
sr.redisCmd.Command = "SET" redisCmd := RedisCmd{
sr.redisCmd.Name = name Command: "SET",
sr.redisCmd.Data = data Name: name,
sr.redisCmd.Duration = duration Data: data,
sr.redisChanWrite <- sr.redisCmd Duration: duration,
resp := <-sr.redisChanRead
if resp.Error != nil {
return resp.Error
} }
go askRedis(sr.redisHost, redisCmd, nil)
return nil return nil
} }
func (sr *SimpleRedis) Del(name string) error { func (sr *SimpleRedis) Del(name string) error {
sr.redisCmd.Command = "DEL" redisCmd := RedisCmd{
sr.redisCmd.Name = name Command: "DEL",
sr.redisChanWrite <- sr.redisCmd Name: name,
resp := <-sr.redisChanRead
if resp.Error != nil {
return resp.Error
} }
go askRedis(sr.redisHost, redisCmd, nil)
return nil return nil
} }