Jelajahi Sumber

Restrict the set of servers accessed through upstream proxies

Rod Hynes 4 tahun lalu
induk
melakukan
3fc88b0e11

+ 3 - 0
psiphon/common/parameters/parameters.go

@@ -295,6 +295,7 @@ const (
 	RestrictFrontingProviderIDs                      = "RestrictFrontingProviderIDs"
 	RestrictFrontingProviderIDs                      = "RestrictFrontingProviderIDs"
 	RestrictFrontingProviderIDsServerProbability     = "RestrictFrontingProviderIDsServerProbability"
 	RestrictFrontingProviderIDsServerProbability     = "RestrictFrontingProviderIDsServerProbability"
 	RestrictFrontingProviderIDsClientProbability     = "RestrictFrontingProviderIDsClientProbability"
 	RestrictFrontingProviderIDsClientProbability     = "RestrictFrontingProviderIDsClientProbability"
+	UpstreamProxyAllowAllServerEntrySources          = "UpstreamProxyAllowAllServerEntrySources"
 )
 )
 
 
 const (
 const (
@@ -620,6 +621,8 @@ var defaultParameters = map[string]struct {
 	RestrictFrontingProviderIDs:                  {value: []string{}},
 	RestrictFrontingProviderIDs:                  {value: []string{}},
 	RestrictFrontingProviderIDsServerProbability: {value: 0.0, minimum: 0.0, flags: serverSideOnly},
 	RestrictFrontingProviderIDsServerProbability: {value: 0.0, minimum: 0.0, flags: serverSideOnly},
 	RestrictFrontingProviderIDsClientProbability: {value: 0.0, minimum: 0.0},
 	RestrictFrontingProviderIDsClientProbability: {value: 0.0, minimum: 0.0},
+
+	UpstreamProxyAllowAllServerEntrySources: {value: false},
 }
 }
 
 
 // IsServerSideOnly indicates if the parameter specified by name is used
 // IsServerSideOnly indicates if the parameter specified by name is used

+ 14 - 9
psiphon/common/protocol/protocol.go

@@ -90,6 +90,20 @@ const (
 	CONJURE_TRANSPORT_OBFS4_OSSH = "Obfs4-OSSH"
 	CONJURE_TRANSPORT_OBFS4_OSSH = "Obfs4-OSSH"
 )
 )
 
 
+var SupportedServerEntrySources = []string{
+	SERVER_ENTRY_SOURCE_EMBEDDED,
+	SERVER_ENTRY_SOURCE_REMOTE,
+	SERVER_ENTRY_SOURCE_DISCOVERY,
+	SERVER_ENTRY_SOURCE_TARGET,
+	SERVER_ENTRY_SOURCE_OBFUSCATED,
+	SERVER_ENTRY_SOURCE_EXCHANGED,
+}
+
+func AllowServerEntrySourceWithUpstreamProxy(source string) bool {
+	return source == SERVER_ENTRY_SOURCE_EMBEDDED ||
+		source == SERVER_ENTRY_SOURCE_REMOTE
+}
+
 type TunnelProtocols []string
 type TunnelProtocols []string
 
 
 func (t TunnelProtocols) Validate() error {
 func (t TunnelProtocols) Validate() error {
@@ -133,15 +147,6 @@ var DefaultDisabledTunnelProtocols = TunnelProtocols{
 	TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH,
 	TUNNEL_PROTOCOL_CONJURE_OBFUSCATED_SSH,
 }
 }
 
 
-var SupportedServerEntrySources = TunnelProtocols{
-	SERVER_ENTRY_SOURCE_EMBEDDED,
-	SERVER_ENTRY_SOURCE_REMOTE,
-	SERVER_ENTRY_SOURCE_DISCOVERY,
-	SERVER_ENTRY_SOURCE_TARGET,
-	SERVER_ENTRY_SOURCE_OBFUSCATED,
-	SERVER_ENTRY_SOURCE_EXCHANGED,
-}
-
 func TunnelProtocolUsesTCP(protocol string) bool {
 func TunnelProtocolUsesTCP(protocol string) bool {
 	// Limitation: Marionette network protocol depends on its format configuration.
 	// Limitation: Marionette network protocol depends on its format configuration.
 	return protocol != TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH &&
 	return protocol != TUNNEL_PROTOCOL_QUIC_OBFUSCATED_SSH &&

+ 12 - 0
psiphon/config.go

@@ -748,6 +748,9 @@ type Config struct {
 	RestrictFrontingProviderIDs                  []string
 	RestrictFrontingProviderIDs                  []string
 	RestrictFrontingProviderIDsClientProbability *float64
 	RestrictFrontingProviderIDsClientProbability *float64
 
 
+	// UpstreamProxyAllowAllServerEntrySources is for testing purposes.
+	UpstreamProxyAllowAllServerEntrySources *bool
+
 	// params is the active parameters.Parameters with defaults, config values,
 	// params is the active parameters.Parameters with defaults, config values,
 	// and, optionally, tactics applied.
 	// and, optionally, tactics applied.
 	//
 	//
@@ -1713,6 +1716,10 @@ func (config *Config) makeConfigParameters() map[string]interface{} {
 		applyParameters[parameters.RestrictFrontingProviderIDsClientProbability] = *config.RestrictFrontingProviderIDsClientProbability
 		applyParameters[parameters.RestrictFrontingProviderIDsClientProbability] = *config.RestrictFrontingProviderIDsClientProbability
 	}
 	}
 
 
+	if config.UpstreamProxyAllowAllServerEntrySources != nil {
+		applyParameters[parameters.UpstreamProxyAllowAllServerEntrySources] = *config.UpstreamProxyAllowAllServerEntrySources
+	}
+
 	// When adding new config dial parameters that may override tactics, also
 	// When adding new config dial parameters that may override tactics, also
 	// update setDialParametersHash.
 	// update setDialParametersHash.
 
 
@@ -2032,6 +2039,11 @@ func (config *Config) setDialParametersHash() {
 		binary.Write(hash, binary.LittleEndian, *config.RestrictFrontingProviderIDsClientProbability)
 		binary.Write(hash, binary.LittleEndian, *config.RestrictFrontingProviderIDsClientProbability)
 	}
 	}
 
 
+	if config.UpstreamProxyAllowAllServerEntrySources != nil {
+		hash.Write([]byte("UpstreamProxyAllowAllServerEntrySources"))
+		binary.Write(hash, binary.LittleEndian, *config.UpstreamProxyAllowAllServerEntrySources)
+	}
+
 	config.dialParametersHash = hash.Sum(nil)
 	config.dialParametersHash = hash.Sum(nil)
 }
 }
 
 

