Repository navigation
Releases
impersonate-mcp 0.1.0
Full Changelog: v1.5.0...mcp-v0.1.0
v1.5.0 — Optional MCP server
Highlights
Optional MCP server (impersonate-mcp/)
A new stdio MCP server, built on the official Go SDK, lets an AI agent configure the proxy and verify the result. It is a plain client of the existing management API, so the proxy itself is unchanged.
| Tool | What it does |
|---|---|
get_config |
Show the active runtime settings |
set_tls_preset |
Switch the TLS preset (chrome, firefox, safari, …) |
set_custom_hello |
Set an arbitrary ClientHello and switch to the custom preset |
set_user_agent |
Set the User-Agent ("auto" derives it from the preset) |
set_client_ip |
Set the spoofed client IP |
set_upstream |
Enable/disable the upstream proxy and select one by name |
verify_fingerprint |
Fetch tls.peet.ws/api/all through the proxy and summarise ja3_hash, ja4, akamai_fingerprint, user_agent and ip |
--read-onlyexposes onlyget_configandverify_fingerprint.- The management API URL, proxy URL, CA certificate and echo endpoint can be set by flag or environment variable (
IMPERSONATE_MGMT_URL, …). verify_fingerprintwarns when the host filter excludes the echo host, since the result would not be impersonated.
Opt-in, no impact on existing setups
The MCP server is a separate Go module. make build and the Docker image are unchanged and do not include it. To use it:
make build-mcp # → impersonate-mcp/impersonate-mcp
make test-mcpSetup instructions, the full option list and notes are in the README (English / 日本語 / 简体中文): MCP server (optional).
Notes
- The management API has no authentication, and the MCP server lets an agent change the proxy's settings. Keep
mgmt_listenon loopback and use--read-onlywhen the agent should only observe. verify_fingerprintmakes one request to an external service (tls.peet.wsby default).- The HTTP/2 fingerprint is controlled by
http2inconfig.yaml, not by the TLS preset.
Full changelog: v1.4.0...v1.5.0
v1.4.0
v1.4.0
Highlights
Host filter. The proxy can now impersonate only the targets you choose, instead of every host it sees.
filter:
mode: "include" # off (default) | include | exclude
hosts:
- "www.example.com" # exact host
- "*.example.com" # any subdomain, but not example.com itselfincludeimpersonates only the listed hosts,excludeimpersonates everything except them, andoff(the default) keeps the previous behavior.- Hosts that are not impersonated are passed through untouched. An HTTPS
CONNECTis relayed as raw bytes without terminating TLS, so the client keeps seeing the origin's real certificate (no CA trust is needed for these hosts) and its own TLS fingerprint. Plain-HTTP requests are forwarded without header rewriting. Both still go through the upstream proxy when one is enabled. - The decision is made before a certificate is minted or a ClientHello is built, so a pass-through costs neither. It also means
CONNECTto non-TLS ports works for hosts outside the filter. - Passed-through tunnels are logged as
PASSTHROUGH host:port, which helps you find the CDN and challenge hosts a page loads and add them to the list.
Runtime control. filter_mode and filter_hosts are available through the management API (POST /api/config), and GET /api/config reports them. filter_hosts replaces the whole list, and changes apply to new connections only.
curl -s -X POST http://127.0.0.1:8081/api/config \
-H "Content-Type: application/json" \
-d '{"filter_mode":"include","filter_hosts":["www.example.com","*.example.com"]}'Behavior notes
- Matching: case-insensitive, port ignored.
example.commatches exactly that host.*.example.commatches subdomains at any depth but notexample.com, so list both to cover the apex. IP literals match exactly. - Validation: ports, paths, CIDR ranges and any other use of
*are rejected, at startup and from the management API. - Atomic updates:
POST /api/confignow validates every part of a patch before applying any of it, withupstream_selectapplied last. A rejected patch changes nothing; previously an invalid value could leave an earlier field applied. - Upgrading: with the default
mode: off, nothing changes. Existing configurations keep working without edits.
Known limitations
- Plain-HTTP requests to hosts that are not impersonated are re-serialized by the proxy, so their header order is not preserved. HTTPS pass-through is relayed byte for byte.
- A page usually loads other hosts too (CDNs, challenge scripts). Add them to the list, or the WAF will see a mix of impersonated and real fingerprints.
- The Chrome extension has no UI for the filter yet; use
config.yamlor the management API. - The limitations from earlier releases still apply (see the README's Limitations section), including
ws://throughCONNECTfor impersonated hosts.
Docker
docker pull ghcr.io/ytkoka/impersonate-proxy:1.4.0The Chrome extension is unchanged in this release (still 1.2.0).
Full changelog: v1.3.0...v1.4.0
v1.3.0
v1.3.0
Highlights
WebSocket support. The proxy now carries WebSocket upgrades instead of breaking them.
wss://works through theCONNECTtunnel, and plainws://works when the client sends it as an ordinary proxy request.- When the origin speaks HTTP/2, the upgrade goes over a separate HTTP/1.1 connection whose TLS ClientHello offers only
http/1.1in ALPN and omits the ALPS extension, the way a browser opens a WebSocket socket. Every other request on the tunnel stays on HTTP/2, so the JA4 and HTTP/2 fingerprints of ordinary traffic are unchanged (verified againsttls.peet.ws: identical to v1.2.0). - After the
101 Switching Protocols, the stream is relayed byte for byte, so extensions such aspermessage-deflatepass through untouched.
Before this release, an upgrade over an h2 origin lost its Upgrade and Connection headers and got an ordinary response, and over an HTTP/1.1 origin the connection stalled right after the 101.
Fixes
- Chunked request bodies (
Transfer-Encoding: chunked) are now forwarded with their framing intact on HTTP/1.1 upstreams. Previously the body was sent unframed. Expect: 100-continueis answered by the proxy and the header is stripped;1xxresponses from the origin are relayed instead of being mistaken for the final response.
Tests
- Table-driven tests for
isPublicIPv4, thanks to @Macawls (#9). - New tests for the WebSocket relay on all three paths, declined upgrades, upgrade dial failures, and the ClientHello ALPN/ALPS handling for every TLS preset.
Known limitations
ws://throughCONNECT(for example from browsers) is not supported yet; the tunnel assumes TLS.- WebSocket over HTTP/2 (RFC 8441) is not supported.
- The ClientHello used for WebSocket connections has not been compared against a real browser capture, and the handshake keeps the configured
header_orderrather than a browser's WebSocket-specific order. - Request trailers on chunked bodies are dropped, and large HTTP/2 request bodies are still sent as a single DATA frame.
See the README's Limitations section for the full list.
Docker
docker pull ghcr.io/ytkoka/impersonate-proxy:1.3.0The Chrome extension is unchanged in this release (still 1.2.0).
Full changelog: v1.2.0...v1.3.0
v1.2.0
v1.2.0 — User-Agent auto mode
user_agent: "auto" now picks the User-Agent that matches the current tls.preset, so the TLS fingerprint and the UA no longer contradict each other. Switching the preset switches the UA too.
chrome/firefox/safari/edge/iosmap to Chrome 131, Firefox 132, Safari 17.2, Edge 131, and iPhone iOS 17.1random/golang/custompresets leave the client's User-Agent untouched"random"remains a separate mode that picks a random UA from a built-in list per request
Behavior change
- The
edgepreset now sends the same TLS ClientHello aschrome, andiosthe same assafari. The previous uTLS Edge 85 / iOS 14 hellos contradicted the Edg/131 and iOS 17 User-Agents. Their JA3/JA4 values now match Chrome and Safari respectively
Chrome extension
- Added an "Auto" checkbox next to "Random" for the User-Agent field (mutually exclusive)
v1.1.0
v1.1.0 — Upstream proxy support
Support for routing target connections through an upstream proxy (SOCKS5 / HTTP CONNECT).
Until now, source-IP spoofing only rewrote the X-Forwarded-For / True-Client-IP headers — the TCP connection's real source IP never changed. With this release, the connection to the target can be routed through a SOCKS5 or HTTP CONNECT proxy first, so the real egress IP changes too. This lets you separate "fingerprint looks fine but the IP is bad" from "IP is clean but the fingerprint is wrong" when testing WAF/bot-detection rules.
- Supported schemes: socks5 / socks5h (remote DNS resolution) / http (CONNECT)
- https:// (TLS-terminating) upstreams are intentionally not supported — they would strip the uTLS ClientHello this tool exists to control
- Multiple proxies can be registered; select by name, or use rotate / random
- suppress_ip_headers_when_active: while an upstream is active, XFF/True-Client-IP are automatically skipped (on by default) so a clean real IP doesn't also claim a spoofed one
- Can be enabled and switched at runtime via the management API or Chrome extension — no restart required
API changes
- New GET /api/upstream — returns upstream state (enabled, select, proxy names only). URLs and credentials are never exposed
- POST /api/config is now a genuine partial update — fields you omit are left unchanged (previously, omitted fields were silently overwritten with empty values)
Chrome extension
- Added an "Upstream proxy" toggle and proxy-selection dropdown
⚠️ Developer Mode (unpacked) builds only, for now. The Chrome Web Store release hasn't been updated for this yet. If you're using the Web Store version, control upstream settings via the management API directly (curl, etc.) in the meantime
Docker
- Added an upstream: section to config.docker.yaml
- Fixed a build-breaking bug where the Dockerfile didn't copy the new upstream/ package into the build stage
v1.0.0
Highlights
First tagged release. This establishes a versioning baseline going forward — it
bundles everything shipped so far, not just changes since some prior release.
- TLS fingerprinting (JA3 / JA4) via uTLS presets (
chrome/firefox/
safari/edge/ios/random/golang) or a fully custom
custom_helloClientHello spec - HTTP/2 fingerprinting: SETTINGS values/order, WINDOW_UPDATE, pseudo-header
order - HTTP header control: header order, User-Agent override, arbitrary
add/remove, source IP spoofing (X-Forwarded-For/True-Client-IP) - Runtime reconfiguration via a local management API — switch fingerprint
profiles without restarting the proxy - Chrome extension (Manifest V3) to toggle the proxy and fingerprint
profile from the toolbar - Docker support:
docker compose up -d— no local Go/Make install
required. Multi-arch images (amd64/arm64) published to
ghcr.io/ytkoka/impersonate-proxy
Installation
- Native (Go 1.22+): see README
- Docker: see README#docker
Notes
Intended for authorized security testing only (WAF / bot-detection testing on
systems you own or have explicit permission to test).