Skip to content

fix(security): allow RFC 2544 range bypass for TUN-mode proxy / Fake-IP users #3779

Closed
dlkakbs wants to merge 1 commit into
NousResearch:mainfrom
dlkakbs:fix/ssrf-rfc2544-fake-ip-tun-proxy
Closed

fix(security): allow RFC 2544 range bypass for TUN-mode proxy / Fake-IP users #3779
dlkakbs wants to merge 1 commit into
NousResearch:mainfrom
dlkakbs:fix/ssrf-rfc2544-fake-ip-tun-proxy

Conversation

@dlkakbs

@dlkakbs dlkakbs commented Mar 29, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Python 3.11 expanded ipaddress.is_private to cover all IANA special-purpose ranges, including 198.18.0.0/15 (RFC 2544 Benchmarking Test Range). This broke Hermes for users running TUN-mode proxy software (Clash, Mihomo, Sing-box, Surge) that uses this range as a Fake-IP pool.

How Fake-IP works: DNS returns a virtual 198.18.x.x address → the HTTP request goes to the TUN interface → the proxy forwards it to the real public destination. SSRF protection saw 198.18.x.x, classified it as private (correctly, per Python 3.11 semantics), and blocked it — even though no internal service was reachable at that address.

Adds HERMES_ALLOW_RFC2544=true env var to unblock the range. Secure by default: the range stays blocked unless explicitly opted in. Documents the env var in config.py alongside other settings.

Related Issue

Fixes #3777

Type of Change

  • 🐛 Bug fix (non-breaking change that fixes an issue)
  • ✅ Tests (adding or improving test coverage)

Changes Made

  • tools/url_safety.py: add _RFC2544_BENCHMARK constant, _ALLOW_RFC2544 env var flag, early-return in _is_blocked_ip when flag is set; document Python 3.11 regression in comment
  • tests/tools/test_url_safety.py: add TestRfc2544FakeIp class (4 tests: blocked by default, allowed when enabled, upper bound, RFC 1918 still blocked); extend TestIsBlockedIp parametrize to cover 198.18.x.x
  • hermes_cli/config.py: document HERMES_ALLOW_RFC2544 env var with context on when/why to enable it

How to Test

  1. Set HERMES_ALLOW_RFC2544=true in your env or .env
  2. With TUN proxy + Fake-IP active, run browser_navigate(url="https://www.baidu.com") or any web tool — should no longer be blocked
  3. Without the env var, 198.18.x.x remains blocked
  4. pytest tests/tools/test_url_safety.py -q — 55 tests pass

Checklist

Code

  • Commit messages follow Conventional Commits
  • PR contains only changes related to this fix
  • pytest tests/tools/test_url_safety.py -q passes (55/55)
  • Tests added for new behaviour

Documentation & Housekeeping

  • hermes_cli/config.py updated with env var documentation
  • Cross-platform impact considered — N/A (pure Python, no OS-specific code)

…IP users

Python 3.11 expanded ipaddress.is_private to cover all IANA special-purpose
ranges, including 198.18.0.0/15 (RFC 2544 Benchmarking). This caused a
regression for users running TUN-mode proxy software (Clash, Mihomo, Sing-box,
Surge) that uses this range as a Fake-IP pool — DNS returns a virtual 198.18.x.x
address, but actual traffic is forwarded by the TUN interface to the real public
destination. SSRF protection blocked these requests with "private address".

Adds HERMES_ALLOW_RFC2544=true env var to unblock the range. Secure by default:
the range remains blocked unless explicitly opted in. Documents the env var in
config.py alongside other security-relevant settings.

Fixes NousResearch#3777
@huohua-dev

Copy link
Copy Markdown

Tested this class of bug locally under Clash TUN / Fake-IP and confirmed the 198.18.0.0/15 false positive.

One alternative design that worked well for us was a scoped allowlist instead of a global RFC2544 bypass, e.g.:

browser:
  allow_private_urls: false
  trusted_tun_cidrs:
    - 198.18.0.0/15

Then in browser_tool.py, pass those CIDRs into is_safe_url(..., extra_allowed_cidrs=...) for:

  • pre-navigation SSRF check
  • post-redirect SSRF check

That keeps 192.168/16, 127/8, 169.254/16, etc. blocked while still unbreaking Browser Use / cloud browser flows under Clash TUN.

So if maintainers want the narrowest possible blast radius, browser.trusted_tun_cidrs may be worth considering versus a repo-wide RFC2544 opt-in.

teknium1 pushed a commit that referenced this pull request Apr 22, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes #3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
@teknium1

Copy link
Copy Markdown
Contributor

Superseded by #14166 (merged) — same class of users (TUN-mode proxy / Fake-IP via 198.18.0.0/15), now handled by the broader security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle that covers all of RFC 2544 + CGNAT + RFC 1918 + localhost. Thanks for the original write-up on the Python 3.11 is_private behavior change — it was useful context when reviewing the broader fix.

@teknium1 teknium1 closed this Apr 22, 2026
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists area/config Config system, migrations, profiles labels Apr 22, 2026
@alt-glitch

Copy link
Copy Markdown
Collaborator

Superseded by #14166 which provides a broader toggle covering this use case and more. Merged.

@alt-glitch

Copy link
Copy Markdown
Collaborator

Superseded by #14166 which provides a broader security.allow_private_urls toggle covering this use case and more. Merged.

ulasbilgen pushed a commit to ulasbilgen/hermes-adhd-agent that referenced this pull request May 1, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
aj-nt pushed a commit to aj-nt/hermes-agent that referenced this pull request May 1, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
02356abc pushed a commit to 02356abc/hermes-agent that referenced this pull request May 14, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
gweeteve pushed a commit to gweeteve/hermes-agent that referenced this pull request Jun 2, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
Egavasyug pushed a commit to Egavasyug/hermes-agent that referenced this pull request Jun 10, 2026
…ution

Adds security.allow_private_urls / HERMES_ALLOW_PRIVATE_URLS toggle so
users on OpenWrt routers, TUN-mode proxies (Clash/Mihomo/Sing-box),
corporate split-tunnel VPNs, and Tailscale networks — where DNS resolves
public domains to 198.18.0.0/15 or 100.64.0.0/10 — can use web_extract,
browser, vision URL fetching, and gateway media downloads.

Single toggle in tools/url_safety.py; all 23 is_safe_url() call sites
inherit automatically. Cached for process lifetime.

Cloud metadata endpoints stay ALWAYS blocked regardless of the toggle:
169.254.169.254 (AWS/GCP/Azure/DO/Oracle), 169.254.170.2 (AWS ECS task
IAM creds), 169.254.169.253 (Azure IMDS wire server), 100.100.100.200
(Alibaba), fd00:ec2::254 (AWS IPv6), the entire 169.254.0.0/16
link-local range, and the metadata.google.internal / metadata.goog
hostnames (checked pre-DNS so they can't be bypassed on networks where
those names resolve to local IPs).

Supersedes NousResearch#3779 (narrower HERMES_ALLOW_RFC2544 for the same class of
users).

Co-authored-by: kshitijk4poor <82637225+kshitijk4poor@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: RFC 2544 benchmark range (198.18.0.0/15) misclassified as private, breaking TUN proxy users

4 participants