diff --git a/stacks/crowdsec/modules/crowdsec/firewall_bouncer.tf b/stacks/crowdsec/modules/crowdsec/firewall_bouncer.tf index c161486d..dee3d0fe 100644 --- a/stacks/crowdsec/modules/crowdsec/firewall_bouncer.tf +++ b/stacks/crowdsec/modules/crowdsec/firewall_bouncer.tf @@ -161,11 +161,13 @@ resource "kubernetes_daemon_set_v1" "firewall_bouncer" { curl -fsSL "${local.firewall_bouncer_tgz_url}" -o /tmp/fb.tgz # Archive layout (verified @ v0.0.34): a single versioned top dir # `crowdsec-firewall-bouncer-vX.Y.Z/` containing the binary plus - # config/, scripts/, install.sh. Strip that dir and extract ONLY the - # binary — the `*/crowdsec-firewall-bouncer` glob matches one path - # segment after the top dir, so config/...yaml is NOT pulled. - tar -xzf /tmp/fb.tgz -C /opt/firewall-bouncer --strip-components=1 \ - --wildcards '*/crowdsec-firewall-bouncer' + # config/, scripts/, install.sh. The curl image is BusyBox, whose + # tar lacks GNU --wildcards/--strip-components selection — so extract + # everything to a scratch dir, then cp ONLY the binary out via a + # shell glob (`*/` matches the single versioned top dir). + mkdir -p /tmp/fb-extract + tar -xzf /tmp/fb.tgz -C /tmp/fb-extract + cp /tmp/fb-extract/*/crowdsec-firewall-bouncer ${local.firewall_bouncer_bin_path} chmod +x ${local.firewall_bouncer_bin_path} echo "Fetched: $(ls -l ${local.firewall_bouncer_bin_path})" EOT diff --git a/stacks/rybbit/crowdsec_edge.tf b/stacks/rybbit/crowdsec_edge.tf index 0c1ff5dc..08683ff9 100644 --- a/stacks/rybbit/crowdsec_edge.tf +++ b/stacks/rybbit/crowdsec_edge.tf @@ -99,26 +99,49 @@ resource "cloudflare_list" "crowdsec_captcha" { # config.tfvars). Hardcoded here (with the conventional marker comment) because # the rybbit stack does not import the ingress_factory module. # ----------------------------------------------------------------------------- -resource "cloudflare_ruleset" "crowdsec" { - zone_id = "fd2c5dd4efe8fe38958944e74d0ced6d" # cloudflare_zone_id (viktorbarzin.me) - name = "crowdsec-ip-enforcement" - description = "Block/challenge IPs CrowdSec flagged (synced from LAPI into CF IP Lists)" - kind = "zone" - phase = "http_request_firewall_custom" +# Cloudflare allows only ONE entrypoint ruleset per zone+phase, and the zone +# already has the stock `default` http_request_firewall_custom ruleset (created +# out-of-band, id 106a1342bc88454ea59c47ad3431fe0e). Creating a second one fails +# the singleton constraint, so we IMPORT the existing ruleset and manage all of +# its rules here: our CrowdSec ban/captcha rules FIRST, and the pre-existing +# (currently disabled) skip rule preserved verbatim below it. +import { + to = cloudflare_ruleset.crowdsec + id = "fd2c5dd4efe8fe38958944e74d0ced6d/106a1342bc88454ea59c47ad3431fe0e" +} +resource "cloudflare_ruleset" "crowdsec" { + zone_id = "fd2c5dd4efe8fe38958944e74d0ced6d" # cloudflare_zone_id (viktorbarzin.me) + name = "default" + kind = "zone" + phase = "http_request_firewall_custom" + + # CrowdSec ban — evaluated FIRST so a banned IP is blocked before anything else. rules { action = "block" expression = "(ip.src in $crowdsec_ban)" description = "CrowdSec: block banned IPs" enabled = true } - + # CrowdSec captcha — managed challenge for flagged IPs. rules { action = "managed_challenge" expression = "(ip.src in $crowdsec_captcha)" - description = "CrowdSec: managed-challenge captcha'd IPs" + description = "CrowdSec: challenge flagged IPs" enabled = true } + # Pre-existing rule, imported and preserved verbatim (currently disabled). + rules { + action = "skip" + expression = "(http.host contains \"viktorbarzin.me\")" + description = "skip" + enabled = false + action_parameters { + phases = ["http_ratelimit", "http_request_firewall_managed", "http_request_sbfm"] + products = ["uaBlock", "bic", "hot", "securityLevel", "rateLimit", "waf", "zoneLockdown"] + ruleset = "current" + } + } } # -----------------------------------------------------------------------------