Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
252 changes: 252 additions & 0 deletions .github/workflows/integration-test.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,252 @@
name: Integration Tests

on:
push:
branches:
- main
- master
- develop
pull_request:
branches:
- main
- master
- develop

permissions:
contents: read

jobs:
integration-tests:
name: Integration Tests
runs-on: ubuntu-latest
timeout-minutes: 20

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Setup Docker Buildx
uses: docker/setup-buildx-action@v3

- name: Install PowerShell and Pester
run: |
# Set non-interactive mode to avoid prompts
export DEBIAN_FRONTEND=noninteractive

# Update package lists
sudo apt-get update
sudo apt-get install -y wget apt-transport-https software-properties-common

# Download and install Microsoft signing key and repository
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"

# Use dpkg with force-confdef and force-confold to handle config conflicts automatically
sudo dpkg --force-confdef --force-confold -i packages-microsoft-prod.deb

# Install PowerShell
sudo apt-get update
sudo apt-get install -y powershell

# Install Pester testing framework
pwsh -c "Install-Module -Name Pester -Force -Scope CurrentUser -SkipPublisherCheck -AllowClobber"

- name: Verify Docker Compose file
run: |
if [ ! -f "docker-compose.test.yml" ]; then
echo "❌ docker-compose.test.yml not found"
exit 1
fi
echo "✅ Docker Compose test file found"

# Validate Docker Compose file
docker compose -f docker-compose.test.yml config

- name: Pull Docker Images
run: |
echo "🔄 Pulling required Docker images..."
docker compose -f docker-compose.test.yml pull
echo "✅ Docker images pulled successfully"

- name: Start Test Services
run: |
echo "🔄 Starting test services..."
docker compose -f docker-compose.test.yml up -d
echo "✅ Test services started"

echo "📋 Running containers:"
docker compose -f docker-compose.test.yml ps

- name: Wait for Services to be Ready
shell: pwsh
run: |
Write-Host "🔄 Waiting for services to be ready..." -ForegroundColor Cyan

function Test-ServiceHealth {
param([string]$Url, [string]$ServiceName, [int]$TimeoutSeconds = 30)

Write-Host "Checking $ServiceName..." -ForegroundColor Yellow
$elapsed = 0
$interval = 3

do {
try {
$response = Invoke-WebRequest -Uri $Url -Method Get -TimeoutSec 5 -UseBasicParsing
if ($response.StatusCode -eq 200) {
Write-Host "✅ $ServiceName is ready!" -ForegroundColor Green
return $true
}
} catch {
# Service not ready, continue waiting
}

Start-Sleep $interval
$elapsed += $interval

if ($elapsed % 15 -eq 0) {
Write-Host " Still waiting for $ServiceName... ($elapsed/$TimeoutSeconds seconds)" -ForegroundColor Gray
}

} while ($elapsed -lt $TimeoutSeconds)

Write-Host "❌ $ServiceName failed to become ready within $TimeoutSeconds seconds" -ForegroundColor Red
return $false
}

# Test each service
$services = @(
@{ Url = "http://localhost:8080/api/rawdata"; Name = "Traefik API" },
@{ Url = "http://localhost:8000/bypass"; Name = "Bypass Service" },
@{ Url = "http://localhost:8000/protected"; Name = "Protected Service" }
)

$allReady = $true
foreach ($service in $services) {
if (-not (Test-ServiceHealth -Url $service.Url -ServiceName $service.Name)) {
$allReady = $false
break
}
}

if (-not $allReady) {
Write-Host "❌ Not all services became ready" -ForegroundColor Red
exit 1
}

Write-Host "✅ All services are ready for testing!" -ForegroundColor Green

- name: Run Integration Tests
shell: pwsh
run: |
Write-Host "🧪 Running Pester integration tests..." -ForegroundColor Cyan

# Import Pester module
Import-Module Pester -Force

# Configure Pester
$pesterConfig = New-PesterConfiguration
$pesterConfig.Run.Path = "./scripts/integration-tests.Tests.ps1"
$pesterConfig.Output.Verbosity = 'Detailed'
$pesterConfig.Run.Exit = $false
$pesterConfig.Run.PassThru = $true

# Run tests
$result = Invoke-Pester -Configuration $pesterConfig

# Report results
Write-Host ""
Write-Host "📊 Test Results Summary:" -ForegroundColor Cyan
Write-Host " Total: $($result.TotalCount)" -ForegroundColor White
Write-Host " Passed: $($result.PassedCount)" -ForegroundColor Green
Write-Host " Failed: $($result.FailedCount)" -ForegroundColor Red
Write-Host " Skipped: $($result.SkippedCount)" -ForegroundColor Yellow

if ($result.FailedCount -gt 0) {
Write-Host "❌ $($result.FailedCount) test(s) failed" -ForegroundColor Red
exit 1
} else {
Write-Host "✅ All tests passed! 🎉" -ForegroundColor Green
}

- name: Show Container Logs on Failure
if: failure()
run: |
echo "📋 Container Status:"
docker compose -f docker-compose.test.yml ps

