Update build-run-validate.yml #7
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Build, Run and Validate | |
permissions: | |
contents: read | |
pull-requests: write | |
on: | |
push: | |
branches: | |
- main | |
pull_request: | |
branches: | |
- main | |
workflow_dispatch: | |
jobs: | |
build-run-validate: | |
name: Build and Test Caddy WAF | |
runs-on: ubuntu-latest | |
steps: | |
- name: Checkout Code | |
uses: actions/checkout@v3 | |
- name: Install Dependencies | |
run: | | |
sudo apt update | |
sudo apt install -y wget git build-essential curl python3 python3-pip | |
- name: Install Go 1.23.4 | |
uses: actions/setup-go@v4 | |
with: | |
go-version: '1.23.4' | |
- name: Validate Go Installation | |
run: | | |
go version | |
if ! go version | grep -q "go1.23.4"; then | |
echo "Go installation failed or incorrect version" | |
exit 1 | |
fi | |
- name: Clone caddy-waf Repository | |
run: | | |
git clone https://github.com/fabriziosalmi/caddy-waf.git | |
cd caddy-waf | |
- name: Validate Repository Cloning | |
run: | | |
if [ ! -d "caddy-waf" ]; then | |
echo "Repository cloning failed" | |
exit 1 | |
fi | |
- name: Install Go Dependencies | |
run: | | |
cd caddy-waf | |
go mod tidy | |
go get -v github.com/fabriziosalmi/caddy-waf github.com/caddyserver/caddy/v2 github.com/oschwald/maxminddb-golang | |
- name: Download GeoLite2 Country Database | |
run: | | |
cd caddy-waf | |
wget https://git.io/GeoLite2-Country.mmdb | |
- name: Validate GeoLite2 Download | |
run: | | |
cd caddy-waf | |
if [ ! -f "GeoLite2-Country.mmdb" ]; then | |
echo "GeoLite2 database download failed" | |
exit 1 | |
fi | |
- name: Install Python Dependencies | |
run: | | |
python3 -m venv venv | |
source venv/bin/activate | |
pip install --upgrade pip | |
pip install tqdm requests | |
- name: Cache Python Dependencies | |
id: cache-pip | |
uses: actions/cache@v3 | |
with: | |
path: ~/.cache/pip | |
key: pip-${{ runner.os }}-${{ hashFiles('**/requirements.txt') }} | |
restore-keys: | | |
pip-${{ runner.os }}- | |
- name: Retrieve and Initialize IP and DNS Blacklists | |
run: | | |
cd caddy-waf | |
source ../venv/bin/activate # Activate the virtual environment | |
echo "Running get_blacklisted_ip.py..." | |
python3 get_blacklisted_ip.py | |
if [ $? -ne 0 ]; then | |
echo "Failed to retrieve or initialize IP blacklist" | |
exit 1 | |
fi | |
echo "IP blacklist retrieved and initialized successfully." | |
echo "Running get_blacklisted_dns.py..." | |
python3 get_blacklisted_dns.py | |
if [ $? -ne 0 ]; then | |
echo "Failed to retrieve or initialize DNS blacklist" | |
exit 1 | |
fi | |
echo "DNS blacklist retrieved and initialized successfully." | |
- name: Build Caddy with caddy-waf | |
run: | | |
cd caddy-waf | |
go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest | |
xcaddy build --with github.com/fabriziosalmi/caddy-waf=./ | |
- name: Validate Build | |
run: | | |
cd caddy-waf | |
if [ ! -f "caddy" ]; then | |
echo "Caddy build failed" | |
exit 1 | |
fi | |
- name: Create Dynamic Files | |
run: | | |
cd caddy-waf | |
# Create a valid JWT token for testing | |
echo "Creating valid.jwt..." | |
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" > valid.jwt | |
# Create a sample JSON file for testing | |
echo "Creating sample.json..." | |
echo '{"key": "value"}' > sample.json | |
echo "Dynamic files created successfully." | |
- name: Test Caddy Run and Validate WAF Provisioning | |
run: | | |
cd caddy-waf | |
chmod +x caddy | |
./caddy run > caddy_output.log 2>&1 & | |
sleep 5 | |
if ! pgrep -f "caddy run"; then | |
echo "Caddy run failed" | |
cat caddy_output.log | |
exit 1 | |
fi | |
if ! grep -q "WAF middleware provisioned successfully" caddy_output.log; then | |
echo "WAF provisioning log not found" | |
cat caddy_output.log | |
exit 1 | |
fi | |
echo "Caddy WAF build and run successful with WAF middleware provisioned" | |
- name: Run Phase-Specific Curl Tests | |
run: | | |
cd caddy-waf | |
# Phase 1: Block known vulnerability scanners (User-Agent) | |
echo "Testing Phase 1: Block known vulnerability scanners..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -A "nikto" http://localhost:8080) | |
if [ "$RESPONSE" != "403" ]; then | |
echo "Failed: Vulnerability scanner request was not blocked. Expected HTTP 403, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: Vulnerability scanner request blocked (HTTP 403)." | |
fi | |
# Phase 1: Allow legitimate traffic (User-Agent) | |
echo "Testing Phase 1: Allow legitimate traffic..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -A "Mozilla/5.0" http://localhost:8080) | |
if [ "$RESPONSE" != "200" ]; then | |
echo "Failed: Legitimate request was blocked. Expected HTTP 200, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: Legitimate request allowed (HTTP 200)." | |
fi | |
# Phase 2: Block NoSQL injection in body | |
echo "Testing Phase 2: Block NoSQL injection..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST -d '{"$gt": ""}' http://localhost:8080) | |
if [ "$RESPONSE" != "403" ]; then | |
echo "Failed: NoSQL injection request was not blocked. Expected HTTP 403, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: NoSQL injection request blocked (HTTP 403)." | |
fi | |
# Phase 2: Allow valid JSON body | |
echo "Testing Phase 2: Allow valid JSON body..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -X POST -d @sample.json http://localhost:8080) | |
if [ "$RESPONSE" != "200" ]; then | |
echo "Failed: Valid JSON request was blocked. Expected HTTP 200, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: Valid JSON request allowed (HTTP 200)." | |
fi | |
# Phase 3: Block HTTP response splitting (Headers) | |
echo "Testing Phase 3: Block HTTP response splitting..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Forwarded-For: 127.0.0.1\r\nInjected-Header: value" http://localhost:8080) | |
if [ "$RESPONSE" != "403" ]; then | |
echo "Failed: HTTP response splitting request was not blocked. Expected HTTP 403, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: HTTP response splitting request blocked (HTTP 403)." | |
fi | |
# Phase 3: Allow valid headers | |
echo "Testing Phase 3: Allow valid headers..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "X-Forwarded-For: 8.8.8.8" http://localhost:8080) | |
if [ "$RESPONSE" != "200" ]; then | |
echo "Failed: Valid headers request was blocked. Expected HTTP 200, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: Valid headers request allowed (HTTP 200)." | |
fi | |
# Phase 4: Block JWT tampering (Headers) | |
echo "Testing Phase 4: Block JWT tampering..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c" http://localhost:8080) | |
if [ "$RESPONSE" != "403" ]; then | |
echo "Failed: JWT tampering request was not blocked. Expected HTTP 403, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: JWT tampering request blocked (HTTP 403)." | |
fi | |
# Phase 4: Allow valid JWT (Headers) | |
echo "Testing Phase 4: Allow valid JWT..." | |
RESPONSE=$(curl -s -o /dev/null -w "%{http_code}" -H "Authorization: Bearer $(cat valid.jwt)" http://localhost:8080) | |
if [ "$RESPONSE" != "200" ]; then | |
echo "Failed: Valid JWT request was blocked. Expected HTTP 200, got $RESPONSE" | |
exit 1 | |
else | |
echo "Success: Valid JWT request allowed (HTTP 200)." | |
fi | |
echo "All phase-specific WAF rule tests passed successfully!" | |
- name: Clean Up | |
if: always() | |
run: | | |
pkill -f "caddy run" || true | |
echo "Cleaned up running Caddy instances" |