Compare commits

..

No commits in common. "256b87321d857b0175e28d8a00d0fadbd1314d92" and "354091087955f6fcb7216ee03a2397376e4526c4" have entirely different histories.

13 changed files with 57 additions and 50 deletions

2
.gitignore vendored
View File

@ -34,8 +34,6 @@ dist/
.idea/ .idea/
.vscode/ .vscode/
.autogen_ssh_key .autogen_ssh_key
client.crt
client.key
# Cache # Cache
*.swp *.swp

View File

@ -132,9 +132,6 @@ issues:
- linters: - linters:
- revive - revive
text: "unused-parameter" text: "unused-parameter"
- linters:
- unparam
text: "is always false"
# Independently from option `exclude` we use default exclude patterns, # Independently from option `exclude` we use default exclude patterns,
# it can be disabled by this option. To list all # it can be disabled by this option. To list all

View File

@ -1,3 +1,11 @@
### Features
* The new command line parameter `--strict_config` has been added to enable strict configuration validation mode. It will throw an error for unknown fields instead of ignoring them. In future versions, we will set the default value of this parameter to true to avoid misconfigurations.
* Support `SSH reverse tunneling`. With this feature, you can expose your local service without running frpc, only using SSH. The SSH reverse tunnel agent has many functional limitations compared to the frpc agent. The currently supported proxy types are tcp, http, https, tcpmux, and stcp.
* The frpc tcpmux command line parameters have been updated to support configuring `http_user` and `http_pwd`.
* The frpc stcp/sudp/xtcp command line parameters have been updated to support configuring `allow_users`.
### Fixes ### Fixes
* frpc has a certain chance to panic when login: close of closed channel. * frpc: Return code 1 when the first login attempt fails and exits.
* When auth.method is `oidc` and auth.additionalScopes contains `HeartBeats`, if obtaining AccessToken fails, the application will be unresponsive.

View File

