1
0

client_server.go 10 KB

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