mirror of
https://gitee.com/IrisVega/frp.git
synced 2024-11-01 22:31:29 +08:00
support wss between frpc and frps (#3503)
This commit is contained in:
parent
b146989703
commit
801e8c6742
15
Release.md
15
Release.md
@ -1,18 +1,7 @@
|
||||
## Notes
|
||||
|
||||
**For enhanced security, the default values for `tls_enable` and `disable_custom_tls_first_byte` have been set to true.**
|
||||
|
||||
If you wish to revert to the previous default values, you need to manually set the values of these two parameters to false.
|
||||
|
||||
### Features
|
||||
|
||||
* Added support for `allow_users` in stcp, sudp, xtcp. By default, only the same user is allowed to access. Use `*` to allow access from any user. The visitor configuration now supports `server_user` to connect to proxies of other users.
|
||||
* Added fallback support to a specified alternative visitor when xtcp connection fails.
|
||||
|
||||
### Improvements
|
||||
|
||||
* Increased the default value of `MaxStreamWindowSize` for yamux to 6MB, improving traffic forwarding rate in high-latency scenarios.
|
||||
* frpc supports connecting to frps via the wss protocol by enabling the configuration `protocol = wss`.
|
||||
|
||||
### Fixes
|
||||
|
||||
* Fixed an issue where having proxies with the same name would cause previously working proxies to become ineffective in `xtcp`.
|
||||
* Fix an issue caused by a bug in yamux that prevents wss from working properly in certain plugins.
|
||||
|
@ -135,7 +135,7 @@ func (svr *Service) Run() error {
|
||||
if svr.cfg.LoginFailExit {
|
||||
return err
|
||||
}
|
||||
util.RandomSleep(10*time.Second, 0.9, 1.1)
|
||||
util.RandomSleep(5*time.Second, 0.9, 1.1)
|
||||
} else {
|
||||
// login success
|
||||
ctl := NewControl(svr.ctx, svr.runID, conn, cm, svr.cfg, svr.pxyCfgs, svr.visitorCfgs, svr.authSetter)
|
||||
@ -427,7 +427,11 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||
xl := xlog.FromContextSafe(cm.ctx)
|
||||
var tlsConfig *tls.Config
|
||||
var err error
|
||||
if cm.cfg.TLSEnable {
|
||||
tlsEnable := cm.cfg.TLSEnable
|
||||
if cm.cfg.Protocol == "wss" {
|
||||
tlsEnable = true
|
||||
}
|
||||
if tlsEnable {
|
||||
sn := cm.cfg.TLSServerName
|
||||
if sn == "" {
|
||||
sn = cm.cfg.ServerAddr
|
||||
@ -451,10 +455,23 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||
}
|
||||
dialOptions := []libdial.DialOption{}
|
||||
protocol := cm.cfg.Protocol
|
||||
if protocol == "websocket" {
|
||||
switch protocol {
|
||||
case "websocket":
|
||||
protocol = "tcp"
|
||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: utilnet.DialHookWebsocket()}))
|
||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: utilnet.DialHookWebsocket(protocol, "")}))
|
||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{
|
||||
Hook: utilnet.DialHookCustomTLSHeadByte(tlsConfig != nil, cm.cfg.DisableCustomTLSFirstByte),
|
||||
}))
|
||||
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
||||
case "wss":
|
||||
protocol = "tcp"
|
||||
dialOptions = append(dialOptions, libdial.WithTLSConfigAndPriority(100, tlsConfig))
|
||||
// Make sure that if it is wss, the websocket hook is executed after the tls hook.
|
||||
dialOptions = append(dialOptions, libdial.WithAfterHook(libdial.AfterHook{Hook: utilnet.DialHookWebsocket(protocol, tlsConfig.ServerName), Priority: 110}))
|
||||
default:
|
||||
dialOptions = append(dialOptions, libdial.WithTLSConfig(tlsConfig))
|
||||
}
|
||||
|
||||
if cm.cfg.ConnectServerLocalIP != "" {
|
||||
dialOptions = append(dialOptions, libdial.WithLocalAddr(cm.cfg.ConnectServerLocalIP))
|
||||
}
|
||||
@ -464,10 +481,6 @@ func (cm *ConnectionManager) realConnect() (net.Conn, error) {
|
||||
libdial.WithKeepAlive(time.Duration(cm.cfg.DialServerKeepAlive)*time.Second),
|
||||
libdial.WithProxy(proxyType, addr),
|
||||
libdial.WithProxyAuth(auth),
|
||||
libdial.WithTLSConfig(tlsConfig),
|
||||
libdial.WithAfterHook(libdial.AfterHook{
|
||||
Hook: utilnet.DialHookCustomTLSHeadByte(tlsConfig != nil, cm.cfg.DisableCustomTLSFirstByte),
|
||||
}),
|
||||
)
|
||||
conn, err := libdial.DialContext(
|
||||
cm.ctx,
|
||||
|
@ -77,6 +77,7 @@ var (
|
||||
bindPort int
|
||||
|
||||
tlsEnable bool
|
||||
tlsServerName string
|
||||
)
|
||||
|
||||
func init() {
|
||||
@ -88,13 +89,14 @@ func init() {
|
||||
func RegisterCommonFlags(cmd *cobra.Command) {
|
||||
cmd.PersistentFlags().StringVarP(&serverAddr, "server_addr", "s", "127.0.0.1:7000", "frp server's address")
|
||||
cmd.PersistentFlags().StringVarP(&user, "user", "u", "", "user")
|
||||
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp or kcp or websocket")
|
||||
cmd.PersistentFlags().StringVarP(&protocol, "protocol", "p", "tcp", "tcp, kcp, quic, websocket, wss")
|
||||
cmd.PersistentFlags().StringVarP(&token, "token", "t", "", "auth token")
|
||||
cmd.PersistentFlags().StringVarP(&logLevel, "log_level", "", "info", "log level")
|
||||
cmd.PersistentFlags().StringVarP(&logFile, "log_file", "", "console", "console or file path")
|
||||
cmd.PersistentFlags().IntVarP(&logMaxDays, "log_max_days", "", 3, "log file reversed days")
|
||||
cmd.PersistentFlags().BoolVarP(&disableLogColor, "disable_log_color", "", false, "disable log color in console")
|
||||
cmd.PersistentFlags().BoolVarP(&tlsEnable, "tls_enable", "", true, "enable frpc tls")
|
||||
cmd.PersistentFlags().StringVarP(&tlsServerName, "tls_server_name", "", "", "specify the custom server name of tls certificate")
|
||||
cmd.PersistentFlags().StringVarP(&dnsServer, "dns_server", "", "", "specify dns server instead of using system default one")
|
||||
}
|
||||
|
||||
@ -186,6 +188,7 @@ func parseClientCommonCfgFromCmd() (cfg config.ClientCommonConf, err error) {
|
||||
cfg.ClientConfig = auth.GetDefaultClientConf()
|
||||
cfg.Token = token
|
||||
cfg.TLSEnable = tlsEnable
|
||||
cfg.TLSServerName = tlsServerName
|
||||
|
||||
cfg.Complete()
|
||||
if err = cfg.Validate(); err != nil {
|
||||
|
@ -95,7 +95,7 @@ user = your_name
|
||||
login_fail_exit = true
|
||||
|
||||
# communication protocol used to connect to server
|
||||
# supports tcp, kcp, quic and websocket now, default is tcp
|
||||
# supports tcp, kcp, quic, websocket and wss now, default is tcp
|
||||
protocol = tcp
|
||||
|
||||
# set client binding ip when connect server, default is empty.
|
||||
|
5
go.mod
5
go.mod
@ -6,7 +6,7 @@ require (
|
||||
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5
|
||||
github.com/coreos/go-oidc/v3 v3.4.0
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb
|
||||
github.com/fatedier/golib v0.1.1-0.20230320133937-a7edcc8c793d
|
||||
github.com/fatedier/golib v0.1.1-0.20230628070619-a1a0c648236a
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible
|
||||
github.com/go-playground/validator/v10 v10.11.0
|
||||
github.com/google/uuid v1.3.0
|
||||
@ -75,3 +75,6 @@ require (
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/utils v0.0.0-20221107191617-1a15be271d1d // indirect
|
||||
)
|
||||
|
||||
// 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
|
||||
|
8
go.sum
8
go.sum
@ -121,10 +121,12 @@ github.com/envoyproxy/go-control-plane v0.10.2-0.20220325020618-49ff273808a1/go.
|
||||
github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c=
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb h1:wCrNShQidLmvVWn/0PikGmpdP0vtQmnvyRg3ZBEhczw=
|
||||
github.com/fatedier/beego v0.0.0-20171024143340-6c6a4f5bd5eb/go.mod h1:wx3gB6dbIfBRcucp94PI9Bt3I0F2c/MyNEWuhzpWiwk=
|
||||
github.com/fatedier/golib v0.1.1-0.20230320133937-a7edcc8c793d h1:/m9Atycn9uKRwwOkxv4c+zaugxRgkdSG/Eg3IJWOpNs=
|
||||
github.com/fatedier/golib v0.1.1-0.20230320133937-a7edcc8c793d/go.mod h1:Wdn1pJ0dHB1lah6FPYwt4AO9NEmWI0OzW13dpzC9g4E=
|
||||
github.com/fatedier/golib v0.1.1-0.20230628070619-a1a0c648236a h1:HiRTFdy3ary86Vi2nsoINy2/YgjDPQ+21j3ikwJSD2E=
|
||||
github.com/fatedier/golib v0.1.1-0.20230628070619-a1a0c648236a/go.mod h1:Wdn1pJ0dHB1lah6FPYwt4AO9NEmWI0OzW13dpzC9g4E=
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible h1:ssXat9YXFvigNge/IkkZvFMn8yeYKFX+uI6wn2mLJ74=
|
||||
github.com/fatedier/kcp-go v2.0.4-0.20190803094908-fe8645b0a904+incompatible/go.mod h1:YpCOaxj7vvMThhIQ9AfTOPW2sfztQR5WDfs7AflSy4s=
|
||||
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/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4=
|
||||
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
|
||||
github.com/ghodss/yaml v1.0.0/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04=
|
||||
@ -270,8 +272,6 @@ github.com/hashicorp/logutils v1.0.0/go.mod h1:QIAnNjmIWmVIIkWDTG1z5v++HQmx9WQRO
|
||||
github.com/hashicorp/mdns v1.0.0/go.mod h1:tL+uN++7HEJ6SQLQ2/p+z2pH24WQKWjBPkE0mNTz8vQ=
|
||||
github.com/hashicorp/memberlist v0.1.3/go.mod h1:ajVTdAv/9Im8oMAAj5G31PhhMCZJV2pPBoIllUwCN7I=
|
||||
github.com/hashicorp/serf v0.8.2/go.mod h1:6hOLApaqBFA1NXqRQAsxw9QxuDEvNxSQRwA/JwenrHc=
|
||||
github.com/hashicorp/yamux v0.1.1 h1:yrQxtgseBDrq9Y652vSRDvsKCJKOUD+GzTS4Y0Y8pvE=
|
||||
github.com/hashicorp/yamux v0.1.1/go.mod h1:CtWFDAQgb7dxtzFs4tWbplKIe2jSi3+5vKbgIO0SLnQ=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc=
|
||||
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
|
||||
|
@ -20,6 +20,7 @@ import (
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/samber/lo"
|
||||
"gopkg.in/ini.v1"
|
||||
|
||||
"github.com/fatedier/frp/pkg/auth"
|
||||
@ -117,7 +118,7 @@ type ClientCommonConf struct {
|
||||
Start []string `ini:"start" json:"start"`
|
||||
// Start map[string]struct{} `json:"start"`
|
||||
// Protocol specifies the protocol to use when interacting with the server.
|
||||
// Valid values are "tcp", "kcp", "quic" and "websocket". By default, this value
|
||||
// Valid values are "tcp", "kcp", "quic", "websocket" and "wss". By default, this value
|
||||
// is "tcp".
|
||||
Protocol string `ini:"protocol" json:"protocol"`
|
||||
// QUIC protocol options
|
||||
@ -230,7 +231,7 @@ func (cfg *ClientCommonConf) Validate() error {
|
||||
}
|
||||
}
|
||||
|
||||
if cfg.Protocol != "tcp" && cfg.Protocol != "kcp" && cfg.Protocol != "websocket" && cfg.Protocol != "quic" {
|
||||
if !lo.Contains([]string{"tcp", "kcp", "quic", "websocket", "wss"}, cfg.Protocol) {
|
||||
return fmt.Errorf("invalid protocol")
|
||||
}
|
||||
|
||||
|
@ -21,9 +21,15 @@ func DialHookCustomTLSHeadByte(enableTLS bool, disableCustomTLSHeadByte bool) li
|
||||
}
|
||||
}
|
||||
|
||||
func DialHookWebsocket() libdial.AfterHookFunc {
|
||||
func DialHookWebsocket(protocol string, host string) libdial.AfterHookFunc {
|
||||
return func(ctx context.Context, c net.Conn, addr string) (context.Context, net.Conn, error) {
|
||||
addr = "ws://" + addr + FrpWebsocketPath
|
||||
if protocol != "wss" {
|
||||
protocol = "ws"
|
||||
}
|
||||
if host == "" {
|
||||
host = addr
|
||||
}
|
||||
addr = protocol + "://" + host + FrpWebsocketPath
|
||||
uri, err := url.Parse(addr)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
|
@ -331,24 +331,29 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
|
||||
proxyExtraConfig string
|
||||
visitorExtraConfig string
|
||||
expectError bool
|
||||
user2 bool
|
||||
deployUser2Client bool
|
||||
// skipXTCP is used to skip xtcp test case
|
||||
skipXTCP bool
|
||||
}{
|
||||
{
|
||||
proxyName: "normal",
|
||||
bindPortName: port.GenName("Normal"),
|
||||
visitorSK: correctSK,
|
||||
skipXTCP: true,
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption",
|
||||
bindPortName: port.GenName("WithEncryption"),
|
||||
visitorSK: correctSK,
|
||||
commonExtraConfig: "use_encryption = true",
|
||||
skipXTCP: true,
|
||||
},
|
||||
{
|
||||
proxyName: "with-compression",
|
||||
bindPortName: port.GenName("WithCompression"),
|
||||
visitorSK: correctSK,
|
||||
commonExtraConfig: "use_compression = true",
|
||||
skipXTCP: true,
|
||||
},
|
||||
{
|
||||
proxyName: "with-encryption-and-compression",
|
||||
@ -371,7 +376,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
|
||||
visitorSK: correctSK,
|
||||
proxyExtraConfig: "allow_users = another, user2",
|
||||
visitorExtraConfig: "server_user = user1",
|
||||
user2: true,
|
||||
deployUser2Client: true,
|
||||
},
|
||||
{
|
||||
proxyName: "not-allowed-user",
|
||||
@ -387,7 +392,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
|
||||
visitorSK: correctSK,
|
||||
proxyExtraConfig: "allow_users = *",
|
||||
visitorExtraConfig: "server_user = user1",
|
||||
user2: true,
|
||||
deployUser2Client: true,
|
||||
},
|
||||
}
|
||||
|
||||
@ -399,7 +404,7 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
|
||||
config := getProxyVisitorConf(
|
||||
test.proxyName, test.bindPortName, test.visitorSK, test.commonExtraConfig+"\n"+test.visitorExtraConfig,
|
||||
) + "\n"
|
||||
if test.user2 {
|
||||
if test.deployUser2Client {
|
||||
clientUser2VisitorConf += config
|
||||
} else {
|
||||
clientVisitorConf += config
|
||||
@ -411,7 +416,10 @@ var _ = ginkgo.Describe("[Feature: Basic]", func() {
|
||||
for _, test := range tests {
|
||||
timeout := time.Second
|
||||
if t == "xtcp" {
|
||||
timeout = 4 * time.Second
|
||||
if test.skipXTCP {
|
||||
continue
|
||||
}
|
||||
timeout = 10 * time.Second
|
||||
}
|
||||
framework.NewRequestExpect(f).
|
||||
RequestModify(func(r *request.Request) {
|
||||
|
@ -3,6 +3,7 @@ package basic
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/onsi/ginkgo/v2"
|
||||
|
||||
@ -15,6 +16,10 @@ import (
|
||||
type generalTestConfigures struct {
|
||||
server string
|
||||
client string
|
||||
clientPrefix string
|
||||
client2 string
|
||||
client2Prefix string
|
||||
testDelay time.Duration
|
||||
expectError bool
|
||||
}
|
||||
|
||||
@ -30,6 +35,9 @@ func renderBindPortConfig(protocol string) string {
|
||||
func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) {
|
||||
serverConf := consts.DefaultServerConfig
|
||||
clientConf := consts.DefaultClientConfig
|
||||
if configures.clientPrefix != "" {
|
||||
clientConf = configures.clientPrefix
|
||||
}
|
||||
|
||||
serverConf += fmt.Sprintf(`
|
||||
%s
|
||||
@ -54,7 +62,23 @@ func runClientServerTest(f *framework.Framework, configures *generalTestConfigur
|
||||
framework.UDPEchoServerPort, udpPortName,
|
||||
)
|
||||
|
||||
f.RunProcesses([]string{serverConf}, []string{clientConf})
|
||||
clientConfs := []string{clientConf}
|
||||
if configures.client2 != "" {
|
||||
client2Conf := consts.DefaultClientConfig
|
||||
if configures.client2Prefix != "" {
|
||||
client2Conf = configures.client2Prefix
|
||||
}
|
||||
client2Conf += fmt.Sprintf(`
|
||||
%s
|
||||
`, configures.client2)
|
||||
clientConfs = append(clientConfs, client2Conf)
|
||||
}
|
||||
|
||||
f.RunProcesses([]string{serverConf}, clientConfs)
|
||||
|
||||
if configures.testDelay > 0 {
|
||||
time.Sleep(configures.testDelay)
|
||||
}
|
||||
|
||||
framework.NewRequestExpect(f).PortName(tcpPortName).ExpectError(configures.expectError).Explain("tcp proxy").Ensure()
|
||||
framework.NewRequestExpect(f).Protocol("udp").
|
||||
@ -84,6 +108,33 @@ var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
|
||||
}
|
||||
})
|
||||
|
||||
// wss is special, it needs to be tested separately.
|
||||
// frps only supports ws, so there should be a proxy to terminate TLS before frps.
|
||||
ginkgo.Describe("Protocol wss", func() {
|
||||
wssPort := f.AllocPort()
|
||||
configures := &generalTestConfigures{
|
||||
clientPrefix: fmt.Sprintf(`
|
||||
[common]
|
||||
server_addr = 127.0.0.1
|
||||
server_port = %d
|
||||
protocol = wss
|
||||
log_level = trace
|
||||
login_fail_exit = false
|
||||
`, wssPort),
|
||||
// Due to the fact that frps cannot directly accept wss connections, we use the https2http plugin of another frpc to terminate TLS.
|
||||
client2: fmt.Sprintf(`
|
||||
[wss2ws]
|
||||
type = tcp
|
||||
remote_port = %d
|
||||
plugin = https2http
|
||||
plugin_local_addr = 127.0.0.1:{{ .%s }}
|
||||
`, wssPort, consts.PortServerName),
|
||||
testDelay: 10 * time.Second,
|
||||
}
|
||||
|
||||
defineClientServerTest("wss", f, configures)
|
||||
})
|
||||
|
||||
ginkgo.Describe("Authentication", func() {
|
||||
defineClientServerTest("Token Correct", f, &generalTestConfigures{
|
||||
server: "token = 123456",
|
||||
|
Loading…
Reference in New Issue
Block a user