Skip to content

Latest commit

 

History

History
45 lines (32 loc) · 2.43 KB

File metadata and controls

45 lines (32 loc) · 2.43 KB

CLAUDE.md

This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.

Setup

git config core.hooksPath .githooks   # enable pre-commit fmt check

Build Commands

cargo build              # dev build
cargo build --release    # release build (stripped, LTO)
cargo check              # type-check without building
cargo clippy             # lint
cargo test                     # run all integration tests
cargo test --test stealth_tests  # run specific test file

Architecture

Stealth HTTPS forward proxy that auto-obtains TLS certs via ACME/Let's Encrypt and disguises itself as a normal nginx web server.

Request Flow

  1. TLS accept (tls.rs): ACME acceptor handles TLS-ALPN-01 challenges transparently; regular connections get a TLS stream (HTTP/1.1 or HTTP/2) with auto-renewed Let's Encrypt cert.
  2. Stealth gate (stealth.rs): Non-proxy requests (no absolute URI, no CONNECT) → fake nginx 404.
  3. Auth gate (auth.rs): Proxy requests with invalid/missing Proxy-Authorization: Basic ... → 407 with Proxy-Authenticate header (enables browser auth prompts). Non-proxy requests → fake 404.
  4. CONNECT tunnel (proxy.rs): hyper::upgrade::on() + tokio::io::copy_bidirectional to target.
  5. HTTP forward (proxy.rs): Rewrites absolute URI to path-only, strips proxy headers, forwards via hyper::client::conn::http1.

Key Design Decisions

  • Stealth for non-proxy traffic: Non-proxy requests (no absolute URI, no CONNECT) return nginx 404. Proxy requests with missing/wrong auth get 407 so real clients (Chrome) can authenticate.
  • HTTP/2 extended CONNECT: enable_connect_protocol() (RFC 8441) enables browser proxy compatibility (Chrome, Firefox).
  • hyper 1.x with upgrades: http1::Builder must use .with_upgrades() for CONNECT tunneling to work.
  • Proxy detection: req.uri().authority().is_some() (absolute URI) or Method::CONNECT.
  • ACME on port 443 only: Uses TLS-ALPN-01 challenge type, no port 80 listener needed.
  • tokio-rustls-acme v0.6 API: AcmeState is a Stream; drive it with StreamExt::next() in a spawned task. start_handshake.into_stream(rustls_config) requires an Arc<ServerConfig> built with state.resolver().

Config

Copy config.example.yaml to config.yaml. Structure: listen, domain, acme (email, staging bool, cache_dir), users (username/password list), stealth (server_name).