forked from Mxmilu666/frp
Compare commits
2 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22ae8166d3 | ||
|
|
af6bc6369d |
@@ -2,7 +2,7 @@ version: 2
|
|||||||
jobs:
|
jobs:
|
||||||
go-version-latest:
|
go-version-latest:
|
||||||
docker:
|
docker:
|
||||||
- image: cimg/go:1.24-node
|
- image: cimg/go:1.23-node
|
||||||
resource_class: large
|
resource_class: large
|
||||||
steps:
|
steps:
|
||||||
- checkout
|
- checkout
|
||||||
|
|||||||
2
.github/workflows/golangci-lint.yml
vendored
2
.github/workflows/golangci-lint.yml
vendored
@@ -17,7 +17,7 @@ jobs:
|
|||||||
- uses: actions/checkout@v4
|
- uses: actions/checkout@v4
|
||||||
- uses: actions/setup-go@v5
|
- uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.24'
|
go-version: '1.23'
|
||||||
cache: false
|
cache: false
|
||||||
- name: golangci-lint
|
- name: golangci-lint
|
||||||
uses: golangci/golangci-lint-action@v8
|
uses: golangci/golangci-lint-action@v8
|
||||||
|
|||||||
2
.github/workflows/goreleaser.yml
vendored
2
.github/workflows/goreleaser.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
|||||||
- name: Set up Go
|
- name: Set up Go
|
||||||
uses: actions/setup-go@v5
|
uses: actions/setup-go@v5
|
||||||
with:
|
with:
|
||||||
go-version: '1.24'
|
go-version: '1.23'
|
||||||
|
|
||||||
- name: Make All
|
- name: Make All
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
13
README.md
13
README.md
@@ -13,20 +13,11 @@ frp is an open source project with its ongoing development made possible entirel
|
|||||||
|
|
||||||
<h3 align="center">Gold Sponsors</h3>
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
<!--gold sponsors start-->
|
<!--gold sponsors start-->
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
## Recall.ai - API for meeting recordings
|
|
||||||
|
|
||||||
If you're looking for a meeting recording API, consider checking out [Recall.ai](https://www.recall.ai/?utm_source=github&utm_medium=sponsorship&utm_campaign=fatedier-frp),
|
|
||||||
|
|
||||||
an API that records Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://go.warp.dev/frp" target="_blank">
|
<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">
|
<img width="360px" src="https://raw.githubusercontent.com/warpdotdev/brand-assets/refs/heads/main/Github/Sponsor/Warp-Github-LG-01.png">
|
||||||
<br>
|
<br>
|
||||||
<b>Warp, built for collaborating with AI Agents</b>
|
<b>Warp, the intelligent terminal</b>
|
||||||
<br>
|
<br>
|
||||||
<sub>Available for macOS, Linux and Windows</sub>
|
<sub>Available for macOS, Linux and Windows</sub>
|
||||||
</a>
|
</a>
|
||||||
@@ -528,7 +519,7 @@ name = "ssh"
|
|||||||
type = "tcp"
|
type = "tcp"
|
||||||
localIP = "127.0.0.1"
|
localIP = "127.0.0.1"
|
||||||
localPort = 22
|
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:
|
With the config above, variables can be passed into `frpc` program like this:
|
||||||
|
|||||||
26
README_zh.md
26
README_zh.md
@@ -15,45 +15,19 @@ frp 是一个完全开源的项目,我们的开发工作完全依靠赞助者
|
|||||||
|
|
||||||
<h3 align="center">Gold Sponsors</h3>
|
<h3 align="center">Gold Sponsors</h3>
|
||||||
<!--gold sponsors start-->
|
<!--gold sponsors start-->
|
||||||
<div align="center">
|
|
||||||
|
|
||||||
## Recall.ai - API for meeting recordings
|
|
||||||
|
|
||||||
If you're looking for a meeting recording API, consider checking out [Recall.ai](https://www.recall.ai/?utm_source=github&utm_medium=sponsorship&utm_campaign=fatedier-frp),
|
|
||||||
|
|
||||||
an API that records Zoom, Google Meet, Microsoft Teams, in-person meetings, and more.
|
|
||||||
|
|
||||||
</div>
|
|
||||||
<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">
|
<p align="center">
|
||||||
<a href="https://jb.gg/frp" target="_blank">
|
<a href="https://jb.gg/frp" target="_blank">
|
||||||
<img width="420px" src="https://raw.githubusercontent.com/fatedier/frp/dev/doc/pic/sponsor_jetbrains.jpg">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/daytonaio/daytona" target="_blank">
|
<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">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/beclab/Olares" target="_blank">
|
<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">
|
<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>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
<!--gold sponsors end-->
|
<!--gold sponsors end-->
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
## Features
|
## Features
|
||||||
|
|
||||||
* Add NAT traversal configuration options for XTCP proxies and visitors. Support disabling assisted addresses to avoid using slow VPN connections during NAT hole punching.
|
* Support tokenSource for loading authentication tokens from files.
|
||||||
* 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.
|
## 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.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ package client
|
|||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -114,8 +115,7 @@ func (c *defaultConnectorImpl) Open() error {
|
|||||||
|
|
||||||
fmuxCfg := fmux.DefaultConfig()
|
fmuxCfg := fmux.DefaultConfig()
|
||||||
fmuxCfg.KeepAliveInterval = time.Duration(c.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
|
fmuxCfg.KeepAliveInterval = time.Duration(c.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
|
||||||
// Use trace level for yamux logs
|
fmuxCfg.LogOutput = io.Discard
|
||||||
fmuxCfg.LogOutput = xlog.NewTraceWriter(xl)
|
|
||||||
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
||||||
session, err := fmux.Client(conn, fmuxCfg)
|
session, err := fmux.Client(conn, fmuxCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
@@ -276,12 +276,10 @@ func (ctl *Control) heartbeatWorker() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (ctl *Control) worker() {
|
func (ctl *Control) worker() {
|
||||||
xl := ctl.xl
|
|
||||||
go ctl.heartbeatWorker()
|
go ctl.heartbeatWorker()
|
||||||
go ctl.msgDispatcher.Run()
|
go ctl.msgDispatcher.Run()
|
||||||
|
|
||||||
<-ctl.msgDispatcher.Done()
|
<-ctl.msgDispatcher.Done()
|
||||||
xl.Debugf("control message dispatcher exited")
|
|
||||||
ctl.closeSession()
|
ctl.closeSession()
|
||||||
|
|
||||||
ctl.pm.Close()
|
ctl.pm.Close()
|
||||||
|
|||||||
@@ -64,19 +64,11 @@ func (pxy *XTCPProxy) InWorkConn(conn net.Conn, startWorkConnMsg *msg.StartWorkC
|
|||||||
}
|
}
|
||||||
|
|
||||||
xl.Tracef("nathole prepare start")
|
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 {
|
if err != nil {
|
||||||
xl.Warnf("nathole prepare error: %v", err)
|
xl.Warnf("nathole prepare error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
|
xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
|
||||||
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
|
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
|
||||||
defer prepareResult.ListenConn.Close()
|
defer prepareResult.ListenConn.Close()
|
||||||
|
|||||||
@@ -149,15 +149,9 @@ func NewService(options ServiceOptions) (*Service, error) {
|
|||||||
}
|
}
|
||||||
webServer = ws
|
webServer = ws
|
||||||
}
|
}
|
||||||
|
|
||||||
authSetter, err := auth.NewAuthSetter(options.Common.Auth)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
s := &Service{
|
s := &Service{
|
||||||
ctx: context.Background(),
|
ctx: context.Background(),
|
||||||
authSetter: authSetter,
|
authSetter: auth.NewAuthSetter(options.Common.Auth),
|
||||||
webServer: webServer,
|
webServer: webServer,
|
||||||
common: options.Common,
|
common: options.Common,
|
||||||
configFilePath: options.ConfigFilePath,
|
configFilePath: options.ConfigFilePath,
|
||||||
|
|||||||
@@ -145,7 +145,7 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
|
|||||||
return
|
return
|
||||||
case <-ticker.C:
|
case <-ticker.C:
|
||||||
xl.Debugf("keepTunnelOpenWorker try to check tunnel...")
|
xl.Debugf("keepTunnelOpenWorker try to check tunnel...")
|
||||||
conn, err := sv.getTunnelConn(sv.ctx)
|
conn, err := sv.getTunnelConn()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
xl.Warnf("keepTunnelOpenWorker get tunnel connection error: %v", err)
|
xl.Warnf("keepTunnelOpenWorker get tunnel connection error: %v", err)
|
||||||
_ = sv.retryLimiter.Wait(sv.ctx)
|
_ = sv.retryLimiter.Wait(sv.ctx)
|
||||||
@@ -161,9 +161,9 @@ func (sv *XTCPVisitor) keepTunnelOpenWorker() {
|
|||||||
|
|
||||||
func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
|
func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
|
||||||
xl := xlog.FromContextSafe(sv.ctx)
|
xl := xlog.FromContextSafe(sv.ctx)
|
||||||
isConnTransferred := false
|
isConnTransfered := false
|
||||||
defer func() {
|
defer func() {
|
||||||
if !isConnTransferred {
|
if !isConnTransfered {
|
||||||
userConn.Close()
|
userConn.Close()
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
@@ -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,
|
// 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.
|
// it will be reused. Otherwise, it will block and wait for a successful hole-punching connection until timeout.
|
||||||
ctx := sv.ctx
|
ctx := context.Background()
|
||||||
if sv.cfg.FallbackTo != "" {
|
if sv.cfg.FallbackTo != "" {
|
||||||
timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(sv.cfg.FallbackTimeoutMs)*time.Millisecond)
|
timeoutCtx, cancel := context.WithTimeout(ctx, time.Duration(sv.cfg.FallbackTimeoutMs)*time.Millisecond)
|
||||||
defer cancel()
|
defer cancel()
|
||||||
@@ -191,7 +191,7 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
|
|||||||
xl.Errorf("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
|
xl.Errorf("transfer connection to visitor %s error: %v", sv.cfg.FallbackTo, err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
isConnTransferred = true
|
isConnTransfered = true
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -219,37 +219,40 @@ func (sv *XTCPVisitor) handleConn(userConn net.Conn) {
|
|||||||
// openTunnel will open a tunnel connection to the target server.
|
// openTunnel will open a tunnel connection to the target server.
|
||||||
func (sv *XTCPVisitor) openTunnel(ctx context.Context) (conn net.Conn, err error) {
|
func (sv *XTCPVisitor) openTunnel(ctx context.Context) (conn net.Conn, err error) {
|
||||||
xl := xlog.FromContextSafe(sv.ctx)
|
xl := xlog.FromContextSafe(sv.ctx)
|
||||||
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
|
ticker := time.NewTicker(500 * time.Millisecond)
|
||||||
defer cancel()
|
defer ticker.Stop()
|
||||||
|
|
||||||
timer := time.NewTimer(0)
|
timeoutC := time.After(20 * time.Second)
|
||||||
defer timer.Stop()
|
immediateTrigger := make(chan struct{}, 1)
|
||||||
|
defer close(immediateTrigger)
|
||||||
|
immediateTrigger <- struct{}{}
|
||||||
|
|
||||||
for {
|
for {
|
||||||
select {
|
select {
|
||||||
case <-sv.ctx.Done():
|
case <-sv.ctx.Done():
|
||||||
return nil, sv.ctx.Err()
|
return nil, sv.ctx.Err()
|
||||||
case <-ctx.Done():
|
case <-ctx.Done():
|
||||||
if errors.Is(ctx.Err(), context.DeadlineExceeded) {
|
|
||||||
return nil, fmt.Errorf("open tunnel timeout")
|
|
||||||
}
|
|
||||||
return nil, ctx.Err()
|
return nil, ctx.Err()
|
||||||
case <-timer.C:
|
case <-immediateTrigger:
|
||||||
conn, err = sv.getTunnelConn(ctx)
|
conn, err = sv.getTunnelConn()
|
||||||
if err != nil {
|
case <-ticker.C:
|
||||||
if !errors.Is(err, ErrNoTunnelSession) {
|
conn, err = sv.getTunnelConn()
|
||||||
xl.Warnf("get tunnel connection error: %v", err)
|
case <-timeoutC:
|
||||||
}
|
return nil, fmt.Errorf("open tunnel timeout")
|
||||||
timer.Reset(500 * time.Millisecond)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
return conn, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
if err != ErrNoTunnelSession {
|
||||||
|
xl.Warnf("get tunnel connection error: %v", err)
|
||||||
|
}
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
return conn, nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sv *XTCPVisitor) getTunnelConn(ctx context.Context) (net.Conn, error) {
|
func (sv *XTCPVisitor) getTunnelConn() (net.Conn, error) {
|
||||||
conn, err := sv.session.OpenConn(ctx)
|
conn, err := sv.session.OpenConn(sv.ctx)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return conn, nil
|
return conn, nil
|
||||||
}
|
}
|
||||||
@@ -276,19 +279,11 @@ func (sv *XTCPVisitor) makeNatHole() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
xl.Tracef("nathole prepare start")
|
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 {
|
if err != nil {
|
||||||
xl.Warnf("nathole prepare error: %v", err)
|
xl.Warnf("nathole prepare error: %v", err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
|
xl.Infof("nathole prepare success, nat type: %s, behavior: %s, addresses: %v, assistedAddresses: %v",
|
||||||
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
|
prepareResult.NatType, prepareResult.Behavior, prepareResult.Addrs, prepareResult.AssistedAddrs)
|
||||||
|
|
||||||
|
|||||||
@@ -55,20 +55,6 @@ auth.token = "12345678"
|
|||||||
# auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
|
# auth.oidc.additionalEndpointParams.audience = "https://dev.auth.com/api/v2/"
|
||||||
# auth.oidc.additionalEndpointParams.var1 = "foobar"
|
# 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
|
# Set admin address for control frpc's action by http api such as reload
|
||||||
webServer.addr = "127.0.0.1"
|
webServer.addr = "127.0.0.1"
|
||||||
webServer.port = 7400
|
webServer.port = 7400
|
||||||
@@ -386,14 +372,6 @@ localPort = 22
|
|||||||
# Otherwise, visitors from same user can connect. '*' means allow all users.
|
# Otherwise, visitors from same user can connect. '*' means allow all users.
|
||||||
allowUsers = ["user1", "user2"]
|
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]]
|
[[proxies]]
|
||||||
name = "vnet-server"
|
name = "vnet-server"
|
||||||
type = "stcp"
|
type = "stcp"
|
||||||
@@ -433,13 +411,6 @@ minRetryInterval = 90
|
|||||||
# fallbackTo = "stcp_visitor"
|
# fallbackTo = "stcp_visitor"
|
||||||
# fallbackTimeoutMs = 500
|
# 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]]
|
[[visitors]]
|
||||||
name = "vnet-visitor"
|
name = "vnet-visitor"
|
||||||
type = "stcp"
|
type = "stcp"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.24 AS building
|
FROM golang:1.23 AS building
|
||||||
|
|
||||||
COPY . /building
|
COPY . /building
|
||||||
WORKDIR /building
|
WORKDIR /building
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
FROM golang:1.24 AS building
|
FROM golang:1.23 AS building
|
||||||
|
|
||||||
COPY . /building
|
COPY . /building
|
||||||
WORKDIR /building
|
WORKDIR /building
|
||||||
|
|||||||
4
go.mod
4
go.mod
@@ -1,6 +1,6 @@
|
|||||||
module github.com/fatedier/frp
|
module github.com/fatedier/frp
|
||||||
|
|
||||||
go 1.24.0
|
go 1.23.0
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||||
@@ -82,4 +82,4 @@ require (
|
|||||||
)
|
)
|
||||||
|
|
||||||
// TODO(fatedier): Temporary use the modified version, update to the official version after merging into the official repository.
|
// 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-20250825093530-d0154be01cd6
|
replace github.com/hashicorp/yamux => github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d
|
||||||
|
|||||||
4
go.sum
4
go.sum
@@ -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/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 h1:hcKAnaw5mdI/1KWRGejxR+i1Hn/NvbY5UsMKDr7o13M=
|
||||||
github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
|
github.com/fatedier/golib v0.5.1/go.mod h1:W6kIYkIFxHsTzbgqg5piCxIiDo4LzwgTY6R5W8l9NFQ=
|
||||||
github.com/fatedier/yamux v0.0.0-20250825093530-d0154be01cd6 h1:u92UUy6FURPmNsMBUuongRWC0rBqN6gd01Dzu+D21NE=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d h1:ynk1ra0RUqDWQfvFi5KtMiSobkVQ3cNc0ODb8CfIETo=
|
||||||
github.com/fatedier/yamux v0.0.0-20250825093530-d0154be01cd6/go.mod h1:c5/tk6G0dSpXGzJN7Wk1OEie8grdSJAmeawId9Zvd34=
|
github.com/fatedier/yamux v0.0.0-20230628132301-7aca4898904d/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||||
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 h1:M6T8+mKZl/+fNNuFHvGIzDz7BTLQPIounk/b9dw3AaE=
|
||||||
github.com/go-jose/go-jose/v4 v4.0.5/go.mod h1:s3P1lRrkT8igV8D9OjyL4WRyHvjB6a4JSllnOrmmBOA=
|
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=
|
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||||
|
|||||||
@@ -27,19 +27,16 @@ type Setter interface {
|
|||||||
SetNewWorkConn(*msg.NewWorkConn) error
|
SetNewWorkConn(*msg.NewWorkConn) error
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter, err error) {
|
func NewAuthSetter(cfg v1.AuthClientConfig) (authProvider Setter) {
|
||||||
switch cfg.Method {
|
switch cfg.Method {
|
||||||
case v1.AuthMethodToken:
|
case v1.AuthMethodToken:
|
||||||
authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
authProvider = NewTokenAuth(cfg.AdditionalScopes, cfg.Token)
|
||||||
case v1.AuthMethodOIDC:
|
case v1.AuthMethodOIDC:
|
||||||
authProvider, err = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
|
authProvider = NewOidcAuthSetter(cfg.AdditionalScopes, cfg.OIDC)
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
default:
|
default:
|
||||||
return nil, fmt.Errorf("unsupported auth method: %s", cfg.Method)
|
panic(fmt.Sprintf("wrong method: '%s'", cfg.Method))
|
||||||
}
|
}
|
||||||
return authProvider, nil
|
return authProvider
|
||||||
}
|
}
|
||||||
|
|
||||||
type Verifier interface {
|
type Verifier interface {
|
||||||
|
|||||||
@@ -16,72 +16,23 @@ package auth
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
|
||||||
"crypto/x509"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
"net/url"
|
|
||||||
"os"
|
|
||||||
"slices"
|
"slices"
|
||||||
|
|
||||||
"github.com/coreos/go-oidc/v3/oidc"
|
"github.com/coreos/go-oidc/v3/oidc"
|
||||||
"golang.org/x/oauth2"
|
|
||||||
"golang.org/x/oauth2/clientcredentials"
|
"golang.org/x/oauth2/clientcredentials"
|
||||||
|
|
||||||
v1 "github.com/fatedier/frp/pkg/config/v1"
|
v1 "github.com/fatedier/frp/pkg/config/v1"
|
||||||
"github.com/fatedier/frp/pkg/msg"
|
"github.com/fatedier/frp/pkg/msg"
|
||||||
)
|
)
|
||||||
|
|
||||||
// createOIDCHTTPClient creates an HTTP client with custom TLS and proxy configuration for OIDC token requests
|
|
||||||
func createOIDCHTTPClient(trustedCAFile string, insecureSkipVerify bool, proxyURL string) (*http.Client, error) {
|
|
||||||
// Clone the default transport to get all reasonable defaults
|
|
||||||
transport := http.DefaultTransport.(*http.Transport).Clone()
|
|
||||||
|
|
||||||
// Configure TLS settings
|
|
||||||
if trustedCAFile != "" || insecureSkipVerify {
|
|
||||||
tlsConfig := &tls.Config{
|
|
||||||
InsecureSkipVerify: insecureSkipVerify,
|
|
||||||
}
|
|
||||||
|
|
||||||
if trustedCAFile != "" && !insecureSkipVerify {
|
|
||||||
caCert, err := os.ReadFile(trustedCAFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to read OIDC CA certificate file %q: %w", trustedCAFile, err)
|
|
||||||
}
|
|
||||||
|
|
||||||
caCertPool := x509.NewCertPool()
|
|
||||||
if !caCertPool.AppendCertsFromPEM(caCert) {
|
|
||||||
return nil, fmt.Errorf("failed to parse OIDC CA certificate from file %q", trustedCAFile)
|
|
||||||
}
|
|
||||||
|
|
||||||
tlsConfig.RootCAs = caCertPool
|
|
||||||
}
|
|
||||||
transport.TLSClientConfig = tlsConfig
|
|
||||||
}
|
|
||||||
|
|
||||||
// Configure proxy settings
|
|
||||||
if proxyURL != "" {
|
|
||||||
parsedURL, err := url.Parse(proxyURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to parse OIDC proxy URL %q: %w", proxyURL, err)
|
|
||||||
}
|
|
||||||
transport.Proxy = http.ProxyURL(parsedURL)
|
|
||||||
} else {
|
|
||||||
// Explicitly disable proxy to override DefaultTransport's ProxyFromEnvironment
|
|
||||||
transport.Proxy = nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return &http.Client{Transport: transport}, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type OidcAuthProvider struct {
|
type OidcAuthProvider struct {
|
||||||
additionalAuthScopes []v1.AuthScope
|
additionalAuthScopes []v1.AuthScope
|
||||||
|
|
||||||
tokenGenerator *clientcredentials.Config
|
tokenGenerator *clientcredentials.Config
|
||||||
httpClient *http.Client
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) (*OidcAuthProvider, error) {
|
func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClientConfig) *OidcAuthProvider {
|
||||||
eps := make(map[string][]string)
|
eps := make(map[string][]string)
|
||||||
for k, v := range cfg.AdditionalEndpointParams {
|
for k, v := range cfg.AdditionalEndpointParams {
|
||||||
eps[k] = []string{v}
|
eps[k] = []string{v}
|
||||||
@@ -99,30 +50,14 @@ func NewOidcAuthSetter(additionalAuthScopes []v1.AuthScope, cfg v1.AuthOIDCClien
|
|||||||
EndpointParams: eps,
|
EndpointParams: eps,
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create custom HTTP client if needed
|
|
||||||
var httpClient *http.Client
|
|
||||||
if cfg.TrustedCaFile != "" || cfg.InsecureSkipVerify || cfg.ProxyURL != "" {
|
|
||||||
var err error
|
|
||||||
httpClient, err = createOIDCHTTPClient(cfg.TrustedCaFile, cfg.InsecureSkipVerify, cfg.ProxyURL)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("failed to create OIDC HTTP client: %w", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return &OidcAuthProvider{
|
return &OidcAuthProvider{
|
||||||
additionalAuthScopes: additionalAuthScopes,
|
additionalAuthScopes: additionalAuthScopes,
|
||||||
tokenGenerator: tokenGenerator,
|
tokenGenerator: tokenGenerator,
|
||||||
httpClient: httpClient,
|
}
|
||||||
}, nil
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
|
func (auth *OidcAuthProvider) generateAccessToken() (accessToken string, err error) {
|
||||||
ctx := context.Background()
|
tokenObj, err := auth.tokenGenerator.Token(context.Background())
|
||||||
if auth.httpClient != nil {
|
|
||||||
ctx = context.WithValue(ctx, oauth2.HTTPClient, auth.httpClient)
|
|
||||||
}
|
|
||||||
|
|
||||||
tokenObj, err := auth.tokenGenerator.Token(ctx)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
return "", fmt.Errorf("couldn't generate OIDC token for login: %v", err)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -228,17 +228,6 @@ type AuthOIDCClientConfig struct {
|
|||||||
// AdditionalEndpointParams specifies additional parameters to be sent
|
// AdditionalEndpointParams specifies additional parameters to be sent
|
||||||
// this field will be transfer to map[string][]string in OIDC token generator.
|
// this field will be transfer to map[string][]string in OIDC token generator.
|
||||||
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
AdditionalEndpointParams map[string]string `json:"additionalEndpointParams,omitempty"`
|
||||||
|
|
||||||
// TrustedCaFile specifies the path to a custom CA certificate file
|
|
||||||
// for verifying the OIDC token endpoint's TLS certificate.
|
|
||||||
TrustedCaFile string `json:"trustedCaFile,omitempty"`
|
|
||||||
// InsecureSkipVerify disables TLS certificate verification for the
|
|
||||||
// OIDC token endpoint. Only use this for debugging, not recommended for production.
|
|
||||||
InsecureSkipVerify bool `json:"insecureSkipVerify,omitempty"`
|
|
||||||
// ProxyURL specifies a proxy to use when connecting to the OIDC token endpoint.
|
|
||||||
// Supports http, https, socks5, and socks5h proxy protocols.
|
|
||||||
// If empty, no proxy is used for OIDC connections.
|
|
||||||
ProxyURL string `json:"proxyURL,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type VirtualNetConfig struct {
|
type VirtualNetConfig struct {
|
||||||
|
|||||||
@@ -85,9 +85,9 @@ func (c *WebServerConfig) Complete() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
type TLSConfig struct {
|
type TLSConfig struct {
|
||||||
// CertFile specifies the path of the cert file that client will load.
|
// CertPath specifies the path of the cert file that client will load.
|
||||||
CertFile string `json:"certFile,omitempty"`
|
CertFile string `json:"certFile,omitempty"`
|
||||||
// KeyFile specifies the path of the secret key file that client will load.
|
// KeyPath specifies the path of the secret key file that client will load.
|
||||||
KeyFile string `json:"keyFile,omitempty"`
|
KeyFile string `json:"keyFile,omitempty"`
|
||||||
// TrustedCaFile specifies the path of the trusted ca file that will load.
|
// TrustedCaFile specifies the path of the trusted ca file that will load.
|
||||||
TrustedCaFile string `json:"trustedCaFile,omitempty"`
|
TrustedCaFile string `json:"trustedCaFile,omitempty"`
|
||||||
@@ -96,14 +96,6 @@ type TLSConfig struct {
|
|||||||
ServerName string `json:"serverName,omitempty"`
|
ServerName string `json:"serverName,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
// NatTraversalConfig defines configuration options for NAT traversal
|
|
||||||
type NatTraversalConfig struct {
|
|
||||||
// DisableAssistedAddrs disables the use of local network interfaces
|
|
||||||
// for assisted connections during NAT traversal. When enabled,
|
|
||||||
// only STUN-discovered public addresses will be used.
|
|
||||||
DisableAssistedAddrs bool `json:"disableAssistedAddrs,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
type LogConfig struct {
|
type LogConfig struct {
|
||||||
// This is destination where frp should write the logs.
|
// This is destination where frp should write the logs.
|
||||||
// If "console" is used, logs will be printed to stdout, otherwise,
|
// If "console" is used, logs will be printed to stdout, otherwise,
|
||||||
|
|||||||
@@ -422,9 +422,6 @@ type XTCPProxyConfig struct {
|
|||||||
|
|
||||||
Secretkey string `json:"secretKey,omitempty"`
|
Secretkey string `json:"secretKey,omitempty"`
|
||||||
AllowUsers []string `json:"allowUsers,omitempty"`
|
AllowUsers []string `json:"allowUsers,omitempty"`
|
||||||
|
|
||||||
// NatTraversal configuration for NAT traversal
|
|
||||||
NatTraversal *NatTraversalConfig `json:"natTraversal,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *XTCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
func (c *XTCPProxyConfig) MarshalToMsg(m *msg.NewProxy) {
|
||||||
|
|||||||
@@ -160,9 +160,6 @@ type XTCPVisitorConfig struct {
|
|||||||
MinRetryInterval int `json:"minRetryInterval,omitempty"`
|
MinRetryInterval int `json:"minRetryInterval,omitempty"`
|
||||||
FallbackTo string `json:"fallbackTo,omitempty"`
|
FallbackTo string `json:"fallbackTo,omitempty"`
|
||||||
FallbackTimeoutMs int `json:"fallbackTimeoutMs,omitempty"`
|
FallbackTimeoutMs int `json:"fallbackTimeoutMs,omitempty"`
|
||||||
|
|
||||||
// NatTraversal configuration for NAT traversal
|
|
||||||
NatTraversal *NatTraversalConfig `json:"natTraversal,omitempty"`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *XTCPVisitorConfig) Complete(g *ClientCommonConfig) {
|
func (c *XTCPVisitorConfig) Complete(g *ClientCommonConfig) {
|
||||||
|
|||||||
@@ -14,12 +14,11 @@ const (
|
|||||||
var ServerMetrics metrics.ServerMetrics = newServerMetrics()
|
var ServerMetrics metrics.ServerMetrics = newServerMetrics()
|
||||||
|
|
||||||
type serverMetrics struct {
|
type serverMetrics struct {
|
||||||
clientCount prometheus.Gauge
|
clientCount prometheus.Gauge
|
||||||
proxyCount *prometheus.GaugeVec
|
proxyCount *prometheus.GaugeVec
|
||||||
proxyCountDetailed *prometheus.GaugeVec
|
connectionCount *prometheus.GaugeVec
|
||||||
connectionCount *prometheus.GaugeVec
|
trafficIn *prometheus.CounterVec
|
||||||
trafficIn *prometheus.CounterVec
|
trafficOut *prometheus.CounterVec
|
||||||
trafficOut *prometheus.CounterVec
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *serverMetrics) NewClient() {
|
func (m *serverMetrics) NewClient() {
|
||||||
@@ -30,14 +29,12 @@ func (m *serverMetrics) CloseClient() {
|
|||||||
m.clientCount.Dec()
|
m.clientCount.Dec()
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *serverMetrics) NewProxy(name string, proxyType string) {
|
func (m *serverMetrics) NewProxy(_ string, proxyType string) {
|
||||||
m.proxyCount.WithLabelValues(proxyType).Inc()
|
m.proxyCount.WithLabelValues(proxyType).Inc()
|
||||||
m.proxyCountDetailed.WithLabelValues(proxyType, name).Inc()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *serverMetrics) CloseProxy(name string, proxyType string) {
|
func (m *serverMetrics) CloseProxy(_ string, proxyType string) {
|
||||||
m.proxyCount.WithLabelValues(proxyType).Dec()
|
m.proxyCount.WithLabelValues(proxyType).Dec()
|
||||||
m.proxyCountDetailed.WithLabelValues(proxyType, name).Dec()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
|
func (m *serverMetrics) OpenConnection(name string, proxyType string) {
|
||||||
@@ -70,12 +67,6 @@ func newServerMetrics() *serverMetrics {
|
|||||||
Name: "proxy_counts",
|
Name: "proxy_counts",
|
||||||
Help: "The current proxy counts",
|
Help: "The current proxy counts",
|
||||||
}, []string{"type"}),
|
}, []string{"type"}),
|
||||||
proxyCountDetailed: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
|
||||||
Namespace: namespace,
|
|
||||||
Subsystem: serverSubsystem,
|
|
||||||
Name: "proxy_counts_detailed",
|
|
||||||
Help: "The current number of proxies grouped by type and name",
|
|
||||||
}, []string{"type", "name"}),
|
|
||||||
connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
connectionCount: prometheus.NewGaugeVec(prometheus.GaugeOpts{
|
||||||
Namespace: namespace,
|
Namespace: namespace,
|
||||||
Subsystem: serverSubsystem,
|
Subsystem: serverSubsystem,
|
||||||
@@ -97,7 +88,6 @@ func newServerMetrics() *serverMetrics {
|
|||||||
}
|
}
|
||||||
prometheus.MustRegister(m.clientCount)
|
prometheus.MustRegister(m.clientCount)
|
||||||
prometheus.MustRegister(m.proxyCount)
|
prometheus.MustRegister(m.proxyCount)
|
||||||
prometheus.MustRegister(m.proxyCountDetailed)
|
|
||||||
prometheus.MustRegister(m.connectionCount)
|
prometheus.MustRegister(m.connectionCount)
|
||||||
prometheus.MustRegister(m.trafficIn)
|
prometheus.MustRegister(m.trafficIn)
|
||||||
prometheus.MustRegister(m.trafficOut)
|
prometheus.MustRegister(m.trafficOut)
|
||||||
|
|||||||
@@ -68,13 +68,6 @@ var (
|
|||||||
DetectRoleReceiver = "receiver"
|
DetectRoleReceiver = "receiver"
|
||||||
)
|
)
|
||||||
|
|
||||||
// PrepareOptions defines options for NAT traversal preparation
|
|
||||||
type PrepareOptions struct {
|
|
||||||
// DisableAssistedAddrs disables the use of local network interfaces
|
|
||||||
// for assisted connections during NAT traversal
|
|
||||||
DisableAssistedAddrs bool
|
|
||||||
}
|
|
||||||
|
|
||||||
type PrepareResult struct {
|
type PrepareResult struct {
|
||||||
Addrs []string
|
Addrs []string
|
||||||
AssistedAddrs []string
|
AssistedAddrs []string
|
||||||
@@ -115,7 +108,7 @@ func PreCheck(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Prepare is used to do some preparation work before penetration.
|
// Prepare is used to do some preparation work before penetration.
|
||||||
func Prepare(stunServers []string, opts PrepareOptions) (*PrepareResult, error) {
|
func Prepare(stunServers []string) (*PrepareResult, error) {
|
||||||
// discover for Nat type
|
// discover for Nat type
|
||||||
addrs, localAddr, err := Discover(stunServers, "")
|
addrs, localAddr, err := Discover(stunServers, "")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -140,13 +133,9 @@ func Prepare(stunServers []string, opts PrepareOptions) (*PrepareResult, error)
|
|||||||
return nil, fmt.Errorf("listen local udp addr error: %v", err)
|
return nil, fmt.Errorf("listen local udp addr error: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Apply NAT traversal options
|
assistedAddrs := make([]string, 0, len(localIPs))
|
||||||
var assistedAddrs []string
|
for _, ip := range localIPs {
|
||||||
if !opts.DisableAssistedAddrs {
|
assistedAddrs = append(assistedAddrs, net.JoinHostPort(ip, strconv.Itoa(laddr.Port)))
|
||||||
assistedAddrs = make([]string, 0, len(localIPs))
|
|
||||||
for _, ip := range localIPs {
|
|
||||||
assistedAddrs = append(assistedAddrs, net.JoinHostPort(ip, strconv.Itoa(laddr.Port)))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return &PrepareResult{
|
return &PrepareResult{
|
||||||
Addrs: addrs,
|
Addrs: addrs,
|
||||||
|
|||||||
@@ -14,7 +14,7 @@
|
|||||||
|
|
||||||
package version
|
package version
|
||||||
|
|
||||||
var version = "0.65.0"
|
var version = "0.64.0"
|
||||||
|
|
||||||
func Full() string {
|
func Full() string {
|
||||||
return version
|
return version
|
||||||
|
|||||||
@@ -1,65 +0,0 @@
|
|||||||
// Copyright 2025 The frp Authors
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
// you may not use this file except in compliance with the License.
|
|
||||||
// You may obtain a copy of the License at
|
|
||||||
//
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
//
|
|
||||||
// Unless required by applicable law or agreed to in writing, software
|
|
||||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
// See the License for the specific language governing permissions and
|
|
||||||
// limitations under the License.
|
|
||||||
|
|
||||||
package xlog
|
|
||||||
|
|
||||||
import "strings"
|
|
||||||
|
|
||||||
// LogWriter forwards writes to frp's logger at configurable level.
|
|
||||||
// It is safe for concurrent use as long as the underlying Logger is thread-safe.
|
|
||||||
type LogWriter struct {
|
|
||||||
xl *Logger
|
|
||||||
logFunc func(string)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (w LogWriter) Write(p []byte) (n int, err error) {
|
|
||||||
msg := strings.TrimSpace(string(p))
|
|
||||||
w.logFunc(msg)
|
|
||||||
return len(p), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewTraceWriter(xl *Logger) LogWriter {
|
|
||||||
return LogWriter{
|
|
||||||
xl: xl,
|
|
||||||
logFunc: func(msg string) { xl.Tracef("%s", msg) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewDebugWriter(xl *Logger) LogWriter {
|
|
||||||
return LogWriter{
|
|
||||||
xl: xl,
|
|
||||||
logFunc: func(msg string) { xl.Debugf("%s", msg) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewInfoWriter(xl *Logger) LogWriter {
|
|
||||||
return LogWriter{
|
|
||||||
xl: xl,
|
|
||||||
logFunc: func(msg string) { xl.Infof("%s", msg) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewWarnWriter(xl *Logger) LogWriter {
|
|
||||||
return LogWriter{
|
|
||||||
xl: xl,
|
|
||||||
logFunc: func(msg string) { xl.Warnf("%s", msg) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewErrorWriter(xl *Logger) LogWriter {
|
|
||||||
return LogWriter{
|
|
||||||
xl: xl,
|
|
||||||
logFunc: func(msg string) { xl.Errorf("%s", msg) },
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,6 +19,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/tls"
|
"crypto/tls"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net"
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
@@ -515,8 +516,7 @@ func (svr *Service) HandleListener(l net.Listener, internal bool) {
|
|||||||
if lo.FromPtr(svr.cfg.Transport.TCPMux) && !internal {
|
if lo.FromPtr(svr.cfg.Transport.TCPMux) && !internal {
|
||||||
fmuxCfg := fmux.DefaultConfig()
|
fmuxCfg := fmux.DefaultConfig()
|
||||||
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
|
fmuxCfg.KeepAliveInterval = time.Duration(svr.cfg.Transport.TCPMuxKeepaliveInterval) * time.Second
|
||||||
// Use trace level for yamux logs
|
fmuxCfg.LogOutput = io.Discard
|
||||||
fmuxCfg.LogOutput = xlog.NewTraceWriter(xlog.FromContextSafe(ctx))
|
|
||||||
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
fmuxCfg.MaxStreamWindowSize = 6 * 1024 * 1024
|
||||||
session, err := fmux.Server(frpConn, fmuxCfg)
|
session, err := fmux.Server(frpConn, fmuxCfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
|||||||
Reference in New Issue
Block a user