Homeserver Whitlisting
parent
088176e5b2
commit
dde461814d
|
@ -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,
|
||||||
|
|
|
@ -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]
|
||||||
|
|
||||||
|
|
|
@ -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={
|
||||||
|
|
|
@ -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):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -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()]
|
||||||
|
|
Loading…
Reference in New Issue