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

Meek HTTP header fixes

- Don't default to including X-Psiphon-Fronting-Address for non-fronted HTTPS;
  only include when configured by tactics

- Always include any selected User-Agent (the metric user_agent already
  reports the selection)
Rod Hynes 3 лет назад
Родитель
Сommit
42e3f41a14
4 измененных файлов с 88 добавлено и 18 удалено
  1. 21 0
      psiphon/common/parameters/parameters.go
  2. 20 0
      psiphon/common/protocol/protocol.go
  3. 16 4
      psiphon/dialParameters.go
  4. 31 14
      psiphon/meekConn.go

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

@@ -320,6 +320,7 @@ const (
 	DNSResolverIncludeEDNS0Probability               = "DNSResolverIncludeEDNS0Probability"
 	DNSResolverIncludeEDNS0Probability               = "DNSResolverIncludeEDNS0Probability"
 	DNSResolverCacheExtensionInitialTTL              = "DNSResolverCacheExtensionInitialTTL"
 	DNSResolverCacheExtensionInitialTTL              = "DNSResolverCacheExtensionInitialTTL"
 	DNSResolverCacheExtensionVerifiedTTL             = "DNSResolverCacheExtensionVerifiedTTL"
 	DNSResolverCacheExtensionVerifiedTTL             = "DNSResolverCacheExtensionVerifiedTTL"
+	AddFrontingProviderPsiphonFrontingHeader         = "AddFrontingProviderPsiphonFrontingHeader"
 )
 )
 
 
 const (
 const (
@@ -675,6 +676,8 @@ var defaultParameters = map[string]struct {
 	DNSResolverIncludeEDNS0Probability:          {value: 0.0, minimum: 0.0},
 	DNSResolverIncludeEDNS0Probability:          {value: 0.0, minimum: 0.0},
 	DNSResolverCacheExtensionInitialTTL:         {value: time.Duration(0), minimum: time.Duration(0)},
 	DNSResolverCacheExtensionInitialTTL:         {value: time.Duration(0), minimum: time.Duration(0)},
 	DNSResolverCacheExtensionVerifiedTTL:        {value: time.Duration(0), minimum: time.Duration(0)},
 	DNSResolverCacheExtensionVerifiedTTL:        {value: time.Duration(0), minimum: time.Duration(0)},
+
+	AddFrontingProviderPsiphonFrontingHeader: {value: protocol.LabeledTunnelProtocols{}},
 }
 }
 
 
 // IsServerSideOnly indicates if the parameter specified by name is used
 // IsServerSideOnly indicates if the parameter specified by name is used
@@ -902,6 +905,15 @@ func (p *Parameters) Set(
 						return nil, errors.Trace(err)
 						return nil, errors.Trace(err)
 					}
 					}
 				}
 				}
+			case protocol.LabeledTunnelProtocols:
+				if skipOnError {
+					newValue = v.PruneInvalid()
+				} else {
+					err := v.Validate()
+					if err != nil {
+						return nil, errors.Trace(err)
+					}
+				}
 			case protocol.TLSProfiles:
 			case protocol.TLSProfiles:
 				if skipOnError {
 				if skipOnError {
 					newValue = v.PruneInvalid(customTLSProfileNames)
 					newValue = v.PruneInvalid(customTLSProfileNames)
@@ -1323,6 +1335,15 @@ func (p ParametersAccessor) TunnelProtocols(name string) protocol.TunnelProtocol
 	return value
 	return value
 }
 }
 
 
+// LabeledTunnelProtocols returns a protocol.TunnelProtocols parameter value
+// corresponding to the specified labeled set and label value. The return
+// value is nil when no set is found.
+func (p ParametersAccessor) LabeledTunnelProtocols(name, label string) protocol.TunnelProtocols {
+	var value protocol.LabeledTunnelProtocols
+	p.snapshot.getValue(name, &value)
+	return value[label]
+}
+
 // TLSProfiles returns a protocol.TLSProfiles parameter value.
 // TLSProfiles returns a protocol.TLSProfiles parameter value.
 // If there is a corresponding Probability value, a weighted coin flip
 // If there is a corresponding Probability value, a weighted coin flip
 // will be performed and, depending on the result, the value or the
 // will be performed and, depending on the result, the value or the

