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
27 changes: 18 additions & 9 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,19 @@ jobs:
strategy:
fail-fast: false
matrix:
go: ['1.27.1', '1.27.x']
include:
- go: '1.27.2'
version: '1.27.2'
- go: '1.27.x'
# Reject stale manifests and caches below the security baseline.
version: '>=1.27.2 <1.28.0'
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: ${{ matrix.go }}
go-version: ${{ matrix.version }}
# Floating versions must not reuse an older cached security patch.
check-latest: ${{ matrix.go == '1.27.x' }}
cache: true
- name: Verify formatting
if: matrix.go == '1.27.x'
Expand Down Expand Up @@ -64,26 +71,26 @@ jobs:
if-no-files-found: error

macos-test:
name: macOS test (Go 1.27.1)
name: macOS test (Go 1.27.2)
runs-on: macos-14
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.1
go-version: 1.27.2
cache: true
- name: Test
run: go test -shuffle=on -count=1 ./...

windows-udp-test:
name: Windows UDP continuity (Go 1.27.1)
name: Windows UDP continuity (Go 1.27.2)
runs-on: windows-2022
timeout-minutes: 10
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.1
go-version: 1.27.2
cache: true
- name: Exercise native UDP continuity and platform bind errors
shell: pwsh
Expand Down Expand Up @@ -144,7 +151,8 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x
go-version: '>=1.27.2 <1.28.0'
check-latest: true
cache: true
- run: make race

Expand All @@ -155,7 +163,7 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.1
go-version: 1.27.2
cache: true
- name: Run govulncheck
run: ./scripts/govulncheck.sh
Expand Down Expand Up @@ -203,7 +211,8 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x
go-version: '>=1.27.2 <1.28.0'
check-latest: true
cache: true
- name: Cross-build
env:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/codeql.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x
go-version: '>=1.27.2 <1.28.0'
check-latest: true
cache: true
- uses: github/codeql-action/init@2892aa5e19bbd11bc0cff5427e3b750a04d9e3c2 # v4.38.2
with:
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/fuzz.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x
go-version: '>=1.27.2 <1.28.0'
check-latest: true
cache: true
- name: Fuzz ${{ matrix.target }}
run: >-
Expand Down
3 changes: 2 additions & 1 deletion .github/workflows/netem.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ jobs:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.x
go-version: '>=1.27.2 <1.28.0'
check-latest: true
cache: true
- name: Install network test tools
run: |
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ jobs:
persist-credentials: false
- uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
with:
go-version: 1.27.1
go-version: 1.27.2
cache: true
- name: Verify tagged source and successful main checks
id: source
Expand Down
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# syntax=docker/dockerfile:1

FROM --platform=$BUILDPLATFORM golang:1.27.1-alpine3.24@sha256:cf6fca6641884b8433441b2b0652976f975e1d0fdd26d177eaaf8596087f3125 AS build
FROM --platform=$BUILDPLATFORM golang:1.27.2-alpine3.24@sha256:85dc1069ac644ea3c527b177303a406eb3358192816cd7f9e5848eb658851673 AS build

ARG TARGETOS
ARG TARGETARCH
Expand Down
9 changes: 8 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ Web cover 的目标是正常网站兼容、减少主动探测暴露并在 UDP

## 快速开始

客户端和服务端分别在各自机器上构建,需要 Go 1.27.1 或更高版本:
客户端和服务端分别在各自机器上构建,需要 Go 1.27.2 或更高版本:

```bash
git clone https://github.com/cppla/autocar.git
Expand Down Expand Up @@ -227,6 +227,7 @@ H3。`--cover-root` 和 `--cover-upstream` 必须且只能选一个:
--ca server.crt \
--token-file token \
--transport web-auto \
--h2-fingerprint chrome-155 \
--h3-fingerprint chrome-2026-10
```

Expand All @@ -237,6 +238,12 @@ CONNECT-UDP,`h2` 不支持 UDP。Web 模式要求 TCP/UDP 使用相同数字
`--disable-tcp-fallback`,也不能在两端配置 mTLS。详细能力、边界和安全的验证方法见
[Web-cover 模式](docs/WEB_COVER.md)。

H2 可显式选择 `--h2-fingerprint=chrome-155`(固定的新模板)、`chrome-133`
(兼容旧模板)或 `native`(Go TLS 调试/互操作)。新生成的 web `init` 客户端配置
明确固定为 `chrome-155`;旧配置省略该字段时仍使用 `chrome-133`,不会静默改变。
此选项也作用于 `web-auto` 的 H2 回退路径,不改变 H3。已加入与真实 Chrome 155
历史抓取样本的冷 ClientHello 逐字段回归,但不代表完整流量等同浏览器。

`--h3-fingerprint=chrome-2026-10` 是默认值,固定使用上述依赖版本提供的完整客户端
QUIC/TLS 握手画像,并固定为与画像中版本参数一致的 QUIC v1。
`--h3-fingerprint=native` 是互操作与故障回滚选项:它关闭该
Expand Down
4 changes: 2 additions & 2 deletions THIRD_PARTY_NOTICES.md
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,7 @@ SOFTWARE.

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

