mirror of
https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin.git
synced 2026-07-21 19:48:59 +02:00
✨ rewrite redis communication
This commit is contained in:
@@ -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
|
||||||
|
|||||||
Vendored
+6
-1
@@ -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")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user