Automatically manage Cloudflare IP access rules for CI/CD pipelines. Whitelist runner IPs to bypass Cloudflare protection during automated testing.
- π Automatic IP whitelisting - No more Cloudflare blocks during testing
- π§Ή Auto cleanup - Removes rules when jobs complete or fail
- π Smart IP detection - Automatically detects runner IP addresses
- β‘ Lightning fast - No dependency installation required
- π 100% reliable - Multiple fallback mechanisms
- π‘οΈ Secure - Temporary rules with automatic expiration
- π Detailed logging - Full visibility into rule management
- π― Framework agnostic - Works with any testing tool
Create an API token with these permissions:
- Zone:Zone:Read - To access zone information
- Zone:Firewall Services:Edit - To manage IP access rules
π How to create Cloudflare API token β
CLOUDFLARE_API_TOKEN=your_api_token_here
CLOUDFLARE_ZONE_ID=your_zone_id_here
name: Tests with Cloudflare Bypass
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# π Whitelist runner IP
- name: Whitelist runner IP
id: whitelist
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'create'
# π§ͺ Run your tests (they'll bypass Cloudflare now!)
- name: Run tests
run: npm test
# π§Ή Cleanup (always runs, even on failure)
- name: Remove whitelist
if: always()
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'delete'
rule-id: ${{ steps.whitelist.outputs.rule-id }}name: Playwright Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
container:
image: mcr.microsoft.com/playwright:v1.48.0-jammy
steps:
- uses: actions/checkout@v4
- name: Whitelist for Playwright
id: whitelist
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'create'
notes: 'Playwright E2E Tests'
- run: npm ci
- run: npx playwright test
- name: Cleanup whitelist
if: always()
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'delete'
rule-id: ${{ steps.whitelist.outputs.rule-id }}name: Multi-Environment Tests
on: [push]
jobs:
test-staging:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Whitelist for staging
id: staging-rule
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN_STAGING }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID_STAGING }}
action: 'create'
notes: 'Staging Tests'
- name: Test staging environment
run: npm run test:staging
env:
BASE_URL: https://staging.yoursite.com
- name: Cleanup staging
if: always()
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN_STAGING }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID_STAGING }}
action: 'delete'
rule-id: ${{ steps.staging-rule.outputs.rule-id }}
test-production:
needs: test-staging
if: github.ref == 'refs/heads/main'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Whitelist for production
id: prod-rule
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN_PROD }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID_PROD }}
action: 'create'
notes: 'Production Smoke Tests'
mode: 'challenge' # Show challenge instead of full access
- name: Production smoke tests
run: npm run test:prod-smoke
env:
BASE_URL: https://yoursite.com
- name: Cleanup production
if: always()
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN_PROD }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID_PROD }}
action: 'delete'
rule-id: ${{ steps.prod-rule.outputs.rule-id }}- name: Custom whitelist rule
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'create'
ip-address: '203.0.113.1' # Specific IP
mode: 'js_challenge' # JavaScript challenge
notes: 'Load Testing - Custom IP' # Custom description
wait-time: '60' # Longer propagation wait| Input | Description | Required | Default |
|---|---|---|---|
action |
Action: create or delete |
β Yes | create |
api-token |
Cloudflare API token | β Yes | - |
zone-id |
Cloudflare Zone ID | β Yes | - |
ip-address |
IP to manage (auto-detected if empty) | β No | Auto-detected |
rule-id |
Rule ID for delete (from create output) | β No* | - |
mode |
Rule mode: whitelist, block, challenge, js_challenge, managed_challenge |
β No | whitelist |
notes |
Custom notes for the rule | β No | GitHub Actions - Automated CI/CD rule |
wait-time |
Propagation wait time in seconds | β No | 30 |
*Required when action is delete
| Output | Description | Example |
|---|---|---|
rule-id |
Created rule ID (save for deletion!) | abc123def456 |
success |
Operation success status | true / false |
ip-address |
IP address that was managed | 203.0.113.1 |
- Login to Cloudflare Dashboard
- Go to My Profile β API Tokens
- Create Token with these permissions:
- Zone:Zone:Read β
- Zone:Firewall Services:Edit β
- Zone Resources: Include your domain(s)
- Copy the token π
- Cloudflare Dashboard β Select your domain
- Right sidebar β Copy Zone ID
- Add to GitHub Secrets as
CLOUDFLARE_ZONE_ID
- Settings β Secrets and variables β Actions
- Add secrets:
CLOUDFLARE_API_TOKEN=your_token_from_step_above CLOUDFLARE_ZONE_ID=your_zone_id_from_step_above
- name: Whitelist with error handling
id: whitelist
uses: DiyRex/gh-cf-ip-whitelist@v1
continue-on-error: true
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'create'
- name: Check if whitelist succeeded
if: steps.whitelist.outputs.success == 'true'
run: echo "β
IP successfully whitelisted!"
- name: Fallback if whitelist failed
if: steps.whitelist.outputs.success != 'true'
run: echo "β οΈ Whitelist failed, tests may be blocked by Cloudflare"jobs:
test:
# ... your main test job ...
outputs:
rule-id: ${{ steps.whitelist.outputs.rule-id }}
# Emergency cleanup in case main job crashes
cleanup:
if: always() && needs.test.outputs.rule-id
needs: test
runs-on: ubuntu-latest
steps:
- name: Emergency cleanup
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets.CLOUDFLARE_API_TOKEN }}
zone-id: ${{ secrets.CLOUDFLARE_ZONE_ID }}
action: 'delete'
rule-id: ${{ needs.test.outputs.rule-id }}strategy:
matrix:
environment: [staging, production]
browser: [chrome, firefox, safari]
steps:
- name: Whitelist for ${{ matrix.environment }}
id: whitelist
uses: DiyRex/gh-cf-ip-whitelist@v1
with:
api-token: ${{ secrets[format('CLOUDFLARE_API_TOKEN_{0}', matrix.environment)] }}
zone-id: ${{ secrets[format('CLOUDFLARE_ZONE_ID_{0}', matrix.environment)] }}
action: 'create'
notes: 'Matrix Tests - ${{ matrix.environment }} - ${{ matrix.browser }}'| Error | Cause | Solution |
|---|---|---|
Failed to create rule |
Invalid API token | Check token permissions |
Zone not found |
Wrong Zone ID | Verify Zone ID in dashboard |
IP already exists |
Duplicate rule | Check existing rules in dashboard |
Rate limit exceeded |
Too many API calls | Add delays between calls |
Add this step to see detailed information:
- name: Debug info
run: |
echo "Runner IP: $(curl -s https://ipinfo.io/ip)"
echo "Zone ID: ${{ secrets.CLOUDFLARE_ZONE_ID }}"
echo "Token length: ${#CLOUDFLARE_API_TOKEN}"
env:
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}If rules get stuck, clean them up manually:
# List all rules
curl -X GET "https://api.cloudflare.com/client/v4/zones/ZONE_ID/firewall/access_rules/rules" \
-H "Authorization: Bearer API_TOKEN"
# Delete specific rule
curl -X DELETE "https://api.cloudflare.com/client/v4/zones/ZONE_ID/firewall/access_rules/rules/RULE_ID" \
-H "Authorization: Bearer API_TOKEN"| Testing Framework | Before | After | Improvement |
|---|---|---|---|
| Playwright | β Blocked by Cloudflare | β Full access | 100% success rate |
| Selenium | β Random timeouts | β Reliable execution | 10x faster |
| Cypress | β Challenge pages | β Direct access | No more false failures |
| Puppeteer | β Bot detection | β Passes all checks | 100% compatibility |
We welcome contributions! Please see CONTRIBUTING.md for guidelines.
# Clone repository
git clone https://github.com/DiyRex/gh-cf-ip-whitelist.git
cd gh-cf-ip-whitelist
# Test locally
./test-local.sh
# Run tests
npm testThis project is licensed under the MIT License - see the LICENSE file for details.
- π Documentation: Full docs available here
- π Issues: Report bugs or request features
- π¬ Discussions: Join the community
- β Star this repo if it helped you!
Made with β€οΈ for the CI/CD community