client_server.go 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343
  1. package basic
  2. import (
  3. "fmt"
  4. "strings"
  5. "time"
  6. "github.com/onsi/ginkgo/v2"
  7. "github.com/fatedier/frp/test/e2e/framework"
  8. "github.com/fatedier/frp/test/e2e/framework/consts"
  9. "github.com/fatedier/frp/test/e2e/pkg/cert"
  10. "github.com/fatedier/frp/test/e2e/pkg/port"
  11. )
  12. type generalTestConfigures struct {
  13. server string
  14. client string
  15. clientPrefix string
  16. client2 string
  17. client2Prefix string
  18. testDelay time.Duration
  19. expectError bool
  20. }
  21. func renderBindPortConfig(protocol string) string {
  22. if protocol == "kcp" {
  23. return fmt.Sprintf(`kcpBindPort = {{ .%s }}`, consts.PortServerName)
  24. } else if protocol == "quic" {
  25. return fmt.Sprintf(`quicBindPort = {{ .%s }}`, consts.PortServerName)
  26. }
  27. return ""
  28. }
  29. func runClientServerTest(f *framework.Framework, configures *generalTestConfigures) {
  30. serverConf := consts.DefaultServerConfig
  31. clientConf := consts.DefaultClientConfig
  32. if configures.clientPrefix != "" {
  33. clientConf = configures.clientPrefix
  34. }
  35. serverConf += fmt.Sprintf(`
  36. %s
  37. `, configures.server)
  38. tcpPortName := port.GenName("TCP")
  39. udpPortName := port.GenName("UDP")
  40. clientConf += fmt.Sprintf(`
  41. %s
  42. [[proxies]]
  43. name = "tcp"
  44. type = "tcp"
  45. localPort = {{ .%s }}
  46. remotePort = {{ .%s }}
  47. [[proxies]]
  48. name = "udp"
  49. type = "udp"
  50. localPort = {{ .%s }}
  51. remotePort = {{ .%s }}
  52. `, configures.client,
  53. framework.TCPEchoServerPort, tcpPortName,
  54. framework.UDPEchoServerPort, udpPortName,
  55. )
  56. clientConfs := []string{clientConf}
  57. if configures.client2 != "" {
  58. client2Conf := consts.DefaultClientConfig
  59. if configures.client2Prefix != "" {
  60. client2Conf = configures.client2Prefix
  61. }
  62. client2Conf += fmt.Sprintf(`
  63. %s
  64. `, configures.client2)
  65. clientConfs = append(clientConfs, client2Conf)
  66. }
  67. f.RunProcesses([]string{serverConf}, clientConfs)
  68. if configures.testDelay > 0 {
  69. time.Sleep(configures.testDelay)
  70. }
  71. framework.NewRequestExpect(f).PortName(tcpPortName).ExpectError(configures.expectError).Explain("tcp proxy").Ensure()
  72. framework.NewRequestExpect(f).Protocol("udp").
  73. PortName(udpPortName).ExpectError(configures.expectError).Explain("udp proxy").Ensure()
  74. }
  75. // defineClientServerTest test a normal tcp and udp proxy with specified TestConfigures.
  76. func defineClientServerTest(desc string, f *framework.Framework, configures *generalTestConfigures) {
  77. ginkgo.It(desc, func() {
  78. runClientServerTest(f, configures)
  79. })
  80. }
  81. var _ = ginkgo.Describe("[Feature: Client-Server]", func() {
  82. f := framework.NewDefaultFramework()
  83. ginkgo.Describe("Protocol", func() {
  84. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  85. for _, protocol := range supportProtocols {
  86. configures := &generalTestConfigures{
  87. server: fmt.Sprintf(`
  88. %s
  89. `, renderBindPortConfig(protocol)),
  90. client: fmt.Sprintf(`transport.protocol = "%s"`, protocol),
  91. }
  92. defineClientServerTest(protocol, f, configures)
  93. }
  94. })
  95. // wss is special, it needs to be tested separately.
  96. // frps only supports ws, so there should be a proxy to terminate TLS before frps.
  97. ginkgo.Describe("Protocol wss", func() {
  98. wssPort := f.AllocPort()
  99. configures := &generalTestConfigures{
  100. clientPrefix: fmt.Sprintf(`
  101. serverAddr = "127.0.0.1"
  102. serverPort = %d
  103. loginFailExit = false
  104. transport.protocol = "wss"
  105. log.level = "trace"
  106. `, wssPort),
  107. // Due to the fact that frps cannot directly accept wss connections, we use the https2http plugin of another frpc to terminate TLS.
  108. client2: fmt.Sprintf(`
  109. [[proxies]]
  110. name = "wss2ws"
  111. type = "tcp"
  112. remotePort = %d
  113. [proxies.plugin]
  114. type = "https2http"
  115. localAddr = "127.0.0.1:{{ .%s }}"
  116. `, wssPort, consts.PortServerName),
  117. testDelay: 10 * time.Second,
  118. }
  119. defineClientServerTest("wss", f, configures)
  120. })
  121. ginkgo.Describe("Authentication", func() {
  122. defineClientServerTest("Token Correct", f, &generalTestConfigures{
  123. server: `auth.token = "123456"`,
  124. client: `auth.token = "123456"`,
  125. })
  126. defineClientServerTest("Token Incorrect", f, &generalTestConfigures{
  127. server: `auth.token = "123456"`,
  128. client: `auth.token = "invalid"`,
  129. expectError: true,
  130. })
  131. })
  132. ginkgo.Describe("TLS", func() {
  133. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  134. for _, protocol := range supportProtocols {
  135. tmp := protocol
  136. // Since v0.50.0, the default value of tls_enable has been changed to true.
  137. // Therefore, here it needs to be set as false to test the scenario of turning it off.
  138. defineClientServerTest("Disable TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{
  139. server: fmt.Sprintf(`
  140. %s
  141. `, renderBindPortConfig(protocol)),
  142. client: fmt.Sprintf(`transport.tls.enable = false
  143. transport.protocol = "%s"
  144. `, protocol),
  145. })
  146. }
  147. defineClientServerTest("enable tls force, client with TLS", f, &generalTestConfigures{
  148. server: "transport.tls.force = true",
  149. })
  150. defineClientServerTest("enable tls force, client without TLS", f, &generalTestConfigures{
  151. server: "transport.tls.force = true",
  152. client: "transport.tls.enable = false",
  153. expectError: true,
  154. })
  155. })
  156. ginkgo.Describe("TLS with custom certificate", func() {
  157. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  158. var (
  159. caCrtPath string
  160. serverCrtPath, serverKeyPath string
  161. clientCrtPath, clientKeyPath string
  162. )
  163. ginkgo.JustBeforeEach(func() {
  164. generator := &cert.SelfSignedCertGenerator{}
  165. artifacts, err := generator.Generate("127.0.0.1")
  166. framework.ExpectNoError(err)
  167. caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
  168. serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
  169. serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
  170. generator.SetCA(artifacts.CACert, artifacts.CAKey)
  171. _, err = generator.Generate("127.0.0.1")
  172. framework.ExpectNoError(err)
  173. clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
  174. clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
  175. })
  176. for _, protocol := range supportProtocols {
  177. tmp := protocol
  178. ginkgo.It("one-way authentication: "+tmp, func() {
  179. runClientServerTest(f, &generalTestConfigures{
  180. server: fmt.Sprintf(`
  181. %s
  182. transport.tls.trustedCaFile = "%s"
  183. `, renderBindPortConfig(tmp), caCrtPath),
  184. client: fmt.Sprintf(`
  185. transport.protocol = "%s"
  186. transport.tls.certFile = "%s"
  187. transport.tls.keyFile = "%s"
  188. `, tmp, clientCrtPath, clientKeyPath),
  189. })
  190. })
  191. ginkgo.It("mutual authentication: "+tmp, func() {
  192. runClientServerTest(f, &generalTestConfigures{
  193. server: fmt.Sprintf(`
  194. %s
  195. transport.tls.certFile = "%s"
  196. transport.tls.keyFile = "%s"
  197. transport.tls.trustedCaFile = "%s"
  198. `, renderBindPortConfig(tmp), serverCrtPath, serverKeyPath, caCrtPath),
  199. client: fmt.Sprintf(`
  200. transport.protocol = "%s"
  201. transport.tls.certFile = "%s"
  202. transport.tls.keyFile = "%s"
  203. transport.tls.trustedCaFile = "%s"
  204. `, tmp, clientCrtPath, clientKeyPath, caCrtPath),
  205. })
  206. })
  207. }
  208. })
  209. ginkgo.Describe("TLS with custom certificate and specified server name", func() {
  210. var (
  211. caCrtPath string
  212. serverCrtPath, serverKeyPath string
  213. clientCrtPath, clientKeyPath string
  214. )
  215. ginkgo.JustBeforeEach(func() {
  216. generator := &cert.SelfSignedCertGenerator{}
  217. artifacts, err := generator.Generate("example.com")
  218. framework.ExpectNoError(err)
  219. caCrtPath = f.WriteTempFile("ca.crt", string(artifacts.CACert))
  220. serverCrtPath = f.WriteTempFile("server.crt", string(artifacts.Cert))
  221. serverKeyPath = f.WriteTempFile("server.key", string(artifacts.Key))
  222. generator.SetCA(artifacts.CACert, artifacts.CAKey)
  223. _, err = generator.Generate("example.com")
  224. framework.ExpectNoError(err)
  225. clientCrtPath = f.WriteTempFile("client.crt", string(artifacts.Cert))
  226. clientKeyPath = f.WriteTempFile("client.key", string(artifacts.Key))
  227. })
  228. ginkgo.It("mutual authentication", func() {
  229. runClientServerTest(f, &generalTestConfigures{
  230. server: fmt.Sprintf(`
  231. transport.tls.certFile = "%s"
  232. transport.tls.keyFile = "%s"
  233. transport.tls.trustedCaFile = "%s"
  234. `, serverCrtPath, serverKeyPath, caCrtPath),
  235. client: fmt.Sprintf(`
  236. transport.tls.serverName = "example.com"
  237. transport.tls.certFile = "%s"
  238. transport.tls.keyFile = "%s"
  239. transport.tls.trustedCaFile = "%s"
  240. `, clientCrtPath, clientKeyPath, caCrtPath),
  241. })
  242. })
  243. ginkgo.It("mutual authentication with incorrect server name", func() {
  244. runClientServerTest(f, &generalTestConfigures{
  245. server: fmt.Sprintf(`
  246. transport.tls.certFile = "%s"
  247. transport.tls.keyFile = "%s"
  248. transport.tls.trustedCaFile = "%s"
  249. `, serverCrtPath, serverKeyPath, caCrtPath),
  250. client: fmt.Sprintf(`
  251. transport.tls.serverName = "invalid.com"
  252. transport.tls.certFile = "%s"
  253. transport.tls.keyFile = "%s"
  254. transport.tls.trustedCaFile = "%s"
  255. `, clientCrtPath, clientKeyPath, caCrtPath),
  256. expectError: true,
  257. })
  258. })
  259. })
  260. ginkgo.Describe("TLS with disableCustomTLSFirstByte set to false", func() {
  261. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  262. for _, protocol := range supportProtocols {
  263. tmp := protocol
  264. defineClientServerTest("TLS over "+strings.ToUpper(tmp), f, &generalTestConfigures{
  265. server: fmt.Sprintf(`
  266. %s
  267. `, renderBindPortConfig(protocol)),
  268. client: fmt.Sprintf(`
  269. transport.protocol = "%s"
  270. transport.tls.disableCustomTLSFirstByte = false
  271. `, protocol),
  272. })
  273. }
  274. })
  275. ginkgo.Describe("IPv6 bind address", func() {
  276. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  277. for _, protocol := range supportProtocols {
  278. tmp := protocol
  279. defineClientServerTest("IPv6 bind address: "+strings.ToUpper(tmp), f, &generalTestConfigures{
  280. server: fmt.Sprintf(`
  281. bindAddr = "::"
  282. %s
  283. `, renderBindPortConfig(protocol)),
  284. client: fmt.Sprintf(`
  285. transport.protocol = "%s"
  286. `, protocol),
  287. })
  288. }
  289. })
  290. ginkgo.Describe("Use same port for bindPort and vhostHTTPSPort", func() {
  291. supportProtocols := []string{"tcp", "kcp", "quic", "websocket"}
  292. for _, protocol := range supportProtocols {
  293. tmp := protocol
  294. defineClientServerTest("Use same port for bindPort and vhostHTTPSPort: "+strings.ToUpper(tmp), f, &generalTestConfigures{
  295. server: fmt.Sprintf(`
  296. vhostHTTPSPort = {{ .%s }}
  297. %s
  298. `, consts.PortServerName, renderBindPortConfig(protocol)),
  299. // transport.tls.disableCustomTLSFirstByte should set to false when vhostHTTPSPort is same as bindPort
  300. client: fmt.Sprintf(`
  301. transport.protocol = "%s"
  302. transport.tls.disableCustomTLSFirstByte = false
  303. `, protocol),
  304. })
  305. }
  306. })
  307. })