Files
home-docker/adhoc-config/crowdsec-nginx-sync.yaml
T
2026-07-20 16:15:57 +10:00

142 lines
4.9 KiB
YAML

---
# CrowdSec → NGINX Ingress IP Block Sync
#
# Runs every minute, reads all active CrowdSec ban decisions,
# and updates the NGINX Ingress Controller ConfigMap's block-cidrs field.
# This propagates CrowdSec's automated threat detection into NGINX's global IP blocklist.
# VPN/client IPs in ALLOWLIST below are excluded from the NGINX blocklist.
apiVersion: v1
kind: ServiceAccount
metadata:
name: crowdsec-nginx-sync
namespace: crowdsec
---
# Allow the sync job to read CrowdSec LAPI pod exec (to run cscli)
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRole
metadata:
name: crowdsec-nginx-sync
rules:
# Exec into CrowdSec LAPI pod to run cscli
- apiGroups: [""]
resources: ["pods/exec"]
verbs: ["create"]
- apiGroups: [""]
resources: ["pods"]
verbs: ["get", "list"]
# Read/patch the NGINX Ingress ConfigMap
- apiGroups: [""]
resources: ["configmaps"]
verbs: ["get", "patch"]
---
apiVersion: rbac.authorization.k8s.io/v1
kind: ClusterRoleBinding
metadata:
name: crowdsec-nginx-sync
roleRef:
apiGroup: rbac.authorization.k8s.io
kind: ClusterRole
name: crowdsec-nginx-sync
subjects:
- kind: ServiceAccount
name: crowdsec-nginx-sync
namespace: crowdsec
---
apiVersion: batch/v1
kind: CronJob
metadata:
name: crowdsec-nginx-sync
namespace: crowdsec
spec:
schedule: "* * * * *" # every minute
concurrencyPolicy: Forbid # skip if previous run is still going
successfulJobsHistoryLimit: 3
failedJobsHistoryLimit: 3
jobTemplate:
spec:
activeDeadlineSeconds: 55 # must finish before next run
template:
spec:
serviceAccountName: crowdsec-nginx-sync
restartPolicy: Never
containers:
- name: sync
image: bitnami/kubectl:latest
command:
- /bin/sh
- -c
- |
set -e
# Find the CrowdSec LAPI pod
LAPI_POD=$(kubectl get pods -n crowdsec \
-l "type=lapi" \
-o jsonpath='{.items[0].metadata.name}')
if [ -z "$LAPI_POD" ]; then
echo "ERROR: CrowdSec LAPI pod not found"
exit 1
fi
echo "Using LAPI pod: $LAPI_POD"
# Fetch all active ban decisions in raw CSV format:
# id,source,ip,reason,action,country,as,events_count,expiration,simulated,alert_id
# The ip column has format "Ip:1.2.3.4" or "Range:1.2.3.0/24"
RAW=$(kubectl exec -n crowdsec "$LAPI_POD" -- \
cscli decisions list -t ban -o raw 2>/dev/null || true)
# Skip CSV header, extract column 3, strip the "Ip:" / "Range:" prefix
CIDRS=$(echo "$RAW" | \
tail -n +2 | \
cut -d',' -f3 | \
sed 's/^[^:]*://g' | \
grep -v '^$' | \
tr '\n' ',' | \
sed 's/,$//')
echo "Active ban count: $(echo "$RAW" | tail -n +2 | grep -c '.' || echo 0)"
# Read any manually managed IPs from block-cidrs-manual key in the same ConfigMap.
# Edit that key to permanently block IPs that CrowdSec won't remove.
MANUAL=$(kubectl get configmap ingress-nginx-controller \
-n ingress-nginx \
-o jsonpath='{.data.block-cidrs-manual}' 2>/dev/null || true)
# VPN/client IPs that should never be written into NGINX block-cidrs.
ALLOWLIST="195.86.27.8,14.137.198.99"
# Merge CrowdSec bans + manual list, remove allowlisted entries, deduplicate
ALL=$(printf '%s,%s' "$CIDRS" "$MANUAL" | \
tr ',' '\n' | \
grep -v '^$' | \
grep -v '^140\.82\.115\.' | \
awk -v allowlist="$ALLOWLIST" 'BEGIN { split(allowlist, ips, ","); for (i in ips) allow[ips[i]] = 1 } !allow[$0]' | \
sort -u | \
tr '\n' ',' | \
sed 's/,$//')
echo "CrowdSec bans: ${CIDRS:-<none>}"
echo "Manual bans: ${MANUAL:-<none>}"
echo "Allowlist: ${ALLOWLIST:-<none>}"
echo "Merged total: ${ALL:-<none>}"
# Patch the NGINX Ingress ConfigMap (JSON merge patch — only updates block-cidrs)
kubectl patch configmap ingress-nginx-controller \
-n ingress-nginx \
--type merge \
-p "{\"data\":{\"block-cidrs\":\"${ALL}\"}}"
echo "ConfigMap patched successfully"
resources:
requests:
cpu: 10m
memory: 32Mi
limits:
cpu: 100m
memory: 64Mi