echo ""
echo "📝 Service Logs:"
echo "==================== Traefik Logs ===================="
docker compose -f docker-compose.test.yml logs traefik --tail=50

echo ""
echo "==================== WAF Logs ===================="
docker compose -f docker-compose.test.yml logs waf --tail=50

echo ""
echo "==================== Protected Service Logs ===================="
docker compose -f docker-compose.test.yml logs whoami-protected --tail=50

echo ""
echo "==================== Bypass Service Logs ===================="
docker compose -f docker-compose.test.yml logs whoami-bypass --tail=50

- name: Cleanup Test Environment
if: always()
run: |
echo "🧹 Cleaning up test environment..."
docker compose -f docker-compose.test.yml down -v --remove-orphans
echo "✅ Cleanup completed"

# Additional job to test the PowerShell runner script
test-runner-script:
name: Test Runner Script Validation
runs-on: ubuntu-latest

steps:
- name: Checkout Repository
uses: actions/checkout@v4

- name: Install PowerShell
run: |
export DEBIAN_FRONTEND=noninteractive
sudo apt-get update
sudo apt-get install -y wget apt-transport-https software-properties-common
wget -q "https://packages.microsoft.com/config/ubuntu/$(lsb_release -rs)/packages-microsoft-prod.deb"
sudo dpkg --force-confdef --force-confold -i packages-microsoft-prod.deb
sudo apt-get update
sudo apt-get install -y powershell

- name: Validate Test Runner Script
shell: pwsh
run: |
# Check if the script exists and is valid PowerShell
if (-not (Test-Path "./Test-Integration.ps1")) {
Write-Host "❌ Test-Integration.ps1 not found" -ForegroundColor Red
exit 1
}

# Basic syntax validation
try {
$null = [System.Management.Automation.PSParser]::Tokenize((Get-Content "./Test-Integration.ps1" -Raw), [ref]$null)
Write-Host "✅ Test-Integration.ps1 syntax is valid" -ForegroundColor Green
} catch {
Write-Host "❌ Test-Integration.ps1 has syntax errors: $($_.Exception.Message)" -ForegroundColor Red
exit 1
}

# Check for required test files
$requiredFiles = @(
"./docker-compose.test.yml",
"./scripts/integration-tests.Tests.ps1"
)

foreach ($file in $requiredFiles) {
if (Test-Path $file) {
Write-Host "✅ Found: $file" -ForegroundColor Green
} else {
Write-Host "❌ Missing: $file" -ForegroundColor Red
exit 1
}
}

Write-Host "✅ All required files are present" -ForegroundColor Green
23 changes: 16 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

this is a fork of the original: https://github.com/acouvreur/traefik-modsecurity-plugin

This fork introduces alpine images, CRS 4.x suppport, a custom http.transport, and a 429 jail for repeat offenders
This fork introduces alpine images, CRS 4.x suppport, and a custom http.transport

see: https://github.com/traefik/plugindemo#troubleshooting

Expand Down Expand Up @@ -55,13 +55,22 @@ time.

This plugin supports these configuration:

### Basic Configuration

* `modSecurityUrl`: (**mandatory**) it's the URL for the owasp/modsecurity container.
* `timeoutMillis`: (optional) timeout in milliseconds for the http client to talk with modsecurity container. (default 2
seconds)
* `jailEnabled`: (optional) 429 jail for repeat offenders (based on threshold settings)
* `JailTimeDurationSecs`: (optional) how long a client will be jailed for, in seconds
* `badRequestsThresholdCount`: (optional) # of 403s a clientIP can trigger from OWASP before being adding to jail
* `badRequestsThresholdPeriodSecs` (optional) # the period, in seconds, that the threshold must meet before a client is added to the 429 jail
* `timeoutMillis`: (optional) timeout in milliseconds for the http client to talk with modsecurity container. (default 2000ms)
* `unhealthyWafBackOffPeriodSecs` (optional) the period, in seconds, to backoff if calls to modsecurity fail. Default to 0. Default behaviour is to send a 502 Bad Gateway when there are problems communicating with modsec.
* `modSecurityStatusRequestHeader`: (optional) name of the header to add to the request when requests are blocked by ModSecurity (for logging purposes). The header value will contain the HTTP status code returned by ModSecurity. Default is empty (no header added).

### Advanced Transport Configuration

These parameters allow fine-tuning of the HTTP client behavior for high-load scenarios:

* `maxConnsPerHost`: (optional) maximum number of concurrent connections allowed per ModSecurity host. Set to 0 for unlimited connections (default: 0 - unlimited, original behavior).
* `maxIdleConnsPerHost`: (optional) maximum number of idle connections to keep per ModSecurity host. Set to 0 for unlimited idle connections (default: 0 - unlimited, original behavior).
* `responseHeaderTimeoutMillis`: (optional) timeout in milliseconds for waiting for response headers from ModSecurity. Set to 0 for no timeout (default: 0 - no timeout, original behavior).
* `expectContinueTimeoutMillis`: (optional) timeout in milliseconds for Expect: 100-continue handshake. Used for large payload uploads (default: 1000ms).


## Local development (docker-compose.local.yml)

Expand Down
Loading