Repository navigation
Conversation
fix(docker): install web dependencies from the workspace lockfile
#5546) transport.tls.trustedCaFile is installed as RootCAs, and omitting it sets InsecureSkipVerify, so system-root verification cannot be selected directly. Add an opt-in transport.tls.useSystemRoots that enables certificate verification with RootCAs nil when no trusted CA file is set, allowing Go to use system roots and native verification on macOS and Windows. Defaults are unchanged.
Move the frpc-only TLS option into the shared TLSConfig as transport.tls.useSystemCa, replacing the unpublished useSystemRoots. When set, frpc verifies the server with the system CAs; when trustedCaFile is also set, its CAs are appended to the system pool so both are trusted. Failures to load either source abort the connection instead of falling back. Enabling the option without effective TLS is a configuration error, and the field is rejected in frps transport.tls and in both webServer.tls sections. Fixes #5545
A constructor that binds a port before its fallible preparation leaks the listener when a later step returns an error, and no one can close it because the returned service is nil. pkg/util/http: load the TLS keypair and build the auth middleware before net.Listen, so binding is the last step that can run and a failure leaves no bound port behind. server: create the web server after auth, the tcpmux, main, kcp, quic, ssh gateway and vhost listeners, and the nat hole controller, following what frpc already does. If the web server itself fails, close the resources created so far before returning. The port managers start their cleanup workers on creation, so they are created after every fallible step too, and the metrics collectors keep being enabled only when the web server is configured. Add regression tests for all three failure paths.
KCPListener hands accepted sessions to a consumer through an unbuffered channel, but its accept goroutine wrote to that channel without watching for shutdown. A Close racing with a handoff left the goroutine blocked forever, holding the session it was about to deliver, because closing the underlying listener does not unblock a send. The close flag it read was also written by Close without synchronisation. Watch a done channel in the handoff and close the connection that was never delivered, so the goroutine can exit and the session is released. Accept now returns ErrKCPListenerClosed as soon as the listener is closed instead of blocking on the channel, and the close state is a done channel guarded by sync.Once. A ready handoff can still win the race with Close; the session then belongs to the caller of Accept, matching how WebsocketListener behaves. Tests cover a pending handoff that is cancelled by Close and a blocked Accept that is woken by it.
The https2http and https2https plugins read their certificate and key once when the proxy starts, so a certificate that an external tool renews on disk is only picked up after the proxy itself is recreated. frpc reload rebuilt proxies only when their configuration changed, and a rebuild closes the work connections that are already in use. Let the plugin own the refresh. The HTTPS plugin server keeps the active keypair and the last refresh error in one immutable snapshot published through an atomic pointer, so a TLS handshake only loads that pointer and never touches the disk. Reload rereads both files, and when their bytes match the published pair it returns without parsing or replacing anything; otherwise it parses the pair and swaps the snapshot in. A read or parse failure keeps the previous certificate serving and records the error next to it. frpc reload collects the reloadable plugins of the live proxies and applies the new configuration inside the same reload transaction that also serializes control handoff, so a reload that races a reconnect still refreshes the plugins of the published control. The certificate files are read only after that transaction has been released. The config store API keeps applying configuration without refreshing certificates, because only an explicit reload may reread files. Proxy status reports a refresh failure next to the still running proxy instead of dropping its remote address. No configuration field is added, and frps and the wire protocol are unchanged. Without a reload nothing changes: certificates are still read once at startup, generated self-signed certificates still never reload, and a proxy whose configuration changed still goes through the existing rebuild path.
Add a separate HTTP listener for observability, configured through metricsServer in both frps and frpc. It serves /healthz and, when pprofEnable is set, the pprof handlers except /debug/pprof/cmdline. It never serves the dashboard, static assets or the admin API, and it does not touch the global assets filesystem. frps also serves /metrics on this listener with both runtime and business metrics, enabled by metricsServer.port alone. The in-memory collector still starts only with the dashboard, and the Prometheus collector starts either with metricsServer or with enablePrometheus plus webServer, so a configuration that collected nothing before still collects nothing. The /healthz, /metrics and pprof endpoints on webServer, and enablePrometheus, keep working but are deprecated and log a hint at startup. frpc does not serve /metrics yet.
Add pkg/metrics/text, a small Prometheus text-format writer, and use it for frpc's /metrics. The endpoint lives only on metricsServer and is covered by its Basic Auth, like the other endpoints there. The package emits the Go runtime families and, on Linux, the process families read from /proc. Their names, types and help strings match client_golang's default collectors, so existing Go runtime dashboards keep working. It deliberately emits no frp_ business metrics and none of client_golang's promhttp_ handler metrics. Writing the subset ourselves keeps client_golang and protobuf out of the frpc binary; both are still used by frps and by the tests here.
Record the deprecation of the observability endpoints on webServer in doc/deprecations.md, so the removal target is in one place next to the other active deprecations. The webServer /healthz, the frps webServer /metrics with its top-level enablePrometheus switch, and the pprof handlers with webServer.pprofEnable are deprecated since v0.72.0, when metricsServer was added. They are replaced by metricsServer, whose /healthz, /metrics and pprofEnable are independent of the dashboard listener.
) InWorkConn published readCh and sendCh into pxy fields while holding pxy.mu, then read those same fields again after unlocking to start the sender, reader and heartbeat goroutines and to call udp.Forwarder. Two work connections installed concurrently made that unlocked read race with the next generation's write to the same fields. Create the channels as locals, publish them under the existing lock, and use only those locals for the goroutines and the forwarder. The fields stay guarded by pxy.mu, so Close still closes the current generation. No behavior change for a single generation. Add TestUDPProxyInWorkConnGenerationChannels, which drives two InWorkConn calls concurrently so the race detector covers the field accesses.
) Logger kept its rendered prefix in a plain string field that was written by AddPrefix, AppendPrefix and ResetPrefixes and read without synchronization by Tracef, Spawn and the other logging methods. In frpc the race is reachable on the service-level logger, which every component of a session shares through its context: adding the run ID after a login writes the prefix while control, proxy manager, connector and visitor goroutines keep logging through the same logger, and a reload that creates proxies calls Spawn on it. A torn string header can pair a nil data pointer with a non-zero length, and formatting such a prefix can crash the process when the log call's level is enabled. Serialize the writers with a per-Logger mutex and publish the rendered prefix as an immutable string through an atomic pointer, so the log path takes no lock. Spawn copies the parent prefix list under the same mutex, keeping children independent snapshots. Logging output is unchanged.
…5592) When an HTTPS or tcpmux route listener is closed after a connection has been resolved to it but before the handoff, Muxer.handle clears the deadline and then sends on the closed accept channel. The recovered panic returns without closing the connection, so the peer waits until its own read timeout. Add a done channel and sync.Once to Listener. Close closes done and is idempotent; handle and Accept select on accept/done, and a pending connection is closed when done is closed. Connections already handed to Accept keep their current ownership. Remove the PanicToError-based control flow. Cover the pending handoff with net.Pipe and real TCP tests, plus an idempotent Close test. Fixes #5573
Update github.com/fatedier/yamux from v0.2.0 to v0.3.0. go.mod and go.sum are the only dependency changes. v0.3.0 adds a public Stream.Reset() and fixes several stream and session lifecycle problems: a read or write blocked on a shutting-down session returns instead of spinning, the stream close lifecycle is safe to retry, failed stream opens release their tracking state, and ConnectionWriteTimeout is now enforced as a write deadline on the underlying transport instead of only bounding how long a caller waits for its own frame. The last change is observable in frp. When a tcpMux connection stalls, the write now fails at the transport and the session is torn down, so a new stream returns ErrSessionShutdown; before, the caller timed out while the session stayed blocked but alive until a keepalive ping also timed out. All frp transports implement SetWriteDeadline (TCP, TLS, WebSocket, WSS and KCP); KCP reports its own timeout error instead of the mapped one. Reads on an established stream now return ErrSessionShutdown instead of io.EOF when the session dies; a graceful stream close still returns io.EOF, and frp data paths already treat any read error as the end of the stream. Adapt TestDispatcherYamuxWriteTimeoutInterruptsRead to the new behaviour: the delayed test connection now honours SetWriteDeadline, and the test asserts that both yamux sessions close after the timed-out write instead of expecting the session to stay usable.
#5596) When a client logs in again with the same run ID, frps replaces the old control and closes its control stream, but the yamux session that carried it stays open until keepalive reclaims it. The replaced control's work connections and streams share that session, so they are left behind for up to tcpMuxKeepaliveInterval plus the ping budget. A mux session carries exactly one control connection, so the control now records its session and closes it from the single teardown point shared by replacement, fatal exit and shutdown. QUIC and non-multiplexed transports keep the previous path. Closing the session ends every stream that still belongs to the replaced control. Those proxies are already being torn down, so the cost is that in-flight connections of the old control are interrupted immediately instead of at the keepalive deadline. A replacement control is not affected, and the ownership rules for a still-online control do not change. Tests: replacement and fatal exit close the session while the replacement control keeps working, and concurrent logins with the same run ID leave exactly one live session.
Bump the version to 0.72.0 and update Release.md for the v0.72.0 release.
Upgrade golang.org/x/net v0.57.0 -> v0.62.0 and github.com/gorilla/websocket v1.5.0 -> v1.5.3 to clear the reported HTTP/2 server and websocket advisories, along with patch/minor updates to the other direct dependencies: x/crypto, x/sys, x/sync, x/oauth2, x/time, go-oidc, go-toml/v2, samber/lo, cobra, pflag, rodaine/table, netlink, ini, and the test-only testify, ginkgo, gomega and gjson modules. go.mod keeps `go 1.26.0` and has no toolchain line. Only go.mod and go.sum change.
Move client_golang to v1.25.0 with its required module updates (client_model v0.6.3, common v0.72.0, procfs v0.22.0, xxhash v2.3.0, protobuf v1.36.12, munnerz/goautoneg). go.mod keeps go 1.26.0 and has no toolchain line. Sync the frpc text metrics clone with the new default Go collector: add go_gc_gogc_percent, go_gc_gomemlimit_bytes and go_sched_gomaxprocs_threads, update the go_memstats help texts, and remove go_memstats_lookups_total. frpc still exposes go_* metrics only.
Upgrade github.com/quic-go/quic-go from v0.60.0 to v0.65.0. Only go.mod and go.sum change; the go and toolchain lines are unchanged and no other dependencies move. This brings the QUIC transport fixes from v0.61-v0.65: connection migration and NAT rebinding handling, congestion control and PMTU recovery, and stream error handling. frp uses the core QUIC API only: Dial/Listen, config timeouts and stream limits, Conn and Stream.
Replace the single use of k8s.io/apimachinery/pkg/util/validation .IsQualifiedName (annotation key validation in pkg/config/v1/validation) with a local implementation in pkg/util/validation. Newer apimachinery versions link klog only for IP/CIDR warning helpers, which grows frpc and frps, and this was the only frp import of that package. The local IsQualifiedName keeps the exact behavior and error messages of apimachinery v0.28.8, including the qualified-name (label key) format and the DNS-1123 subdomain prefix rules. Annotation keys are still lowercased before validation, so the accepted set is unchanged. Verified against apimachinery v0.28.8 on 228,220 inputs (exhaustive short strings, boundary lengths and seeded random data, including lowercased variants): every error message matches. No dependency versions change; go.mod and go.sum are untouched.
Upgrade k8s.io/apimachinery and k8s.io/client-go from v0.28.8 to v0.37.2, the latest stable minor, and k8s.io/utils from its 2023 pseudo-version to v0.0.0-20261023123148-ca04ba58ecab. The go and toolchain lines are unchanged. The upgrade is possible now because the annotation key check no longer imports apimachinery's util/validation package, so apimachinery's klog dependency does not reach frpc/frps; both binaries stay free of klog and client-go remains test-only. Indirect changes: sigs.k8s.io/yaml 1.3.0 -> 1.6.0 on top of go.yaml.in/yaml/v2, sigs.k8s.io/json updated to 2025-07-30, and gorilla/websocket moved by MVS from v1.5.3 to the 2025-03 pseudo-version v1.5.4-0.20250319132907-e064f32e3674, which apimachinery v0.37.2 requires. gopkg.in/yaml.v2 leaves the module graph. YAML and configuration parsing behave the same before and after: the apimachinery YAML helper produces identical output on sample documents, and frp's LoadConfigure output is byte-identical for YAML client/server configs, strict dot-field handling, and JSON input. This is not a security fix: govulncheck results are unchanged (11 reachable standard-library advisories only). Binary size changes by +368 bytes for frpc and +496 bytes for frps with embedded web assets.
Upgrade golang.zx2c4.com/wireguard from v0.0.0-20231211153847-12269c276173 (2023-12-11) to v0.0.0-20261024220952-b3bc704ef0ee (2026-10-24). Only go.mod and go.sum change; the go and toolchain lines are unchanged. The new snapshot requires a newer gvisor snapshot (v0.0.0-20250503011706-39ed1f5ac29c), which moves through MVS and is reflected in go.sum. frp uses wireguard-go only through its tun package for the virtual net feature. The tun changes in this range are the Linux GRO path (clear the virtioNetHdr in the receive buffer), checksum add-with-carry handling, and darwin reading the interface flags and MTU directly from route events. The Device API is unchanged and no code changes are needed. This is not a security fix: govulncheck reports the same result before and after. Binary size is unchanged for both frpc and frps. Validation: - go build ./... - go test -count=1 ./... (all packages pass except the pre-existing test/e2e/compatibility failure, which needs explicit baseline binary flags and fails on a clean checkout as well) - make e2e with the real binaries: 261 passed, 2 skipped - repo-wide golangci-lint run: 0 issues - govulncheck ./...: unchanged (11 reachable standard-library advisories, no wireguard or gvisor entries) - Binary size with embedded web assets (CGO_ENABLED=0, -trimpath, -ldflags "-s -w"): frpc 15,607,842 bytes and frps 19,307,122 bytes, identical before and after - Manual virtual net smoke test on linux/arm64 (frps, an frpc virtual-net server and an frpc visitor over the built-in TUN): TUN name, MTU and routes match the old version, ICMP has 0% loss both ways, a 256 MB TCP transfer matches sha256, throughput is about 41 MB/s on both versions, and concurrent connections, high-rate pings, a 45s idle connection and restart recovery all pass without panics. The repository e2e suite has no virtual net coverage, so this is a one-off manual check.
Upgrade github.com/xtaci/kcp-go/v5 from v5.6.13 to v5.6.73. The templexxx/cpu and templexxx/xorsimd indirect dependencies are no longer required and are dropped from go.mod and go.sum. The go and toolchain lines are unchanged and no other module changes. This release carries the KCP-side fixes frp relies on: write backpressure and duplicate fast-retransmit suppression, ACK and fast-retransmit flush timing, the zero-window probe interval, and MTU accounting for FEC overhead. The wire format is unchanged and frp interoperates with peers on the old version in both directions. kcp-go marks UDPSession.SetStreamMode as deprecated, but frp still needs stream mode and there is no replacement. The calls stay as they are and a staticcheck exclusion scoped to pkg/util/net/kcp.go is added to .golangci.yml instead of //nolint comments. No code changes. This is not a security fix: govulncheck reports the same result before and after. Binary size with embedded web assets (CGO_ENABLED=0, -trimpath, -ldflags "-s -w"): darwin/arm64 frpc 15,607,842 -> 15,591,282 (-16,560) and frps 19,307,122 -> 19,290,434 (-16,688); linux/amd64 frpc 17,416,354 -> 17,449,122 (+32,768) and frps 21,274,786 -> 21,311,650 (+36,864). Validation: - go build ./... - go test -count=1 ./... (all packages pass except the pre-existing test/e2e/compatibility failure, which needs explicit baseline binary flags and fails on a clean checkout as well) - make e2e with the real binaries (263 specs: 261 passed, 2 skipped) - repo-wide golangci-lint run: 2 SA1019 reports without the exclusion, 0 issues with it - govulncheck ./...: unchanged (11 reachable standard-library advisories, no kcp-go or xtaci entries) - Manual KCP throughput check on linux/arm64 loopback (frpc <-> frps over transport.protocol=kcp with TLS, yamux and FEC 10/3): a single 256 MB transfer goes from 7.8 MB/s to about 153 MB/s and 8 x 16 MB concurrent transfers from 7.8 MB/s to about 470 MB/s with matching sha256, and old and new peers interoperate in both directions. The repository e2e suite has no KCP throughput assertions, so this is a one-off manual check.
Code Review SummaryStatus: No Issues Found | Recommendation: Merge Files Reviewed (69 files)
Reviewed HEAD Static review only: no code, tests, builds, or workflows were executed. Upstream dependency implementations, external Action commit targets, and platform-specific TLS behavior were not independently validated. Reviewed by gpt-6.1-sol · Input: 42 · Output: 8K · Cached: 671.2K |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
WHY
Prepare v0.72.0 from
devfor release.Summary
See
Release.mdfor the user-facing changes: system CA verification for server certificates, plugin certificate reload onfrpc reload, the newmetricsServer, and fixes to transport, control connection teardown, logging, and route listener handoff.The release also updates Go dependencies (yamux, quic-go, prometheus/client_golang, k8s.io libraries, wireguard-go, kcp-go, and others) and replaces the single apimachinery annotation-key validation call with a local, behavior-equivalent implementation. Beyond the tree already merged into
dev, the release preparation adds no further code changes.Validation
Validation ran on the
devhead used by this PR:make build— passed;frpc --versionandfrps --versionreport0.72.0.go build ./...andgo vet ./...— passed.go testacrossassets,cmd,client,serverandpkg— passed.golangci-lint run— 0 issues.make e2e— 261 passed, 0 failed, 2 existing STUN skips.This PR does not publish v0.72.0. Release merging, tagging, and publication are separate steps.