Compare commits

..
Author SHA1 Message Date
maxlerebourg 022747488c Adjust lease duration to prevent cache update conflicts
Updated lease duration logic to ensure a minimum of 1 second.
2026-08-07 12:12:59 +02:00
mhx 0ed8154340 🐛 keep the stream lease alive when updateIntervalSeconds is 1
handleStreamCache takes a lease so a single node polls LAPI per interval,
and stores it for updateInterval-1 seconds. The e2e stream scenario now
sets updateIntervalSeconds to 1, which makes that a 0 second duration:
golang-ttl-map returns early on a zero ttl (map.go:114) and redis rejects
a non positive EX, so the lease is never stored and the guard silently
does nothing.

Floor the duration at 1 second. At an interval of 1 the lease can survive
a tick that fires slightly early and cost one skipped poll, which is far
better than every node polling every tick against a shared redis.
2026-08-03 13:47:43 +02:00
github-actions[bot]andgithub-actions[bot] bef5dfaadb 🔖 release v1.7.1 (#367)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
2026-07-31 13:05:36 +02:00
99cf9712f4 cicd: bump the version before tagging so releases report their own version (#365)
*  cicd: bump the version before tagging instead of after

The version reported to the Crowdsec LAPI lives in version.go, so it must
be correct in the very commit the tag points at. Every mechanism so far
updated it *after* the tag existed, which cannot work:

- release.yml ran on `release: published` and force-moved the tag. It also
  failed on all four of its runs and was removed in #360.
- The Renovate customManager on version.go uses the github-tags datasource,
  so it can only propose vX once vX is already tagged. The bump always lands
  after the tag.

Result: v1.7.0 is tagged at a commit reading v1.6.0 (#363), same shape as
the earlier #322.

Replace both with a two-step flow that bumps first and tags last, so the
released source always matches its tag.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

* 🍱 reduce loc + remove claude code comment

* 🍱 remove useless spellcheck disable

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: maxlerebourg <maxlerebourg@gmail.com>
2026-07-30 20:18:15 +02:00
maxlerebourgandRenovate Bot ed4a9e8262 ⬆️ renovate: Update all (#362)
Co-authored-by: Renovate Bot <22881669+maxlerebourg@users.noreply.github.com>
2026-07-27 21:09:55 +02:00
maxlerebourg f6ef95cf38 🐛 fix default value for CrowdsecAppsecUnreadableBodyBlock (#361) 2026-07-27 08:53:32 +02:00
maxlerebourgandRenovate Bot 9daba9739c ⬆️ renovate: Update all (#350)
Co-authored-by: Renovate Bot <22881669+maxlerebourg@users.noreply.github.com>
2026-07-26 17:06:02 +02:00
maxlerebourg e98b8ed5ba Renovate update version.go (#360)
*  Renovate update version.go

* 🍱 renovate every day
2026-07-26 17:05:43 +02:00
20 changed files with 174 additions and 42 deletions
+83
View File
@@ -0,0 +1,83 @@
name: Release (1/2) Prepare
# Step 1 of the release process: bump pluginVersion *before* the tag exists.
#
# The version reported to the Crowdsec LAPI lives in version.go, so it has to
# be correct in the very commit the tag points at. Anything that patches
# version.go after the release is published is too late: Traefik's plugin
# service caches the plugin archive per module+version, so users keep the
# source that was there when the tag was first resolved (see #322, #363).
#
# This workflow opens a "release" PR containing only that bump. Merging it
# triggers Release (2/2) Publish, which creates the tag and the GitHub release
# on the merged commit.
on:
workflow_dispatch:
inputs:
version:
description: "Version to release, e.g. v1.7.1 or v1.8.0-alpha"
required: true
type: string
permissions:
contents: write
pull-requests: write
jobs:
prepare:
name: Open release PR for ${{ inputs.version }}
runs-on: ubuntu-latest
steps:
- name: Check out main
uses: actions/checkout@v7
with:
ref: main
fetch-depth: 0
- name: Validate version
env:
VERSION: ${{ inputs.version }}
run: |
if ! [[ "$VERSION" =~ ^v[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?$ ]]; then
echo "::error::'$VERSION' is not a vX.Y.Z / vX.Y.Z-suffix version"
exit 1
fi
if git rev-parse -q --verify "refs/tags/$VERSION" >/dev/null; then
echo "::error::tag $VERSION already exists"
exit 1
fi
- name: Bump version.go
env:
VERSION: ${{ inputs.version }}
run: |
sed -i 's/pluginVersion = "[^"]*"/pluginVersion = "'"$VERSION"'"/' version.go
cat version.go
if git diff --quiet -- version.go; then
echo "::error::version.go already reads $VERSION, nothing to release"
exit 1
fi
- name: Push release branch and open PR
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ inputs.version }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git switch -c "release/$VERSION"
git commit -am "🔖 release $VERSION"
git push -u origin "release/$VERSION"
cat > /tmp/pr-body.md <<EOF
Bumps \`pluginVersion\` to \`$VERSION\` so the tag carries the version
the plugin reports to the Crowdsec LAPI.
Merging this PR tags \`$VERSION\` on the resulting commit and publishes
the GitHub release automatically.
> Keep the PR title as-is: **Release (2/2) Publish** matches on it.
EOF
gh pr create --base main --head "release/$VERSION" --title "🔖 release $VERSION" --body-file /tmp/pr-body.md
+53
View File
@@ -0,0 +1,53 @@
name: Release (2/2) Publish
# Step 2 of the release process: tag and publish the commit prepared by
# Release (1/2) Prepare.
#
# Triggered by the release PR landing on main. The tag is created on that
# commit, so version.go inside the released source always matches the tag —
# no post-release patching, no force-moved tags.
on:
push:
branches: [main]
paths: ["version.go"]
permissions:
contents: write
jobs:
publish:
name: Tag and publish
runs-on: ubuntu-latest
steps:
- name: Check out the pushed commit
uses: actions/checkout@v7
with:
fetch-depth: 0
- name: Resolve release version
id: resolve
run: |
version="$(git log -1 --format='%B' | grep -oP '🔖 release \Kv[0-9]+\.[0-9]+\.[0-9]+(-[0-9A-Za-z.]+)?' || true)"
[ -z "$version" ] && { echo "version.go changed outside a release commit, nothing to do"; echo "release=false" >> "$GITHUB_OUTPUT"; exit 0; }
in_source="$(sed -n 's/.*pluginVersion = "\([^"]*\)".*/\1/p' version.go)"
[ "$in_source" != "$version" ] && { echo "::error::commit says $version but version.go reads $in_source"; exit 1; }
git rev-parse -q --verify "refs/tags/$version" >/dev/null && { echo "::error::tag $version already exists"; exit 1; }
echo "release=true" >> "$GITHUB_OUTPUT"
echo "version=$version" >> "$GITHUB_OUTPUT"
echo "prerelease=$([[ "$version" == *-* ]] && echo '--prerelease')" >> "$GITHUB_OUTPUT"
- name: Tag and create the GitHub release
if: steps.resolve.outputs.release == 'true'
env:
GH_TOKEN: ${{ github.token }}
VERSION: ${{ steps.resolve.outputs.version }}
PRERELEASE: ${{ steps.resolve.outputs.prerelease }}
run: |
git config user.name "github-actions[bot]"
git config user.email "github-actions[bot]@users.noreply.github.com"
git tag -a "$VERSION" -m "$VERSION"
git push origin "$VERSION"
gh release create "$VERSION" --title "$VERSION" --generate-notes $PRERELEASE
+1 -1
View File
@@ -28,7 +28,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Run Renovate
uses: renovatebot/github-action@v46.1.17
uses: renovatebot/github-action@v46.1.21
with:
token: ${{ secrets.RENOVATE_TOKEN }}
env:
+2 -2
View File
@@ -384,8 +384,8 @@ make run
- Transmit only the first number of bytes to Crowdsec Appsec Server.
- CrowdsecAppsecUnreadableBodyBlock
- bool
- default: false
- Behaviour when the request body cannot be buffered for inspection (HTTP/2 or HTTP/3 request without a `Content-Length`, typically a bidirectional gRPC stream). When `false` (default) the request is forwarded to the Appsec Server with headers only (the body is left to stream through untouched). When `true` the request is blocked outright. Mirrors the reference bouncers' `APPSEC_DROP_UNREADABLE_BODY` option.
- default: true
- Behaviour when the request body cannot be buffered for inspection (HTTP/2 or HTTP/3 request without a `Content-Length`, typically a bidirectional gRPC stream). When `false` the request is forwarded to the Appsec Server with headers only (the body is left to stream through untouched). When `true` the request is blocked outright. Mirrors the reference bouncers' `APPSEC_DROP_UNREADABLE_BODY` option.
- CrowdsecAppsecKey
- string
- default: value of `CrowdsecLapiKey`
+6 -1
View File
@@ -641,7 +641,12 @@ func handleStreamCache(bouncer *Bouncer) error {
if err.Error() != cache.CacheMiss {
return err
}
bouncer.cacheClient.Set(cacheTimeoutKey, cache.NoBannedValue, bouncer.updateInterval-1)
// To avoid every instance trying to update the cache, set 1 second at least
leaseDuration := bouncer.updateInterval - 1
if leaseDuration < 1 {
leaseDuration = 1
}
bouncer.cacheClient.Set(cacheTimeoutKey, cache.NoBannedValue, leaseDuration)
streamRouteURL := url.URL{
Scheme: bouncer.crowdsecScheme,
Host: bouncer.crowdsecHost,
+1 -1
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -12,7 +12,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
volumes:
- "/var/run/docker.sock:/var/run/docker.sock:ro"
# - './ban.html:/ban.html:ro'
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+3 -3
View File
@@ -1,6 +1,6 @@
services:
cloudflare:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "cloudflare"
restart: unless-stopped
command:
@@ -19,7 +19,7 @@ services:
- 8080:8080
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -33,7 +33,7 @@ services:
- "--entrypoints.web.forwardedheaders.trustedips=172.21.0.5"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
- logs-traefik:/var/log/traefik
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -14,7 +14,7 @@ services:
- "--entrypoints.web.forwardedheaders.trustedips=172.18.0.0/24"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+2 -2
View File
@@ -1,5 +1,5 @@
image:
tag: v3.7.6
tag: v3.7.9
logs:
general:
@@ -15,4 +15,4 @@ experimental:
plugins:
bouncer:
moduleName: "github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
version: "v1.6.0"
version: "v1.7.0"
+3 -3
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
@@ -87,7 +87,7 @@ services:
- "traefik.enable=false"
redis-secure:
image: "redis:8.8.0-alpine"
image: "redis:8.8.1-alpine"
container_name: "redis-secure"
hostname: redis-secure
restart: unless-stopped
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
+2 -2
View File
@@ -1,6 +1,6 @@
services:
traefik:
image: "traefik:v3.7.6"
image: "traefik:v3.7.9"
container_name: "traefik"
restart: unless-stopped
command:
@@ -13,7 +13,7 @@ services:
- "--entrypoints.web.address=:80"
- "--experimental.plugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
- "--experimental.plugins.bouncer.version=v1.6.0"
- "--experimental.plugins.bouncer.version=v1.7.0"
# - "--experimental.localplugins.bouncer.modulename=github.com/maxlerebourg/crowdsec-bouncer-traefik-plugin"
volumes:
- /var/run/docker.sock:/var/run/docker.sock:ro
-10
View File
@@ -25,16 +25,6 @@
}
],
"customManagers": [
{
"description": "Plugin self-pin in version.go (pluginVersion)",
"customType": "regex",
"managerFilePatterns": ["/^version\\.go$/"],
"matchStrings": [
"pluginVersion\\s*=\\s*\"(?<currentValue>v[0-9]+\\.[0-9]+\\.[0-9]+)\""
],
"depNameTemplate": "maxlerebourg/crowdsec-bouncer-traefik-plugin",
"datasourceTemplate": "github-tags"
},
{
"description": "Plugin self-pin in docker-compose CLI args (--experimental.plugins.bouncer.version=vX)",
"customType": "regex",
+1 -1
View File
@@ -14,7 +14,7 @@
set -euo pipefail
# Pinned to match the Docker suite (tests/e2e/scenarios/*/docker-compose.yml).
TRAEFIK_VERSION="${TRAEFIK_VERSION:-v3.7.6}"
TRAEFIK_VERSION="${TRAEFIK_VERSION:-v3.7.9}"
WEB_PORT="${WEB_PORT:-8000}"
LAPI_PORT="${LAPI_PORT:-8090}"
+3 -2
View File
@@ -1,4 +1,5 @@
package crowdsec_bouncer_traefik_plugin //nolint:revive,stylecheck
// pluginVersion is updated automatically by the release workflow and Renovate.
var pluginVersion = "v1.6.0" //nolint:gochecknoglobals
// pluginVersion is what the plugin reports to the Crowdsec LAPI.
// Do not edit by hand: the "Release (1/2) Prepare" workflow bumps it.
var pluginVersion = "v1.7.1" //nolint:gochecknoglobals