Homeserver Whitlisting

pull/5544/head
Jorik Schellekens 2019-06-25 18:16:48 +01:00
parent 088176e5b2
commit dde461814d
5 changed files with 110 additions and 47 deletions

View File

@ -18,6 +18,7 @@ import logging
from jaeger_client import Config as JaegerConfig from jaeger_client import Config as JaegerConfig
from synapse.util.scopecontextmanager import LogContextScopeManager from synapse.util.scopecontextmanager import LogContextScopeManager
from synapse.util.tracerutils import TracerUtil
from ._base import Config, ConfigError from ._base import Config, ConfigError
@ -37,7 +38,7 @@ class TracerConfig(Config):
# If no whitelists are given # If no whitelists are given
self.tracer_config.setdefault("homeserver_whitelist", []) 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") raise ConfigError("Tracer homesererver_whitelist config is malformed")
def generate_config_section(cls, **kwargs): def generate_config_section(cls, **kwargs):
@ -55,7 +56,7 @@ class TracerConfig(Config):
def init_tracing(config): def init_tracing(config):
"""Initialise the JaegerClient tracer """Set the whitelists and initialise the JaegerClient tracer
Args: Args:
config (Config) config (Config)
@ -64,6 +65,9 @@ def init_tracing(config):
""" """
if config.tracer_config.get("tracer_enabled", False): if config.tracer_config.get("tracer_enabled", False):
TracerUtil.set_homeserver_whitelist(
config.tracer_config["homeserver_whitelist"]
)
jaeger_config = JaegerConfig( jaeger_config = JaegerConfig(
config={"sampler": {"type": "const", "param": 1}, "logging": True}, config={"sampler": {"type": "const", "param": 1}, "logging": True},
service_name=config.server_name, service_name=config.server_name,

View File

@ -23,8 +23,6 @@ from six import PY3, raise_from, string_types
from six.moves import urllib from six.moves import urllib
import attr import attr
import opentracing
from opentracing import tags
import treq import treq
from canonicaljson import encode_canonical_json from canonicaljson import encode_canonical_json
from opentracing.propagation import Format 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._newclient import ResponseDone
from twisted.web.http_headers import Headers from twisted.web.http_headers import Headers
import opentracing
from opentracing import tags
from synapse.util.tracerutils import TracerUtil
import synapse.metrics import synapse.metrics
import synapse.util.retryutils import synapse.util.retryutils
from synapse.api.errors import ( from synapse.api.errors import (
@ -356,8 +358,9 @@ class MatrixFederationHttpClient(object):
# Inject the span into the headers # Inject the span into the headers
headers_dict = {} headers_dict = {}
opentracing.tracer.inject(scope.span, Format.HTTP_HEADERS, headers_dict) TracerUtil.inject_span_context_byte_dict(
headers_dict = {k.encode(): [v.encode()] for k, v in headers_dict.items()} headers_dict, scope.span, request.destination
)
headers_dict[b"User-Agent"] = [self.version_string_bytes] headers_dict[b"User-Agent"] = [self.version_string_bytes]

View File

@ -23,7 +23,7 @@ from twisted.web.server import Request, Site
from synapse.http import redact_uri from synapse.http import redact_uri
from synapse.http.request_metrics import RequestMetrics, requests_counter from synapse.http.request_metrics import RequestMetrics, requests_counter
from synapse.util.logcontext import LoggingContext, PreserveLoggingContext 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__) logger = logging.getLogger(__name__)
@ -239,7 +239,7 @@ class SynapseRequest(Request):
) )
# Start a span # Start a span
span_context = extract_span_context(self.requestHeaders) span_context = TracerUtil.extract_span_context(self.requestHeaders)
opentracing.tracer.start_active_span( opentracing.tracer.start_active_span(
"incoming-federation-request", "incoming-federation-request",
tags={ tags={

View File

@ -86,9 +86,6 @@ class LogContextScopeManager(ScopeManager):
ctx.scope = scope ctx.scope = scope
return scope return scope
def request_from_whitelisted_homeserver(self, request):
pass
class _LogContextScope(Scope): class _LogContextScope(Scope):
""" """

View File

@ -13,51 +13,110 @@
# See the License for the specific language governing permissions and # See the License for the specific language governing permissions and
# limitations under the License.import opentracing # limitations under the License.import opentracing
import logging
logger = logging.getLogger(__name__)
import opentracing import opentracing
from opentracing.propagation import Format from opentracing.propagation import Format
import re
def extract_span_context(headers): # block everything by default
"""
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)
def inject_span_context(headers, span): class TracerUtil:
""" _homeserver_whitelist = None
Injects a span context into twisted headers inplace
args:
headers (twisted.web.http_headers.Headers)
span (opentracing.Span)
note: @staticmethod
The headers set by the tracer are custom to the tracer implementation which def set_homeserver_whitelist(homeserver_whitelist):
should be unique enough that they don't interfere with any headers set by """Sets the whitelist
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, {})
for key, value in carrier: Args:
headers.addRawHeaders(key, value) 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 @staticmethod
def request_from_whitelisted_homeserver(request): def extract_span_context(headers):
pass """
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 Args:
def user_whitelisted(request): headers (twisted.web.http_headers.Headers)
pass 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()]