Thanks to visit codestin.com
Credit goes to github.com

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
2 changes: 1 addition & 1 deletion .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ version: 2
jobs:
go-version-latest:
docker:
- image: cimg/go:1.23-node
- image: cimg/go:1.24-node
resource_class: large
steps:
- checkout
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/golangci-lint.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ jobs:
- uses: actions/checkout@v4
- uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'
cache: false
- name: golangci-lint
uses: golangci/golangci-lint-action@v8
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ jobs:
- name: Set up Go
uses: actions/setup-go@v5
with:
go-version: '1.23'
go-version: '1.24'

- name: Make All
run: |
Expand Down
11 changes: 9 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,18 @@ frp is an open source project with its ongoing development made possible entirel

<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->
<p align="center">
<a href="https://www.recall.ai/?utm_source=github&utm_medium=sponsorship&utm_campaign=fatedier-frp" target="_blank">
<b>Recall.ai - API for meeting recordings</b><br>
<br>
<sup>If you're looking for a meeting recording API, consider checking out Recall.ai, an API that records Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.</sup>
</a>
</p>
<p align="center">
<a href="https://go.warp.dev/frp" target="_blank">
<img width="360px" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-01.png">
<br>
<b>Warp, the intelligent terminal</b>
<b>Warp, built for collaborating with AI Agents</b>
<br>
<sub>Available for macOS, Linux and Windows</sub>
</a>
Expand Down Expand Up @@ -519,7 +526,7 @@ name = "ssh"
type = "tcp"
localIP = "127.0.0.1"
localPort = 22
remotePort = "{{ .Envs.FRP_SSH_REMOTE_PORT }}"
remotePort = {{ .Envs.FRP_SSH_REMOTE_PORT }}
```

With the config above, variables can be passed into `frpc` program like this:
Expand Down
24 changes: 24 additions & 0 deletions README_zh.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,19 +15,43 @@ frp 是一个完全开源的项目,我们的开发工作完全依靠赞助者

<h3 align="center">Gold Sponsors</h3>
<!--gold sponsors start-->
<p align="center">
<a href="https://www.recall.ai/?utm_source=github&utm_medium=sponsorship&utm_campaign=fatedier-frp" target="_blank">
<b>Recall.ai - API for meeting recordings</b><br>
<br>
<sup>If you're looking for a meeting recording API, consider checking out Recall.ai, an API that records Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.</sup>
</a>
</p>
<p align="center">
<a href="https://go.warp.dev/frp" target="_blank">
<img width="360px" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-01.png">
<br>
<b>Warp, built for collaborating with AI Agents</b>
<br>
<sub>Available for macOS, Linux and Windows</sub>
</a>
</p>
<p align="center">
<a href="https://jb.gg/frp" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_jetbrains.jpg">
<br>
<b>The complete IDE crafted for professional Go developers</b>
</a>
</p>
<p align="center">
<a href="https://github.com/daytonaio/daytona" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_daytona.png">
<br>
<b>Secure and Elastic Infrastructure for Running Your AI-Generated Code</b>
</a>
</p>
<p align="center">
<a href="https://github.com/beclab/Olares" target="_blank">
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_olares.jpeg">
<br>
<b>The sovereign cloud that puts you in control</b>
<br>
<sub>An open source, self-hosted alternative to public clouds, built for data ownership and privacy</sub>
</a>
</p>
<!--gold sponsors end-->
Expand Down
8 changes: 3 additions & 5 deletions Release.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
## Features

* Support tokenSource for loading authentication tokens from files.

## Fixes

* Fix SSH tunnel gateway incorrectly binding to proxyBindAddr instead of bindAddr, which caused external connections to fail when proxyBindAddr was set to 127.0.0.1.
* Add NAT traversal configuration options for XTCP proxies and visitors. Support disabling assisted addresses to avoid using slow VPN connections during NAT hole punching.
* Enhanced OIDC client configuration with support for custom TLS certificate verification and proxy settings. Added `trustedCaFile`, `insecureSkipVerify`, and `proxyURL` options for OIDC token endpoint connections.
* Added detailed Prometheus metrics with `proxy_counts_detailed` metric that includes both proxy type and proxy name labels, enabling monitoring of individual proxy connections instead of just aggregate counts.
4 changes: 2 additions & 2 deletions client/connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ package client
import (
"context"
"crypto/tls"
"io"
"net"
"strconv"
"strings"
Expand Down Expand Up @@ -115,7 +114,8 @@ func (c *defaultConnectorImpl) Open() error {

fmuxCfg := fmux.DefaultConfig()
fmuxCfg.KeepAliveInterval = time.Duration(c.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
fmuxCfg.LogOutput = io.Discard
// Use trace level for yamux logs
fmuxCfg.LogOutput = xlog.NewTraceWriter(xl)
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
session, err := fmux.Client(conn, fmuxCfg)
if err != nil {
Expand Down
2 changes: 2 additions & 0 deletions client/control.go
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,12 @@ func (ctl *Control) heartbeatWorker() {
}

func (ctl *Control) worker() {
xl := ctl.xl
go ctl.heartbeatWorker()
go ctl.msgDispatcher.Run()

<-ctl.msgDispatcher.Done()
xl.Debugf("control message dispatcher exited")
ctl.closeSession()

ctl.pm.Close()
Expand Down
10 changes: 9 additions & 1 deletion client/proxy/xtcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,11 +64,19 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
}

xl.Tracef("nathole prepare start")
prepareResult, err := nathole.Prepare([]string{pxy.clientCfg.NatHoleSTUNServer})

// Prepare NAT traversal options
var opts nathole.PrepareOptions
if pxy.cfg.NatTraversal != nil && pxy.cfg.NatTraversal.DisableAssistedAddrs {
opts.DisableAssistedAddrs = true
}

prepareResult, err := nathole.Prepare([]string{pxy.clientCfg.NatHoleSTUNServer}, opts)
if err != nil {
xl.Warnf("nathole prepare error: %v", err)
return
}

xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
defer prepareResult.ListenConn.Close()
Expand Down
8 changes: 7 additions & 1 deletion client/service.go
Original file line number Diff line number Diff line change
Expand Up @@ -149,9 +149,15 @@ func NewService(options ServiceOptions) (*Service, error) {
}
webServer = ws
}

authSetter, err := auth.NewAuthSetter(options.Common.Auth)
if err != nil {
return nil, err
}

s := &Service{
ctx: context.Background(),
authSetter: auth.NewAuthSetter(options.Common.Auth),
authSetter: authSetter,
webServer: webServer,
common: options.Common,
configFilePath: options.ConfigFilePath,
Expand Down
59 changes: 32 additions & 27 deletions client/visitor/xtcp.go
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,7 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
return
case <-ticker.C:
xl.Debugf("keepTunnelOpenWorker try to check tunnel...")
conn, err := sv.getTunnelConn()
conn, err := sv.getTunnelConn(sv.ctx)
if err != nil {
xl.Warnf("keepTunnelOpenWorker get tunnel connection error: %v", err)
_ = sv.retryLimiter.Wait(sv.ctx)
Expand All @@ -161,9 +161,9 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {

func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
xl := xlog.FromContextSafe(sv.ctx)
isConnTransfered := false
isConnTransferred := false
defer func() {
if !isConnTransfered {
if !isConnTransferred {
userConn.Close()
}
}()
Expand All @@ -172,7 +172,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {

// Open a tunnel connection to the server. If there is already a successful hole-punching connection,
// it will be reused. Otherwise, it will block and wait for a successful hole-punching connection until timeout.
ctx := context.Background()
ctx := sv.ctx
if sv.cfg.FallbackTo != "" {
timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(sv.cfg.FallbackTimeoutMs)*time.Millisecond)
defer cancel()
Expand All @@ -191,7 +191,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
xl.Errorf("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
return
}
isConnTransfered = true
isConnTransferred = true
return
}

Expand Down Expand Up @@ -219,40 +219,37 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
// openTunnel will open a tunnel connection to the target server.
func (sv *XTCPVisitor) openTunnel(ctx context.Context) (conn net.Conn, err error) {
xl := xlog.FromContextSafe(sv.ctx)
ticker := time.NewTicker(500 * time.Millisecond)
defer ticker.Stop()
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()

timeoutC := time.After(20 * time.Second)
immediateTrigger := make(chan struct{}, 1)
defer close(immediateTrigger)
immediateTrigger <- struct{}{}
timer := time.NewTimer(0)
defer timer.Stop()

for {
select {
case <-sv.ctx.Done():
return nil, sv.ctx.Err()
case <-ctx.Done():
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
return nil, fmt.Errorf("open tunnel timeout")
}
return nil, ctx.Err()
case <-immediateTrigger:
conn, err = sv.getTunnelConn()
case <-ticker.C:
conn, err = sv.getTunnelConn()
case <-timeoutC:
return nil, fmt.Errorf("open tunnel timeout")
}

if err != nil {
if err != ErrNoTunnelSession {
xl.Warnf("get tunnel connection error: %v", err)
case <-timer.C:
conn, err = sv.getTunnelConn(ctx)
if err != nil {
if !errors.Is(err, ErrNoTunnelSession) {
xl.Warnf("get tunnel connection error: %v", err)
}
timer.Reset(500 * time.Millisecond)
continue
}
continue
return conn, nil
}
return conn, nil
}
}

func (sv *XTCPVisitor) getTunnelConn() (net.Conn, error) {
conn, err := sv.session.OpenConn(sv.ctx)
func (sv *XTCPVisitor) getTunnelConn(ctx context.Context) (net.Conn, error) {
conn, err := sv.session.OpenConn(ctx)
if err == nil {
return conn, nil
}
Expand All @@ -279,11 +276,19 @@ func (sv *XTCPVisitor) makeNatHole() {
}

xl.Tracef("nathole prepare start")
prepareResult, err := nathole.Prepare([]string{sv.clientCfg.NatHoleSTUNServer})

// Prepare NAT traversal options
var opts nathole.PrepareOptions
if sv.cfg.NatTraversal != nil && sv.cfg.NatTraversal.DisableAssistedAddrs {
opts.DisableAssistedAddrs = true
}

prepareResult, err := nathole.Prepare([]string{sv.clientCfg.NatHoleSTUNServer}, opts)
if err != nil {
xl.Warnf("nathole prepare error: %v", err)
return
}

xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)

Expand Down
29 changes: 29 additions & 0 deletions conf/frpc_full_example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,20 @@ auth.token = "12345678"
# auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
# auth.oidc.additionalEndpointParams.var1 = "foobar"

# OIDC TLS and proxy configuration
# Specify a custom CA certificate file for verifying the OIDC token endpoint's TLS certificate.
# This is useful when the OIDC provider uses a self-signed certificate or a custom CA.
# auth.oidc.trustedCaFile = "/path/to/ca.crt"

# Skip TLS certificate verification for the OIDC token endpoint.
# INSECURE: Only use this for debugging purposes, not recommended for production.
# auth.oidc.insecureSkipVerify = false

# Specify a proxy server for OIDC token endpoint connections.
# Supports http, https, socks5, and socks5h proxy protocols.
# If not specified, no proxy is used for OIDC connections.
# auth.oidc.proxyURL = "http://proxy.example.com:8080"

# Set admin address for control frpc's action by http api such as reload
webServer.addr = "127.0.0.1"
webServer.port = 7400
Expand Down Expand Up @@ -372,6 +386,14 @@ localPort = 22
# Otherwise, visitors from same user can connect. '*' means allow all users.
allowUsers = ["user1", "user2"]

# NAT traversal configuration (optional)
[proxies.natTraversal]
# Disable the use of local network interfaces (assisted addresses) for NAT traversal.
# When enabled, only STUN-discovered public addresses will be used.
# This can improve performance when you have slow VPN connections.
# Default: false
disableAssistedAddrs = false

[[proxies]]
name = "vnet-server"
type = "stcp"
Expand Down Expand Up @@ -411,6 +433,13 @@ minRetryInterval = 90
# fallbackTo = "stcp_visitor"
# fallbackTimeoutMs = 500

# NAT traversal configuration (optional)
[visitors.natTraversal]
# Disable the use of local network interfaces (assisted addresses) for NAT traversal.
# When enabled, only STUN-discovered public addresses will be used.
# Default: false
disableAssistedAddrs = false

[[visitors]]
name = "vnet-visitor"
type = "stcp"
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/Dockerfile-for-frpc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23 AS building
FROM golang:1.24 AS building

COPY . /building
WORKDIR /building
Expand Down
2 changes: 1 addition & 1 deletion dockerfiles/Dockerfile-for-frps
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.23 AS building
FROM golang:1.24 AS building

COPY . /building
WORKDIR /building
Expand Down
4 changes: 2 additions & 2 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
module github.com/fatedier/frp

go 1.23.0
go 1.24.0

require (
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
Expand Down Expand Up @@ -82,4 +82,4 @@ require (
)

// TODO(fatedier): Temporary use the modified version, update to the official version after merging into the official repository.
replace github.com/hashicorp/yamux => github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d
replace github.com/hashicorp/yamux => github.com/fatedier/yamux v0.0.0-20250825093530-d0154be01cd6
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,8 @@ github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1m
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
github.com/fatedier/golib v0.5.1 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M=
github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
github.com/fatedier/yamux v0.0.0-20250825093530-d0154be01cd6 h1:u92UUy6FURPmNsMBUuongRWC0rBqN6gd01Dzu+D21NE=
github.com/fatedier/yamux v0.0.0-20250825093530-d0154be01cd6/go.mod h1:c5/tk6G0dSpXGzJN7Wk1OEie8grdSJAmeawId9Zvd34=
github.com/go-jose/go-jose/v4 v4.0.5 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
Expand Down
Loading