github.com/Psiphon-Labs/psiphon-tunnel-core@v2.0.28+incompatible/psiphon/server/replay_test.go (about)

     1  /*
     2   * Copyright (c) 2020, 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  
    20  package server
    21  
    22  import (
    23  	"context"
    24  	"encoding/json"
    25  	"fmt"
    26  	"io/ioutil"
    27  	"os"
    28  	"path/filepath"
    29  	"reflect"
    30  	"sync"
    31  	"testing"
    32  	"time"
    33  
    34  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon"
    35  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/protocol"
    36  	"github.com/Psiphon-Labs/psiphon-tunnel-core/psiphon/common/tactics"
    37  )
    38  
    39  func TestServerFragmentorReplay(t *testing.T) {
    40  	runServerReplayTests(t, false)
    41  }
    42  
    43  func runServerReplayTests(t *testing.T, runPacketManipulation bool) {
    44  
    45  	// Do not use OSSH, which has a different fragmentor replay mechanism. Meek
    46  	// has a unique code path for passing around replay parameters and metrics.
    47  	testCases := protocol.TunnelProtocols{
    48  		protocol.TUNNEL_PROTOCOL_SSH,
    49  		protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK,
    50  	}
    51  
    52  	for _, tunnelProtocol := range testCases {
    53  		t.Run(tunnelProtocol, func(t *testing.T) {
    54  			runServerReplayTest(t, runPacketManipulation, tunnelProtocol)
    55  		})
    56  	}
    57  }
    58  
    59  func runServerReplayTest(
    60  	t *testing.T,
    61  	runPacketManipulation bool,
    62  	tunnelProtocol string) {
    63  
    64  	psiphon.SetEmitDiagnosticNotices(true, true)
    65  
    66  	// Configure tactics
    67  
    68  	tacticsConfigJSONFormat := `
    69      {
    70        "RequestPublicKey" : "%s",
    71        "RequestPrivateKey" : "%s",
    72        "RequestObfuscatedKey" : "%s",
    73        "DefaultTactics" : {
    74          "TTL" : "60s",
    75          "Probability" : 1.0,
    76          "Parameters" : {
    77            "LimitTunnelProtocols" : ["%s"],
    78            "FragmentorDownstreamLimitProtocols" : ["%s"],
    79            "FragmentorDownstreamProbability" : 1.0,
    80            "FragmentorDownstreamMinTotalBytes" : 10,
    81            "FragmentorDownstreamMaxTotalBytes" : 10,
    82            "FragmentorDownstreamMinWriteBytes" : 1,
    83            "FragmentorDownstreamMaxWriteBytes" : 1,
    84            "FragmentorDownstreamMinDelay" : "1ms",
    85            "FragmentorDownstreamMaxDelay" : "1ms",
    86            "ServerPacketManipulationSpecs" : [{"Name": "test-packetman-spec", "PacketSpecs": [[]]}],
    87            "ServerPacketManipulationProbability" : 1.0,
    88            "ServerProtocolPacketManipulations": {"%s" : ["test-packetman-spec"]},
    89            "ServerReplayPacketManipulation" : true,
    90            "ServerReplayFragmentor" : true,
    91            "ServerReplayUnknownGeoIP" : true,
    92            "ServerReplayTTL" : "5s",
    93            "ServerReplayTargetWaitDuration" : "200ms",
    94            "ServerReplayTargetTunnelDuration" : "50ms",
    95            "ServerReplayTargetUpstreamBytes" : 0,
    96            "ServerReplayTargetDownstreamBytes" : 0,
    97            "ServerReplayFailedCountThreshold" : 1,
    98            "ServerReplayFailedCountThreshold" : 1
    99          }
   100        }
   101      }
   102      `
   103  
   104  	tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey, err :=
   105  		tactics.GenerateKeys()
   106  	if err != nil {
   107  		t.Fatalf("error generating tactics keys: %s", err)
   108  	}
   109  
   110  	tacticsConfigJSON := fmt.Sprintf(
   111  		tacticsConfigJSONFormat,
   112  		tacticsRequestPublicKey, tacticsRequestPrivateKey, tacticsRequestObfuscatedKey,
   113  		tunnelProtocol, tunnelProtocol, tunnelProtocol)
   114  
   115  	tacticsConfigFilename := filepath.Join(testDataDirName, "tactics_config.json")
   116  
   117  	err = ioutil.WriteFile(tacticsConfigFilename, []byte(tacticsConfigJSON), 0600)
   118  	if err != nil {
   119  		t.Fatalf("error paving tactics config file: %s", err)
   120  	}
   121  
   122  	// Run Psiphon server
   123  
   124  	generateConfigParams := &GenerateConfigParams{
   125  		ServerIPAddress:      "127.0.0.1",
   126  		EnableSSHAPIRequests: true,
   127  		WebServerPort:        8000,
   128  		TunnelProtocolPorts:  map[string]int{tunnelProtocol: 4000},
   129  	}
   130  
   131  	serverConfigJSON, _, _, _, encodedServerEntry, err := GenerateConfig(generateConfigParams)
   132  	if err != nil {
   133  		t.Fatalf("error generating server config: %s", err)
   134  	}
   135  
   136  	var serverConfig map[string]interface{}
   137  	json.Unmarshal(serverConfigJSON, &serverConfig)
   138  
   139  	serverConfig["LogFilename"] = filepath.Join(testDataDirName, "psiphond.log")
   140  	serverConfig["LogLevel"] = "debug"
   141  	serverConfig["TacticsConfigFilename"] = tacticsConfigFilename
   142  
   143  	// Ensure server_tunnels emit quickly.
   144  	serverConfig["MeekMaxSessionStalenessMilliseconds"] = 500
   145  
   146  	if runPacketManipulation {
   147  		serverConfig["RunPacketManipulator"] = true
   148  	}
   149  
   150  	serverConfigJSON, _ = json.Marshal(serverConfig)
   151  
   152  	serverTunnelLog := make(chan map[string]interface{}, 1)
   153  
   154  	setLogCallback(func(log []byte) {
   155  		logFields := make(map[string]interface{})
   156  		err := json.Unmarshal(log, &logFields)
   157  		if err != nil {
   158  			return
   159  		}
   160  		if logFields["event_name"] == nil {
   161  			return
   162  		}
   163  		if logFields["event_name"].(string) == "server_tunnel" {
   164  			select {
   165  			case serverTunnelLog <- logFields:
   166  			default:
   167  			}
   168  		}
   169  	})
   170  
   171  	serverWaitGroup := new(sync.WaitGroup)
   172  	serverWaitGroup.Add(1)
   173  	go func() {
   174  		defer serverWaitGroup.Done()
   175  		err := RunServices(serverConfigJSON)
   176  		if err != nil {
   177  			t.Errorf("error running server: %s", err)
   178  		}
   179  	}()
   180  
   181  	defer func() {
   182  		p, _ := os.FindProcess(os.Getpid())
   183  		p.Signal(os.Interrupt)
   184  		serverWaitGroup.Wait()
   185  	}()
   186  
   187  	// TODO: monitor logs for more robust wait-until-loaded.
   188  	time.Sleep(1 * time.Second)
   189  
   190  	checkServerTunnelLog := func(expectReplay bool) {
   191  
   192  		// Numbers are float64 due to JSON decoding.
   193  		expectedServerTunnelFields := map[string]interface{}{
   194  			"downstream_bytes_fragmented":       float64(10),
   195  			"downstream_min_bytes_written":      float64(1),
   196  			"downstream_max_bytes_written":      float64(1),
   197  			"downstream_min_delayed":            float64(1000),
   198  			"downstream_max_delayed":            float64(1000),
   199  			"server_replay_fragmentation":       expectReplay,
   200  			"server_replay_packet_manipulation": expectReplay && runPacketManipulation,
   201  		}
   202  		if runPacketManipulation {
   203  			expectedServerTunnelFields["server_packet_manipulation"] = "test-packetman-spec"
   204  		}
   205  
   206  		logFields := <-serverTunnelLog
   207  
   208  		for name, value := range expectedServerTunnelFields {
   209  			logValue, ok := logFields[name]
   210  			if !ok {
   211  				t.Fatalf("Missing expected server_tunnel field: %s", name)
   212  			}
   213  			if !reflect.DeepEqual(logValue, value) {
   214  				t.Fatalf(
   215  					"Unexpected server_tunnel %s value: got %T(%v); expected %T(%v)",
   216  					name, logValue, logValue, value, value)
   217  			}
   218  		}
   219  	}
   220  
   221  	t.Log("first client run; no replay")
   222  
   223  	runServerReplayClient(t, encodedServerEntry, true)
   224  	checkServerTunnelLog(false)
   225  
   226  	t.Log("second client run; is replay")
   227  
   228  	runServerReplayClient(t, encodedServerEntry, true)
   229  	checkServerTunnelLog(true)
   230  
   231  	t.Log("TTL expires; no replay")
   232  
   233  	// Wait until TTL expires.
   234  	time.Sleep(5100 * time.Millisecond)
   235  
   236  	runServerReplayClient(t, encodedServerEntry, true)
   237  	checkServerTunnelLog(false)
   238  
   239  	t.Log("failure clears replay; no replay")
   240  
   241  	runServerReplayClient(t, encodedServerEntry, true)
   242  	checkServerTunnelLog(true)
   243  
   244  	runServerReplayClient(t, encodedServerEntry, false)
   245  	// No server_tunnel for SSH handshake failure.
   246  
   247  	// Wait for session to be retired, which will trigger replay failure.
   248  	if protocol.TunnelProtocolUsesMeek(tunnelProtocol) {
   249  		time.Sleep(1000 * time.Millisecond)
   250  	}
   251  
   252  	runServerReplayClient(t, encodedServerEntry, true)
   253  	checkServerTunnelLog(false)
   254  }
   255  
   256  func runServerReplayClient(
   257  	t *testing.T,
   258  	encodedServerEntry []byte,
   259  	handshakeSuccess bool) {
   260  
   261  	if !handshakeSuccess {
   262  		serverEntry, err := protocol.DecodeServerEntry(string(encodedServerEntry), "", "")
   263  		if err != nil {
   264  			t.Fatalf("error decoding server entry: %s", err)
   265  		}
   266  		serverEntry.SshPassword = ""
   267  		encodedServerEntryStr, err := protocol.EncodeServerEntry(serverEntry)
   268  		if err != nil {
   269  			t.Fatalf("error encoding server entry: %s", err)
   270  		}
   271  		encodedServerEntry = []byte(encodedServerEntryStr)
   272  	}
   273  
   274  	dataRootDir, err := ioutil.TempDir(testDataDirName, "serverReplayClient")
   275  	if err != nil {
   276  		t.Fatalf("error createing temp dir: %s", err)
   277  	}
   278  	defer os.RemoveAll(dataRootDir)
   279  
   280  	clientConfigJSON := fmt.Sprintf(`
   281      {
   282          "DataRootDirectory" : "%s",
   283          "ClientPlatform" : "Windows",
   284          "ClientVersion" : "0",
   285          "SponsorId" : "0",
   286          "PropagationChannelId" : "0",
   287          "TargetServerEntry" : "%s"
   288      }`, dataRootDir, string(encodedServerEntry))
   289  
   290  	clientConfig, err := psiphon.LoadConfig([]byte(clientConfigJSON))
   291  	if err != nil {
   292  		t.Fatalf("error processing configuration file: %s", err)
   293  	}
   294  
   295  	err = clientConfig.Commit(false)
   296  	if err != nil {
   297  		t.Fatalf("error committing configuration file: %s", err)
   298  	}
   299  
   300  	err = psiphon.OpenDataStore(clientConfig)
   301  	if err != nil {
   302  		t.Fatalf("error initializing client datastore: %s", err)
   303  	}
   304  	defer psiphon.CloseDataStore()
   305  
   306  	controller, err := psiphon.NewController(clientConfig)
   307  	if err != nil {
   308  		t.Fatalf("error creating client controller: %s", err)
   309  	}
   310  
   311  	tunnelEstablished := make(chan struct{}, 1)
   312  
   313  	psiphon.SetNoticeWriter(psiphon.NewNoticeReceiver(
   314  		func(notice []byte) {
   315  			noticeType, payload, err := psiphon.GetNotice(notice)
   316  			if err != nil {
   317  				return
   318  			}
   319  			if noticeType == "Tunnels" {
   320  				count := int(payload["count"].(float64))
   321  				if count >= 1 {
   322  					tunnelEstablished <- struct{}{}
   323  				}
   324  			}
   325  		}))
   326  
   327  	ctx, cancelFunc := context.WithCancel(context.Background())
   328  	controllerWaitGroup := new(sync.WaitGroup)
   329  	controllerWaitGroup.Add(1)
   330  	go func() {
   331  		defer controllerWaitGroup.Done()
   332  		controller.Run(ctx)
   333  	}()
   334  
   335  	if handshakeSuccess {
   336  		<-tunnelEstablished
   337  	}
   338  
   339  	// Meet tunnel duration critera.
   340  	for i := 0; i < 20; i++ {
   341  		time.Sleep(10 * time.Millisecond)
   342  		_, _ = controller.Dial("127.0.0.1:80", nil)
   343  	}
   344  
   345  	cancelFunc()
   346  	controllerWaitGroup.Wait()
   347  }