server_test.go 34 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282
  1. /*
  2. * Copyright (c) 2016, Psiphon Inc.
  3. * All rights reserved.
  4. *
  5. * This program is free software: you can redistribute it and/or modify
  6. * it under the terms of the GNU General Public License as published by
  7. * the Free Software Foundation, either version 3 of the License, or
  8. * (at your option) any later version.
  9. *
  10. * This program is distributed in the hope that it will be useful,
  11. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. * GNU General Public License for more details.
  14. *
  15. * You should have received a copy of the GNU General Public License
  16. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. *
  18. */
  19. package server
  20. import (
  21. "context"
  22. "encoding/json"
  23. "errors"
  24. "flag"
  25. "fmt"
  26. "io/ioutil"
  27. "net"
  28. "net/http"
  29. "net/url"
  30. "os"
  31. "path/filepath"
  32. "strconv"
  33. "sync"
  34. "syscall"
  35. "testing"
  36. "time"
  37. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
  38. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common"
  39. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/accesscontrol"
  40. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/marionette"
  41. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/parameters"
  42. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
  43. "github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics"
  44. "golang.org/x/net/proxy"
  45. )
  46. var serverIPAddress, testDataDirName string
  47. var mockWebServerURL, mockWebServerExpectedResponse string
  48. var mockWebServerPort = 8080
  49. func TestMain(m *testing.M) {
  50. flag.Parse()
  51. var err error
  52. for _, interfaceName := range []string{"eth0", "en0"} {
  53. var serverIPv4Address, serverIPv6Address net.IP
  54. serverIPv4Address, serverIPv6Address, err = common.GetInterfaceIPAddresses(interfaceName)
  55. if err == nil {
  56. if serverIPv4Address != nil {
  57. serverIPAddress = serverIPv4Address.String()
  58. } else {
  59. serverIPAddress = serverIPv6Address.String()
  60. }
  61. break
  62. }
  63. }
  64. if err != nil {
  65. fmt.Printf("error getting server IP address: %s\n", err)
  66. os.Exit(1)
  67. }
  68. testDataDirName, err = ioutil.TempDir("", "psiphon-server-test")
  69. if err != nil {
  70. fmt.Printf("TempDir failed: %s\n", err)
  71. os.Exit(1)
  72. }
  73. defer os.RemoveAll(testDataDirName)
  74. os.Remove(filepath.Join(testDataDirName, psiphon.DATA_STORE_FILENAME))
  75. psiphon.SetEmitDiagnosticNotices(true)
  76. mockWebServerURL, mockWebServerExpectedResponse = runMockWebServer()
  77. os.Exit(m.Run())
  78. }
  79. func runMockWebServer() (string, string) {
  80. responseBody, _ := common.MakeSecureRandomStringHex(100000)
  81. serveMux := http.NewServeMux()
  82. serveMux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  83. w.Write([]byte(responseBody))
  84. })
  85. webServerAddress := fmt.Sprintf("%s:%d", serverIPAddress, mockWebServerPort)
  86. server := &http.Server{
  87. Addr: webServerAddress,
  88. Handler: serveMux,
  89. }
  90. go func() {
  91. err := server.ListenAndServe()
  92. if err != nil {
  93. fmt.Printf("error running mock web server: %s\n", err)
  94. os.Exit(1)
  95. }
  96. }()
  97. // TODO: properly synchronize with web server readiness
  98. time.Sleep(1 * time.Second)
  99. return fmt.Sprintf("http://%s/", webServerAddress), responseBody
  100. }
  101. // Note: not testing fronting meek protocols, which client is
  102. // hard-wired to except running on privileged ports 80 and 443.
  103. func TestSSH(t *testing.T) {
  104. runServer(t,
  105. &runServerConfig{
  106. tunnelProtocol: "SSH",
  107. enableSSHAPIRequests: true,
  108. doHotReload: false,
  109. doDefaultSponsorID: false,
  110. denyTrafficRules: false,
  111. requireAuthorization: true,
  112. omitAuthorization: false,
  113. doTunneledWebRequest: true,
  114. doTunneledNTPRequest: true,
  115. })
  116. }
  117. func TestOSSH(t *testing.T) {
  118. runServer(t,
  119. &runServerConfig{
  120. tunnelProtocol: "OSSH",
  121. enableSSHAPIRequests: true,
  122. doHotReload: false,
  123. doDefaultSponsorID: false,
  124. denyTrafficRules: false,
  125. requireAuthorization: true,
  126. omitAuthorization: false,
  127. doTunneledWebRequest: true,
  128. doTunneledNTPRequest: true,
  129. })
  130. }
  131. func TestUnfrontedMeek(t *testing.T) {
  132. runServer(t,
  133. &runServerConfig{
  134. tunnelProtocol: "UNFRONTED-MEEK-OSSH",
  135. enableSSHAPIRequests: true,
  136. doHotReload: false,
  137. doDefaultSponsorID: false,
  138. denyTrafficRules: false,
  139. requireAuthorization: true,
  140. omitAuthorization: false,
  141. doTunneledWebRequest: true,
  142. doTunneledNTPRequest: true,
  143. })
  144. }
  145. func TestUnfrontedMeekHTTPS(t *testing.T) {
  146. runServer(t,
  147. &runServerConfig{
  148. tunnelProtocol: "UNFRONTED-MEEK-HTTPS-OSSH",
  149. enableSSHAPIRequests: true,
  150. doHotReload: false,
  151. doDefaultSponsorID: false,
  152. denyTrafficRules: false,
  153. requireAuthorization: true,
  154. omitAuthorization: false,
  155. doTunneledWebRequest: true,
  156. doTunneledNTPRequest: true,
  157. })
  158. }
  159. func TestUnfrontedMeekSessionTicket(t *testing.T) {
  160. runServer(t,
  161. &runServerConfig{
  162. tunnelProtocol: "UNFRONTED-MEEK-SESSION-TICKET-OSSH",
  163. enableSSHAPIRequests: true,
  164. doHotReload: false,
  165. doDefaultSponsorID: false,
  166. denyTrafficRules: false,
  167. requireAuthorization: true,
  168. omitAuthorization: false,
  169. doTunneledWebRequest: true,
  170. doTunneledNTPRequest: true,
  171. })
  172. }
  173. func TestQUICOSSH(t *testing.T) {
  174. runServer(t,
  175. &runServerConfig{
  176. tunnelProtocol: "QUIC-OSSH",
  177. enableSSHAPIRequests: true,
  178. doHotReload: false,
  179. doDefaultSponsorID: false,
  180. denyTrafficRules: false,
  181. requireAuthorization: true,
  182. omitAuthorization: false,
  183. doTunneledWebRequest: true,
  184. doTunneledNTPRequest: true,
  185. })
  186. }
  187. func TestMarionetteOSSH(t *testing.T) {
  188. if !marionette.Enabled() {
  189. t.Skip("Marionette is not enabled")
  190. }
  191. runServer(t,
  192. &runServerConfig{
  193. tunnelProtocol: "MARIONETTE-OSSH",
  194. enableSSHAPIRequests: true,
  195. doHotReload: false,
  196. doDefaultSponsorID: false,
  197. denyTrafficRules: false,
  198. requireAuthorization: true,
  199. omitAuthorization: false,
  200. doTunneledWebRequest: true,
  201. doTunneledNTPRequest: true,
  202. })
  203. }
  204. func TestWebTransportAPIRequests(t *testing.T) {
  205. runServer(t,
  206. &runServerConfig{
  207. tunnelProtocol: "OSSH",
  208. enableSSHAPIRequests: false,
  209. doHotReload: false,
  210. doDefaultSponsorID: false,
  211. denyTrafficRules: false,
  212. requireAuthorization: false,
  213. omitAuthorization: true,
  214. doTunneledWebRequest: true,
  215. doTunneledNTPRequest: true,
  216. })
  217. }
  218. func TestHotReload(t *testing.T) {
  219. runServer(t,
  220. &runServerConfig{
  221. tunnelProtocol: "OSSH",
  222. enableSSHAPIRequests: true,
  223. doHotReload: true,
  224. doDefaultSponsorID: false,
  225. denyTrafficRules: false,
  226. requireAuthorization: true,
  227. omitAuthorization: false,
  228. doTunneledWebRequest: true,
  229. doTunneledNTPRequest: true,
  230. })
  231. }
  232. func TestDefaultSessionID(t *testing.T) {
  233. runServer(t,
  234. &runServerConfig{
  235. tunnelProtocol: "OSSH",
  236. enableSSHAPIRequests: true,
  237. doHotReload: true,
  238. doDefaultSponsorID: true,
  239. denyTrafficRules: false,
  240. requireAuthorization: true,
  241. omitAuthorization: false,
  242. doTunneledWebRequest: true,
  243. doTunneledNTPRequest: true,
  244. })
  245. }
  246. func TestDenyTrafficRules(t *testing.T) {
  247. runServer(t,
  248. &runServerConfig{
  249. tunnelProtocol: "OSSH",
  250. enableSSHAPIRequests: true,
  251. doHotReload: true,
  252. doDefaultSponsorID: false,
  253. denyTrafficRules: true,
  254. requireAuthorization: true,
  255. omitAuthorization: false,
  256. doTunneledWebRequest: true,
  257. doTunneledNTPRequest: true,
  258. })
  259. }
  260. func TestOmitAuthorization(t *testing.T) {
  261. runServer(t,
  262. &runServerConfig{
  263. tunnelProtocol: "OSSH",
  264. enableSSHAPIRequests: true,
  265. doHotReload: true,
  266. doDefaultSponsorID: false,
  267. denyTrafficRules: false,
  268. requireAuthorization: true,
  269. omitAuthorization: true,
  270. doTunneledWebRequest: true,
  271. doTunneledNTPRequest: true,
  272. })
  273. }
  274. func TestNoAuthorization(t *testing.T) {
  275. runServer(t,
  276. &runServerConfig{
  277. tunnelProtocol: "OSSH",
  278. enableSSHAPIRequests: true,
  279. doHotReload: true,
  280. doDefaultSponsorID: false,
  281. denyTrafficRules: false,
  282. requireAuthorization: false,
  283. omitAuthorization: true,
  284. doTunneledWebRequest: true,
  285. doTunneledNTPRequest: true,
  286. })
  287. }
  288. func TestUnusedAuthorization(t *testing.T) {
  289. runServer(t,
  290. &runServerConfig{
  291. tunnelProtocol: "OSSH",
  292. enableSSHAPIRequests: true,
  293. doHotReload: true,
  294. doDefaultSponsorID: false,
  295. denyTrafficRules: false,
  296. requireAuthorization: false,
  297. omitAuthorization: false,
  298. doTunneledWebRequest: true,
  299. doTunneledNTPRequest: true,
  300. })
  301. }
  302. func TestTCPOnlySLOK(t *testing.T) {
  303. runServer(t,
  304. &runServerConfig{
  305. tunnelProtocol: "OSSH",
  306. enableSSHAPIRequests: true,
  307. doHotReload: false,
  308. doDefaultSponsorID: false,
  309. denyTrafficRules: false,
  310. requireAuthorization: true,
  311. omitAuthorization: false,
  312. doTunneledWebRequest: true,
  313. doTunneledNTPRequest: false,
  314. })
  315. }
  316. func TestUDPOnlySLOK(t *testing.T) {
  317. runServer(t,
  318. &runServerConfig{
  319. tunnelProtocol: "OSSH",
  320. enableSSHAPIRequests: true,
  321. doHotReload: false,
  322. doDefaultSponsorID: false,
  323. denyTrafficRules: false,
  324. requireAuthorization: true,
  325. omitAuthorization: false,
  326. doTunneledWebRequest: false,
  327. doTunneledNTPRequest: true,
  328. })
  329. }
  330. type runServerConfig struct {
  331. tunnelProtocol string
  332. enableSSHAPIRequests bool
  333. doHotReload bool
  334. doDefaultSponsorID bool
  335. denyTrafficRules bool
  336. requireAuthorization bool
  337. omitAuthorization bool
  338. doTunneledWebRequest bool
  339. doTunneledNTPRequest bool
  340. }
  341. func runServer(t *testing.T, runConfig *runServerConfig) {
  342. // configure authorized access
  343. accessType := "test-access-type"
  344. accessControlSigningKey, accessControlVerificationKey, err := accesscontrol.NewKeyPair(accessType)
  345. if err != nil {
  346. t.Fatalf("error creating access control key pair: %s", err)
  347. }
  348. accessControlVerificationKeyRing := accesscontrol.VerificationKeyRing{
  349. Keys: []*accesscontrol.VerificationKey{accessControlVerificationKey},
  350. }
  351. var authorizationID [32]byte
  352. clientAuthorization, err := accesscontrol.IssueAuthorization(
  353. accessControlSigningKey,
  354. authorizationID[:],
  355. time.Now().Add(1*time.Hour))
  356. if err != nil {
  357. t.Fatalf("error issuing authorization: %s", err)
  358. }
  359. // Enable tactics when the test protocol is meek. Both the client and the
  360. // server will be configured to support tactics. The client config will be
  361. // set with a nonfunctional config so that the tactics request must
  362. // succeed, overriding the nonfunctional values, for the tunnel to
  363. // establish.
  364. doTactics := protocol.TunnelProtocolUsesMeek(runConfig.tunnelProtocol)
  365. // All servers require a tactics config with valid keys.
  366. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err :=
  367. tactics.GenerateKeys()
  368. if err != nil {
  369. t.Fatalf("error generating tactics keys: %s", err)
  370. }
  371. // create a server
  372. psiphonServerIPAddress := serverIPAddress
  373. if protocol.TunnelProtocolUsesQUIC(runConfig.tunnelProtocol) ||
  374. protocol.TunnelProtocolUsesMarionette(runConfig.tunnelProtocol) {
  375. // Workaround for macOS firewall.
  376. psiphonServerIPAddress = "127.0.0.1"
  377. }
  378. generateConfigParams := &GenerateConfigParams{
  379. ServerIPAddress: psiphonServerIPAddress,
  380. EnableSSHAPIRequests: runConfig.enableSSHAPIRequests,
  381. WebServerPort: 8000,
  382. TunnelProtocolPorts: map[string]int{runConfig.tunnelProtocol: 4000},
  383. }
  384. if protocol.TunnelProtocolUsesMarionette(runConfig.tunnelProtocol) {
  385. generateConfigParams.TunnelProtocolPorts[runConfig.tunnelProtocol] = 0
  386. generateConfigParams.MarionetteFormat = "http_simple_nonblocking"
  387. }
  388. if doTactics {
  389. generateConfigParams.TacticsRequestPublicKey = tacticsRequestPublicKey
  390. generateConfigParams.TacticsRequestObfuscatedKey = tacticsRequestObfuscatedKey
  391. }
  392. serverConfigJSON, _, _, _, encodedServerEntry, err := GenerateConfig(generateConfigParams)
  393. if err != nil {
  394. t.Fatalf("error generating server config: %s", err)
  395. }
  396. // customize server config
  397. // Pave psinet with random values to test handshake homepages.
  398. psinetFilename := filepath.Join(testDataDirName, "psinet.json")
  399. sponsorID, expectedHomepageURL := pavePsinetDatabaseFile(
  400. t, runConfig.doDefaultSponsorID, psinetFilename)
  401. // Pave OSL config for SLOK testing
  402. oslConfigFilename := filepath.Join(testDataDirName, "osl_config.json")
  403. propagationChannelID := paveOSLConfigFile(t, oslConfigFilename)
  404. // Pave traffic rules file which exercises handshake parameter filtering. Client
  405. // must handshake with specified sponsor ID in order to allow ports for tunneled
  406. // requests.
  407. trafficRulesFilename := filepath.Join(testDataDirName, "traffic_rules.json")
  408. paveTrafficRulesFile(
  409. t, trafficRulesFilename, propagationChannelID, accessType,
  410. runConfig.requireAuthorization, runConfig.denyTrafficRules)
  411. var tacticsConfigFilename string
  412. // Only pave the tactics config when tactics are required. This exercises the
  413. // case where the tactics config is omitted.
  414. if doTactics {
  415. tacticsConfigFilename = filepath.Join(testDataDirName, "tactics_config.json")
  416. paveTacticsConfigFile(
  417. t, tacticsConfigFilename,
  418. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
  419. runConfig.tunnelProtocol,
  420. propagationChannelID)
  421. }
  422. var serverConfig map[string]interface{}
  423. json.Unmarshal(serverConfigJSON, &serverConfig)
  424. serverConfig["GeoIPDatabaseFilename"] = ""
  425. serverConfig["PsinetDatabaseFilename"] = psinetFilename
  426. serverConfig["TrafficRulesFilename"] = trafficRulesFilename
  427. serverConfig["OSLConfigFilename"] = oslConfigFilename
  428. if doTactics {
  429. serverConfig["TacticsConfigFilename"] = tacticsConfigFilename
  430. }
  431. serverConfig["LogFilename"] = filepath.Join(testDataDirName, "psiphond.log")
  432. serverConfig["LogLevel"] = "debug"
  433. serverConfig["AccessControlVerificationKeyRing"] = accessControlVerificationKeyRing
  434. // Set this parameter so at least the semaphore functions are called.
  435. // TODO: test that the concurrency limit is correctly enforced.
  436. serverConfig["MaxConcurrentSSHHandshakes"] = 1
  437. // Exercise this option.
  438. serverConfig["PeriodicGarbageCollectionSeconds"] = 1
  439. serverConfigJSON, _ = json.Marshal(serverConfig)
  440. // run server
  441. serverWaitGroup := new(sync.WaitGroup)
  442. serverWaitGroup.Add(1)
  443. go func() {
  444. defer serverWaitGroup.Done()
  445. err := RunServices(serverConfigJSON)
  446. if err != nil {
  447. // TODO: wrong goroutine for t.FatalNow()
  448. t.Fatalf("error running server: %s", err)
  449. }
  450. }()
  451. defer func() {
  452. // Test: orderly server shutdown
  453. p, _ := os.FindProcess(os.Getpid())
  454. p.Signal(os.Interrupt)
  455. shutdownTimeout := time.NewTimer(5 * time.Second)
  456. shutdownOk := make(chan struct{}, 1)
  457. go func() {
  458. serverWaitGroup.Wait()
  459. shutdownOk <- *new(struct{})
  460. }()
  461. select {
  462. case <-shutdownOk:
  463. case <-shutdownTimeout.C:
  464. t.Fatalf("server shutdown timeout exceeded")
  465. }
  466. }()
  467. // TODO: monitor logs for more robust wait-until-loaded
  468. time.Sleep(1 * time.Second)
  469. // Test: hot reload (of psinet and traffic rules)
  470. if runConfig.doHotReload {
  471. // Pave new config files with different random values.
  472. sponsorID, expectedHomepageURL = pavePsinetDatabaseFile(
  473. t, runConfig.doDefaultSponsorID, psinetFilename)
  474. propagationChannelID = paveOSLConfigFile(t, oslConfigFilename)
  475. paveTrafficRulesFile(
  476. t, trafficRulesFilename, propagationChannelID, accessType,
  477. runConfig.requireAuthorization, runConfig.denyTrafficRules)
  478. p, _ := os.FindProcess(os.Getpid())
  479. p.Signal(syscall.SIGUSR1)
  480. // TODO: monitor logs for more robust wait-until-reloaded
  481. time.Sleep(1 * time.Second)
  482. // After reloading psinet, the new sponsorID/expectedHomepageURL
  483. // should be active, as tested in the client "Homepage" notice
  484. // handler below.
  485. }
  486. // Exercise server_load logging
  487. p, _ := os.FindProcess(os.Getpid())
  488. p.Signal(syscall.SIGUSR2)
  489. // connect to server with client
  490. // TODO: currently, TargetServerEntry only works with one tunnel
  491. numTunnels := 1
  492. localSOCKSProxyPort := 1081
  493. localHTTPProxyPort := 8081
  494. jsonNetworkID := ""
  495. if doTactics {
  496. // Use a distinct prefix for network ID for each test run to
  497. // ensure tactics from different runs don't apply; this is
  498. // a workaround for the singleton datastore.
  499. prefix := time.Now().String()
  500. jsonNetworkID = fmt.Sprintf(`,"NetworkID" : "%s-%s"`, prefix, "NETWORK1")
  501. }
  502. clientConfigJSON := fmt.Sprintf(`
  503. {
  504. "ClientPlatform" : "Windows",
  505. "ClientVersion" : "0",
  506. "SponsorId" : "0",
  507. "PropagationChannelId" : "0",
  508. "DisableRemoteServerListFetcher" : true,
  509. "EstablishTunnelPausePeriodSeconds" : 1,
  510. "ConnectionWorkerPoolSize" : %d,
  511. "LimitTunnelProtocols" : ["%s"]
  512. %s
  513. }`, numTunnels, runConfig.tunnelProtocol, jsonNetworkID)
  514. clientConfig, err := psiphon.LoadConfig([]byte(clientConfigJSON))
  515. if err != nil {
  516. t.Fatalf("error processing configuration file: %s", err)
  517. }
  518. clientConfig.DataStoreDirectory = testDataDirName
  519. if !runConfig.doDefaultSponsorID {
  520. clientConfig.SponsorId = sponsorID
  521. }
  522. clientConfig.PropagationChannelId = propagationChannelID
  523. clientConfig.TunnelPoolSize = numTunnels
  524. clientConfig.TargetServerEntry = string(encodedServerEntry)
  525. clientConfig.LocalSocksProxyPort = localSOCKSProxyPort
  526. clientConfig.LocalHttpProxyPort = localHTTPProxyPort
  527. clientConfig.EmitSLOKs = true
  528. if !runConfig.omitAuthorization {
  529. clientConfig.Authorizations = []string{clientAuthorization}
  530. }
  531. err = clientConfig.Commit()
  532. if err != nil {
  533. t.Fatalf("error committing configuration file: %s", err)
  534. }
  535. if doTactics {
  536. // Configure nonfunctional values that must be overridden by tactics.
  537. applyParameters := make(map[string]interface{})
  538. applyParameters[parameters.TunnelConnectTimeout] = "1s"
  539. applyParameters[parameters.TunnelRateLimits] = common.RateLimits{WriteBytesPerSecond: 1}
  540. err = clientConfig.SetClientParameters("", true, applyParameters)
  541. if err != nil {
  542. t.Fatalf("SetClientParameters failed: %s", err)
  543. }
  544. }
  545. err = psiphon.OpenDataStore(clientConfig)
  546. if err != nil {
  547. t.Fatalf("error initializing client datastore: %s", err)
  548. }
  549. defer psiphon.CloseDataStore()
  550. psiphon.DeleteSLOKs()
  551. controller, err := psiphon.NewController(clientConfig)
  552. if err != nil {
  553. t.Fatalf("error creating client controller: %s", err)
  554. }
  555. tunnelsEstablished := make(chan struct{}, 1)
  556. homepageReceived := make(chan struct{}, 1)
  557. slokSeeded := make(chan struct{}, 1)
  558. psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
  559. func(notice []byte) {
  560. //fmt.Printf("%s\n", string(notice))
  561. noticeType, payload, err := psiphon.GetNotice(notice)
  562. if err != nil {
  563. return
  564. }
  565. switch noticeType {
  566. case "Tunnels":
  567. count := int(payload["count"].(float64))
  568. if count >= numTunnels {
  569. sendNotificationReceived(tunnelsEstablished)
  570. }
  571. case "Homepage":
  572. homepageURL := payload["url"].(string)
  573. if homepageURL != expectedHomepageURL {
  574. // TODO: wrong goroutine for t.FatalNow()
  575. t.Fatalf("unexpected homepage: %s", homepageURL)
  576. }
  577. sendNotificationReceived(homepageReceived)
  578. case "SLOKSeeded":
  579. sendNotificationReceived(slokSeeded)
  580. }
  581. }))
  582. ctx, cancelFunc := context.WithCancel(context.Background())
  583. controllerWaitGroup := new(sync.WaitGroup)
  584. controllerWaitGroup.Add(1)
  585. go func() {
  586. defer controllerWaitGroup.Done()
  587. controller.Run(ctx)
  588. }()
  589. defer func() {
  590. cancelFunc()
  591. shutdownTimeout := time.NewTimer(20 * time.Second)
  592. shutdownOk := make(chan struct{}, 1)
  593. go func() {
  594. controllerWaitGroup.Wait()
  595. shutdownOk <- *new(struct{})
  596. }()
  597. select {
  598. case <-shutdownOk:
  599. case <-shutdownTimeout.C:
  600. t.Fatalf("controller shutdown timeout exceeded")
  601. }
  602. }()
  603. // Test: tunnels must be established, and correct homepage
  604. // must be received, within 30 seconds
  605. timeoutSignal := make(chan struct{})
  606. go func() {
  607. timer := time.NewTimer(30 * time.Second)
  608. <-timer.C
  609. close(timeoutSignal)
  610. }()
  611. waitOnNotification(t, tunnelsEstablished, timeoutSignal, "tunnel establish timeout exceeded")
  612. waitOnNotification(t, homepageReceived, timeoutSignal, "homepage received timeout exceeded")
  613. expectTrafficFailure := runConfig.denyTrafficRules || (runConfig.omitAuthorization && runConfig.requireAuthorization)
  614. if runConfig.doTunneledWebRequest {
  615. // Test: tunneled web site fetch
  616. err = makeTunneledWebRequest(
  617. t, localHTTPProxyPort, mockWebServerURL, mockWebServerExpectedResponse)
  618. if err == nil {
  619. if expectTrafficFailure {
  620. t.Fatalf("unexpected tunneled web request success")
  621. }
  622. } else {
  623. if !expectTrafficFailure {
  624. t.Fatalf("tunneled web request failed: %s", err)
  625. }
  626. }
  627. }
  628. if runConfig.doTunneledNTPRequest {
  629. // Test: tunneled UDP packets
  630. udpgwServerAddress := serverConfig["UDPInterceptUdpgwServerAddress"].(string)
  631. err = makeTunneledNTPRequest(t, localSOCKSProxyPort, udpgwServerAddress)
  632. if err == nil {
  633. if expectTrafficFailure {
  634. t.Fatalf("unexpected tunneled NTP request success")
  635. }
  636. } else {
  637. if !expectTrafficFailure {
  638. t.Fatalf("tunneled NTP request failed: %s", err)
  639. }
  640. }
  641. }
  642. // Test: await SLOK payload
  643. if !expectTrafficFailure {
  644. time.Sleep(1 * time.Second)
  645. waitOnNotification(t, slokSeeded, timeoutSignal, "SLOK seeded timeout exceeded")
  646. numSLOKs := psiphon.CountSLOKs()
  647. if numSLOKs != expectedNumSLOKs {
  648. t.Fatalf("unexpected number of SLOKs: %d", numSLOKs)
  649. }
  650. }
  651. }
  652. func makeTunneledWebRequest(
  653. t *testing.T,
  654. localHTTPProxyPort int,
  655. requestURL, expectedResponseBody string) error {
  656. roundTripTimeout := 30 * time.Second
  657. proxyUrl, err := url.Parse(fmt.Sprintf("http://127.0.0.1:%d", localHTTPProxyPort))
  658. if err != nil {
  659. return fmt.Errorf("error initializing proxied HTTP request: %s", err)
  660. }
  661. httpClient := &http.Client{
  662. Transport: &http.Transport{
  663. Proxy: http.ProxyURL(proxyUrl),
  664. },
  665. Timeout: roundTripTimeout,
  666. }
  667. response, err := httpClient.Get(requestURL)
  668. if err != nil {
  669. return fmt.Errorf("error sending proxied HTTP request: %s", err)
  670. }
  671. body, err := ioutil.ReadAll(response.Body)
  672. if err != nil {
  673. return fmt.Errorf("error reading proxied HTTP response: %s", err)
  674. }
  675. response.Body.Close()
  676. if string(body) != expectedResponseBody {
  677. return fmt.Errorf("unexpected proxied HTTP response")
  678. }
  679. return nil
  680. }
  681. func makeTunneledNTPRequest(t *testing.T, localSOCKSProxyPort int, udpgwServerAddress string) error {
  682. timeout := 20 * time.Second
  683. var err error
  684. for _, testHostname := range []string{"time.google.com", "time.nist.gov", "pool.ntp.org"} {
  685. err = makeTunneledNTPRequestAttempt(t, testHostname, timeout, localSOCKSProxyPort, udpgwServerAddress)
  686. if err == nil {
  687. break
  688. }
  689. t.Logf("makeTunneledNTPRequestAttempt failed: %s", err)
  690. }
  691. return err
  692. }
  693. var nextUDPProxyPort = 7300
  694. func makeTunneledNTPRequestAttempt(
  695. t *testing.T, testHostname string, timeout time.Duration, localSOCKSProxyPort int, udpgwServerAddress string) error {
  696. nextUDPProxyPort++
  697. localUDPProxyAddress, err := net.ResolveUDPAddr("udp", fmt.Sprintf("127.0.0.1:%d", nextUDPProxyPort))
  698. if err != nil {
  699. return fmt.Errorf("ResolveUDPAddr failed: %s", err)
  700. }
  701. // Note: this proxy is intended for this test only -- it only accepts a single connection,
  702. // handles it, and then terminates.
  703. localUDPProxy := func(destinationIP net.IP, destinationPort uint16, waitGroup *sync.WaitGroup) {
  704. if waitGroup != nil {
  705. defer waitGroup.Done()
  706. }
  707. destination := net.JoinHostPort(destinationIP.String(), strconv.Itoa(int(destinationPort)))
  708. serverUDPConn, err := net.ListenUDP("udp", localUDPProxyAddress)
  709. if err != nil {
  710. t.Logf("ListenUDP for %s failed: %s", destination, err)
  711. return
  712. }
  713. defer serverUDPConn.Close()
  714. udpgwPreambleSize := 11 // see writeUdpgwPreamble
  715. buffer := make([]byte, udpgwProtocolMaxMessageSize)
  716. packetSize, clientAddr, err := serverUDPConn.ReadFromUDP(
  717. buffer[udpgwPreambleSize:])
  718. if err != nil {
  719. t.Logf("serverUDPConn.Read for %s failed: %s", destination, err)
  720. return
  721. }
  722. socksProxyAddress := fmt.Sprintf("127.0.0.1:%d", localSOCKSProxyPort)
  723. dialer, err := proxy.SOCKS5("tcp", socksProxyAddress, nil, proxy.Direct)
  724. if err != nil {
  725. t.Logf("proxy.SOCKS5 for %s failed: %s", destination, err)
  726. return
  727. }
  728. socksTCPConn, err := dialer.Dial("tcp", udpgwServerAddress)
  729. if err != nil {
  730. t.Logf("dialer.Dial for %s failed: %s", destination, err)
  731. return
  732. }
  733. defer socksTCPConn.Close()
  734. flags := uint8(0)
  735. if destinationPort == 53 {
  736. flags = udpgwProtocolFlagDNS
  737. }
  738. err = writeUdpgwPreamble(
  739. udpgwPreambleSize,
  740. flags,
  741. 0,
  742. destinationIP,
  743. destinationPort,
  744. uint16(packetSize),
  745. buffer)
  746. if err != nil {
  747. t.Logf("writeUdpgwPreamble for %s failed: %s", destination, err)
  748. return
  749. }
  750. _, err = socksTCPConn.Write(buffer[0 : udpgwPreambleSize+packetSize])
  751. if err != nil {
  752. t.Logf("socksTCPConn.Write for %s failed: %s", destination, err)
  753. return
  754. }
  755. udpgwProtocolMessage, err := readUdpgwMessage(socksTCPConn, buffer)
  756. if err != nil {
  757. t.Logf("readUdpgwMessage for %s failed: %s", destination, err)
  758. return
  759. }
  760. _, err = serverUDPConn.WriteToUDP(udpgwProtocolMessage.packet, clientAddr)
  761. if err != nil {
  762. t.Logf("serverUDPConn.Write for %s failed: %s", destination, err)
  763. return
  764. }
  765. }
  766. // Tunneled DNS request
  767. waitGroup := new(sync.WaitGroup)
  768. waitGroup.Add(1)
  769. go localUDPProxy(
  770. net.IP(make([]byte, 4)), // ignored due to transparent DNS forwarding
  771. 53,
  772. waitGroup)
  773. // TODO: properly synchronize with local UDP proxy startup
  774. time.Sleep(1 * time.Second)
  775. clientUDPConn, err := net.DialUDP("udp", nil, localUDPProxyAddress)
  776. if err != nil {
  777. return fmt.Errorf("DialUDP failed: %s", err)
  778. }
  779. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  780. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  781. addrs, _, err := psiphon.ResolveIP(testHostname, clientUDPConn)
  782. clientUDPConn.Close()
  783. if err == nil && (len(addrs) == 0 || len(addrs[0]) < 4) {
  784. err = errors.New("no address")
  785. }
  786. if err != nil {
  787. return fmt.Errorf("ResolveIP failed: %s", err)
  788. }
  789. waitGroup.Wait()
  790. // Tunneled NTP request
  791. waitGroup = new(sync.WaitGroup)
  792. waitGroup.Add(1)
  793. go localUDPProxy(
  794. addrs[0][len(addrs[0])-4:],
  795. 123,
  796. waitGroup)
  797. // TODO: properly synchronize with local UDP proxy startup
  798. time.Sleep(1 * time.Second)
  799. clientUDPConn, err = net.DialUDP("udp", nil, localUDPProxyAddress)
  800. if err != nil {
  801. return fmt.Errorf("DialUDP failed: %s", err)
  802. }
  803. clientUDPConn.SetReadDeadline(time.Now().Add(timeout))
  804. clientUDPConn.SetWriteDeadline(time.Now().Add(timeout))
  805. // NTP protocol code from: https://groups.google.com/d/msg/golang-nuts/FlcdMU5fkLQ/CAeoD9eqm-IJ
  806. ntpData := make([]byte, 48)
  807. ntpData[0] = 3<<3 | 3
  808. _, err = clientUDPConn.Write(ntpData)
  809. if err != nil {
  810. clientUDPConn.Close()
  811. return fmt.Errorf("NTP Write failed: %s", err)
  812. }
  813. _, err = clientUDPConn.Read(ntpData)
  814. if err != nil {
  815. clientUDPConn.Close()
  816. return fmt.Errorf("NTP Read failed: %s", err)
  817. }
  818. clientUDPConn.Close()
  819. var sec, frac uint64
  820. sec = uint64(ntpData[43]) | uint64(ntpData[42])<<8 | uint64(ntpData[41])<<16 | uint64(ntpData[40])<<24
  821. frac = uint64(ntpData[47]) | uint64(ntpData[46])<<8 | uint64(ntpData[45])<<16 | uint64(ntpData[44])<<24
  822. nsec := sec * 1e9
  823. nsec += (frac * 1e9) >> 32
  824. ntpNow := time.Date(1900, 1, 1, 0, 0, 0, 0, time.UTC).Add(time.Duration(nsec)).Local()
  825. now := time.Now()
  826. diff := ntpNow.Sub(now)
  827. if diff < 0 {
  828. diff = -diff
  829. }
  830. if diff > 1*time.Minute {
  831. return fmt.Errorf("Unexpected NTP time: %s; local time: %s", ntpNow, now)
  832. }
  833. waitGroup.Wait()
  834. return nil
  835. }
  836. func pavePsinetDatabaseFile(
  837. t *testing.T, useDefaultSponsorID bool, psinetFilename string) (string, string) {
  838. sponsorID, _ := common.MakeSecureRandomStringHex(8)
  839. fakeDomain, _ := common.MakeSecureRandomStringHex(4)
  840. fakePath, _ := common.MakeSecureRandomStringHex(4)
  841. expectedHomepageURL := fmt.Sprintf("https://%s.com/%s", fakeDomain, fakePath)
  842. psinetJSONFormat := `
  843. {
  844. "default_sponsor_id" : "%s",
  845. "sponsors": {
  846. "%s": {
  847. "home_pages": {
  848. "None": [
  849. {
  850. "region": null,
  851. "url": "%s"
  852. }
  853. ]
  854. }
  855. }
  856. }
  857. }
  858. `
  859. defaultSponsorID := ""
  860. if useDefaultSponsorID {
  861. defaultSponsorID = sponsorID
  862. }
  863. psinetJSON := fmt.Sprintf(
  864. psinetJSONFormat, defaultSponsorID, sponsorID, expectedHomepageURL)
  865. err := ioutil.WriteFile(psinetFilename, []byte(psinetJSON), 0600)
  866. if err != nil {
  867. t.Fatalf("error paving psinet database file: %s", err)
  868. }
  869. return sponsorID, expectedHomepageURL
  870. }
  871. func paveTrafficRulesFile(
  872. t *testing.T, trafficRulesFilename, propagationChannelID, accessType string,
  873. requireAuthorization, deny bool) {
  874. allowTCPPorts := fmt.Sprintf("%d", mockWebServerPort)
  875. allowUDPPorts := "53, 123"
  876. if deny {
  877. allowTCPPorts = "0"
  878. allowUDPPorts = "0"
  879. }
  880. authorizationFilterFormat := `,
  881. "AuthorizedAccessTypes" : ["%s"]
  882. `
  883. authorizationFilter := ""
  884. if requireAuthorization {
  885. authorizationFilter = fmt.Sprintf(authorizationFilterFormat, accessType)
  886. }
  887. trafficRulesJSONFormat := `
  888. {
  889. "DefaultRules" : {
  890. "RateLimits" : {
  891. "ReadBytesPerSecond": 16384,
  892. "WriteBytesPerSecond": 16384
  893. },
  894. "AllowTCPPorts" : [0],
  895. "AllowUDPPorts" : [0],
  896. "MeekRateLimiterHistorySize" : 10,
  897. "MeekRateLimiterThresholdSeconds" : 1,
  898. "MeekRateLimiterGarbageCollectionTriggerCount" : 1,
  899. "MeekRateLimiterReapHistoryFrequencySeconds" : 1,
  900. "MeekRateLimiterRegions" : []
  901. },
  902. "FilteredRules" : [
  903. {
  904. "Filter" : {
  905. "HandshakeParameters" : {
  906. "propagation_channel_id" : ["%s"]
  907. }%s
  908. },
  909. "Rules" : {
  910. "RateLimits" : {
  911. "ReadUnthrottledBytes": 132352,
  912. "WriteUnthrottledBytes": 132352
  913. },
  914. "AllowTCPPorts" : [%s],
  915. "AllowUDPPorts" : [%s]
  916. }
  917. }
  918. ]
  919. }
  920. `
  921. trafficRulesJSON := fmt.Sprintf(
  922. trafficRulesJSONFormat, propagationChannelID, authorizationFilter, allowTCPPorts, allowUDPPorts)
  923. err := ioutil.WriteFile(trafficRulesFilename, []byte(trafficRulesJSON), 0600)
  924. if err != nil {
  925. t.Fatalf("error paving traffic rules file: %s", err)
  926. }
  927. }
  928. var expectedNumSLOKs = 3
  929. func paveOSLConfigFile(t *testing.T, oslConfigFilename string) string {
  930. oslConfigJSONFormat := `
  931. {
  932. "Schemes" : [
  933. {
  934. "Epoch" : "%s",
  935. "Regions" : [],
  936. "PropagationChannelIDs" : ["%s"],
  937. "MasterKey" : "wFuSbqU/pJ/35vRmoM8T9ys1PgDa8uzJps1Y+FNKa5U=",
  938. "SeedSpecs" : [
  939. {
  940. "ID" : "IXHWfVgWFkEKvgqsjmnJuN3FpaGuCzQMETya+DSQvsk=",
  941. "UpstreamSubnets" : ["0.0.0.0/0"],
  942. "Targets" :
  943. {
  944. "BytesRead" : 1,
  945. "BytesWritten" : 1,
  946. "PortForwardDurationNanoseconds" : 1
  947. }
  948. },
  949. {
  950. "ID" : "qvpIcORLE2Pi5TZmqRtVkEp+OKov0MhfsYPLNV7FYtI=",
  951. "UpstreamSubnets" : ["0.0.0.0/0"],
  952. "Targets" :
  953. {
  954. "BytesRead" : 1,
  955. "BytesWritten" : 1,
  956. "PortForwardDurationNanoseconds" : 1
  957. }
  958. }
  959. ],
  960. "SeedSpecThreshold" : 2,
  961. "SeedPeriodNanoseconds" : 2592000000000000,
  962. "SeedPeriodKeySplits": [
  963. {
  964. "Total": 2,
  965. "Threshold": 2
  966. }
  967. ]
  968. },
  969. {
  970. "Epoch" : "%s",
  971. "Regions" : [],
  972. "PropagationChannelIDs" : ["%s"],
  973. "MasterKey" : "HDc/mvd7e+lKDJD0fMpJW66YJ/VW4iqDRjeclEsMnro=",
  974. "SeedSpecs" : [
  975. {
  976. "ID" : "/M0vsT0IjzmI0MvTI9IYe8OVyeQGeaPZN2xGxfLw/UQ=",
  977. "UpstreamSubnets" : ["0.0.0.0/0"],
  978. "Targets" :
  979. {
  980. "BytesRead" : 1,
  981. "BytesWritten" : 1,
  982. "PortForwardDurationNanoseconds" : 1
  983. }
  984. }
  985. ],
  986. "SeedSpecThreshold" : 1,
  987. "SeedPeriodNanoseconds" : 2592000000000000,
  988. "SeedPeriodKeySplits": [
  989. {
  990. "Total": 1,
  991. "Threshold": 1
  992. }
  993. ]
  994. }
  995. ]
  996. }
  997. `
  998. propagationChannelID, _ := common.MakeSecureRandomStringHex(8)
  999. now := time.Now().UTC()
  1000. epoch := now.Truncate(720 * time.Hour)
  1001. epochStr := epoch.Format(time.RFC3339Nano)
  1002. oslConfigJSON := fmt.Sprintf(
  1003. oslConfigJSONFormat,
  1004. epochStr, propagationChannelID,
  1005. epochStr, propagationChannelID)
  1006. err := ioutil.WriteFile(oslConfigFilename, []byte(oslConfigJSON), 0600)
  1007. if err != nil {
  1008. t.Fatalf("error paving osl config file: %s", err)
  1009. }
  1010. return propagationChannelID
  1011. }
  1012. func paveTacticsConfigFile(
  1013. t *testing.T, tacticsConfigFilename string,
  1014. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey string,
  1015. tunnelProtocol string,
  1016. propagationChannelID string) {
  1017. // Setting LimitTunnelProtocols passively exercises the
  1018. // server-side LimitTunnelProtocols enforcement.
  1019. tacticsConfigJSONFormat := `
  1020. {
  1021. "RequestPublicKey" : "%s",
  1022. "RequestPrivateKey" : "%s",
  1023. "RequestObfuscatedKey" : "%s",
  1024. "EnforceServerSide" : true,
  1025. "DefaultTactics" : {
  1026. "TTL" : "60s",
  1027. "Probability" : 1.0,
  1028. "Parameters" : {
  1029. "LimitTunnelProtocols" : ["%s"]
  1030. }
  1031. },
  1032. "FilteredTactics" : [
  1033. {
  1034. "Filter" : {
  1035. "APIParameters" : {"propagation_channel_id" : ["%s"]},
  1036. "SpeedTestRTTMilliseconds" : {
  1037. "Aggregation" : "Median",
  1038. "AtLeast" : 1
  1039. }
  1040. },
  1041. "Tactics" : {
  1042. "Parameters" : {
  1043. "TunnelConnectTimeout" : "20s",
  1044. "TunnelRateLimits" : {"WriteBytesPerSecond": 1000000}
  1045. }
  1046. }
  1047. }
  1048. ]
  1049. }
  1050. `
  1051. tacticsConfigJSON := fmt.Sprintf(
  1052. tacticsConfigJSONFormat,
  1053. tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
  1054. tunnelProtocol,
  1055. propagationChannelID)
  1056. err := ioutil.WriteFile(tacticsConfigFilename, []byte(tacticsConfigJSON), 0600)
  1057. if err != nil {
  1058. t.Fatalf("error paving tactics config file: %s", err)
  1059. }
  1060. }
  1061. func sendNotificationReceived(c chan<- struct{}) {
  1062. select {
  1063. case c <- *new(struct{}):
  1064. default:
  1065. }
  1066. }
  1067. func waitOnNotification(t *testing.T, c, timeoutSignal <-chan struct{}, timeoutMessage string) {
  1068. select {
  1069. case <-c:
  1070. case <-timeoutSignal:
  1071. t.Fatalf(timeoutMessage)
  1072. }
  1073. }