token_source.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217
  1. // Copyright 2025 The frp Authors
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package basic
  15. import (
  16. "fmt"
  17. "os"
  18. "path/filepath"
  19. "github.com/onsi/ginkgo/v2"
  20. "github.com/fatedier/frp/test/e2e/framework"
  21. "github.com/fatedier/frp/test/e2e/framework/consts"
  22. "github.com/fatedier/frp/test/e2e/pkg/port"
  23. )
  24. var _ = ginkgo.Describe("[Feature: TokenSource]", func() {
  25. f := framework.NewDefaultFramework()
  26. ginkgo.Describe("File-based token loading", func() {
  27. ginkgo.It("should work with file tokenSource", func() {
  28. // Create a temporary token file
  29. tmpDir := f.TempDirectory
  30. tokenFile := filepath.Join(tmpDir, "test_token")
  31. tokenContent := "test-token-123"
  32. err := os.WriteFile(tokenFile, []byte(tokenContent), 0o600)
  33. framework.ExpectNoError(err)
  34. serverConf := consts.DefaultServerConfig
  35. clientConf := consts.DefaultClientConfig
  36. portName := port.GenName("TCP")
  37. // Server config with tokenSource
  38. serverConf += fmt.Sprintf(`
  39. auth.tokenSource.type = "file"
  40. auth.tokenSource.file.path = "%s"
  41. `, tokenFile)
  42. // Client config with matching token
  43. clientConf += fmt.Sprintf(`
  44. auth.token = "%s"
  45. [[proxies]]
  46. name = "tcp"
  47. type = "tcp"
  48. localPort = {{ .%s }}
  49. remotePort = {{ .%s }}
  50. `, tokenContent, framework.TCPEchoServerPort, portName)
  51. f.RunProcesses([]string{serverConf}, []string{clientConf})
  52. framework.NewRequestExpect(f).PortName(portName).Ensure()
  53. })
  54. ginkgo.It("should work with client tokenSource", func() {
  55. // Create a temporary token file
  56. tmpDir := f.TempDirectory
  57. tokenFile := filepath.Join(tmpDir, "client_token")
  58. tokenContent := "client-token-456"
  59. err := os.WriteFile(tokenFile, []byte(tokenContent), 0o600)
  60. framework.ExpectNoError(err)
  61. serverConf := consts.DefaultServerConfig
  62. clientConf := consts.DefaultClientConfig
  63. portName := port.GenName("TCP")
  64. // Server config with matching token
  65. serverConf += fmt.Sprintf(`
  66. auth.token = "%s"
  67. `, tokenContent)
  68. // Client config with tokenSource
  69. clientConf += fmt.Sprintf(`
  70. auth.tokenSource.type = "file"
  71. auth.tokenSource.file.path = "%s"
  72. [[proxies]]
  73. name = "tcp"
  74. type = "tcp"
  75. localPort = {{ .%s }}
  76. remotePort = {{ .%s }}
  77. `, tokenFile, framework.TCPEchoServerPort, portName)
  78. f.RunProcesses([]string{serverConf}, []string{clientConf})
  79. framework.NewRequestExpect(f).PortName(portName).Ensure()
  80. })
  81. ginkgo.It("should work with both server and client tokenSource", func() {
  82. // Create temporary token files
  83. tmpDir := f.TempDirectory
  84. serverTokenFile := filepath.Join(tmpDir, "server_token")
  85. clientTokenFile := filepath.Join(tmpDir, "client_token")
  86. tokenContent := "shared-token-789"
  87. err := os.WriteFile(serverTokenFile, []byte(tokenContent), 0o600)
  88. framework.ExpectNoError(err)
  89. err = os.WriteFile(clientTokenFile, []byte(tokenContent), 0o600)
  90. framework.ExpectNoError(err)
  91. serverConf := consts.DefaultServerConfig
  92. clientConf := consts.DefaultClientConfig
  93. portName := port.GenName("TCP")
  94. // Server config with tokenSource
  95. serverConf += fmt.Sprintf(`
  96. auth.tokenSource.type = "file"
  97. auth.tokenSource.file.path = "%s"
  98. `, serverTokenFile)
  99. // Client config with tokenSource
  100. clientConf += fmt.Sprintf(`
  101. auth.tokenSource.type = "file"
  102. auth.tokenSource.file.path = "%s"
  103. [[proxies]]
  104. name = "tcp"
  105. type = "tcp"
  106. localPort = {{ .%s }}
  107. remotePort = {{ .%s }}
  108. `, clientTokenFile, framework.TCPEchoServerPort, portName)
  109. f.RunProcesses([]string{serverConf}, []string{clientConf})
  110. framework.NewRequestExpect(f).PortName(portName).Ensure()
  111. })
  112. ginkgo.It("should fail with mismatched tokens", func() {
  113. // Create temporary token files with different content
  114. tmpDir := f.TempDirectory
  115. serverTokenFile := filepath.Join(tmpDir, "server_token")
  116. clientTokenFile := filepath.Join(tmpDir, "client_token")
  117. err := os.WriteFile(serverTokenFile, []byte("server-token"), 0o600)
  118. framework.ExpectNoError(err)
  119. err = os.WriteFile(clientTokenFile, []byte("client-token"), 0o600)
  120. framework.ExpectNoError(err)
  121. serverConf := consts.DefaultServerConfig
  122. clientConf := consts.DefaultClientConfig
  123. portName := port.GenName("TCP")
  124. // Server config with tokenSource
  125. serverConf += fmt.Sprintf(`
  126. auth.tokenSource.type = "file"
  127. auth.tokenSource.file.path = "%s"
  128. `, serverTokenFile)
  129. // Client config with different tokenSource
  130. clientConf += fmt.Sprintf(`
  131. auth.tokenSource.type = "file"
  132. auth.tokenSource.file.path = "%s"
  133. [[proxies]]
  134. name = "tcp"
  135. type = "tcp"
  136. localPort = {{ .%s }}
  137. remotePort = {{ .%s }}
  138. `, clientTokenFile, framework.TCPEchoServerPort, portName)
  139. f.RunProcesses([]string{serverConf}, []string{clientConf})
  140. // This should fail due to token mismatch - the client should not be able to connect
  141. // We expect the request to fail because the proxy tunnel is not established
  142. framework.NewRequestExpect(f).PortName(portName).ExpectError(true).Ensure()
  143. })
  144. ginkgo.It("should fail with non-existent token file", func() {
  145. // This test verifies that server fails to start when tokenSource points to non-existent file
  146. // We'll verify this by checking that the configuration loading itself fails
  147. // Create a config that references a non-existent file
  148. tmpDir := f.TempDirectory
  149. nonExistentFile := filepath.Join(tmpDir, "non_existent_token")
  150. serverConf := consts.DefaultServerConfig
  151. // Server config with non-existent tokenSource file
  152. serverConf += fmt.Sprintf(`
  153. auth.tokenSource.type = "file"
  154. auth.tokenSource.file.path = "%s"
  155. `, nonExistentFile)
  156. // The test expectation is that this will fail during the RunProcesses call
  157. // because the server cannot load the configuration due to missing token file
  158. defer func() {
  159. if r := recover(); r != nil {
  160. // Expected: server should fail to start due to missing file
  161. ginkgo.By(fmt.Sprintf("Server correctly failed to start: %v", r))
  162. }
  163. }()
  164. // This should cause a panic or error during server startup
  165. f.RunProcesses([]string{serverConf}, []string{})
  166. })
  167. })
  168. })