+ 20 - 0
psiphon/common/protocol/protocol.go

@@ -124,6 +124,26 @@ func (t TunnelProtocols) PruneInvalid() TunnelProtocols {
 	return u
 	return u
 }
 }
 
 
+type LabeledTunnelProtocols map[string]TunnelProtocols
+
+func (labeledProtocols LabeledTunnelProtocols) Validate() error {
+	for _, protocols := range labeledProtocols {
+		err := protocols.Validate()
+		if err != nil {
+			return errors.Trace(err)
+		}
+	}
+	return nil
+}
+
+func (labeledProtocols LabeledTunnelProtocols) PruneInvalid() LabeledTunnelProtocols {
+	l := make(LabeledTunnelProtocols)
+	for label, protocols := range labeledProtocols {
+		l[label] = protocols.PruneInvalid()
+	}
+	return l
+}
+
 var SupportedTunnelProtocols = TunnelProtocols{
 var SupportedTunnelProtocols = TunnelProtocols{
 	TUNNEL_PROTOCOL_SSH,
 	TUNNEL_PROTOCOL_SSH,
 	TUNNEL_PROTOCOL_OBFUSCATED_SSH,
 	TUNNEL_PROTOCOL_OBFUSCATED_SSH,

+ 16 - 4
psiphon/dialParameters.go

@@ -934,6 +934,17 @@ func MakeDialParameters(
 	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) ||
 	if protocol.TunnelProtocolUsesMeek(dialParams.TunnelProtocol) ||
 		dialParams.ConjureAPIRegistration {
 		dialParams.ConjureAPIRegistration {
 
 
+		// For tactics requests, AddPsiphonFrontingHeader is set when set for
+		// the related tunnel protocol. E.g., FRONTED-OSSH-MEEK for
+		// FRONTED-MEEK-TACTICS. AddPsiphonFrontingHeader is not replayed.
+		addPsiphonFrontingHeader := false
+		if dialParams.FrontingProviderID != "" {
+			addPsiphonFrontingHeader = common.Contains(
+				p.LabeledTunnelProtocols(
+					parameters.AddFrontingProviderPsiphonFrontingHeader, dialParams.FrontingProviderID),
+				dialParams.TunnelProtocol)
+		}
+
 		dialParams.meekConfig = &MeekConfig{
 		dialParams.meekConfig = &MeekConfig{
 			DiagnosticID:                  serverEntry.GetDiagnosticID(),
 			DiagnosticID:                  serverEntry.GetDiagnosticID(),
 			Parameters:                    config.GetParameters(),
 			Parameters:                    config.GetParameters(),
@@ -949,6 +960,7 @@ func MakeDialParameters(
 			RandomizedTLSProfileSeed:      dialParams.RandomizedTLSProfileSeed,
 			RandomizedTLSProfileSeed:      dialParams.RandomizedTLSProfileSeed,
 			UseObfuscatedSessionTickets:   dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
 			UseObfuscatedSessionTickets:   dialParams.TunnelProtocol == protocol.TUNNEL_PROTOCOL_UNFRONTED_MEEK_SESSION_TICKET,
 			SNIServerName:                 dialParams.MeekSNIServerName,
 			SNIServerName:                 dialParams.MeekSNIServerName,
+			AddPsiphonFrontingHeader:      addPsiphonFrontingHeader,
 			VerifyServerName:              dialParams.MeekVerifyServerName,
 			VerifyServerName:              dialParams.MeekVerifyServerName,
 			VerifyPins:                    dialParams.MeekVerifyPins,
 			VerifyPins:                    dialParams.MeekVerifyPins,
 			HostHeader:                    dialParams.MeekHostHeader,
 			HostHeader:                    dialParams.MeekHostHeader,
@@ -1066,11 +1078,11 @@ func (dialParams *DialParameters) GetTLSVersionForMetrics() string {
 // There are two concerns regarding which dial parameter fields are safe to
 // There are two concerns regarding which dial parameter fields are safe to
 // exchange:
 // exchange:
 //
 //
-// - Unlike signed server entries, there's no independent trust anchor
-//   that can certify that the exchange data is valid.
+//   - Unlike signed server entries, there's no independent trust anchor
+//     that can certify that the exchange data is valid.
 //
 //
-// - While users should only perform the exchange with trusted peers,
-//   the user's trust in their peer may be misplaced.
+//   - While users should only perform the exchange with trusted peers,
+//     the user's trust in their peer may be misplaced.
 //
 //
 // This presents the possibility of attack such as the peer sending dial
 // This presents the possibility of attack such as the peer sending dial
 // parameters that could be used to trace/monitor/flag the importer; or
 // parameters that could be used to trace/monitor/flag the importer; or

+ 31 - 14
psiphon/meekConn.go

@@ -159,6 +159,10 @@ type MeekConfig struct {
 	// in effect. This value is used for stats reporting.
 	// in effect. This value is used for stats reporting.
 	TransformedHostName bool
 	TransformedHostName bool
 
 
+	// AddPsiphonFrontingHeader specifies whether to add the
+	// X-Psiphon-Fronting-Address custom header.
+	AddPsiphonFrontingHeader bool
+
 	// VerifyServerName specifies a domain name that must appear in the server
 	// VerifyServerName specifies a domain name that must appear in the server
 	// certificate. When blank, server certificate verification is disabled.
 	// certificate. When blank, server certificate verification is disabled.
 	VerifyServerName string
 	VerifyServerName string
@@ -614,24 +618,37 @@ func DialMeek(
 		Opaque: opaqueURL,
 		Opaque: opaqueURL,
 	}
 	}
 
 
-	if meekConfig.UseHTTPS {
-		host, _, err := net.SplitHostPort(meekConfig.DialAddress)
-		if err != nil {
-			return nil, errors.Trace(err)
-		}
-		additionalHeaders = map[string][]string{
-			"X-Psiphon-Fronting-Address": {host},
-		}
+	if scheme == "http" && proxyUrl == nil {
+
+		// Add custom headers to HTTP. This may be unproxied HTTP, or CONNECT
+		// method proxied HTTP, which is handled implicitly by DialTCP (in the
+		// latter case, the CONNECT request itself will also have custom
+		// headers via upstreamproxy applied by the dialer).
+		//
+		// When proxyUrl != nil, proxying is handled by http.Transport and
+		// custom headers are set in upstreamproxy.NewProxyAuthTransport, above.
+
+		additionalHeaders = dialConfig.CustomHeaders
+
 	} else {
 	} else {
-		if proxyUrl == nil {
 
 
-			// Add custom headers to plain, unproxied HTTP and to CONNECT
-			// method proxied HTTP (in the latter case, the CONNECT request
-			// itself will also have custom headers via upstreamproxy applied
-			// by the dialer).
+		additionalHeaders = make(http.Header)
+
+		// User-Agent is passed in via dialConfig.CustomHeaders. Always use
+		// any User-Agent header, even when not using all custom headers.
+
+		userAgent := dialConfig.CustomHeaders.Get("User-Agent")
+		if userAgent != "" {
+			additionalHeaders.Set("User-Agent", userAgent)
+		}
+	}
 
 
-			additionalHeaders = dialConfig.CustomHeaders
+	if meekConfig.AddPsiphonFrontingHeader {
+		host, _, err := net.SplitHostPort(meekConfig.DialAddress)
+		if err != nil {
+			return nil, errors.Trace(err)
 		}
 		}
+		additionalHeaders.Set("X-Psiphon-Fronting-Address", host)
 	}
 	}
 
 
 	meek.url = url
 	meek.url = url