Effective source replacement: `github.com/cppla/utls v0.0.0-20261008110020-7295e1b508c3`.
Effective source replacement: `github.com/cppla/utls v0.0.0-20261009014536-ff869e255a30`.

### `LICENSE`

Expand Down Expand Up @@ -527,7 +527,7 @@ rights granted to you under this License for this implementation of Go
shall terminate as of the date such litigation is filed.
```

## `golang.org/x/net` `v0.59.0`
## `golang.org/x/net` `v0.60.0`

### `LICENSE`

Expand Down
50 changes: 44 additions & 6 deletions cmd/autocar/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ type tunnelFlags struct {
openTimeout time.Duration
h2WriteTimeout time.Duration
fallbackTTL time.Duration
h2Fingerprint h2FingerprintFlag
h3Fingerprint h3FingerprintFlag
pacing string
pacingProfile string
Expand All @@ -44,6 +45,36 @@ type tunnelFlags struct {
eventHandler tunnel.ClientEventHandler
}

// h2FingerprintFlag validates both CLI and JSON input before any credentials or
// network activity. An empty legacy value keeps the Chrome 133 default.
type h2FingerprintFlag string

func (value *h2FingerprintFlag) String() string {
if value == nil {
return ""
}
return string(*value)
}

func (value *h2FingerprintFlag) Get() any { return value.String() }

func (value *h2FingerprintFlag) Set(profile string) error {
if err := validateH2Fingerprint(profile); err != nil {
return err
}
*value = h2FingerprintFlag(profile)
return nil
}

func validateH2Fingerprint(profile string) error {
switch tunnel.FingerprintProfile(profile) {
case "", tunnel.FingerprintChrome133, tunnel.FingerprintChrome155, tunnel.FingerprintNative:
return nil
default:
return errors.New("invalid --h2-fingerprint; want chrome-133, chrome-155, or native (empty uses chrome-133)")
}
}

// h3FingerprintFlag rejects the retired wire image at parse time, including
// commands that would not otherwise construct an H3 client. Other values retain
// their existing transport-specific validation behavior.
Expand Down Expand Up @@ -81,6 +112,8 @@ func addTunnelFlags(fs *flag.FlagSet, flags *tunnelFlags) {
fs.DurationVar(&flags.openTimeout, "open-timeout", 15*time.Second, "overall remote stream open timeout")
fs.DurationVar(&flags.h2WriteTimeout, "h2-write-timeout", 30*time.Second, "shared H2 connection write timeout (h2/web-auto only; 0 uses 30s; not an idle or per-stream timeout)")
fs.DurationVar(&flags.fallbackTTL, "fallback-cooldown", 30*time.Second, "base time to prefer the TCP fallback after a UDP path failure (each retry is jittered +/-20%)")
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.StringVar(&flags.pacing, "pacing", "adaptive", "QUIC application pacing: adaptive, reno, or fixed-rate")
Expand All @@ -95,6 +128,9 @@ type closeDialer interface {
}

func buildTunnelDialer(flags tunnelFlags) (closeDialer, error) {
if err := validateH2Fingerprint(string(flags.h2Fingerprint)); err != nil {
return nil, err
}
if tunnel.H3FingerprintProfile(flags.h3Fingerprint) == tunnel.H3FingerprintChrome202608 {
return nil, tunnel.ErrH3FingerprintProfileRetired
}
Expand Down Expand Up @@ -251,12 +287,13 @@ func buildTunnelDialer(flags tunnelFlags) (closeDialer, error) {
return newWebSnapshotDialer(client), nil
case "h2":
client, err := tunnel.NewWebH2Client(tunnel.WebH2ClientConfig{
ServerAddress: flags.server,
Token: token,
TLSConfig: tlsConfig,
HandshakeTimeout: flags.openTimeout,
DialTimeout: flags.dialTimeout,
WriteByteTimeout: flags.h2WriteTimeout,
ServerAddress: flags.server,
Token: token,
TLSConfig: tlsConfig,
FingerprintProfile: tunnel.FingerprintProfile(flags.h2Fingerprint),
HandshakeTimeout: flags.openTimeout,
DialTimeout: flags.dialTimeout,
WriteByteTimeout: flags.h2WriteTimeout,
})
if err != nil {
return nil, err
Expand All @@ -267,6 +304,7 @@ func buildTunnelDialer(flags tunnelFlags) (closeDialer, error) {
ServerAddress: flags.server,
Token: token,
TLSConfig: tlsConfig,
H2FingerprintProfile: tunnel.FingerprintProfile(flags.h2Fingerprint),
H3FingerprintProfile: tunnel.H3FingerprintProfile(flags.h3Fingerprint),
HandshakeTimeout: flags.openTimeout,
H3DialTimeout: flags.dialTimeout,
Expand Down
Loading
Loading