+ 24 - 8
psiphon/dialParameters.go

@@ -362,15 +362,31 @@ func MakeDialParameters(
 		}
 		}
 	}
 	}
 
 
-	if config.UseUpstreamProxy() &&
-		!protocol.TunnelProtocolSupportsUpstreamProxy(dialParams.TunnelProtocol) {
+	if config.UseUpstreamProxy() {
 
 
-		// When UpstreamProxy is configured, ServerEntry.GetSupportedProtocols, when
-		// called via selectProtocol, will filter out protocols such that will not
-		// select a protocol incompatible with UpstreamProxy. This additional check
-		// will catch cases where selectProtocol does not apply this filter.
-		return nil, errors.Tracef(
-			"protocol does not support upstream proxy: %s", dialParams.TunnelProtocol)
+		if !protocol.TunnelProtocolSupportsUpstreamProxy(dialParams.TunnelProtocol) {
+
+			// When UpstreamProxy is configured, ServerEntry.GetSupportedProtocols, when
+			// called via selectProtocol, will filter out protocols such that will not
+			// select a protocol incompatible with UpstreamProxy. This additional check
+			// will catch cases where selectProtocol does not apply this filter.
+			return nil, errors.Tracef(
+				"protocol does not support upstream proxy: %s", dialParams.TunnelProtocol)
+		}
+
+		// Skip this candidate when the server entry is not to be used with an
+		// upstream proxy. By not exposing servers from sources that are
+		// relatively hard to enumerate, this mechanism mitigates the risk of
+		// a malicious upstream proxy enumerating Psiphon servers. Populate
+		// the allowed sources with fronted servers to provide greater
+		// blocking resistence for clients using upstream proxy clients that
+		// are subject to blocking.
+		source := dialParams.ServerEntry.LocalSource
+		if !protocol.AllowServerEntrySourceWithUpstreamProxy(source) &&
+			!p.Bool(parameters.UpstreamProxyAllowAllServerEntrySources) {
+			return nil, errors.Tracef(
+				"server entry source disallowed with upstream proxy: %s", source)
+		}
 	}
 	}
 
 
 	if (!isReplay || !replayBPF) &&
 	if (!isReplay || !replayBPF) &&

+ 5 - 4
psiphon/remoteServerList_test.go

@@ -394,10 +394,11 @@ func testObfuscatedRemoteServerLists(t *testing.T, omitMD5Sums bool) {
         "ConnectionWorkerPoolSize" : 1,
         "ConnectionWorkerPoolSize" : 1,
         "EstablishTunnelPausePeriodSeconds" : 1,
         "EstablishTunnelPausePeriodSeconds" : 1,
         "FetchRemoteServerListRetryPeriodMilliseconds" : 250,
         "FetchRemoteServerListRetryPeriodMilliseconds" : 250,
-		"RemoteServerListSignaturePublicKey" : "%s",
-		"RemoteServerListUrl" : "%s",
-		"ObfuscatedServerListRootURLs" : %s,
-		"UpstreamProxyUrl" : "%s"
+        "RemoteServerListSignaturePublicKey" : "%s",
+        "RemoteServerListUrl" : "%s",
+        "ObfuscatedServerListRootURLs" : %s,
+        "UpstreamProxyUrl" : "%s",
+        "UpstreamProxyAllowAllServerEntrySources" : true
     }`
     }`
 
 
 	clientConfigJSON := fmt.Sprintf(
 	clientConfigJSON := fmt.Sprintf(

+ 2 - 1
psiphon/userAgent_test.go

@@ -198,7 +198,8 @@ func attemptConnectionsWithUserAgent(
         "EstablishTunnelPausePeriodSeconds" : 1,
         "EstablishTunnelPausePeriodSeconds" : 1,
         "DisableRemoteServerListFetcher" : true,
         "DisableRemoteServerListFetcher" : true,
         "TransformHostNameProbability" : 0.0,
         "TransformHostNameProbability" : 0.0,
-        "UpstreamProxyUrl" : "http://127.0.0.1:2163"
+        "UpstreamProxyUrl" : "http://127.0.0.1:2163",
+        "UpstreamProxyAllowAllServerEntrySources" : true
     }`
     }`
 	clientConfig, err := LoadConfig([]byte(clientConfigJSON))
 	clientConfig, err := LoadConfig([]byte(clientConfigJSON))
 	if err != nil {
 	if err != nil {