mirror of
https://github.com/aykhans/my-self-host-services.git
synced 2026-05-29 15:35:59 +00:00
add crowdsec
This commit is contained in:
@@ -50,13 +50,63 @@ Edit the generated `.env` files to fill in the required fields:
|
|||||||
- `./croc/.env`
|
- `./croc/.env`
|
||||||
- `./stalwart/.env`
|
- `./stalwart/.env`
|
||||||
- `./caddy/.env`
|
- `./caddy/.env`
|
||||||
|
- `./crowdsec/.env`
|
||||||
- `./caddy/Caddyfile.private`
|
- `./caddy/Caddyfile.private`
|
||||||
|
|
||||||
### 4. Start Services
|
### 4. Bouncer Keys (CrowdSec)
|
||||||
|
Generate two keys and write them into the matching `.env` files:
|
||||||
|
```sh
|
||||||
|
CADDY_KEY=$(openssl rand -hex 32)
|
||||||
|
FW_KEY=$(openssl rand -hex 32)
|
||||||
|
|
||||||
|
# crowdsec/.env
|
||||||
|
sed -i "s|^CROWDSEC_BOUNCER_KEY_CADDY=.*|CROWDSEC_BOUNCER_KEY_CADDY=$CADDY_KEY|" ./crowdsec/.env
|
||||||
|
sed -i "s|^CROWDSEC_BOUNCER_KEY_FW=.*|CROWDSEC_BOUNCER_KEY_FW=$FW_KEY|" ./crowdsec/.env
|
||||||
|
|
||||||
|
# caddy/.env (same value as CADDY key above)
|
||||||
|
sed -i "s|^CROWDSEC_API_KEY=.*|CROWDSEC_API_KEY=$CADDY_KEY|" ./caddy/.env
|
||||||
|
```
|
||||||
|
|
||||||
|
(Optional) get a Console enroll key from https://app.crowdsec.net and put it in `CROWDSEC_ENROLL_KEY`.
|
||||||
|
|
||||||
|
### 5. Start Services
|
||||||
Launch all services with the following command:
|
Launch all services with the following command:
|
||||||
```sh
|
```sh
|
||||||
./main.sh start
|
./main.sh start
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### 6. Host Firewall Bouncer (CrowdSec, nftables)
|
||||||
|
The Caddy bouncer protects HTTP services. Stalwart's mail ports (25/465/587/143/993/110/995/4190) bypass Caddy, so install a firewall bouncer on the host:
|
||||||
|
```sh
|
||||||
|
sudo apt install crowdsec-firewall-bouncer-nftables
|
||||||
|
```
|
||||||
|
|
||||||
|
Edit `/etc/crowdsec/bouncers/crowdsec-firewall-bouncer.yaml`:
|
||||||
|
```yaml
|
||||||
|
mode: nftables
|
||||||
|
api_url: http://127.0.0.1:18080/
|
||||||
|
api_key: <value of CROWDSEC_BOUNCER_KEY_FW from crowdsec/.env>
|
||||||
|
update_frequency: 10s
|
||||||
|
```
|
||||||
|
|
||||||
|
Enable and start:
|
||||||
|
```sh
|
||||||
|
sudo systemctl enable --now crowdsec-firewall-bouncer
|
||||||
|
```
|
||||||
|
|
||||||
|
Verify:
|
||||||
|
```sh
|
||||||
|
docker exec crowdsec cscli bouncers list # should show 'caddy' and 'firewall'
|
||||||
|
docker exec crowdsec cscli decisions list # current bans
|
||||||
|
sudo nft list ruleset | grep -A2 crowdsec # kernel-level rules in place
|
||||||
|
```
|
||||||
|
|
||||||
|
Allowlist your operator IP at any time:
|
||||||
|
```sh
|
||||||
|
docker exec crowdsec cscli allowlist create operator -d "Operator IPs"
|
||||||
|
docker exec crowdsec cscli allowlist add operator <your-public-ip>
|
||||||
|
```
|
||||||
|
|
||||||
## Stopping Services
|
## Stopping Services
|
||||||
|
|
||||||
To stop all running services, use:
|
To stop all running services, use:
|
||||||
|
|||||||
@@ -44,3 +44,7 @@ GOPKG_PROXY_DOMAIN=
|
|||||||
|
|
||||||
############# Ech0 #############
|
############# Ech0 #############
|
||||||
ECH0_DOMAIN=
|
ECH0_DOMAIN=
|
||||||
|
|
||||||
|
############# CrowdSec #############
|
||||||
|
# Same value as CROWDSEC_BOUNCER_KEY_CADDY in crowdsec/.env
|
||||||
|
CROWDSEC_API_KEY=
|
||||||
|
|||||||
+84
-21
@@ -3,11 +3,30 @@
|
|||||||
metrics {
|
metrics {
|
||||||
per_host
|
per_host
|
||||||
}
|
}
|
||||||
# log {
|
|
||||||
# output file /var/log/caddy/access.log
|
crowdsec {
|
||||||
# format json
|
api_url http://crowdsec:8080
|
||||||
# level DEBUG
|
api_key {env.CROWDSEC_API_KEY}
|
||||||
# }
|
ticker_interval 15s
|
||||||
|
appsec_url http://crowdsec:7422
|
||||||
|
}
|
||||||
|
|
||||||
|
servers {
|
||||||
|
trusted_proxies static private_ranges
|
||||||
|
client_ip_headers X-Forwarded-For
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(access-log) {
|
||||||
|
log {
|
||||||
|
output stdout
|
||||||
|
format json
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
(security) {
|
||||||
|
crowdsec
|
||||||
|
appsec
|
||||||
}
|
}
|
||||||
|
|
||||||
:2019 {
|
:2019 {
|
||||||
@@ -16,152 +35,195 @@
|
|||||||
|
|
||||||
############## grafana ##############
|
############## grafana ##############
|
||||||
{$GRAFANA_DOMAIN} {
|
{$GRAFANA_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 2048MB
|
max_size 2048MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://grafana:3000
|
reverse_proxy http://grafana:3000
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## gitea ##############
|
############## gitea ##############
|
||||||
{$GITEA_DOMAIN} {
|
{$GITEA_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 512MB
|
max_size 512MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://gitea:3000
|
reverse_proxy http://gitea:3000
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## slash ##############
|
############## slash ##############
|
||||||
{$SLASH_DOMAIN} {
|
{$SLASH_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 10MB
|
max_size 10MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://slash:5231
|
reverse_proxy http://slash:5231
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## memos ##############
|
############## memos ##############
|
||||||
{$MEMOS_DOMAIN} {
|
{$MEMOS_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 1024MB
|
max_size 1024MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://memos:5230
|
reverse_proxy http://memos:5230
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## wg-easy ##############
|
############## wg-easy ##############
|
||||||
{$WG_EASY_DOMAIN} {
|
{$WG_EASY_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 10MB
|
max_size 10MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://wg-easy:51821
|
reverse_proxy http://wg-easy:51821
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## vaultwarden ##############
|
############## vaultwarden ##############
|
||||||
{$VAULTWARDEN_DOMAIN} {
|
{$VAULTWARDEN_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 128MB
|
max_size 128MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://vaultwarden
|
reverse_proxy http://vaultwarden
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## sftpgo ##############
|
############## sftpgo ##############
|
||||||
{$SFTPGO_DOMAIN} {
|
{$SFTPGO_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 8120MB
|
max_size 8120MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://sftpgo:8080
|
reverse_proxy http://sftpgo:8080
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## glance ##############
|
############## glance ##############
|
||||||
{$GLANCE_DOMAIN} {
|
{$GLANCE_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 64MB
|
max_size 64MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://glance:8080
|
reverse_proxy http://glance:8080
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## ghost ##############
|
############## ghost ##############
|
||||||
{$GHOST_DOMAIN} {
|
{$GHOST_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 124MB
|
max_size 124MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://ghost:2368 {
|
reverse_proxy http://ghost:2368 {
|
||||||
header_up X-Forwarded-Proto {http.request.scheme}
|
header_up X-Forwarded-Proto {http.request.scheme}
|
||||||
header_up Host {http.request.host}
|
header_up Host {http.request.host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## immich ##############
|
############## immich ##############
|
||||||
{$IMMICH_DOMAIN} {
|
{$IMMICH_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 1024MB
|
max_size 1024MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://immich_server:2283 {
|
reverse_proxy http://immich_server:2283 {
|
||||||
header_up X-Forwarded-Proto {http.request.scheme}
|
header_up X-Forwarded-Proto {http.request.scheme}
|
||||||
header_up Host {http.request.host}
|
header_up Host {http.request.host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## uptime-kuma ##############
|
############## uptime-kuma ##############
|
||||||
{$UPTIME_KUMA_DOMAIN} {
|
{$UPTIME_KUMA_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 1024MB
|
max_size 1024MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://uptime_kuma:3001 {
|
reverse_proxy http://uptime_kuma:3001 {
|
||||||
header_up X-Forwarded-Proto {http.request.scheme}
|
header_up X-Forwarded-Proto {http.request.scheme}
|
||||||
header_up Host {http.request.host}
|
header_up Host {http.request.host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## stalwart ##############
|
############## stalwart ##############
|
||||||
{$STALWART_DOMAIN}, {$STALWART_AUTOCONFIG_DOMAIN}, {$STALWART_AUTODISCOVER_DOMAIN} {
|
{$STALWART_DOMAIN}, {$STALWART_AUTOCONFIG_DOMAIN}, {$STALWART_AUTODISCOVER_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 4048MB
|
max_size 4048MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://stalwart:8080 {
|
reverse_proxy http://stalwart:8080 {
|
||||||
header_up X-Forwarded-Proto {http.request.scheme}
|
header_up X-Forwarded-Proto {http.request.scheme}
|
||||||
header_up X-Forwarded-For {http.request.remote_host}
|
header_up X-Forwarded-For {http.request.remote_host}
|
||||||
header_up Host {http.request.host}
|
header_up Host {http.request.host}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## textarea ##############
|
############## textarea ##############
|
||||||
{$TEXTAREA_DOMAIN} {
|
{$TEXTAREA_DOMAIN} {
|
||||||
|
import access-log
|
||||||
|
route {
|
||||||
|
import security
|
||||||
root * /volume/textarea
|
root * /volume/textarea
|
||||||
file_server {
|
file_server {
|
||||||
browse off
|
browse off
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## gopkg-proxy ##############
|
############## gopkg-proxy ##############
|
||||||
{$GOPKG_PROXY_DOMAIN} {
|
{$GOPKG_PROXY_DOMAIN} {
|
||||||
|
import access-log
|
||||||
request_body {
|
request_body {
|
||||||
max_size 2MB
|
max_size 2MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://gopkg_proxy:8421
|
reverse_proxy http://gopkg_proxy:8421
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
############## ech0 ##############
|
############## ech0 ##############
|
||||||
{$ECH0_DOMAIN} {
|
{$ECH0_DOMAIN} {
|
||||||
|
import access-log
|
||||||
header -Server
|
header -Server
|
||||||
|
|
||||||
request_body {
|
request_body {
|
||||||
max_size 5MB
|
max_size 5MB
|
||||||
}
|
}
|
||||||
|
route {
|
||||||
|
import security
|
||||||
reverse_proxy http://ech0:8421 {
|
reverse_proxy http://ech0:8421 {
|
||||||
# wheader_up -X-Forwarded-For
|
|
||||||
header_up -X-Forwarded-Host
|
header_up -X-Forwarded-Host
|
||||||
header_up -X-Forwarded-Proto
|
header_up -X-Forwarded-Proto
|
||||||
header_up -Via
|
header_up -Via
|
||||||
@@ -171,5 +233,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
import Caddyfile.private
|
import Caddyfile.private
|
||||||
|
|||||||
@@ -0,0 +1,7 @@
|
|||||||
|
FROM caddy:2-builder-alpine AS builder
|
||||||
|
RUN xcaddy build \
|
||||||
|
--with github.com/hslatman/caddy-crowdsec-bouncer/http \
|
||||||
|
--with github.com/hslatman/caddy-crowdsec-bouncer/appsec
|
||||||
|
|
||||||
|
FROM caddy:2-alpine
|
||||||
|
COPY --from=builder /usr/bin/caddy /usr/bin/caddy
|
||||||
@@ -6,11 +6,9 @@ networks:
|
|||||||
|
|
||||||
services:
|
services:
|
||||||
caddy:
|
caddy:
|
||||||
image: caddy:2-alpine
|
build: .
|
||||||
container_name: caddy
|
container_name: caddy
|
||||||
restart: unless-stopped
|
restart: unless-stopped
|
||||||
labels:
|
|
||||||
- "com.centurylinklabs.watchtower.enable=true"
|
|
||||||
networks:
|
networks:
|
||||||
- caddy
|
- caddy
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
############# Bouncer keys #############
|
||||||
|
# Generate with: openssl rand -hex 32
|
||||||
|
# These are PRE-SEEDED into CrowdSec on first boot. Use the same value here
|
||||||
|
# and in caddy/.env (CADDY) and the host firewall bouncer config (FW).
|
||||||
|
CROWDSEC_BOUNCER_KEY_CADDY=
|
||||||
|
CROWDSEC_BOUNCER_KEY_FW=
|
||||||
|
|
||||||
|
############# Console enrollment #############
|
||||||
|
# Enroll key from https://app.crowdsec.net (free).
|
||||||
|
# Leave blank to run without Console (no community blocklist subscriptions).
|
||||||
|
CROWDSEC_ENROLL_KEY=
|
||||||
|
CROWDSEC_ENROLL_INSTANCE_NAME=
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
appsec_configs:
|
||||||
|
- crowdsecurity/appsec-default
|
||||||
|
labels:
|
||||||
|
type: appsec
|
||||||
|
listen_addr: 0.0.0.0:7422
|
||||||
|
source: appsec
|
||||||
|
name: caddy-appsec
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
source: docker
|
||||||
|
container_name:
|
||||||
|
- caddy
|
||||||
|
labels:
|
||||||
|
type: caddy
|
||||||
|
follow_stdout: true
|
||||||
|
follow_stderr: true
|
||||||
|
check_interval: 5s
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
source: docker
|
||||||
|
container_name:
|
||||||
|
- gitea
|
||||||
|
labels:
|
||||||
|
type: gitea
|
||||||
|
follow_stdout: true
|
||||||
|
follow_stderr: true
|
||||||
|
check_interval: 5s
|
||||||
|
---
|
||||||
|
source: docker
|
||||||
|
container_name:
|
||||||
|
- vaultwarden
|
||||||
|
labels:
|
||||||
|
type: vaultwarden
|
||||||
|
follow_stdout: true
|
||||||
|
follow_stderr: true
|
||||||
|
check_interval: 5s
|
||||||
|
---
|
||||||
|
source: docker
|
||||||
|
container_name:
|
||||||
|
- stalwart
|
||||||
|
labels:
|
||||||
|
type: stalwart
|
||||||
|
follow_stdout: true
|
||||||
|
follow_stderr: true
|
||||||
|
check_interval: 5s
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
source: journalctl
|
||||||
|
journalctl_filter:
|
||||||
|
- "_SYSTEMD_UNIT=ssh.service"
|
||||||
|
labels:
|
||||||
|
type: syslog
|
||||||
@@ -0,0 +1,3 @@
|
|||||||
|
/db/*
|
||||||
|
/config/*
|
||||||
|
!.gitkeep
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
networks:
|
||||||
|
caddy:
|
||||||
|
name: caddy
|
||||||
|
driver: bridge
|
||||||
|
external: true
|
||||||
|
|
||||||
|
services:
|
||||||
|
crowdsec:
|
||||||
|
image: crowdsecurity/crowdsec:latest-debian
|
||||||
|
container_name: crowdsec
|
||||||
|
restart: unless-stopped
|
||||||
|
labels:
|
||||||
|
- "com.centurylinklabs.watchtower.enable=true"
|
||||||
|
environment:
|
||||||
|
TZ: "Etc/UTC"
|
||||||
|
COLLECTIONS: >-
|
||||||
|
crowdsecurity/linux
|
||||||
|
crowdsecurity/caddy
|
||||||
|
crowdsecurity/http-cve
|
||||||
|
crowdsecurity/sshd
|
||||||
|
crowdsecurity/whitelist-good-actors
|
||||||
|
crowdsecurity/appsec-virtual-patching
|
||||||
|
crowdsecurity/appsec-generic-rules
|
||||||
|
LePresidente/gitea
|
||||||
|
Dominic-Wagner/vaultwarden
|
||||||
|
BOUNCER_KEY_caddy: "${CROWDSEC_BOUNCER_KEY_CADDY}"
|
||||||
|
BOUNCER_KEY_firewall: "${CROWDSEC_BOUNCER_KEY_FW}"
|
||||||
|
ENROLL_KEY: "${CROWDSEC_ENROLL_KEY:-}"
|
||||||
|
ENROLL_INSTANCE_NAME: "${CROWDSEC_ENROLL_INSTANCE_NAME:-aykhans-prod}"
|
||||||
|
ports:
|
||||||
|
- "127.0.0.1:18080:8080"
|
||||||
|
networks:
|
||||||
|
- caddy
|
||||||
|
volumes:
|
||||||
|
- ./data/db:/var/lib/crowdsec/data
|
||||||
|
- ./data/config:/etc/crowdsec
|
||||||
|
- ./acquis.d:/etc/crowdsec/acquis.d:ro
|
||||||
|
- ./parsers/s00-raw/stalwart-logs.yaml:/etc/crowdsec/parsers/s00-raw/stalwart-logs.yaml:ro
|
||||||
|
- ./parsers/s01-parse/stalwart-logs-extended.yaml:/etc/crowdsec/parsers/s01-parse/stalwart-logs-extended.yaml:ro
|
||||||
|
- ./parsers/s02-enrich/whitelist-trusted.yaml:/etc/crowdsec/parsers/s02-enrich/whitelist-trusted.yaml:ro
|
||||||
|
- ./scenarios/stalwart-smtp-bruteforce.yaml:/etc/crowdsec/scenarios/stalwart-smtp-bruteforce.yaml:ro
|
||||||
|
- ./scenarios/stalwart-auth-bruteforce.yaml:/etc/crowdsec/scenarios/stalwart-auth-bruteforce.yaml:ro
|
||||||
|
- /var/log/journal:/var/log/journal:ro
|
||||||
|
- /run/log/journal:/run/log/journal:ro
|
||||||
|
- /etc/machine-id:/etc/machine-id:ro
|
||||||
|
- /var/run/docker.sock:/var/run/docker.sock:ro
|
||||||
|
logging:
|
||||||
|
driver: "json-file"
|
||||||
|
options:
|
||||||
|
max-size: "100m"
|
||||||
|
max-file: "3"
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
name: stalwart/parse-logs
|
||||||
|
description: Raw parser for Stalwart logs
|
||||||
|
stage: s00-raw
|
||||||
|
onsuccess: next_stage
|
||||||
|
filter: "evt.Parsed.program == 'stalwart' || evt.Line.Labels.type == 'stalwart'"
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
- grok:
|
||||||
|
apply_on: Line.Raw
|
||||||
|
pattern: '^%{TIMESTAMP_ISO8601:timestamp}\s+%{WORD:log_level}\s+%{DATA:message_text}\s*\(%{DATA:event_type}\)\s*(%{GREEDYDATA:kvpairs})?$'
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
name: stalwart/parse-extended
|
||||||
|
description: Parse Stalwart logs including key fields without kv parser
|
||||||
|
stage: s01-parse
|
||||||
|
onsuccess: next_stage
|
||||||
|
filter: "evt.Parsed.program == 'stalwart' || evt.Line.Labels.type == 'stalwart'"
|
||||||
|
|
||||||
|
nodes:
|
||||||
|
- grok:
|
||||||
|
apply_on: Line.Raw
|
||||||
|
pattern: '^%{TIMESTAMP_ISO8601:timestamp}\s+%{WORD:log_level}\s+%{DATA:message_text}\s+\(%{DATA:event_type}\)\s*(?:listenerId\s*=\s*"%{DATA:listenerId}",\s*)?(?:localPort\s*=\s*%{INT:localPort},\s*)?(?:remoteIp\s*=\s*%{IP:remoteIp},\s*)?(?:remotePort\s*=\s*%{INT:remotePort},\s*)?(?:reason\s*=\s*"%{DATA:reason}")?.*$'
|
||||||
|
statics:
|
||||||
|
- meta: source_ip
|
||||||
|
expression: evt.Parsed.remoteIp
|
||||||
|
- meta: service
|
||||||
|
value: stalwart
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
name: aykhans/whitelist-trusted
|
||||||
|
description: Trusted operator IPs and Docker private network ranges
|
||||||
|
whitelist:
|
||||||
|
reason: "operator + internal docker networks"
|
||||||
|
ip:
|
||||||
|
- "127.0.0.1"
|
||||||
|
- "::1"
|
||||||
|
cidr:
|
||||||
|
- "10.0.0.0/8"
|
||||||
|
- "172.16.0.0/12"
|
||||||
|
- "192.168.0.0/16"
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
type: leaky
|
||||||
|
name: aykhans/stalwart-auth-bruteforce
|
||||||
|
description: Detect SMTP/IMAP/POP3 authentication brute force on Stalwart
|
||||||
|
filter: |
|
||||||
|
evt.Parsed.event_type == "auth.failed"
|
||||||
|
groupby: evt.Meta.source_ip
|
||||||
|
capacity: 3
|
||||||
|
leakspeed: 10m
|
||||||
|
blackhole: 1h
|
||||||
|
labels:
|
||||||
|
type: bruteforce
|
||||||
|
service: stalwart
|
||||||
|
remediation: true
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
type: leaky
|
||||||
|
name: stalwart/smtp-bruteforce
|
||||||
|
description: Detect SMTP bruteforce and scanners on Stalwart logs
|
||||||
|
filter: |
|
||||||
|
evt.Parsed.event_type in [
|
||||||
|
"smtp.invalid-ehlo",
|
||||||
|
"smtp.auth-not-allowed",
|
||||||
|
"smtp.auth-mechanism-not-supported"
|
||||||
|
]
|
||||||
|
groupby: evt.Meta.source_ip
|
||||||
|
capacity: 5
|
||||||
|
leakspeed: 10m
|
||||||
|
blackhole: 1h
|
||||||
|
labels:
|
||||||
|
type: attack
|
||||||
|
service: smtp
|
||||||
|
remediation: true
|
||||||
@@ -64,6 +64,7 @@ generate_env_files() {
|
|||||||
cp --update=none ./uptime_kuma/.env.example ./uptime_kuma/.env
|
cp --update=none ./uptime_kuma/.env.example ./uptime_kuma/.env
|
||||||
cp --update=none ./croc/.env.example ./croc/.env
|
cp --update=none ./croc/.env.example ./croc/.env
|
||||||
cp --update=none ./stalwart/.env.example ./stalwart/.env
|
cp --update=none ./stalwart/.env.example ./stalwart/.env
|
||||||
|
cp --update=none ./crowdsec/.env.example ./crowdsec/.env
|
||||||
cp --update=none ./caddy/Caddyfile.private.example ./caddy/Caddyfile.private
|
cp --update=none ./caddy/Caddyfile.private.example ./caddy/Caddyfile.private
|
||||||
print_success ".env files generated."
|
print_success ".env files generated."
|
||||||
}
|
}
|
||||||
@@ -190,8 +191,17 @@ start_services() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Starting crowdsec..."
|
||||||
|
$DOCKER_COMPOSE_COMMAND -f ./crowdsec/docker-compose.yaml up --pull always -d
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Crowdsec started successfully."
|
||||||
|
else
|
||||||
|
print_error "failed to start Crowdsec!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Starting caddy..."
|
echo "Starting caddy..."
|
||||||
$DOCKER_COMPOSE_COMMAND -f ./caddy/docker-compose.yaml up --pull always -d
|
$DOCKER_COMPOSE_COMMAND -f ./caddy/docker-compose.yaml up --pull always --build -d
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
print_success "Caddy started successfully."
|
print_success "Caddy started successfully."
|
||||||
else
|
else
|
||||||
@@ -368,6 +378,15 @@ stop_services() {
|
|||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
|
echo "Stopping crowdsec..."
|
||||||
|
$DOCKER_COMPOSE_COMMAND -f ./crowdsec/docker-compose.yaml down
|
||||||
|
if [ $? -eq 0 ]; then
|
||||||
|
print_success "Crowdsec stopped successfully."
|
||||||
|
else
|
||||||
|
print_error "failed to stop Crowdsec!"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
echo "Stopping watchtower..."
|
echo "Stopping watchtower..."
|
||||||
$DOCKER_COMPOSE_COMMAND -f ./watchtower/docker-compose.yaml down
|
$DOCKER_COMPOSE_COMMAND -f ./watchtower/docker-compose.yaml down
|
||||||
if [ $? -eq 0 ]; then
|
if [ $? -eq 0 ]; then
|
||||||
|
|||||||
Reference in New Issue
Block a user