diff --git a/synapse/config/tracer.py b/synapse/config/tracer.py index 2525503964..506b2afae7 100644 --- a/synapse/config/tracer.py +++ b/synapse/config/tracer.py @@ -18,6 +18,7 @@ import logging from jaeger_client import Config as JaegerConfig from synapse.util.scopecontextmanager import LogContextScopeManager +from synapse.util.tracerutils import TracerUtil from ._base import Config, ConfigError @@ -37,7 +38,7 @@ class TracerConfig(Config): # If no whitelists are given self.tracer_config.setdefault("homeserver_whitelist", []) - if isinstance(self.tracer_config.get("homeserver_whitelist"), list): + if not isinstance(self.tracer_config.get("homeserver_whitelist"), list): raise ConfigError("Tracer homesererver_whitelist config is malformed") def generate_config_section(cls, **kwargs): @@ -55,7 +56,7 @@ class TracerConfig(Config): def init_tracing(config): - """Initialise the JaegerClient tracer + """Set the whitelists and initialise the JaegerClient tracer Args: config (Config) @@ -64,6 +65,9 @@ def init_tracing(config): """ if config.tracer_config.get("tracer_enabled", False): + TracerUtil.set_homeserver_whitelist( + config.tracer_config["homeserver_whitelist"] + ) jaeger_config = JaegerConfig( config={"sampler": {"type": "const", "param": 1}, "logging": True}, service_name=config.server_name, diff --git a/synapse/http/matrixfederationclient.py b/synapse/http/matrixfederationclient.py index aa14e269c6..4bc5ceeeee 100644 --- a/synapse/http/matrixfederationclient.py +++ b/synapse/http/matrixfederationclient.py @@ -23,8 +23,6 @@ from six import PY3, raise_from, string_types from six.moves import urllib import attr -import opentracing -from opentracing import tags import treq from canonicaljson import encode_canonical_json from opentracing.propagation import Format @@ -39,6 +37,10 @@ from twisted.internet.task import _EPSILON, Cooperator from twisted.web._newclient import ResponseDone from twisted.web.http_headers import Headers +import opentracing +from opentracing import tags +from synapse.util.tracerutils import TracerUtil + import synapse.metrics import synapse.util.retryutils from synapse.api.errors import ( @@ -356,8 +358,9 @@ class MatrixFederationHttpClient(object): # Inject the span into the headers headers_dict = {} - opentracing.tracer.inject(scope.span, Format.HTTP_HEADERS, headers_dict) - headers_dict = {k.encode(): [v.encode()] for k, v in headers_dict.items()} + TracerUtil.inject_span_context_byte_dict( + headers_dict, scope.span, request.destination + ) headers_dict[b"User-Agent"] = [self.version_string_bytes] diff --git a/synapse/http/site.py b/synapse/http/site.py index 082ada57c6..e093ec1c0e 100644 --- a/synapse/http/site.py +++ b/synapse/http/site.py @@ -23,7 +23,7 @@ from twisted.web.server import Request, Site from synapse.http import redact_uri from synapse.http.request_metrics import RequestMetrics, requests_counter from synapse.util.logcontext import LoggingContext, PreserveLoggingContext -from synapse.util.tracerutils import extract_span_context +from synapse.util.tracerutils import TracerUtil logger = logging.getLogger(__name__) @@ -239,7 +239,7 @@ class SynapseRequest(Request): ) # Start a span - span_context = extract_span_context(self.requestHeaders) + span_context = TracerUtil.extract_span_context(self.requestHeaders) opentracing.tracer.start_active_span( "incoming-federation-request", tags={ diff --git a/synapse/util/scopecontextmanager.py b/synapse/util/scopecontextmanager.py index 91893ebe40..6e0acab42e 100644 --- a/synapse/util/scopecontextmanager.py +++ b/synapse/util/scopecontextmanager.py @@ -86,9 +86,6 @@ class LogContextScopeManager(ScopeManager): ctx.scope = scope return scope - def request_from_whitelisted_homeserver(self, request): - pass - class _LogContextScope(Scope): """ diff --git a/synapse/util/tracerutils.py b/synapse/util/tracerutils.py index 5ba4cfe38a..e2337d654f 100644 --- a/synapse/util/tracerutils.py +++ b/synapse/util/tracerutils.py @@ -13,51 +13,110 @@ # See the License for the specific language governing permissions and # limitations under the License.import opentracing +import logging + +logger = logging.getLogger(__name__) + import opentracing from opentracing.propagation import Format +import re -def extract_span_context(headers): - """ - Extracts a span context from Twisted Headers. - args: - headers (twisted.web.http_headers.Headers) - returns: - span_context (opentracing.span.SpanContext) - """ - # Twisted encodes the values as lists whereas opentracing doesn't. - # So, we take the first item in the list. - # Also, twisted uses byte arrays while opentracing expects strings. - header_dict = {k.decode(): v[0].decode() for k, v in headers.getAllRawHeaders()} - return opentracing.tracer.extract(Format.HTTP_HEADERS, header_dict) +# block everything by default -def inject_span_context(headers, span): - """ - Injects a span context into twisted headers inplace - args: - headers (twisted.web.http_headers.Headers) - span (opentracing.Span) +class TracerUtil: + _homeserver_whitelist = None - note: - The headers set by the tracer are custom to the tracer implementation which - should be unique enough that they don't interfere with any headers set by - synapse or twisted. If we're still using jaeger these headers would be those - here: - https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/constants.py - """ - carrier = {} - carrier = opentracing.tracer.inject(span, Format.HTTP_HEADERS, {}) + @staticmethod + def set_homeserver_whitelist(homeserver_whitelist): + """Sets the whitelist - for key, value in carrier: - headers.addRawHeaders(key, value) + Args: + homeserver_whitelist (iterable of strings): regex of whitelisted homeservers + """ + if homeserver_whitelist: + # Makes a single regex which accepts all passed in regexes in the list + TracerUtil._homeserver_whitelist = re.compile( + "({})".format(")|(".join(homeserver_whitelist)) + ) + logger.info("Set whitelist to {}".format(TracerUtil._homeserver_whitelist)) + @staticmethod + def whitelisted_homeserver(destination): + if TracerUtil._homeserver_whitelist: + return TracerUtil._homeserver_whitelist.match(destination) + return False -# TODO: Implement whitelisting -def request_from_whitelisted_homeserver(request): - pass + @staticmethod + def extract_span_context(headers): + """ + Extracts a span context from Twisted Headers. + args: + headers (twisted.web.http_headers.Headers) + returns: + span_context (opentracing.span.SpanContext) + """ + # Twisted encodes the values as lists whereas opentracing doesn't. + # So, we take the first item in the list. + # Also, twisted uses byte arrays while opentracing expects strings. + header_dict = {k.decode(): v[0].decode() for k, v in headers.getAllRawHeaders()} + return opentracing.tracer.extract(Format.HTTP_HEADERS, header_dict) + @staticmethod + def inject_span_context(headers, span, destination): + """ + Injects a span context into twisted headers inplace -# TODO: Implement whitelisting -def user_whitelisted(request): - pass + Args: + headers (twisted.web.http_headers.Headers) + span (opentracing.Span) + + Returns: + Inplace modification of headers + + Note: + The headers set by the tracer are custom to the tracer implementation which + should be unique enough that they don't interfere with any headers set by + synapse or twisted. If we're still using jaeger these headers would be those + here: + https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/constants.py + """ + + if not TracerUtil.whitelisted_homeserver(destination): + return + carrier = {} + opentracing.tracer.inject(span, Format.HTTP_HEADERS, carrier) + + for key, value in carrier.items(): + headers.addRawHeaders(key, value) + + @staticmethod + def inject_span_context_byte_dict(headers, span, destination): + """ + Injects a span context into a dict where the headers are encoded as byte + strings + + Args: + headers (dict) + span (opentracing.Span) + + Returns: + Inplace modification of headers + + Note: + The headers set by the tracer are custom to the tracer implementation which + should be unique enough that they don't interfere with any headers set by + synapse or twisted. If we're still using jaeger these headers would be those + here: + https://github.com/jaegertracing/jaeger-client-python/blob/master/jaeger_client/constants.py + """ + if not TracerUtil.whitelisted_homeserver(destination): + logger.info("{}".format(TracerUtil._homeserver_whitelist)) + return + + carrier = {} + opentracing.tracer.inject(span, Format.HTTP_HEADERS, carrier) + + for key, value in carrier.items(): + headers[key.encode()] = [value.encode()]