mirror of
https://github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin.git
synced 2026-09-02 20:28:50 +02:00
Compare commits
15
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
07c8fae927 | ||
|
|
13c209be3f | ||
|
|
93340ffa55 | ||
|
|
0c2668d578 | ||
|
|
abae7ee028 | ||
|
|
1fcd4f4e2f | ||
|
|
39fcc38980 | ||
|
|
dd322a966a | ||
|
|
f0bb140596 | ||
|
|
46e581eca2 | ||
|
|
50690d1ac7 | ||
|
|
b079073ff6 | ||
|
|
976cbb7d1f | ||
|
|
80726df450 | ||
|
|
4132445a79 |
@@ -30,4 +30,6 @@ Steps to reproduce the behavior:
|
|||||||
3. Scroll down to '....'
|
3. Scroll down to '....'
|
||||||
4. See error
|
4. See error
|
||||||
|
|
||||||
|
<!---
|
||||||
If you like the plugin, please consider starring it, so you can get updates and we get some more visibility ✨
|
If you like the plugin, please consider starring it, so you can get updates and we get some more visibility ✨
|
||||||
|
-->
|
||||||
|
|||||||
@@ -16,4 +16,6 @@ A clear and concise description of what you want to happen.
|
|||||||
**Additional context**
|
**Additional context**
|
||||||
Add any other context or screenshots about the feature request here.
|
Add any other context or screenshots about the feature request here.
|
||||||
|
|
||||||
|
<!---
|
||||||
If you like the plugin, please consider starring it, so you can get updates and we get some more visibility ✨
|
If you like the plugin, please consider starring it, so you can get updates and we get some more visibility ✨
|
||||||
|
-->
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ There are 4 operating modes (CrowdsecMode) for this plugin:
|
|||||||
The `streaming mode` is recommended for performance, decisions are updated every 60 sec by default and that's the only communication between Traefik and Crowdsec. Every request that happens hits the cache for quick decisions.
|
The `streaming mode` is recommended for performance, decisions are updated every 60 sec by default and that's the only communication between Traefik and Crowdsec. Every request that happens hits the cache for quick decisions.
|
||||||
|
|
||||||
The cache can be local to Traefik using the filesystem, or a separate Redis instance.
|
The cache can be local to Traefik using the filesystem, or a separate Redis instance.
|
||||||
Support for Redis is currently in beta (requires version 7.0.X of Redis).
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
@@ -44,6 +44,7 @@ make run
|
|||||||
|
|
||||||
**/!\ Cache is shared by all services**
|
**/!\ Cache is shared by all services**
|
||||||
*This means if an IP is banned, all services which are protected by an instance of the plugin will deny requests from that IP*
|
*This means if an IP is banned, all services which are protected by an instance of the plugin will deny requests from that IP*
|
||||||
|
Only one instance of the plugin is *possible*.
|
||||||
|
|
||||||
### Variables
|
### Variables
|
||||||
- Enabled
|
- Enabled
|
||||||
@@ -103,6 +104,18 @@ make run
|
|||||||
- string
|
- string
|
||||||
- default: "redis:6379"
|
- default: "redis:6379"
|
||||||
- hostname and port for the Redis service
|
- hostname and port for the Redis service
|
||||||
|
- RedisCachePassword
|
||||||
|
- string
|
||||||
|
- default: ""
|
||||||
|
- Password for the Redis service
|
||||||
|
- RedisCacheDatabase
|
||||||
|
- string
|
||||||
|
- default: ""
|
||||||
|
- Database selection for the Redis service
|
||||||
|
- HTTPTimeoutSeconds
|
||||||
|
- int64
|
||||||
|
- default: 10
|
||||||
|
- Default timeout in seconds for contacting Crowdsec LAPI
|
||||||
- UpdateIntervalSeconds
|
- UpdateIntervalSeconds
|
||||||
- int64
|
- int64
|
||||||
- default: 60
|
- default: 60
|
||||||
@@ -110,7 +123,7 @@ make run
|
|||||||
- DefaultDecisionSeconds
|
- DefaultDecisionSeconds
|
||||||
- int64
|
- int64
|
||||||
- default: 60
|
- default: 60
|
||||||
- Used only in `live` mode, decision duration of accepted IPs
|
- Used only in `live` mode, maximum decision duration
|
||||||
- CrowdsecCapiMachineId
|
- CrowdsecCapiMachineId
|
||||||
- string
|
- string
|
||||||
- Used only in `alone` mode, login for Crowdsec CAPI
|
- Used only in `alone` mode, login for Crowdsec CAPI
|
||||||
@@ -134,6 +147,7 @@ experimental:
|
|||||||
plugins:
|
plugins:
|
||||||
bouncer:
|
bouncer:
|
||||||
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
moduleName: github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
||||||
|
version: vX.Y.Z # To update
|
||||||
```
|
```
|
||||||
|
|
||||||
```yaml
|
```yaml
|
||||||
@@ -163,6 +177,7 @@ http:
|
|||||||
logLevel: DEBUG
|
logLevel: DEBUG
|
||||||
updateIntervalSeconds: 60
|
updateIntervalSeconds: 60
|
||||||
defaultDecisionSeconds: 60
|
defaultDecisionSeconds: 60
|
||||||
|
httpTimeoutSeconds: 10
|
||||||
crowdsecMode: live
|
crowdsecMode: live
|
||||||
crowdsecLapiKey: privateKey-foo
|
crowdsecLapiKey: privateKey-foo
|
||||||
crowdsecLapiKeyFile: /etc/traefik/cs-privateKey-foo
|
crowdsecLapiKeyFile: /etc/traefik/cs-privateKey-foo
|
||||||
@@ -183,6 +198,8 @@ http:
|
|||||||
forwardedHeadersCustomName: X-Custom-Header
|
forwardedHeadersCustomName: X-Custom-Header
|
||||||
redisCacheEnabled: false
|
redisCacheEnabled: false
|
||||||
redisCacheHost: "redis:6379"
|
redisCacheHost: "redis:6379"
|
||||||
|
redisCachePassword: password
|
||||||
|
redisCacheDatabase: "5"
|
||||||
crowdsecLapiTLSCertificateAuthority: |-
|
crowdsecLapiTLSCertificateAuthority: |-
|
||||||
-----BEGIN CERTIFICATE-----
|
-----BEGIN CERTIFICATE-----
|
||||||
MIIEBzCCAu+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT
|
MIIEBzCCAu+gAwIBAgICEAAwDQYJKoZIhvcNAQELBQAwgZQxCzAJBgNVBAYTAlVT
|
||||||
|
|||||||
+44
-24
@@ -15,8 +15,6 @@ import (
|
|||||||
"text/template"
|
"text/template"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
simpleredis "github.com/maxlerebourg/simpleredis"
|
|
||||||
|
|
||||||
cache "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/cache"
|
cache "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/cache"
|
||||||
configuration "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/configuration"
|
configuration "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/configuration"
|
||||||
ip "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/ip"
|
ip "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin/pkg/ip"
|
||||||
@@ -35,6 +33,7 @@ const (
|
|||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
|
isStartup = true
|
||||||
isCrowdsecStreamHealthy = true
|
isCrowdsecStreamHealthy = true
|
||||||
ticker chan bool
|
ticker chan bool
|
||||||
)
|
)
|
||||||
@@ -85,7 +84,7 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n
|
|||||||
crowdsecStreamRoute := ""
|
crowdsecStreamRoute := ""
|
||||||
crowdsecHeader := ""
|
crowdsecHeader := ""
|
||||||
if config.CrowdsecMode == configuration.AloneMode {
|
if config.CrowdsecMode == configuration.AloneMode {
|
||||||
config.CrowdsecCapiMachineID, _ = configuration.GetVariable(config, "CrowdsecCapiMachineId")
|
config.CrowdsecCapiMachineID, _ = configuration.GetVariable(config, "CrowdsecCapiMachineID")
|
||||||
config.CrowdsecCapiPassword, _ = configuration.GetVariable(config, "CrowdsecCapiPassword")
|
config.CrowdsecCapiPassword, _ = configuration.GetVariable(config, "CrowdsecCapiPassword")
|
||||||
config.CrowdsecLapiHost = "api.crowdsec.net"
|
config.CrowdsecLapiHost = "api.crowdsec.net"
|
||||||
config.CrowdsecLapiScheme = "https"
|
config.CrowdsecLapiScheme = "https"
|
||||||
@@ -138,24 +137,30 @@ func New(ctx context.Context, next http.Handler, config *configuration.Config, n
|
|||||||
IdleConnTimeout: 30 * time.Second,
|
IdleConnTimeout: 30 * time.Second,
|
||||||
TLSClientConfig: tlsConfig,
|
TLSClientConfig: tlsConfig,
|
||||||
},
|
},
|
||||||
Timeout: 10 * time.Second,
|
Timeout: time.Duration(config.HTTPTimeoutSeconds) * time.Second,
|
||||||
},
|
},
|
||||||
cacheClient: &cache.Client{},
|
cacheClient: &cache.Client{},
|
||||||
}
|
}
|
||||||
bouncer.cacheClient.New(config.RedisCacheEnabled, config.RedisCacheHost)
|
config.RedisCachePassword, _ = configuration.GetVariable(config, "RedisCachePassword")
|
||||||
|
bouncer.cacheClient.New(
|
||||||
|
config.RedisCacheEnabled,
|
||||||
|
config.RedisCacheHost,
|
||||||
|
config.RedisCachePassword,
|
||||||
|
config.RedisCacheDatabase,
|
||||||
|
)
|
||||||
|
|
||||||
if (config.CrowdsecMode == configuration.StreamMode || config.CrowdsecMode == configuration.AloneMode) && ticker == nil {
|
if (config.CrowdsecMode == configuration.StreamMode || config.CrowdsecMode == configuration.AloneMode) && ticker == nil {
|
||||||
if config.CrowdsecMode == configuration.AloneMode {
|
if config.CrowdsecMode == configuration.AloneMode {
|
||||||
err = getToken(bouncer)
|
if err := getToken(bouncer); err != nil {
|
||||||
if err != nil {
|
|
||||||
logger.Error(fmt.Sprintf("New:getToken %s", err.Error()))
|
logger.Error(fmt.Sprintf("New:getToken %s", err.Error()))
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
handleStreamTicker(bouncer)
|
||||||
|
isStartup = false
|
||||||
ticker = startTicker(config, func() {
|
ticker = startTicker(config, func() {
|
||||||
handleStreamCache(bouncer)
|
handleStreamTicker(bouncer)
|
||||||
})
|
})
|
||||||
go handleStreamCache(bouncer)
|
|
||||||
}
|
}
|
||||||
logger.Debug(fmt.Sprintf("New initialized mode:%s", config.CrowdsecMode))
|
logger.Debug(fmt.Sprintf("New initialized mode:%s", config.CrowdsecMode))
|
||||||
|
|
||||||
@@ -193,10 +198,12 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
|
|
||||||
// TODO This should be simplified
|
// TODO This should be simplified
|
||||||
if bouncer.crowdsecMode != configuration.NoneMode {
|
if bouncer.crowdsecMode != configuration.NoneMode {
|
||||||
isBanned, erro := bouncer.cacheClient.GetDecision(remoteIP)
|
isBanned, cacheErr := bouncer.cacheClient.GetDecision(remoteIP)
|
||||||
if erro != nil {
|
if cacheErr != nil {
|
||||||
logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s isBanned:true %s", remoteIP, erro.Error()))
|
errString := cacheErr.Error()
|
||||||
if erro.Error() == simpleredis.RedisUnreachable {
|
logger.Debug(fmt.Sprintf("ServeHTTP:getDecision ip:%s isBanned:false %s", remoteIP, errString))
|
||||||
|
if errString != cache.CacheMiss {
|
||||||
|
logger.Error(fmt.Sprintf("ServeHTTP:getDecision ip:%s %s", remoteIP, errString))
|
||||||
rw.WriteHeader(http.StatusForbidden)
|
rw.WriteHeader(http.StatusForbidden)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -216,7 +223,7 @@ func (bouncer *Bouncer) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
|
|||||||
if isCrowdsecStreamHealthy {
|
if isCrowdsecStreamHealthy {
|
||||||
bouncer.next.ServeHTTP(rw, req)
|
bouncer.next.ServeHTTP(rw, req)
|
||||||
} else {
|
} else {
|
||||||
logger.Error(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s", remoteIP))
|
logger.Debug(fmt.Sprintf("ServeHTTP isCrowdsecStreamHealthy:false ip:%s", remoteIP))
|
||||||
rw.WriteHeader(http.StatusForbidden)
|
rw.WriteHeader(http.StatusForbidden)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -259,6 +266,15 @@ type Login struct {
|
|||||||
Expire string `json:"expire"`
|
Expire string `json:"expire"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func handleStreamTicker(bouncer *Bouncer) {
|
||||||
|
if err := handleStreamCache(bouncer); err != nil {
|
||||||
|
isCrowdsecStreamHealthy = false
|
||||||
|
logger.Error(err.Error())
|
||||||
|
} else {
|
||||||
|
isCrowdsecStreamHealthy = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func startTicker(config *configuration.Config, work func()) chan bool {
|
func startTicker(config *configuration.Config, work func()) chan bool {
|
||||||
ticker := time.NewTicker(time.Duration(config.UpdateIntervalSeconds) * time.Second)
|
ticker := time.NewTicker(time.Duration(config.UpdateIntervalSeconds) * time.Second)
|
||||||
stop := make(chan bool, 1)
|
stop := make(chan bool, 1)
|
||||||
@@ -313,7 +329,11 @@ func handleNoStreamCache(bouncer *Bouncer, remoteIP string) error {
|
|||||||
return fmt.Errorf("handleNoStreamCache:parseDuration %w", err)
|
return fmt.Errorf("handleNoStreamCache:parseDuration %w", err)
|
||||||
}
|
}
|
||||||
if isLiveMode {
|
if isLiveMode {
|
||||||
bouncer.cacheClient.SetDecision(remoteIP, true, int64(duration.Seconds()))
|
durationSecond := int64(duration.Seconds())
|
||||||
|
if bouncer.defaultDecisionTimeout < durationSecond {
|
||||||
|
durationSecond = bouncer.defaultDecisionTimeout
|
||||||
|
}
|
||||||
|
bouncer.cacheClient.SetDecision(remoteIP, true, durationSecond)
|
||||||
}
|
}
|
||||||
return fmt.Errorf("handleNoStreamCache:banned")
|
return fmt.Errorf("handleNoStreamCache:banned")
|
||||||
}
|
}
|
||||||
@@ -342,7 +362,7 @@ func getToken(bouncer *Bouncer) error {
|
|||||||
return fmt.Errorf("getToken statusCode:%d", login.Code)
|
return fmt.Errorf("getToken statusCode:%d", login.Code)
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleStreamCache(bouncer *Bouncer) {
|
func handleStreamCache(bouncer *Bouncer) error {
|
||||||
// TODO clean properly on exit.
|
// TODO clean properly on exit.
|
||||||
// Instead of blocking the goroutine interval for all the secondary node,
|
// Instead of blocking the goroutine interval for all the secondary node,
|
||||||
// if the master service is shut down, other goroutine can take the lead
|
// if the master service is shut down, other goroutine can take the lead
|
||||||
@@ -350,27 +370,26 @@ func handleStreamCache(bouncer *Bouncer) {
|
|||||||
_, err := bouncer.cacheClient.GetDecision(cacheTimeoutKey)
|
_, err := bouncer.cacheClient.GetDecision(cacheTimeoutKey)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
logger.Debug("handleStreamCache:alreadyUpdated")
|
logger.Debug("handleStreamCache:alreadyUpdated")
|
||||||
return
|
return nil
|
||||||
|
}
|
||||||
|
if err.Error() != cache.CacheMiss {
|
||||||
|
return err
|
||||||
}
|
}
|
||||||
bouncer.cacheClient.SetDecision(cacheTimeoutKey, false, bouncer.updateInterval-1)
|
bouncer.cacheClient.SetDecision(cacheTimeoutKey, false, bouncer.updateInterval-1)
|
||||||
streamRouteURL := url.URL{
|
streamRouteURL := url.URL{
|
||||||
Scheme: bouncer.crowdsecScheme,
|
Scheme: bouncer.crowdsecScheme,
|
||||||
Host: bouncer.crowdsecHost,
|
Host: bouncer.crowdsecHost,
|
||||||
Path: bouncer.crowdsecStreamRoute,
|
Path: bouncer.crowdsecStreamRoute,
|
||||||
RawQuery: fmt.Sprintf("startup=%t", !isCrowdsecStreamHealthy),
|
RawQuery: fmt.Sprintf("startup=%t", !isCrowdsecStreamHealthy || isStartup),
|
||||||
}
|
}
|
||||||
body, err := crowdsecQuery(bouncer, streamRouteURL.String(), false)
|
body, err := crowdsecQuery(bouncer, streamRouteURL.String(), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(err.Error())
|
return err
|
||||||
isCrowdsecStreamHealthy = false
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
var stream Stream
|
var stream Stream
|
||||||
err = json.Unmarshal(body, &stream)
|
err = json.Unmarshal(body, &stream)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
logger.Error(fmt.Sprintf("handleStreamCache:parsingBody %s", err.Error()))
|
return fmt.Errorf("handleStreamCache:parsingBody %w", err)
|
||||||
isCrowdsecStreamHealthy = false
|
|
||||||
return
|
|
||||||
}
|
}
|
||||||
for _, decision := range stream.New {
|
for _, decision := range stream.New {
|
||||||
duration, err := time.ParseDuration(decision.Duration)
|
duration, err := time.ParseDuration(decision.Duration)
|
||||||
@@ -383,6 +402,7 @@ func handleStreamCache(bouncer *Bouncer) {
|
|||||||
}
|
}
|
||||||
logger.Debug("handleStreamCache:updated")
|
logger.Debug("handleStreamCache:updated")
|
||||||
isCrowdsecStreamHealthy = true
|
isCrowdsecStreamHealthy = true
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func crowdsecQuery(bouncer *Bouncer, stringURL string, isPost bool) ([]byte, error) {
|
func crowdsecQuery(bouncer *Bouncer, stringURL string, isPost bool) ([]byte, error) {
|
||||||
|
|||||||
+6
-1
@@ -144,12 +144,17 @@ func Test_handleStreamCache(t *testing.T) {
|
|||||||
tests := []struct {
|
tests := []struct {
|
||||||
name string
|
name string
|
||||||
args args
|
args args
|
||||||
|
wantErr bool
|
||||||
}{
|
}{
|
||||||
// TODO: Add test cases.
|
// TODO: Add test cases.
|
||||||
}
|
}
|
||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
handleStreamCache(tt.args.bouncer)
|
err := handleStreamCache(tt.args.bouncer)
|
||||||
|
if (err != nil) != tt.wantErr {
|
||||||
|
t.Errorf("handleStreamCache() error = %v, wantErr %v", err, tt.wantErr)
|
||||||
|
return
|
||||||
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.9.10"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -52,7 +52,7 @@ services:
|
|||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapikey=44c36dac5c4140af9f06f397508e82c7"
|
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapikey=44c36dac5c4140af9f06f397508e82c7"
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.1
|
image: crowdsecurity/crowdsec:v1.4.6
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
+3
-3
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.9.10"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -14,7 +14,7 @@ services:
|
|||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
|
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.8"
|
- "--experimental.plugins.bouncer.version=v1.1.11"
|
||||||
volumes:
|
volumes:
|
||||||
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
- "/var/run/docker.sock:/var/run/docker.sock:ro"
|
||||||
- "logs:/var/log/traefik"
|
- "logs:/var/log/traefik"
|
||||||
@@ -63,7 +63,7 @@ services:
|
|||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.1
|
image: crowdsecurity/crowdsec:v1.4.6
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
cloudflare:
|
cloudflare:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.10.4"
|
||||||
container_name: "cloudflare"
|
container_name: "cloudflare"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -12,7 +12,6 @@ services:
|
|||||||
- "--api.insecure=true"
|
- "--api.insecure=true"
|
||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
- "--providers.file.filename=/cloud.yaml"
|
- "--providers.file.filename=/cloud.yaml"
|
||||||
- "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- ./cloudflare-exemple.yaml:/cloud.yaml:ro
|
- ./cloudflare-exemple.yaml:/cloud.yaml:ro
|
||||||
@@ -22,7 +21,7 @@ services:
|
|||||||
- 8080:8080
|
- 8080:8080
|
||||||
|
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.10.4"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -36,7 +35,7 @@ services:
|
|||||||
- "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5"
|
- "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5"
|
||||||
|
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.7"
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- logs-traefik:/var/log/traefik
|
- logs-traefik:/var/log/traefik
|
||||||
@@ -46,7 +45,7 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- crowdsec
|
- crowdsec
|
||||||
|
|
||||||
whoami1:
|
whoami-foo:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
container_name: "simple-service-foo"
|
container_name: "simple-service-foo"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -58,15 +57,14 @@ services:
|
|||||||
- "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
- "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
||||||
# Definition of the service
|
# Definition of the service
|
||||||
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
||||||
# Definitin of the middleware
|
# Definition of the middleware
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=live"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdsecmode=live"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.loglevel=DEBUG"
|
|
||||||
|
|
||||||
whoami2:
|
whoami-bar:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
container_name: "simple-service-bar"
|
container_name: "simple-service-bar"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
@@ -78,24 +76,22 @@ services:
|
|||||||
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
||||||
# Definition of the service
|
# Definition of the service
|
||||||
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
||||||
# Definitin of the middleware
|
# Definition of the middleware
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapikey=44c36dac5c4140af9f06f397508e82c7"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecmode=live"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecmode=live"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.forwardedheaderstrustedips=172.21.0.5"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.loglevel=DEBUG"
|
|
||||||
|
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.3
|
image: crowdsecurity/crowdsec:v1.5.2
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
COLLECTIONS: crowdsecurity/traefik
|
COLLECTIONS: crowdsecurity/traefik
|
||||||
CUSTOM_HOSTNAME: crowdsec
|
CUSTOM_HOSTNAME: crowdsec
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_1: 40796d93c2958f9e58345514e67740e5
|
BOUNCER_KEY_TRAEFIK_DEV: 40796d93c2958f9e58345514e67740e5
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_2: 44c36dac5c4140af9f06f397508e82c7
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
||||||
- logs-cloudflare:/var/log/traefik:ro
|
- logs-cloudflare:/var/log/traefik:ro
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt-get install wget -y
|
DEBIAN_FRONTEND=noninteractive sudo apt-get update && sudo apt-get install wget -y
|
||||||
# DEBIAN_FRONTEND=noninteractive sudo apt-get upgrade -y --assume-yes
|
# DEBIAN_FRONTEND=noninteractive sudo apt-get upgrade -y --assume-yes
|
||||||
wget -O traefik.tar.gz "https://github.com/traefik/traefik/releases/download/v2.9.6/traefik_v2.9.6_linux_amd64.tar.gz"
|
wget -O traefik.tar.gz "https://github.com/traefik/traefik/releases/download/v2.10.4/traefik_v2.10.4_linux_amd64.tar.gz"
|
||||||
tar -zxvf traefik.tar.gz
|
tar -zxvf traefik.tar.gz
|
||||||
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
|
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
|
||||||
sudo mv ./traefik /usr/local/bin/
|
sudo mv ./traefik /usr/local/bin/
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
DEBIAN_FRONTEND=noninteractive sudo apt-get update && apt-get install wget -y
|
DEBIAN_FRONTEND=noninteractive sudo apt-get update && apt-get install wget -y
|
||||||
wget -O whoami.tar.gz "https://github.com/traefik/whoami/releases/download/v1.8.7/whoami_v1.8.7_linux_amd64.tar.gz"
|
wget -O whoami.tar.gz "https://github.com/traefik/whoami/releases/download/v1.9.0/whoami_v1.9.0_linux_amd64.tar.gz"
|
||||||
tar -zxvf whoami.tar.gz
|
tar -zxvf whoami.tar.gz
|
||||||
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
|
# inspired from https://gist.github.com/ubergesundheit/7c9d875befc2d7bfd0bf43d8b3862d85
|
||||||
sudo mv ./whoami /usr/local/bin/
|
sudo mv ./whoami /usr/local/bin/
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
image:
|
image:
|
||||||
tag: v1.4.4-rc1
|
tag: v1.5.2
|
||||||
|
|
||||||
agent:
|
agent:
|
||||||
acquisition:
|
acquisition:
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
image:
|
image:
|
||||||
tag: v2.9.6
|
tag: v2.10.4
|
||||||
|
|
||||||
logs:
|
logs:
|
||||||
general:
|
general:
|
||||||
@@ -16,4 +16,4 @@ experimental:
|
|||||||
|
|
||||||
additionalArguments:
|
additionalArguments:
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.7-beta1"
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.10.4"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -15,7 +15,7 @@ services:
|
|||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
|
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.7"
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
@@ -26,56 +26,60 @@ services:
|
|||||||
- 8080:8080
|
- 8080:8080
|
||||||
depends_on:
|
depends_on:
|
||||||
- crowdsec
|
- crowdsec
|
||||||
- redis
|
- redis-insecure
|
||||||
|
- redis-secure
|
||||||
|
|
||||||
whoami-foo:
|
# Either use secure, or insecure but do not mix both
|
||||||
image: traefik/whoami
|
# whoami-redis-insecure:
|
||||||
container_name: "simple-service-foo"
|
# image: traefik/whoami
|
||||||
restart: unless-stopped
|
# container_name: "simple-service-foo"
|
||||||
labels:
|
# restart: unless-stopped
|
||||||
- "traefik.enable=true"
|
# labels:
|
||||||
# Definition of the router
|
# - "traefik.enable=true"
|
||||||
- "traefik.http.routers.router-foo.rule=Path(`/foo`)"
|
# # Definition of the router
|
||||||
- "traefik.http.routers.router-foo.entrypoints=web"
|
# - "traefik.http.routers.router-foo.rule=Path(`/redis-insecure`)"
|
||||||
- "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
# - "traefik.http.routers.router-foo.entrypoints=web"
|
||||||
# Definition of the service
|
# - "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
||||||
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
# # Definition of the service
|
||||||
# Definition of the middleware
|
# - "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.enabled=true"
|
# # Definition of the middleware
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.rediscacheenabled=true"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.rediscacheenabled=true"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.loglevel=DEBUG"
|
# # Contact redis-unsecure without a password
|
||||||
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.rediscachehost=redis-insecure:6379"
|
||||||
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
|
|
||||||
whoami-bar:
|
whoami-redis-secure:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
container_name: "simple-service-bar"
|
container_name: "simple-service-bar"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
labels:
|
labels:
|
||||||
- "traefik.enable=true"
|
- "traefik.enable=true"
|
||||||
# Definition of the router
|
# Definition of the router
|
||||||
- "traefik.http.routers.router-bar.rule=Path(`/bar`)"
|
- "traefik.http.routers.router-bar.rule=Path(`/redis-secure`)"
|
||||||
- "traefik.http.routers.router-bar.entrypoints=web"
|
- "traefik.http.routers.router-bar.entrypoints=web"
|
||||||
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
||||||
# Definition of the service
|
# Definition of the service
|
||||||
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
||||||
# Definition of the middleware
|
# Definition of the middleware
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapikey=44c36dac5c4140af9f06f397508e82c7"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.rediscacheenabled=true"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.rediscacheenabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.rediscachepassword=FIXME"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.loglevel=DEBUG"
|
# Contact redis-secure with password
|
||||||
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.rediscachehost=redis-secure:6379"
|
||||||
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
|
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.3
|
image: crowdsecurity/crowdsec:v1.5.2
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
COLLECTIONS: crowdsecurity/traefik
|
COLLECTIONS: crowdsecurity/traefik
|
||||||
CUSTOM_HOSTNAME: crowdsec
|
CUSTOM_HOSTNAME: crowdsec
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_1: 40796d93c2958f9e58345514e67740e5
|
BOUNCER_KEY_TRAEFIK_DEV: 40796d93c2958f9e58345514e67740e5
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_2: 44c36dac5c4140af9f06f397508e82c7
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
||||||
- logs-redis:/var/log/traefik:ro
|
- logs-redis:/var/log/traefik:ro
|
||||||
@@ -84,18 +88,27 @@ services:
|
|||||||
labels:
|
labels:
|
||||||
- "traefik.enable=false"
|
- "traefik.enable=false"
|
||||||
|
|
||||||
redis:
|
redis-secure:
|
||||||
image: "redis:7.0.5-alpine"
|
image: "redis:7.0.12-alpine"
|
||||||
container_name: "redis"
|
container_name: "redis-secure"
|
||||||
|
hostname: redis-secure
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command: "redis-server --save 60 1"
|
command: "redis-server --save 60 1 --loglevel debug --requirepass FIXME"
|
||||||
volumes:
|
volumes:
|
||||||
- redis-data:/data
|
- redis-secure-data:/data
|
||||||
ports:
|
|
||||||
- 6379:6379
|
# redis-insecure:
|
||||||
|
# image: "redis:7.0.12-alpine"
|
||||||
|
# container_name: "redis-insecure"
|
||||||
|
# hostname: redis-unsecure
|
||||||
|
# restart: unless-stopped
|
||||||
|
# command: "redis-server --save 60 1 --loglevel debug"
|
||||||
|
# volumes:
|
||||||
|
# - redis-unsecure-data:/data
|
||||||
|
|
||||||
volumes:
|
volumes:
|
||||||
logs-redis:
|
logs-redis:
|
||||||
crowdsec-db-redis:
|
crowdsec-db-redis:
|
||||||
crowdsec-config-redis:
|
crowdsec-config-redis:
|
||||||
redis-data:
|
redis-unsecure-data:
|
||||||
|
redis-secure-data:
|
||||||
|
|||||||
@@ -11,10 +11,11 @@ These CAPI credentials must be set in your docker-compose.yml or in your config
|
|||||||
...
|
...
|
||||||
whoami:
|
whoami:
|
||||||
labels:
|
labels:
|
||||||
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecMode=alone"
|
||||||
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecCapiMachineId=LOGIN"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecCapiMachineId=LOGIN"
|
||||||
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecCapiPassword=PASSWORD"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecCapiPassword=PASSWORD"
|
||||||
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseccapiscenarios=crowdsecurity/http-generic-bf,crowdsecurity/http-xss-probing,..."
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecCapiScenarios=crowdsecurity/http-generic-bf,crowdsecurity/http-xss-probing,..."
|
||||||
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
|
||||||
```
|
```
|
||||||
|
|
||||||
You can then run all the containers:
|
You can then run all the containers:
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
version: "3.8"
|
||||||
|
|
||||||
|
services:
|
||||||
|
traefik:
|
||||||
|
image: "traefik:v2.10.4"
|
||||||
|
container_name: "traefik"
|
||||||
|
restart: unless-stopped
|
||||||
|
command:
|
||||||
|
# - "--log.level=DEBUG"
|
||||||
|
- "--accesslog"
|
||||||
|
- "--accesslog.filepath=/var/log/traefik/access.log"
|
||||||
|
- "--api.insecure=true"
|
||||||
|
- "--providers.docker=true"
|
||||||
|
- "--providers.docker.exposedbydefault=false"
|
||||||
|
- "--entrypoints.web.address=:80"
|
||||||
|
|
||||||
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
|
volumes:
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
ports:
|
||||||
|
- 80:80
|
||||||
|
- 8080:8080
|
||||||
|
|
||||||
|
whoami-foo:
|
||||||
|
image: traefik/whoami
|
||||||
|
container_name: "simple-service-foo"
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "traefik.enable=true"
|
||||||
|
- "traefik.http.routers.router-foo.rule=Path(`/foo`)"
|
||||||
|
- "traefik.http.routers.router-foo.entrypoints=web"
|
||||||
|
- "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
||||||
|
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
||||||
|
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.enabled=true"
|
||||||
|
# - "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.loglevel=DEBUG"
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdsecmode=alone"
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.CrowdsecCapiMachineId=logincacacalfkrjebfreifgzfblezgyfoerxsqxsqxsqxsr"
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.CrowdsecCapiPassword=Password2"
|
||||||
|
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseccapiscenarios=crowdsecurity/sshd,crowdsecurity/asterisk_bf,crowdsecurity/asterisk_user_enum,crowdsecurity/base-http-scenarios"
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
logs-local:
|
||||||
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.10.4"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -15,14 +15,14 @@ services:
|
|||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
|
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.7"
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- ./LAPIKEY:/etc/traefik/LAPIKEY:ro
|
- ./LAPIKEY:/etc/traefik/LAPIKEY:ro
|
||||||
- logs-tls-auth:/var/log/traefik
|
- logs-tls-auth:/var/log/traefik
|
||||||
- crowdsec-certs-tls-auth:/etc/traefik/crowdsec-certs
|
- crowdsec-certs-tls-auth:/etc/traefik/crowdsec-certs
|
||||||
- ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
# - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
@@ -30,22 +30,24 @@ services:
|
|||||||
depends_on:
|
depends_on:
|
||||||
- crowdsec
|
- crowdsec
|
||||||
|
|
||||||
whoami-foo:
|
# Use HTTPS scheme but with lapikey authentication
|
||||||
image: traefik/whoami
|
# whoami-foo:
|
||||||
container_name: "simple-service-foo"
|
# image: traefik/whoami
|
||||||
restart: unless-stopped
|
# container_name: "simple-service-foo"
|
||||||
labels:
|
# restart: unless-stopped
|
||||||
- "traefik.enable=true"
|
# labels:
|
||||||
- "traefik.http.routers.router-foo.rule=Path(`/foo`)"
|
# - "traefik.enable=true"
|
||||||
- "traefik.http.routers.router-foo.entrypoints=web"
|
# - "traefik.http.routers.router-foo.rule=Path(`/foo`)"
|
||||||
- "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
# - "traefik.http.routers.router-foo.entrypoints=web"
|
||||||
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
# - "traefik.http.routers.router-foo.middlewares=crowdsec-foo@docker"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.enabled=true"
|
# - "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.loglevel=DEBUG"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapischeme=https"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdsecLapiTLSCertificateAuthorityFile=/etc/traefik/crowdsec-certs/inter.pem"
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapischeme=https"
|
||||||
|
# - "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateAuthorityFile=/etc/traefik/crowdsec-certs/inter.pem"
|
||||||
|
|
||||||
|
# Use HTTPS scheme with TLS cert authentication
|
||||||
whoami-bar:
|
whoami-bar:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
container_name: "simple-service-bar"
|
container_name: "simple-service-bar"
|
||||||
@@ -56,15 +58,15 @@ services:
|
|||||||
- "traefik.http.routers.router-bar.entrypoints=web"
|
- "traefik.http.routers.router-bar.entrypoints=web"
|
||||||
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
- "traefik.http.routers.router-bar.middlewares=crowdsec-bar@docker"
|
||||||
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.loglevel=DEBUG"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapischeme=https"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapischeme=https"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecLapiTLSCertificateAuthorityFile=/etc/traefik/crowdsec-certs/inter.pem"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateAuthorityFile=/etc/traefik/crowdsec-certs/inter.pem"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecLapiTLSCertificateBouncerFile=/etc/traefik/crowdsec-certs/bouncer.pem"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateBouncerFile=/etc/traefik/crowdsec-certs/bouncer.pem"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdsecLapiTLSCertificateBouncerKeyFile=/etc/traefik/crowdsec-certs/bouncer-key.pem"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdsecLapiTLSCertificateBouncerKeyFile=/etc/traefik/crowdsec-certs/bouncer-key.pem"
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.3
|
image: crowdsecurity/crowdsec:v1.5.2
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ version: "3.8"
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
traefik:
|
traefik:
|
||||||
image: "traefik:v2.9.6"
|
image: "traefik:v2.10.4"
|
||||||
container_name: "traefik"
|
container_name: "traefik"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
command:
|
command:
|
||||||
@@ -15,12 +15,12 @@ services:
|
|||||||
- "--entrypoints.web.address=:80"
|
- "--entrypoints.web.address=:80"
|
||||||
|
|
||||||
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
- "--experimental.plugins.bouncer.version=v1.1.7"
|
- "--experimental.plugins.bouncer.version=v1.1.13"
|
||||||
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
|
||||||
volumes:
|
volumes:
|
||||||
- /var/run/docker.sock:/var/run/docker.sock:ro
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
- logs-trustedips:/var/log/traefik
|
- logs-trustedips:/var/log/traefik
|
||||||
- ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
# - ./../../:/plugins-local/src/github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin
|
||||||
ports:
|
ports:
|
||||||
- 80:80
|
- 80:80
|
||||||
- 8080:8080
|
- 8080:8080
|
||||||
@@ -40,12 +40,11 @@ services:
|
|||||||
# Definition of the service
|
# Definition of the service
|
||||||
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
- "traefik.http.services.service-foo.loadbalancer.server.port=80"
|
||||||
# Definition of the middleware
|
# Definition of the middleware
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
|
||||||
# Replace 10.0.10.30/32 by your IP range which is "trusted"
|
# Replace 10.0.10.30/32 by your IP range which is "trusted"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.clienttrustedips=10.0.10.30/32"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.clienttrustedips=10.0.10.30/32"
|
||||||
- "traefik.http.middlewares.crowdsec-foo.plugin.bouncer.loglevel=DEBUG"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
|
|
||||||
whoami2:
|
whoami2:
|
||||||
image: traefik/whoami
|
image: traefik/whoami
|
||||||
@@ -60,23 +59,21 @@ services:
|
|||||||
# Definition of the service
|
# Definition of the service
|
||||||
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
- "traefik.http.services.service-bar.loadbalancer.server.port=80"
|
||||||
# Definition of the middleware
|
# Definition of the middleware
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.enabled=true"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.enabled=true"
|
||||||
# crowdseclapikey must be uniq to the middleware attached to the service
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.crowdseclapikey=40796d93c2958f9e58345514e67740e5"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.crowdseclapikey=44c36dac5c4140af9f06f397508e82c7"
|
|
||||||
# Replace 10.0.10.30/32 by your IP range which is "trusted"
|
# Replace 10.0.10.30/32 by your IP range which is "trusted"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.clienttrustedips=10.0.10.30/32"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.clienttrustedips=10.0.10.30/32"
|
||||||
- "traefik.http.middlewares.crowdsec-bar.plugin.bouncer.loglevel=DEBUG"
|
- "traefik.http.middlewares.crowdsec.plugin.bouncer.loglevel=DEBUG"
|
||||||
|
|
||||||
|
|
||||||
crowdsec:
|
crowdsec:
|
||||||
image: crowdsecurity/crowdsec:v1.4.3
|
image: crowdsecurity/crowdsec:v1.5.2
|
||||||
container_name: "crowdsec"
|
container_name: "crowdsec"
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
environment:
|
environment:
|
||||||
COLLECTIONS: crowdsecurity/traefik
|
COLLECTIONS: crowdsecurity/traefik
|
||||||
CUSTOM_HOSTNAME: crowdsec
|
CUSTOM_HOSTNAME: crowdsec
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_1: 40796d93c2958f9e58345514e67740e5
|
BOUNCER_KEY_TRAEFIK_DEV: 40796d93c2958f9e58345514e67740e5
|
||||||
BOUNCER_KEY_TRAEFIK_DEV_2: 44c36dac5c4140af9f06f397508e82c7
|
|
||||||
volumes:
|
volumes:
|
||||||
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
- ./acquis.yaml:/etc/crowdsec/acquis.yaml:ro
|
||||||
- logs-trustedips:/var/log/traefik:ro
|
- logs-trustedips:/var/log/traefik:ro
|
||||||
|
|||||||
@@ -4,5 +4,5 @@ go 1.19
|
|||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/leprosus/golang-ttl-map v1.1.7
|
github.com/leprosus/golang-ttl-map v1.1.7
|
||||||
github.com/maxlerebourg/simpleredis v1.0.3
|
github.com/maxlerebourg/simpleredis v1.0.9
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
github.com/leprosus/golang-ttl-map v1.1.7 h1:cF4AAFDDnJTFSV+/42sKLhmMluvLdRlCGS2UaifH6UM=
|
github.com/leprosus/golang-ttl-map v1.1.7 h1:cF4AAFDDnJTFSV+/42sKLhmMluvLdRlCGS2UaifH6UM=
|
||||||
github.com/leprosus/golang-ttl-map v1.1.7/go.mod h1:4QWHJPeVBbrkhOhXdhCv9IEiyj/YzkO04/iexy4vSe0=
|
github.com/leprosus/golang-ttl-map v1.1.7/go.mod h1:4QWHJPeVBbrkhOhXdhCv9IEiyj/YzkO04/iexy4vSe0=
|
||||||
github.com/maxlerebourg/simpleredis v1.0.3 h1:VhXq9bVytWDqD/TS/GjHKayvQb/VUeEql5F+yUbdOiI=
|
github.com/maxlerebourg/simpleredis v1.0.9 h1:aj1hKaYPeOVE4Ksu3TV/zsreUDDWOpKXBAvoFysiqII=
|
||||||
github.com/maxlerebourg/simpleredis v1.0.3/go.mod h1:/DH8zOK6kDskSqoX/m5CJJdNGfkIQZd/ERBJgytDDSk=
|
github.com/maxlerebourg/simpleredis v1.0.9/go.mod h1:/DH8zOK6kDskSqoX/m5CJJdNGfkIQZd/ERBJgytDDSk=
|
||||||
|
|||||||
Vendored
+10
-4
@@ -16,6 +16,9 @@ const (
|
|||||||
cacheNoBannedValue = "f"
|
cacheNoBannedValue = "f"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// CacheMiss error string when cache is miss.
|
||||||
|
const CacheMiss = "cache:miss"
|
||||||
|
|
||||||
//nolint:gochecknoglobals
|
//nolint:gochecknoglobals
|
||||||
var (
|
var (
|
||||||
redis simpleredis.SimpleRedis
|
redis simpleredis.SimpleRedis
|
||||||
@@ -30,7 +33,7 @@ func (localCache) getDecision(clientIP string) (bool, error) {
|
|||||||
if isCached && isValid && len(bannedString) > 0 {
|
if isCached && isValid && len(bannedString) > 0 {
|
||||||
return bannedString == cacheBannedValue, nil
|
return bannedString == cacheBannedValue, nil
|
||||||
}
|
}
|
||||||
return false, fmt.Errorf("cache:miss")
|
return false, fmt.Errorf(CacheMiss)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (localCache) setDecision(clientIP string, value string, duration int64) {
|
func (localCache) setDecision(clientIP string, value string, duration int64) {
|
||||||
@@ -49,6 +52,9 @@ func (redisCache) getDecision(clientIP string) (bool, error) {
|
|||||||
if err == nil && len(bannedString) > 0 {
|
if err == nil && len(bannedString) > 0 {
|
||||||
return bannedString == cacheBannedValue, nil
|
return bannedString == cacheBannedValue, nil
|
||||||
}
|
}
|
||||||
|
if err.Error() == simpleredis.RedisMiss {
|
||||||
|
return false, fmt.Errorf(CacheMiss)
|
||||||
|
}
|
||||||
return false, err
|
return false, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -76,9 +82,9 @@ type Client struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// New Initialize cache client.
|
// New Initialize cache client.
|
||||||
func (client *Client) New(isRedis bool, host string) {
|
func (client *Client) New(isRedis bool, host, pass, database string) {
|
||||||
if isRedis {
|
if isRedis {
|
||||||
redis.Init(host)
|
redis.Init(host, pass, database)
|
||||||
client.cache = &redisCache{}
|
client.cache = &redisCache{}
|
||||||
} else {
|
} else {
|
||||||
client.cache = &localCache{}
|
client.cache = &localCache{}
|
||||||
@@ -101,7 +107,7 @@ func (client *Client) GetDecision(clientIP string) (bool, error) {
|
|||||||
|
|
||||||
// SetDecision update the cache with the IP as key and the value banned / not banned.
|
// SetDecision update the cache with the IP as key and the value banned / not banned.
|
||||||
func (client *Client) SetDecision(clientIP string, isBanned bool, duration int64) {
|
func (client *Client) SetDecision(clientIP string, isBanned bool, duration int64) {
|
||||||
logger.Debug(fmt.Sprintf("cache:SetDecision ip:%v isBanned:%v", clientIP, isBanned))
|
logger.Debug(fmt.Sprintf("cache:SetDecision ip:%v isBanned:%v duration:%vs", clientIP, isBanned, duration))
|
||||||
var value string
|
var value string
|
||||||
if isBanned {
|
if isBanned {
|
||||||
value = cacheBannedValue
|
value = cacheBannedValue
|
||||||
|
|||||||
@@ -50,11 +50,15 @@ type Config struct {
|
|||||||
CrowdsecCapiScenarios []string `json:"crowdsecCapiScenarios,omitempty"`
|
CrowdsecCapiScenarios []string `json:"crowdsecCapiScenarios,omitempty"`
|
||||||
UpdateIntervalSeconds int64 `json:"updateIntervalSeconds,omitempty"`
|
UpdateIntervalSeconds int64 `json:"updateIntervalSeconds,omitempty"`
|
||||||
DefaultDecisionSeconds int64 `json:"defaultDecisionSeconds,omitempty"`
|
DefaultDecisionSeconds int64 `json:"defaultDecisionSeconds,omitempty"`
|
||||||
|
HTTPTimeoutSeconds int64 `json:"httpTimeoutSeconds,omitempty"`
|
||||||
ForwardedHeadersCustomName string `json:"forwardedheaderscustomheader,omitempty"`
|
ForwardedHeadersCustomName string `json:"forwardedheaderscustomheader,omitempty"`
|
||||||
ForwardedHeadersTrustedIPs []string `json:"forwardedHeadersTrustedIps,omitempty"`
|
ForwardedHeadersTrustedIPs []string `json:"forwardedHeadersTrustedIps,omitempty"`
|
||||||
ClientTrustedIPs []string `json:"clientTrustedIps,omitempty"`
|
ClientTrustedIPs []string `json:"clientTrustedIps,omitempty"`
|
||||||
RedisCacheEnabled bool `json:"redisCacheEnabled,omitempty"`
|
RedisCacheEnabled bool `json:"redisCacheEnabled,omitempty"`
|
||||||
RedisCacheHost string `json:"redisCacheHost,omitempty"`
|
RedisCacheHost string `json:"redisCacheHost,omitempty"`
|
||||||
|
RedisCachePassword string `json:"redisCachePassword,omitempty"`
|
||||||
|
RedisCachePasswordFile string `json:"redisCachePasswordFile,omitempty"`
|
||||||
|
RedisCacheDatabase string `json:"redisCacheDatabase,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
func contains(source []string, target string) bool {
|
func contains(source []string, target string) bool {
|
||||||
@@ -78,11 +82,14 @@ func New() *Config {
|
|||||||
CrowdsecLapiTLSInsecureVerify: false,
|
CrowdsecLapiTLSInsecureVerify: false,
|
||||||
UpdateIntervalSeconds: 60,
|
UpdateIntervalSeconds: 60,
|
||||||
DefaultDecisionSeconds: 60,
|
DefaultDecisionSeconds: 60,
|
||||||
|
HTTPTimeoutSeconds: 10,
|
||||||
ForwardedHeadersCustomName: "X-Forwarded-For",
|
ForwardedHeadersCustomName: "X-Forwarded-For",
|
||||||
ForwardedHeadersTrustedIPs: []string{},
|
ForwardedHeadersTrustedIPs: []string{},
|
||||||
ClientTrustedIPs: []string{},
|
ClientTrustedIPs: []string{},
|
||||||
RedisCacheEnabled: false,
|
RedisCacheEnabled: false,
|
||||||
RedisCacheHost: "redis:6379",
|
RedisCacheHost: "redis:6379",
|
||||||
|
RedisCachePassword: "",
|
||||||
|
RedisCacheDatabase: "",
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -115,7 +122,7 @@ func GetVariable(config *Config, key string) (string, error) {
|
|||||||
|
|
||||||
// ValidateParams validate all the param gave by user.
|
// ValidateParams validate all the param gave by user.
|
||||||
//
|
//
|
||||||
//nolint:gocyclo
|
//nolint:gocyclo,gocognit
|
||||||
func ValidateParams(config *Config) error {
|
func ValidateParams(config *Config) error {
|
||||||
if err := validateParamsRequired(config); err != nil {
|
if err := validateParamsRequired(config); err != nil {
|
||||||
return err
|
return err
|
||||||
@@ -128,8 +135,12 @@ func ValidateParams(config *Config) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if _, err := GetVariable(config, "RedisCachePassword"); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
if config.CrowdsecMode == AloneMode {
|
if config.CrowdsecMode == AloneMode {
|
||||||
if _, err := GetVariable(config, "CrowdsecCapiMachineId"); err != nil {
|
if _, err := GetVariable(config, "CrowdsecCapiMachineID"); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := GetVariable(config, "CrowdsecCapiPassword"); err != nil {
|
if _, err := GetVariable(config, "CrowdsecCapiPassword"); err != nil {
|
||||||
@@ -232,6 +243,7 @@ func validateParamsRequired(config *Config) error {
|
|||||||
requiredInt := map[string]int64{
|
requiredInt := map[string]int64{
|
||||||
"UpdateIntervalSeconds": config.UpdateIntervalSeconds,
|
"UpdateIntervalSeconds": config.UpdateIntervalSeconds,
|
||||||
"DefaultDecisionSeconds": config.DefaultDecisionSeconds,
|
"DefaultDecisionSeconds": config.DefaultDecisionSeconds,
|
||||||
|
"HTTPTimeoutSeconds": config.HTTPTimeoutSeconds,
|
||||||
}
|
}
|
||||||
for key, val := range requiredInt {
|
for key, val := range requiredInt {
|
||||||
if val < 1 {
|
if val < 1 {
|
||||||
|
|||||||
+30
-1
@@ -1,2 +1,31 @@
|
|||||||
# simpleredis
|
# simpleredis
|
||||||
Minimal go redis with only get, set and delete operation
|
Minimal go redis with only `get`, `set` and `delete` operation.
|
||||||
|
It supports password authentication with redis.
|
||||||
|
With **NO** external dependencies.
|
||||||
|
|
||||||
|
## Example
|
||||||
|
```go
|
||||||
|
import simpleredis "github.com/maxlerebourg/simpleredis"
|
||||||
|
|
||||||
|
var redis simpleredis.SimpleRedis
|
||||||
|
|
||||||
|
redis.Init("redis:6379", "", "") // redisHost, redisPass, redisDatabase
|
||||||
|
|
||||||
|
err := redis.Set("test", []bytes("whatever"), 60), // Set key "test" with "whatever" for 60 seconds
|
||||||
|
if err != nil {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
val, err := redis.Get("test") // get key test
|
||||||
|
if err != nil {
|
||||||
|
// err could be only redis:unreachable, redis:miss or redis:timeout available in simpleredis.RedisUnreachable
|
||||||
|
...
|
||||||
|
}
|
||||||
|
err = redis.Del("test")
|
||||||
|
if err != nil {
|
||||||
|
...
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Author
|
||||||
|
Max Lerebourg @ [Primadviz.com](https://primadviz.com)
|
||||||
|
Mathieu Hanotaux
|
||||||
+61
-25
@@ -17,10 +17,12 @@ const (
|
|||||||
RedisUnreachable = "redis:unreachable"
|
RedisUnreachable = "redis:unreachable"
|
||||||
RedisMiss = "redis:miss"
|
RedisMiss = "redis:miss"
|
||||||
RedisTimeout = "redis:timeout"
|
RedisTimeout = "redis:timeout"
|
||||||
|
RedisNoAuth = "redis:noauth"
|
||||||
|
RedisIssue = "redis:issue?"
|
||||||
)
|
)
|
||||||
|
|
||||||
// A RedisCmd is used to communicate with redis at low level using commands.
|
// A redisCmd is used to communicate with redis at low level using commands.
|
||||||
type RedisCmd struct {
|
type redisCmd struct {
|
||||||
Command string
|
Command string
|
||||||
Name string
|
Name string
|
||||||
Data []byte
|
Data []byte
|
||||||
@@ -30,7 +32,9 @@ type RedisCmd struct {
|
|||||||
|
|
||||||
// A SimpleRedis is used to communicate with redis.
|
// A SimpleRedis is used to communicate with redis.
|
||||||
type SimpleRedis struct {
|
type SimpleRedis struct {
|
||||||
redisHost string
|
host string
|
||||||
|
pass string
|
||||||
|
database string
|
||||||
}
|
}
|
||||||
|
|
||||||
func genRedisArray(params ...[]byte) []byte {
|
func genRedisArray(params ...[]byte) []byte {
|
||||||
@@ -49,12 +53,29 @@ func send(wr *textproto.Writer, method string, data []byte) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func askRedis(hostnamePort string, cmd RedisCmd, channel chan RedisCmd) {
|
func (sr *SimpleRedis) waitRedis(reader *textproto.Reader, channel chan redisCmd) {
|
||||||
dialer := net.Dialer{Timeout: 2 * time.Second}
|
for {
|
||||||
conn, err := dialer.Dial("tcp", hostnamePort)
|
select {
|
||||||
if err != nil {
|
case <-time.After(time.Second * 1):
|
||||||
channel <- RedisCmd{Error: fmt.Errorf(RedisUnreachable)}
|
channel <- redisCmd{Error: fmt.Errorf(RedisTimeout)}
|
||||||
return
|
return
|
||||||
|
default:
|
||||||
|
read, _ := reader.ReadLineBytes()
|
||||||
|
if string(read) != "+OK" {
|
||||||
|
channel <- redisCmd{Error: fmt.Errorf(RedisNoAuth)}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// breaks out of for
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sr *SimpleRedis) askRedis(cmd redisCmd, channel chan redisCmd) redisCmd {
|
||||||
|
dialer := net.Dialer{Timeout: 2 * time.Second}
|
||||||
|
conn, err := dialer.Dial("tcp", sr.host)
|
||||||
|
if err != nil {
|
||||||
|
return redisCmd{Error: fmt.Errorf(RedisUnreachable)}
|
||||||
}
|
}
|
||||||
defer func() {
|
defer func() {
|
||||||
if err := conn.Close(); err != nil {
|
if err := conn.Close(); err != nil {
|
||||||
@@ -65,6 +86,18 @@ func askRedis(hostnamePort string, cmd RedisCmd, channel chan RedisCmd) {
|
|||||||
writer := textproto.NewWriter(bufio.NewWriter(conn))
|
writer := textproto.NewWriter(bufio.NewWriter(conn))
|
||||||
reader := textproto.NewReader(bufio.NewReader(conn))
|
reader := textproto.NewReader(bufio.NewReader(conn))
|
||||||
|
|
||||||
|
if sr.pass != "" {
|
||||||
|
data := genRedisArray([]byte("AUTH"), []byte(sr.pass))
|
||||||
|
send(writer, "auth", data)
|
||||||
|
sr.waitRedis(reader, channel)
|
||||||
|
}
|
||||||
|
|
||||||
|
if sr.database != "" {
|
||||||
|
data := genRedisArray([]byte("SELECT"), []byte(sr.database))
|
||||||
|
send(writer, "select", data)
|
||||||
|
sr.waitRedis(reader, channel)
|
||||||
|
}
|
||||||
|
|
||||||
switch cmd.Command {
|
switch cmd.Command {
|
||||||
case "SET":
|
case "SET":
|
||||||
data := genRedisArray([]byte("SET"), []byte(cmd.Name), cmd.Data, []byte("EX"), []byte(fmt.Sprintf("%d", cmd.Duration)))
|
data := genRedisArray([]byte("SET"), []byte(cmd.Name), cmd.Data, []byte("EX"), []byte(fmt.Sprintf("%d", cmd.Duration)))
|
||||||
@@ -78,36 +111,39 @@ func askRedis(hostnamePort string, cmd RedisCmd, channel chan RedisCmd) {
|
|||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-time.After(time.Second * 1):
|
case <-time.After(time.Second * 1):
|
||||||
channel <- RedisCmd{Error: fmt.Errorf(RedisTimeout)}
|
return redisCmd{Error: fmt.Errorf(RedisTimeout)}
|
||||||
return
|
|
||||||
default:
|
default:
|
||||||
read, _ := reader.ReadLineBytes()
|
read, _ := reader.ReadLineBytes()
|
||||||
if string(read) != "$1" {
|
str := string(read)
|
||||||
channel <- RedisCmd{Error: fmt.Errorf(RedisMiss)}
|
if strings.Contains(str, "-NOAUTH") {
|
||||||
return
|
return redisCmd{Error: fmt.Errorf(RedisNoAuth)}
|
||||||
|
} else if str != "$1" {
|
||||||
|
return redisCmd{Error: fmt.Errorf(RedisMiss)}
|
||||||
}
|
}
|
||||||
read, _ = reader.ReadLineBytes()
|
read, _ = reader.ReadLineBytes()
|
||||||
channel <- RedisCmd{Data: read}
|
return redisCmd{Data: read}
|
||||||
return
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return redisCmd{Error: fmt.Errorf(RedisIssue)}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Init sets the redisHost used to connect to redis.
|
// Init sets the redisHost used to connect to redis.
|
||||||
func (sr *SimpleRedis) Init(redisHost string) {
|
func (sr *SimpleRedis) Init(host, pass, database string) {
|
||||||
sr.redisHost = redisHost
|
sr.host = host
|
||||||
|
sr.pass = pass
|
||||||
|
sr.database = database
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get fetches the value for key name in redis.
|
// Get fetches the value for key name in redis.
|
||||||
func (sr *SimpleRedis) Get(name string) ([]byte, error) {
|
func (sr *SimpleRedis) Get(name string) ([]byte, error) {
|
||||||
redisCmd := RedisCmd{
|
cmd := redisCmd{
|
||||||
Command: "GET",
|
Command: "GET",
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
channel := make(chan RedisCmd)
|
channel := make(chan redisCmd)
|
||||||
go askRedis(sr.redisHost, redisCmd, channel)
|
resp := sr.askRedis(cmd, channel)
|
||||||
resp := <-channel
|
|
||||||
if resp.Error != nil {
|
if resp.Error != nil {
|
||||||
return nil, resp.Error
|
return nil, resp.Error
|
||||||
}
|
}
|
||||||
@@ -116,22 +152,22 @@ func (sr *SimpleRedis) Get(name string) ([]byte, error) {
|
|||||||
|
|
||||||
// Set updates the value for key name in redis with value data for duration.
|
// Set updates the value for key name in redis with value data for duration.
|
||||||
func (sr *SimpleRedis) Set(name string, data []byte, duration int64) error {
|
func (sr *SimpleRedis) Set(name string, data []byte, duration int64) error {
|
||||||
redisCmd := RedisCmd{
|
cmd := redisCmd{
|
||||||
Command: "SET",
|
Command: "SET",
|
||||||
Name: name,
|
Name: name,
|
||||||
Data: data,
|
Data: data,
|
||||||
Duration: duration,
|
Duration: duration,
|
||||||
}
|
}
|
||||||
go askRedis(sr.redisHost, redisCmd, nil)
|
sr.askRedis(cmd, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Del removes the key name in redis.
|
// Del removes the key name in redis.
|
||||||
func (sr *SimpleRedis) Del(name string) error {
|
func (sr *SimpleRedis) Del(name string) error {
|
||||||
redisCmd := RedisCmd{
|
cmd := redisCmd{
|
||||||
Command: "DEL",
|
Command: "DEL",
|
||||||
Name: name,
|
Name: name,
|
||||||
}
|
}
|
||||||
go askRedis(sr.redisHost, redisCmd, nil)
|
sr.askRedis(cmd, nil)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
Vendored
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
# github.com/leprosus/golang-ttl-map v1.1.7
|
# github.com/leprosus/golang-ttl-map v1.1.7
|
||||||
## explicit; go 1.15
|
## explicit; go 1.15
|
||||||
github.com/leprosus/golang-ttl-map
|
github.com/leprosus/golang-ttl-map
|
||||||
# github.com/maxlerebourg/simpleredis v1.0.3
|
# github.com/maxlerebourg/simpleredis v1.0.9
|
||||||
## explicit; go 1.19
|
## explicit; go 1.19
|
||||||
github.com/maxlerebourg/simpleredis
|
github.com/maxlerebourg/simpleredis
|
||||||
|
|||||||
Reference in New Issue
Block a user