Files
home-docker/terraform/crowdsec.tf
T
2025-10-05 09:20:01 +11:00

355 lines
9.8 KiB
Terraform

# CrowdSec Security Engine Deployment
# Monitors and protects against malicious traffic on NGINX Ingress
resource "kubernetes_namespace" "crowdsec" {
metadata {
name = "crowdsec"
}
}
# PersistentVolume for LAPI config on host path
resource "kubernetes_persistent_volume" "crowdsec_lapi_config" {
metadata {
name = "crowdsec-lapi-config-pv"
}
spec {
capacity = {
storage = "1Gi"
}
access_modes = ["ReadWriteOnce"]
storage_class_name = "local-path"
persistent_volume_source {
host_path {
path = "/mnt/k8s/crowdsec/lapi-config"
type = "DirectoryOrCreate"
}
}
node_affinity {
required {
node_selector_term {
match_expressions {
key = "kubernetes.io/hostname"
operator = "In"
values = ["server-3"]
}
}
}
}
}
}
# PersistentVolume for LAPI data on host path
resource "kubernetes_persistent_volume" "crowdsec_lapi_data" {
metadata {
name = "crowdsec-lapi-data-pv"
}
spec {
capacity = {
storage = "2Gi"
}
access_modes = ["ReadWriteOnce"]
storage_class_name = "local-path"
persistent_volume_source {
host_path {
path = "/mnt/k8s/crowdsec/lapi-data"
type = "DirectoryOrCreate"
}
}
node_affinity {
required {
node_selector_term {
match_expressions {
key = "kubernetes.io/hostname"
operator = "In"
values = ["server-3"]
}
}
}
}
}
}
resource "helm_release" "crowdsec" {
name = "crowdsec"
repository = "https://crowdsecurity.github.io/helm-charts"
chart = "crowdsec"
version = "0.20.0"
namespace = kubernetes_namespace.crowdsec.metadata[0].name
wait = true
timeout = 600
values = [
yamlencode({
container_runtime = "containerd"
image = {
repository = "crowdsecurity/crowdsec"
tag = "v1.7.0"
pullPolicy = "IfNotPresent"
}
lapi = {
enabled = true
replicas = 1
resources = {
limits = {
memory = "512Mi"
cpu = "500m"
}
requests = {
memory = "256Mi"
cpu = "100m"
}
}
persistentVolume = {
config = {
enabled = true
accessModes = ["ReadWriteOnce"]
storageClassName = "local-path"
size = "1Gi"
}
data = {
enabled = true
accessModes = ["ReadWriteOnce"]
storageClassName = "local-path"
size = "2Gi"
}
}
metrics = {
enabled = true
}
env = [
{
name = "ENROLL_KEY"
value = "cmgcsgn14000e02l1xmme5qnm"
},
{
name = "ENROLL_INSTANCE_NAME"
value = "junv-home-k3s-cluster"
},
{
name = "ENROLL_TAGS"
value = "junv k3s homelab nginx"
}
]
}
agent = {
enabled = true
isDeployment = false
resources = {
limits = {
memory = "512Mi"
cpu = "500m"
}
requests = {
memory = "256Mi"
cpu = "100m"
}
}
hostVarLog = true
metrics = {
enabled = true
}
# Monitor NGINX Ingress Controller logs and SSH logs
acquisition = [
{
namespace = "ingress-nginx"
podName = "ingress-nginx-controller-*"
program = "nginx"
}
]
# Additional acquisition for host SSH logs and firewall logs
additionalAcquisition = [
{
source = "file"
filenames = ["/var/log/auth.log"]
labels = {
type = "syslog"
}
},
{
source = "file"
filenames = ["/var/log/kern.log", "/var/log/syslog"]
labels = {
type = "syslog"
}
}
]
# Install NGINX, SSH, and iptables/firewall collections
env = [
{
name = "COLLECTIONS"
value = "crowdsecurity/nginx crowdsecurity/base-http-scenarios crowdsecurity/linux crowdsecurity/sshd crowdsecurity/iptables crowdsecurity/endlessh"
}
]
persistentVolume = {
config = {
enabled = false
}
}
}
config = {
"config.yaml.local" = <<-EOT
api:
server:
auto_registration:
enabled: true
token: "$${REGISTRATION_TOKEN}"
allowed_ranges:
- "127.0.0.1/32"
- "10.0.0.0/8"
- "172.16.0.0/12"
- "192.168.0.0/16"
log_level: info
listen_uri: 0.0.0.0:8080
client:
insecure_skip_verify: false
common:
daemonize: false
log_media: stdout
log_level: info
EOT
"profiles.yaml.local" = <<-EOT
name: default_ip_remediation
filters:
- Alert.Remediation == true && Alert.GetScope() == "Ip"
decisions:
- type: ban
duration: 4h
on_success: break
EOT
}
})
]
depends_on = [
kubernetes_namespace.crowdsec,
kubernetes_persistent_volume.crowdsec_lapi_config,
kubernetes_persistent_volume.crowdsec_lapi_data
]
}
# Wait for CrowdSec to be ready and create bouncer
resource "null_resource" "crowdsec_bouncer_setup" {
depends_on = [helm_release.crowdsec]
provisioner "local-exec" {
command = <<-EOT
echo "Waiting for CrowdSec LAPI to be ready..."
kubectl wait --for=condition=ready pod -l app.kubernetes.io/name=crowdsec,app.kubernetes.io/component=lapi -n crowdsec --timeout=300s
echo "Creating NGINX bouncer..."
BOUNCER_KEY=$(kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli bouncers add nginx-ingress-bouncer -o raw 2>/dev/null || echo "")
if [ -z "$BOUNCER_KEY" ]; then
echo "Bouncer may already exist, retrieving existing key..."
kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli bouncers list
else
echo "Bouncer created successfully!"
echo "Creating Kubernetes secret with bouncer key..."
kubectl create secret generic crowdsec-bouncer-tls -n ingress-nginx \
--from-literal=crowdsec-api=http://crowdsec-service.crowdsec.svc.cluster.local:8080 \
--from-literal=crowdsec-key="$BOUNCER_KEY" \
--dry-run=client -o yaml | kubectl apply -f -
echo "Bouncer API Key: $BOUNCER_KEY"
fi
echo ""
echo "CrowdSec setup complete!"
echo "To view status: kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli metrics"
EOT
}
triggers = {
crowdsec_version = helm_release.crowdsec.version
}
}
# Setup iptables logging for port scan detection (optional but recommended)
resource "ssh_resource" "iptables_logging" {
depends_on = [helm_release.crowdsec]
host = local.ssh_host
user = local.ssh_user
port = local.ssh_port
private_key = local.ssh_private_key
timeout = "5m"
when = "create"
commands = [
"echo 'Setting up iptables logging for CrowdSec...'",
# Create chain for suspicious activity logging
"sudo iptables -N CROWDSEC_SUSPICIOUS 2>/dev/null || true",
"sudo iptables -F CROWDSEC_SUSPICIOUS 2>/dev/null || true",
# IMPORTANT: Exclude local network (192.168.1.0/24) from ALL port scan detection
"sudo iptables -I INPUT 1 -s 192.168.1.0/24 -j ACCEPT || true",
# Track connection attempts for port scan detection (only for external traffic)
"sudo iptables -A INPUT -p tcp -m state --state NEW -m recent --set --name portscan || true",
# If more than 10 connection attempts in 60 seconds from external IPs, log it as suspicious
"sudo iptables -A INPUT -p tcp -m state --state NEW -m recent --update --seconds 60 --hitcount 10 --name portscan -j CROWDSEC_SUSPICIOUS || true",
# Log suspicious activity
"sudo iptables -A CROWDSEC_SUSPICIOUS -j LOG --log-prefix 'iptables_SCAN: ' --log-level 4 --log-tcp-options --log-ip-options || true",
# Drop the suspicious packets
"sudo iptables -A CROWDSEC_SUSPICIOUS -j DROP || true",
# Log invalid packets from external sources only (often used in attacks)
"sudo iptables -A INPUT -m state --state INVALID -j LOG --log-prefix 'iptables_INVALID: ' --log-level 4 || true",
"sudo iptables -A INPUT -m state --state INVALID -j DROP || true",
# Install iptables-persistent to save rules
"sudo DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent 2>/dev/null || true",
# Save the rules
"sudo mkdir -p /etc/iptables",
"sudo iptables-save | sudo tee /etc/iptables/rules.v4 > /dev/null",
"echo '✅ iptables logging configured!'",
"echo 'Port scans and suspicious activity will now be logged and detected by CrowdSec'"
]
}
output "crowdsec_info" {
value = <<-EOT
CrowdSec deployed successfully!
Monitoring:
- ✅ NGINX Ingress (ingress-nginx namespace)
- ✅ SSH attempts (port 22422)
- ✅ Port scans (iptables logging enabled)
- ✅ System logs
Commands:
- Status: kubectl get pods -n crowdsec
- Metrics: kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli metrics
- Decisions: kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli decisions list
- Bouncers: kubectl exec -n crowdsec deployment/crowdsec-lapi -- cscli bouncers list
Storage: /mnt/k8s/crowdsec/ on server-3 (192.168.1.2)
Web Dashboard: https://app.crowdsec.net
Quick view: ./view_crowdsec.sh
EOT
}