Просмотр исходного кода

Add SSHKeepAliveResetOnFailureProbability

Rod Hynes 6 лет назад
Родитель
Сommit
9feb3f5dfb
3 измененных файлов с 66 добавлено и 1 удалено
  1. 2 0
      psiphon/common/parameters/clientParameters.go
  2. 41 1
      psiphon/dataStore.go
  3. 23 0
      psiphon/tunnel.go

+ 2 - 0
psiphon/common/parameters/clientParameters.go

@@ -141,6 +141,7 @@ const (
 	SSHKeepAliveProbeTimeout                         = "SSHKeepAliveProbeTimeout"
 	SSHKeepAliveProbeInactivePeriod                  = "SSHKeepAliveProbeInactivePeriod"
 	SSHKeepAliveNetworkConnectivityPollingPeriod     = "SSHKeepAliveNetworkConnectivityPollingPeriod"
+	SSHKeepAliveResetOnFailureProbability            = "SSHKeepAliveResetOnFailureProbability"
 	HTTPProxyOriginServerTimeout                     = "HTTPProxyOriginServerTimeout"
 	HTTPProxyMaxIdleConnectionsPerHost               = "HTTPProxyMaxIdleConnectionsPerHost"
 	FetchRemoteServerListTimeout                     = "FetchRemoteServerListTimeout"
@@ -362,6 +363,7 @@ var defaultClientParameters = map[string]struct {
 	SSHKeepAliveProbeTimeout:                     {value: 5 * time.Second, minimum: 1 * time.Second, flags: useNetworkLatencyMultiplier},
 	SSHKeepAliveProbeInactivePeriod:              {value: 10 * time.Second, minimum: 1 * time.Second},
 	SSHKeepAliveNetworkConnectivityPollingPeriod: {value: 500 * time.Millisecond, minimum: 1 * time.Millisecond},
+	SSHKeepAliveResetOnFailureProbability:        {value: 0.0, minimum: 0.0},
 
 	HTTPProxyOriginServerTimeout:       {value: 15 * time.Second, minimum: time.Duration(0), flags: useNetworkLatencyMultiplier},
 	HTTPProxyMaxIdleConnectionsPerHost: {value: 50, minimum: 0},

+ 41 - 1
psiphon/dataStore.go

@@ -337,7 +337,43 @@ func PromoteServerEntry(config *Config, ipAddress string) error {
 			return errors.Trace(err)
 		}
 
-		return bucket.put(datastoreLastServerEntryFilterKey, currentFilter)
+		err = bucket.put(datastoreLastServerEntryFilterKey, currentFilter)
+		if err != nil {
+			return errors.Trace(err)
+		}
+
+		return nil
+	})
+
+	if err != nil {
+		return errors.Trace(err)
+	}
+	return nil
+}
+
+// DeleteServerEntryAffinity clears server affinity if set to the specified
+// server.
+func DeleteServerEntryAffinity(ipAddress string) error {
+	err := datastoreUpdate(func(tx *datastoreTx) error {
+
+		serverEntryID := []byte(ipAddress)
+
+		bucket := tx.bucket(datastoreKeyValueBucket)
+
+		affinityServerEntryID := bucket.get(datastoreAffinityServerEntryIDKey)
+
+		if bytes.Equal(affinityServerEntryID, serverEntryID) {
+			err := bucket.delete(datastoreAffinityServerEntryIDKey)
+			if err != nil {
+				return errors.Trace(err)
+			}
+			err = bucket.delete(datastoreLastServerEntryFilterKey)
+			if err != nil {
+				return errors.Trace(err)
+			}
+		}
+
+		return nil
 	})
 
 	if err != nil {
@@ -900,6 +936,10 @@ func pruneServerEntry(config *Config, serverEntryTag string) error {
 				if err != nil {
 					return errors.Trace(err)
 				}
+				err = keyValues.delete(datastoreLastServerEntryFilterKey)
+				if err != nil {
+					return errors.Trace(err)
+				}
 			}
 
 			// TODO: expose boltdb Seek functionality to skip to first matching record.

+ 23 - 0
psiphon/tunnel.go

@@ -1310,6 +1310,9 @@ func (tunnel *Tunnel) sendSshKeepAlive(
 	networkConnectivityPollPeriod := p.Duration(
 		parameters.SSHKeepAliveNetworkConnectivityPollingPeriod)
 
+	resetOnFailure := p.WeightedCoinFlip(
+		parameters.SSHKeepAliveResetOnFailureProbability)
+
 	p.Close()
 
 	// Note: there is no request context since SSH requests cannot be interrupted
@@ -1408,6 +1411,7 @@ loop:
 		tunnel.conn.Close()
 
 		if continuousNetworkConnectivity {
+
 			_ = RecordFailedTunnelStat(
 				tunnel.config,
 				tunnel.dialParams,
@@ -1415,6 +1419,25 @@ loop:
 				bytesUp,
 				bytesDown,
 				err)
+
+			// SSHKeepAliveResetOnFailureProbability is set when a late-lifecycle
+			// impaired protocol attack is suspected. With the given probability, reset
+			// server affinity and replay parameters for this server to avoid
+			// continuously reconnecting to the server and/or using the same protocol
+			// and dial parameters.
+
+			if resetOnFailure {
+				NoticeInfo("Delete dial parameters for %s", tunnel.dialParams.ServerEntry.GetDiagnosticID())
+				err := DeleteDialParameters(tunnel.dialParams.ServerEntry.IpAddress, tunnel.dialParams.NetworkID)
+				if err != nil {
+					NoticeWarning("DeleteDialParameters failed: %s", err)
+				}
+				NoticeInfo("Delete server affinity for %s", tunnel.dialParams.ServerEntry.GetDiagnosticID())
+				err = DeleteServerEntryAffinity(tunnel.dialParams.ServerEntry.IpAddress)
+				if err != nil {
+					NoticeWarning("DeleteServerEntryAffinity failed: %s", err)
+				}
+			}
 		}
 	}