Skip to content

fix(llm): override OpenAI SDK User-Agent to bypass Cloudflare WAF#151

Merged
mabry1985 merged 8 commits intodevfrom
fix/cloudflare-waf-user-agent
Apr 23, 2026
Merged

fix(llm): override OpenAI SDK User-Agent to bypass Cloudflare WAF#151
mabry1985 merged 8 commits intodevfrom
fix/cloudflare-waf-user-agent

Conversation

@mabry1985
Copy link
Copy Markdown
Contributor

@mabry1985 mabry1985 commented Apr 23, 2026

Summary

  • LangChain's ChatOpenAI sent the default OpenAI/Python <ver> User-Agent, which the managed WAF on the proto-labs.ai zone returns HTTP 403 "Your request was blocked." for.
  • /v1/models happened to succeed (different SDK path / UA), so the failure looked like a key/ACL problem when it was a header signature match.
  • Sets default_headers={\"User-Agent\": \"protoAgent/0.1 (+...)\"} on the ChatOpenAI instance in graph/llm.py, matching the identifier tools/lg_tools.py:226 already uses for outbound HTTP fetches.

Reproduction (before fix)

```
curl -H 'User-Agent: OpenAI/Python 1.54.0' -H 'Authorization: Bearer '
-H 'Content-Type: application/json'
https://api.proto-labs.ai/v1/chat/completions
-d '{"model":"groq-llama-70b","messages":[{"role":"user","content":"hi"}]}'

HTTP 403 "Your request was blocked."

```
Same call with any non-OpenAI UA (e.g. curl/8.5, python-httpx/*, MyApp/1.0, empty) → 200.

Why this path (not a WAF skip rule)

A Cloudflare Custom Rule skipping managed rules on api.proto-labs.ai is cleaner at the edge, but couples agent operability to zone config and the current CLOUDFLARE_API_TOKEN is DNS-only. The in-client override keeps self-hosters behind other edges working and removes the per-deployment Cloudflare dependency.

Test plan

  • End-to-end: patched graph/llm.py in running protoagent-local container; llm.invoke(\"reply with: pong\")pong. Captured outbound User-Agent: protoAgent/0.1 (+https://github.com/protoLabsAI/protoAgent) on the wire.
  • CI green on this PR.
  • Image rebuild + redeploy (Watchtower / manual) picks up the fix.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Version

    • Upgraded to v0.2.1
  • Improvements

    • Enhanced LLM client configuration with improved request identification
  • Chores

    • Optimized deployment workflow to automatically sync repository metadata with GitHub Pages publication
    • Expanded workflow permissions for deployment automation

mabry1985 and others added 8 commits April 19, 2026 14:30
First tagged release. Contents of community-improvements project:

M1 — Security Hardening (A2A bearer auth, audit redaction, origin verification)
M2 — Memory On By Default (session persistence + load-on-start)
M3 — Skill Loop (skill-v1 emission + SQLite FTS5 index + curator)

Plus: .gitignore cleanup for .automaker-lock + .worktrees, docs coverage of
security layer, skill-loop architecture, and new env vars.

Manual bump because prepare-release.yml requires GH_PAT secret (not configured).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
promote: dev -> staging (bug fixes v0.2.1)
promote: staging -> main (bug fixes v0.2.1)
Bug fixes from v0.2.0 smoke testing:
- Agent card now advertises bearer scheme when A2A_AUTH_TOKEN is set
- Session memory persistence actually fires (moved from unreachable on_session_end to after_agent)
- Test suite collects cleanly in fresh Docker env
- MemoryMiddleware activates standalone (without knowledge_store)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Writes the deployed GitHub Pages URL back to the repo's `homepage`
field so it renders in the About sidebar on the repo page.

Co-authored-by: Automaker <automaker@localhost>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Cloudflare's managed WAF on the proto-labs.ai zone returns 403 "Your
request was blocked" for any request whose User-Agent starts with
`OpenAI/Python` or `AsyncOpenAI/Python` — which is exactly what
langchain_openai.ChatOpenAI sends by default via the bundled OpenAI
SDK. /v1/models succeeded (different SDK path / UA) while
/v1/chat/completions failed, making the break look like a key/ACL
issue when it was a header signature match.

Reproduction (before fix):
  curl -H 'User-Agent: OpenAI/Python 1.54.0' -H 'Authorization: Bearer <key>' \
    https://api.proto-labs.ai/v1/chat/completions -d '{...}'
  -> HTTP 403 "Your request was blocked."

The same call with User-Agent: curl/*, python-httpx/*, or any
non-OpenAI string returns 200. `tools/lg_tools.py:226` already sets a
protoAgent UA for outbound HTTP fetches — reuse that identifier here
so every egress presents a consistent, allowlisted UA.

Alternative fixes considered:
- A Cloudflare Custom WAF Skip rule on the hostname: cleaner at the
  edge but requires a zone-scoped token and couples agent operability
  to infra config.
- Stripping the UA header at cloudflared: not possible; WAF fires
  before the tunnel sees the request.

The in-client override is the most portable fix: self-hosters on a
different edge keep working, operators behind Cloudflare stop getting
403s.
@mabry1985 mabry1985 merged commit b4434b1 into dev Apr 23, 2026
1 check was pending
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Apr 23, 2026

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: b8045083-9418-49d5-aa42-8c4e03d7002e

📥 Commits

Reviewing files that changed from the base of the PR and between 665fc46 and 8e2e34a.

📒 Files selected for processing (3)
  • .github/workflows/docs.yml
  • graph/llm.py
  • pyproject.toml

Walkthrough

The pull request makes three configuration changes: updating the docs workflow to automatically sync GitHub Pages deployment metadata to the repository homepage via GitHub CLI, adding a custom User-Agent header to the LLM client configuration, and bumping the project version from 0.1.0 to 0.2.1.

Changes

Cohort / File(s) Summary
CI/CD Workflow
.github/workflows/docs.yml
Added administration: write permission and post-deployment step that updates repository homepage field to deployed GitHub Pages URL using GitHub CLI.
LLM Configuration
graph/llm.py
Configured ChatOpenAI client with custom default_headers override setting a specific User-Agent value.
Version Management
pyproject.toml
Bumped package version from 0.1.0 to 0.2.1.

Estimated code review effort

🎯 1 (Trivial) | ⏱️ ~3 minutes

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/cloudflare-waf-user-agent

Comment @coderabbitai help to get the list of available commands and usage tips.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant