Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,18 @@ jobs:
name: utls-upstream-advisories
path: ${{ runner.temp }}/utls-upstream-advisories.json
if-no-files-found: error
- name: Query official QUIC source-lineage advisories
if: always()
run: |
./scripts/govulncheck.sh --upstream-quic > "$RUNNER_TEMP/quic-official-upstream-advisories.json"
./scripts/check-upstream-advisories.sh "$RUNNER_TEMP/quic-official-upstream-advisories.json"
- name: Preserve official QUIC source-lineage advisory query
if: always()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: quic-official-upstream-advisories
path: ${{ runner.temp }}/quic-official-upstream-advisories.json
if-no-files-found: error

build:
name: Build ${{ matrix.goos }}/${{ matrix.goarch }}
Expand Down
26 changes: 17 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,10 @@ HTTP/3/UDP,UDP 不可用时让新 TCP 流继续走 HTTPS/HTTP/2/TCP;SOCKS5 U
AutoCAR 的身份验证、`autocar/2` 协议、TCP/UDP framing、速率协商、pacing、
熔断回退和资源边界均由 AutoCAR 实现;自有协议不提供第三方代理协议兼容模式。
Native 模式继续使用上游 `github.com/quic-go/quic-go`;web H3 则透明依赖
`github.com/apernet/quic-go` fork,并精确锁定到
`v0.63.1-0.20261004180939-a10df75c260c`,用于客户端 Chrome QUIC 握手画像。
`github.com/apernet/quic-go` 模块身份,并通过精确远程 replacement 使用自维护的
`github.com/cppla/quic-go`。其上游基线为
`v0.63.1-0.20261004180939-a10df75c260c`;版本、校验和与补丁维护见
[依赖维护说明](docs/DEPENDENCY-MAINTENANCE.md)。
项目不依赖外部代理应用模块,依赖边界由自动检查验证。

