package plugin import ( "fmt" "time" "github.com/onsi/ginkgo/v2" plugin "github.com/fatedier/frp/pkg/plugin/server" "github.com/fatedier/frp/pkg/transport" "github.com/fatedier/frp/test/e2e/framework" "github.com/fatedier/frp/test/e2e/framework/consts" pluginpkg "github.com/fatedier/frp/test/e2e/pkg/plugin" ) var _ = ginkgo.Describe("[Feature: Server-Plugins]", func() { f := framework.NewDefaultFramework() ginkgo.Describe("Login", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.LoginContent{} return &r } ginkgo.It("Auth for custom meta token", func() { localPort := f.AllocPort() clientAddressGot := false handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.LoginContent) if content.ClientAddress != "" { clientAddressGot = true } if content.Metas["token"] == "123" { ret.Unchange = true } else { ret.Reject = true ret.RejectReason = "invalid token" } return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "user-manager" addr = "127.0.0.1:%d" path = "/handler" ops = ["Login"] `, localPort) clientConf := consts.DefaultClientConfig remotePort := f.AllocPort() clientConf += fmt.Sprintf(` metadatas.token = "123" [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) remotePort2 := f.AllocPort() invalidTokenClientConf := consts.DefaultClientConfig + fmt.Sprintf(` [[proxies]] name = "tcp2" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort2) f.RunProcesses([]string{serverConf}, []string{clientConf, invalidTokenClientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() framework.NewRequestExpect(f).Port(remotePort2).ExpectError(true).Ensure() framework.ExpectTrue(clientAddressGot) }) }) ginkgo.Describe("NewProxy", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.NewProxyContent{} return &r } ginkgo.It("Validate Info", func() { localPort := f.AllocPort() handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewProxyContent) if content.ProxyName == "tcp" { ret.Unchange = true } else { ret.Reject = true } return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["NewProxy"] `, localPort) clientConf := consts.DefaultClientConfig remotePort := f.AllocPort() clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() }) ginkgo.It("Modify RemotePort", func() { localPort := f.AllocPort() remotePort := f.AllocPort() handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewProxyContent) content.RemotePort = remotePort ret.Content = content return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["NewProxy"] `, localPort) clientConf := consts.DefaultClientConfig clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = 0 `, framework.TCPEchoServerPort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() }) }) ginkgo.Describe("CloseProxy", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.CloseProxyContent{} return &r } ginkgo.It("Validate Info", func() { localPort := f.AllocPort() var recordProxyName string handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.CloseProxyContent) recordProxyName = content.ProxyName return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["CloseProxy"] `, localPort) clientConf := consts.DefaultClientConfig remotePort := f.AllocPort() clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) _, clients := f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() for _, c := range clients { _ = c.Stop() } time.Sleep(1 * time.Second) framework.ExpectEqual(recordProxyName, "tcp") }) }) ginkgo.Describe("Ping", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.PingContent{} return &r } ginkgo.It("Validate Info", func() { localPort := f.AllocPort() var record string handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.PingContent) record = content.Ping.PrivilegeKey ret.Unchange = true return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["Ping"] `, localPort) remotePort := f.AllocPort() clientConf := consts.DefaultClientConfig clientConf += fmt.Sprintf(` transport.heartbeatInterval = 1 auth.additionalScopes = ["HeartBeats"] [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() time.Sleep(3 * time.Second) framework.ExpectNotEqual("", record) }) }) ginkgo.Describe("NewWorkConn", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.NewWorkConnContent{} return &r } ginkgo.It("Validate Info", func() { localPort := f.AllocPort() var record string handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewWorkConnContent) record = content.NewWorkConn.RunID ret.Unchange = true return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["NewWorkConn"] `, localPort) remotePort := f.AllocPort() clientConf := consts.DefaultClientConfig clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() framework.ExpectNotEqual("", record) }) }) ginkgo.Describe("NewUserConn", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.NewUserConnContent{} return &r } ginkgo.It("Validate Info", func() { localPort := f.AllocPort() var record string handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewUserConnContent) record = content.RemoteAddr ret.Unchange = true return &ret } pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, nil) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "127.0.0.1:%d" path = "/handler" ops = ["NewUserConn"] `, localPort) remotePort := f.AllocPort() clientConf := consts.DefaultClientConfig clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() framework.ExpectNotEqual("", record) }) }) ginkgo.Describe("HTTPS Protocol", func() { newFunc := func() *plugin.Request { var r plugin.Request r.Content = &plugin.NewUserConnContent{} return &r } ginkgo.It("Validate Login Info, disable tls verify", func() { localPort := f.AllocPort() var record string handler := func(req *plugin.Request) *plugin.Response { var ret plugin.Response content := req.Content.(*plugin.NewUserConnContent) record = content.RemoteAddr ret.Unchange = true return &ret } tlsConfig, err := transport.NewServerTLSConfig("", "", "") framework.ExpectNoError(err) pluginServer := pluginpkg.NewHTTPPluginServer(localPort, newFunc, handler, tlsConfig) f.RunServer("", pluginServer) serverConf := consts.DefaultServerConfig + fmt.Sprintf(` [[httpPlugins]] name = "test" addr = "https://127.0.0.1:%d" path = "/handler" ops = ["NewUserConn"] `, localPort) remotePort := f.AllocPort() clientConf := consts.DefaultClientConfig clientConf += fmt.Sprintf(` [[proxies]] name = "tcp" type = "tcp" localPort = {{ .%s }} remotePort = %d `, framework.TCPEchoServerPort, remotePort) f.RunProcesses([]string{serverConf}, []string{clientConf}) framework.NewRequestExpect(f).Port(remotePort).Ensure() framework.ExpectNotEqual("", record) }) }) })