@ -239,15 +239,15 @@ func (ctl *Control) heartbeatWorker() {
// Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value. // Users can still enable heartbeat feature by setting HeartbeatInterval to a positive value.
if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 { if ctl.sessionCtx.Common.Transport.HeartbeatInterval > 0 {
// send heartbeat to server // send heartbeat to server
sendHeartBeat := func() (bool, error) { sendHeartBeat := func() error {
xl.Debug("send heartbeat to server") xl.Debug("send heartbeat to server")
pingMsg := &msg.Ping{} pingMsg := &msg.Ping{}
if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil { if err := ctl.sessionCtx.AuthSetter.SetPing(pingMsg); err != nil {
xl.Warn("error during ping authentication: %v, skip sending ping message", err) xl.Warn("error during ping authentication: %v, skip sending ping message", err)
return false, err return err
} }
_ = ctl.msgDispatcher.Send(pingMsg) _ = ctl.msgDispatcher.Send(pingMsg)
return false, nil return nil
} }
go wait.BackoffUntil(sendHeartBeat, go wait.BackoffUntil(sendHeartBeat,

View File

@ -192,16 +192,16 @@ func (svr *Service) keepControllerWorking() {
// the control immediately exits. It is necessary to limit the frequency of reconnection in this case. // the control immediately exits. It is necessary to limit the frequency of reconnection in this case.
// The interval for the first three retries in 1 minute will be very short, and then it will increase exponentially. // The interval for the first three retries in 1 minute will be very short, and then it will increase exponentially.
// The maximum interval is 20 seconds. // The maximum interval is 20 seconds.
wait.BackoffUntil(func() (bool, error) { wait.BackoffUntil(func() error {
// loopLoginUntilSuccess is another layer of loop that will continuously attempt to // loopLoginUntilSuccess is another layer of loop that will continuously attempt to
// login to the server until successful. // login to the server until successful.
svr.loopLoginUntilSuccess(20*time.Second, false) svr.loopLoginUntilSuccess(20*time.Second, false)
if svr.ctl != nil { if svr.ctl != nil {
<-svr.ctl.Done() <-svr.ctl.Done()
return false, errors.New("control is closed and try another loop") return errors.New("control is closed and try another loop")
} }
// If the control is nil, it means that the login failed and the service is also closed. // If the control is nil, it means that the login failed and the service is also closed.
return false, nil return nil
}, wait.NewFastBackoffManager( }, wait.NewFastBackoffManager(
wait.FastBackoffOptions{ wait.FastBackoffOptions{
Duration: time.Second, Duration: time.Second,
@ -282,8 +282,9 @@ func (svr *Service) login() (conn net.Conn, connector Connector, err error) {
func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginExit bool) { func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginExit bool) {
xl := xlog.FromContextSafe(svr.ctx) xl := xlog.FromContextSafe(svr.ctx)
successCh := make(chan struct{})
loginFunc := func() (bool, error) { loginFunc := func() error {
xl.Info("try to connect to server...") xl.Info("try to connect to server...")
conn, connector, err := svr.login() conn, connector, err := svr.login()
if err != nil { if err != nil {
@ -291,7 +292,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
if firstLoginExit { if firstLoginExit {
svr.cancel(cancelErr{Err: err}) svr.cancel(cancelErr{Err: err})
} }
return false, err return err
} }
svr.cfgMu.RLock() svr.cfgMu.RLock()
@ -314,7 +315,7 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
if err != nil { if err != nil {
conn.Close() conn.Close()
xl.Error("NewControl error: %v", err) xl.Error("NewControl error: %v", err)
return false, err return err
} }
ctl.SetInWorkConnCallback(svr.handleWorkConnCb) ctl.SetInWorkConnCallback(svr.handleWorkConnCb)
@ -326,7 +327,9 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
} }
svr.ctl = ctl svr.ctl = ctl
svr.ctlMu.Unlock() svr.ctlMu.Unlock()
return true, nil
close(successCh)
return nil
} }
// try to reconnect to server until success // try to reconnect to server until success
@ -336,7 +339,9 @@ func (svr *Service) loopLoginUntilSuccess(maxInterval time.Duration, firstLoginE
Factor: 2, Factor: 2,
Jitter: 0.1, Jitter: 0.1,
MaxDuration: maxInterval, MaxDuration: maxInterval,
}), true, svr.ctx.Done()) }),
true,
wait.MergeAndCloseOnAnyStopChannel(svr.ctx.Done(), successCh))
} }
func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error { func (svr *Service) UpdateAllConfigurer(proxyCfgs []v1.ProxyConfigurer, visitorCfgs []v1.VisitorConfigurer) error {

View File

@ -26,9 +26,5 @@ frpsPath=${ROOT}/bin/frps
if [ "${FRPS_PATH}" ]; then if [ "${FRPS_PATH}" ]; then
frpsPath="${FRPS_PATH}" frpsPath="${FRPS_PATH}"
fi fi
concurrency="16"
if [ "${CONCURRENCY}" ]; then
concurrency="${CONCURRENCY}"
fi
ginkgo -nodes=${concurrency} --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug} ginkgo -nodes=8 --poll-progress-after=60s ${ROOT}/test/e2e -- -frpc-path=${frpcPath} -frps-path=${frpsPath} -log-level=${logLevel} -debug=${debug}

View File

@ -19,7 +19,7 @@ import (
"strings" "strings"
) )
var version = "0.53.2" var version = "0.53.0"
func Full() string { func Full() string {
return version return version

View File

@ -113,7 +113,7 @@ func (f *fastBackoffImpl) Backoff(previousDuration time.Duration, previousCondit
return f.options.Duration return f.options.Duration
} }
func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool, stopCh <-chan struct{}) { func BackoffUntil(f func() error, backoff BackoffManager, sliding bool, stopCh <-chan struct{}) {
var delay time.Duration var delay time.Duration
previousError := false previousError := false
@ -131,9 +131,7 @@ func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool,
delay = backoff.Backoff(delay, previousError) delay = backoff.Backoff(delay, previousError)
} }
if done, err := f(); done { if err := f(); err != nil {
return
} else if err != nil {
previousError = true previousError = true
} else { } else {
previousError = false previousError = false
@ -144,6 +142,12 @@ func BackoffUntil(f func() (bool, error), backoff BackoffManager, sliding bool,
} }
ticker.Reset(delay) ticker.Reset(delay)
select {
case <-stopCh:
return
default:
}
select { select {
case <-stopCh: case <-stopCh:
return return
@ -166,9 +170,9 @@ func Jitter(duration time.Duration, maxFactor float64) time.Duration {
} }
func Until(f func(), period time.Duration, stopCh <-chan struct{}) { func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
ff := func() (bool, error) { ff := func() error {
f() f()
return false, nil return nil
} }
BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration { BackoffUntil(ff, BackoffFunc(func(time.Duration, bool) time.Duration {
return period return period

View File

@ -67,7 +67,7 @@ func NewDefaultFramework() *Framework {
TotalParallelNode: suiteConfig.ParallelTotal, TotalParallelNode: suiteConfig.ParallelTotal,
CurrentNodeIndex: suiteConfig.ParallelProcess, CurrentNodeIndex: suiteConfig.ParallelProcess,
FromPortIndex: 10000, FromPortIndex: 10000,
ToPortIndex: 30000, ToPortIndex: 60000,
} }
return NewFramework(options) return NewFramework(options)
} }

View File

@ -42,7 +42,7 @@ func (f *Framework) RunProcesses(serverTemplates []string, clientTemplates []str
ExpectNoError(err) ExpectNoError(err)
time.Sleep(500 * time.Millisecond) time.Sleep(500 * time.Millisecond)
} }
time.Sleep(2 * time.Second) time.Sleep(1 * time.Second)
currentClientProcesses := make([]*process.Process, 0, len(clientTemplates)) currentClientProcesses := make([]*process.Process, 0, len(clientTemplates))
for i := range clientTemplates { for i := range clientTemplates {
@ -76,7 +76,7 @@ func (f *Framework) RunFrps(args ...string) (*process.Process, string, error) {
return p, p.StdOutput(), err return p, p.StdOutput(), err
} }
// sleep for a while to get std output // sleep for a while to get std output
time.Sleep(2 * time.Second) time.Sleep(time.Second)
return p, p.StdOutput(), nil return p, p.StdOutput(), nil
} }
@ -87,7 +87,7 @@ func (f *Framework) RunFrpc(args ...string) (*process.Process, string, error) {
if err != nil { if err != nil {
return p, p.StdOutput(), err return p, p.StdOutput(), err
} }
time.Sleep(2 * time.Second) time.Sleep(time.Second)
return p, p.StdOutput(), nil return p, p.StdOutput(), nil
} }

View File

@ -22,11 +22,11 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
clientConf := consts.LegacyDefaultClientConfig clientConf := consts.LegacyDefaultClientConfig
serverConf += ` serverConf += `
allow_ports = 10000-11000,11002,12000-13000 allow_ports = 20000-25000,25002,30000-50000
` `
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000)) tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000)) udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp-allowded-in-range] [tcp-allowded-in-range]
type = tcp type = tcp
@ -37,7 +37,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
[tcp-port-not-allowed] [tcp-port-not-allowed]
type = tcp type = tcp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = 11001 remote_port = 25001
`, framework.TCPEchoServerPort) `, framework.TCPEchoServerPort)
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[tcp-port-unavailable] [tcp-port-unavailable]
@ -55,7 +55,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
[udp-port-not-allowed] [udp-port-not-allowed]
type = udp type = udp
local_port = {{ .%s }} local_port = {{ .%s }}
remote_port = 11003 remote_port = 25003
`, framework.UDPEchoServerPort) `, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})
@ -65,7 +65,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure() framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure() framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure()
// Unavailable, already bind by frps // Unavailable, already bind by frps
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure() framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
@ -76,7 +76,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.UDP().Port(11003) r.UDP().Port(25003)
}).ExpectError(true).Ensure() }).ExpectError(true).Ensure()
}) })