## 设计目标与实现边界
Expand Down Expand Up @@ -246,15 +248,21 @@ H2 可显式选择 `--h2-fingerprint=chrome-155`(固定的新模板)、`chro

`--h3-fingerprint=chrome-2026-10` 是默认值,固定使用上述依赖版本提供的完整客户端
QUIC/TLS 握手画像,并固定为与画像中版本参数一致的 QUIC v1。
新选项 `--h3-fingerprint=chrome-2026-10-resume` 在同一客户端重连时使用
有效票据恢复 TLS 1.3 会话;不启用 0-RTT,新连接仍重新认证。缓存仅在内存中、
按客户端隔离,重启进程后不会保留。默认值和已有 `chrome-2026-10` 保持完整握手。
会话恢复用于减少完整握手开销;尚未量化延迟收益,也不代表流量等同真实浏览器。
`--h3-fingerprint=native` 是互操作与故障回滚选项:它关闭该
fork 的 ChromeParrot 行为,但仍属于 web H3,不会切换成 `autocar/2` 或第三方代理
协议,也不会把依赖替换为 native 模式使用的上游模块。

升级前请检查客户端配置:显式的 `chrome-2026-08` 已被拒绝,不会静默映射到新画像;
请有意识地改为 `chrome-2026-10` 或 `native`。未填写 `h3-fingerprint` 的旧配置会采用
新默认值,因此也会改变客户端握手。新生成的 web `init` 客户端配置会明确固定
`chrome-2026-10`;仅运行服务端不需要迁移客户端画像选项。此更新不启用 H3 Chrome
画像的 TLS 会话恢复或 0-RTT,也不构成隐蔽性保证;旧采集结果仍只属于其冻结版本。
从十月画像之前的版本升级时,请检查客户端配置:显式的 `chrome-2026-08` 已被拒绝,
不会静默映射到新画像;请有意识地改为 `chrome-2026-10` 或 `native`。那些版本中
未填写 `h3-fingerprint` 的配置升级后会采用十月默认画像,改变客户端握手。
本次会话恢复改造不再改变默认画像。新生成的 web `init` 客户端配置会明确固定
`chrome-2026-10`;仅运行服务端不需要迁移客户端画像选项。只有显式选择上述
`-resume` 选项才启用 Chrome H3 会话恢复;所有画像仍禁用 0-RTT。
这不构成隐蔽性保证;旧采集结果仍只属于其冻结版本。

在启动本地代理前,可用同一组隧道参数做一次真实端到端探测:

Expand Down Expand Up @@ -451,8 +459,8 @@ iptables 下的 UDP `sendmsg` 直接返回 `EPERM`。有损阶段只硬验证协

`scripts/` 中:

- `check-dependency-boundary.sh` 只允许 go.mod 精确锁定上述唯一
`github.com/apernet/quic-go` 版本,拒绝已知外部代理应用模块、local replace 和
- `check-dependency-boundary.sh` 只允许 go.mod 精确锁定上述 web QUIC 基线及
两个自维护库的远程版本,保持官方 native QUIC 不被替换,拒绝已知外部代理应用模块、local replace 和
vendored/copied 外部源码目录,并扫描已跟踪及未跟踪的 Go 源;
- `docker-integration.sh` 在隔离容器网络中验证 QUIC、TLS、自动回退、`doctor`、错误令牌拒绝和非 root 只读运行;
- `govulncheck.sh` 安装固定版本的扫描器并检查可达漏洞;上游 quic-go 公告不会自动
Expand Down
36 changes: 22 additions & 14 deletions SECURITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,13 +37,16 @@ AutoCAR does not hide endpoint IPs, packet size, timing, traffic volume, or the
use of QUIC/TLS. Web mode serves a real configured H1/H2/H3 origin and routes
unauthenticated requests through that cover, but normal HTTP semantics do not
make all observable behavior identical to a browser. The H2 client uses a fixed
Chrome 133 uTLS ClientHello reference; that is not a claim about the complete
Chrome 133 or explicitly selected Chrome 155 uTLS ClientHello reference; that is not a claim about the complete
TLS/H2 fingerprint. Web H3 defaults to the fixed `chrome-2026-10` client
handshake profile from the exactly pinned `github.com/apernet/quic-go` fork and
uses a zero-length source CID. That client-only profile does not reproduce the
relay, H3 SETTINGS, CONNECT traffic, packet sizes, reuse, or timing. This profile
still disables TLS session resumption: a replacement connection performs a full
handshake even with a configured session cache. Neither the dependency update
handshake even with a configured session cache. The separate opt-in
`chrome-2026-10-resume` profile can resume TLS using a bounded, per-client
memory-only cache. It still requires new proxy authentication for each physical
connection, and all profiles keep 0-RTT disabled. Neither the dependency update
nor the profile is proof of a classification advantage. AutoCAR makes no
undetectability guarantee.

Expand Down Expand Up @@ -241,13 +244,15 @@ parties.
## Dependency boundary

Native AutoCAR builds on official upstream quic-go. Web H3 uses
`github.com/apernet/quic-go` pinned exactly to
`v0.63.1-0.20261004180939-a10df75c260c`; H2 uses x/net HTTP/2 and uTLS for its
fixed ClientHello reference. A targeted CI boundary allows only that exact web
QUIC fork version and one exact published `github.com/cppla/utls` remote
replacement for the original uTLS module. The same replacement covers H2 and
the web-H3 adapter; direct imports of the replacement path are rejected to
avoid a second uTLS module identity. The boundary rejects the known external
`github.com/apernet/quic-go` at source baseline
`v0.63.1-0.20261004180939-a10df75c260c`, globally replaced by an exact
published `github.com/cppla/quic-go` revision; H2 uses x/net HTTP/2 and uTLS.
A targeted CI boundary allows only that exact web QUIC source baseline and
two exact published remote replacements: the maintained QUIC fork and
`github.com/cppla/utls` for the original uTLS module. The uTLS replacement
covers H2 and the web-H3 adapter; direct imports of either replacement path
are rejected to avoid duplicate module identities. Official native QUIC is
not replaced. The boundary rejects the known external
proxy application module, local replacements, and copied/vendored
external-source directories, and scans tracked
and untracked Go source.
Expand All @@ -265,14 +270,17 @@ has no vulnerabilities.

The managed uTLS replacement has the same database-identity limitation, even
though its original import paths are retained. CI separately queries the exact
original uTLS baseline, retains the JSON output, and stops for human review if
advisories are returned. This is not a reachability result for the fork. The
original uTLS baseline and the web-H3 lineage's official QUIC baseline, retains
both JSON outputs, and stops for human review if advisories are returned.
These queries are not reachability results for the forks. The
review must also consider relevant Go TLS security fixes. See the executable
update and advisory-review procedure in
[dependency maintenance](docs/DEPENDENCY-MAINTENANCE.md).

The current client profile is versioned as `chrome-2026-10`; the retired
`chrome-2026-08` name is rejected, not aliased. Existing client configurations
that omit the profile select the new default on upgrade. Review that handshake
change before deployment; new web `init` client files pin the profile explicitly.
`chrome-2026-08` name is rejected, not aliased. When upgrading from builds predating
the October profile, client configurations that omit the profile adopt the
October default and change their handshake. Review that migration before
deployment. This resumption update does not change the current default;
new web `init` client files pin the profile explicitly.
Server-only deployments have no client-profile setting to migrate.
4 changes: 3 additions & 1 deletion THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ The SHA-256 value is calculated from the upstream file's original bytes; line en

## `github.com/apernet/quic-go` `v0.63.1-0.20261004180939-a10df75c260c`

Effective source replacement: `github.com/cppla/quic-go v0.63.1-0.20261009040133-c1cae948af15`.

### `LICENSE`

SHA-256: `77d0b7b53e8abb84cf4dd3f9945a7fdf27044240d2e8023966a721a9a46fe96e`
Expand Down Expand Up @@ -427,7 +429,7 @@ SOFTWARE.

## `github.com/refraction-networking/utls` `v1.8.3-0.20261006222701-ff1b50fbbe9a`

Effective source replacement: `github.com/cppla/utls v0.0.0-20261009014536-ff869e255a30`.
Effective source replacement: `github.com/cppla/utls v0.0.0-20261009031926-14c2a4cb1403`.

### `LICENSE`

Expand Down
2 changes: 1 addition & 1 deletion cmd/autocar/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -115,7 +115,7 @@ func addTunnelFlags(fs *flag.FlagSet, flags *tunnelFlags) {
flags.h2Fingerprint = h2FingerprintFlag(tunnel.FingerprintChrome133)
fs.Var(&flags.h2Fingerprint, "h2-fingerprint", "web H2 wire profile: chrome-133, chrome-155, or native; empty uses chrome-133 (h2/web-auto only)")
flags.h3Fingerprint = h3FingerprintFlag(tunnel.H3FingerprintChrome202610)
fs.Var(&flags.h3Fingerprint, "h3-fingerprint", "web H3 wire profile: chrome-2026-10 or native; chrome-2026-08 is retired")
fs.Var(&flags.h3Fingerprint, "h3-fingerprint", "web H3 wire profile: chrome-2026-10 (full handshake), chrome-2026-10-resume (opt-in tickets, no 0-RTT), or native; chrome-2026-08 is retired")
fs.StringVar(&flags.pacing, "pacing", "adaptive", "QUIC application pacing: adaptive, reno, or fixed-rate")
fs.StringVar(&flags.pacingProfile, "pacing-profile", "balanced", "adaptive pacing profile: conservative, balanced, or aggressive")
fs.Uint64Var(&flags.uploadMbps, "upload-mbps", 0, "client-to-relay fixed pacing rate in Mbit/s")
Expand Down
2 changes: 1 addition & 1 deletion cmd/autocar/h3_profile_migration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func TestRetiredH3FingerprintConfigCommandsStayOffline(t *testing.T) {
}

func TestH3FingerprintFlagPreservesNonRetiredValues(t *testing.T) {
for _, profile := range []string{"", "chrome-2026-10", "native", "unused-by-native"} {
for _, profile := range []string{"", "chrome-2026-10", "chrome-2026-10-resume", "native", "unused-by-native"} {
fs := flag.NewFlagSet("native-client", flag.ContinueOnError)
fs.SetOutput(io.Discard)
var tf tunnelFlags
Expand Down
9 changes: 8 additions & 1 deletion cmd/autocar/web_cli_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,9 @@ func TestTunnelHelpListsWebTransports(t *testing.T) {
if flags.h3Fingerprint != h3FingerprintFlag(tunnel.H3FingerprintChrome202610) || !strings.Contains(output.String(), "h3-fingerprint") || !strings.Contains(output.String(), "chrome-2026-10") {
t.Errorf("tunnel help omitted versioned H3 fingerprint profile: %s", output.String())
}
if !strings.Contains(output.String(), "chrome-2026-10-resume") || !strings.Contains(output.String(), "no 0-RTT") {
t.Errorf("tunnel help omitted the explicit H3 resumption policy: %s", output.String())
}
if flags.h2Fingerprint != h2FingerprintFlag(tunnel.FingerprintChrome133) || !strings.Contains(output.String(), "h2-fingerprint") || !strings.Contains(output.String(), "chrome-155") {
t.Errorf("tunnel help omitted H2 profiles or changed the legacy default: %s", output.String())
}
Expand Down Expand Up @@ -189,12 +192,15 @@ func TestBuildExplicitAndAutomaticWebDialers(t *testing.T) {
for _, test := range []struct {
mode string
wantPacket bool
profile h3FingerprintFlag
}{
{mode: "h3", wantPacket: true},
{mode: "h2", wantPacket: false},
{mode: "web-auto", wantPacket: true},
{mode: "h3", wantPacket: true, profile: h3FingerprintFlag(tunnel.H3FingerprintChrome202610Resume)},
{mode: "web-auto", wantPacket: true, profile: h3FingerprintFlag(tunnel.H3FingerprintChrome202610Resume)},
} {
t.Run(test.mode, func(t *testing.T) {
t.Run(test.mode+"/"+string(test.profile), func(t *testing.T) {
dialer, err := buildTunnelDialer(tunnelFlags{
server: "127.0.0.1:443",
mode: test.mode,
Expand All @@ -206,6 +212,7 @@ func TestBuildExplicitAndAutomaticWebDialers(t *testing.T) {
fallbackTTL: time.Second,
pacing: "adaptive",
pacingProfile: "balanced",
h3Fingerprint: test.profile,
})
if err != nil {
t.Fatal(err)
Expand Down
Loading
Loading