server_test.go 33 KB

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