View File

@ -79,7 +79,6 @@ func (pa *Allocator) GetByName(portName string) int {
udpConn.Close() udpConn.Close()
pa.used.Insert(port) pa.used.Insert(port)
pa.reserved.Delete(port)
return port return port
} }
return 0 return 0

View File

@ -23,14 +23,14 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
serverConf += ` serverConf += `
allowPorts = [ allowPorts = [
{ start = 10000, end = 11000 }, { start = 20000, end = 25000 },
{ single = 11002 }, { single = 25002 },
{ start = 12000, end = 13000 }, { start = 30000, end = 50000 },
] ]
` `
tcpPortName := port.GenName("TCP", port.WithRangePorts(10000, 11000)) tcpPortName := port.GenName("TCP", port.WithRangePorts(20000, 25000))
udpPortName := port.GenName("UDP", port.WithRangePorts(12000, 13000)) udpPortName := port.GenName("UDP", port.WithRangePorts(30000, 50000))
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[[proxies]] [[proxies]]
name = "tcp-allowded-in-range" name = "tcp-allowded-in-range"
@ -43,7 +43,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
name = "tcp-port-not-allowed" name = "tcp-port-not-allowed"
type = "tcp" type = "tcp"
localPort = {{ .%s }} localPort = {{ .%s }}
remotePort = 11001 remotePort = 25001
`, framework.TCPEchoServerPort) `, framework.TCPEchoServerPort)
clientConf += fmt.Sprintf(` clientConf += fmt.Sprintf(`
[[proxies]] [[proxies]]
@ -64,7 +64,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
name = "udp-port-not-allowed" name = "udp-port-not-allowed"
type = "udp" type = "udp"
localPort = {{ .%s }} localPort = {{ .%s }}
remotePort = 11003 remotePort = 25003
`, framework.UDPEchoServerPort) `, framework.UDPEchoServerPort)
f.RunProcesses([]string{serverConf}, []string{clientConf}) f.RunProcesses([]string{serverConf}, []string{clientConf})
@ -74,7 +74,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
framework.NewRequestExpect(f).PortName(tcpPortName).Ensure() framework.NewRequestExpect(f).PortName(tcpPortName).Ensure()
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).Port(11001).ExpectError(true).Ensure() framework.NewRequestExpect(f).Port(25001).ExpectError(true).Ensure()
// Unavailable, already bind by frps // Unavailable, already bind by frps
framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure() framework.NewRequestExpect(f).PortName(consts.PortServerName).ExpectError(true).Ensure()
@ -85,7 +85,7 @@ var _ = ginkgo.Describe("[Feature: Server Manager]", func() {
// Not Allowed // Not Allowed
framework.NewRequestExpect(f).RequestModify(func(r *request.Request) { framework.NewRequestExpect(f).RequestModify(func(r *request.Request) {
r.UDP().Port(11003) r.UDP().Port(25003)
}).ExpectError(true).Ensure() }).ExpectError(true).Ensure()
}) })