Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.
 
 
 
 
 
 

186 строки
8.1 KiB

  1. # Copyright 2016 OpenMarket Ltd
  2. # Copyright 2019 New Vector Ltd.
  3. #
  4. # Licensed under the Apache License, Version 2.0 (the "License");
  5. # you may not use this file except in compliance with the License.
  6. # You may obtain a copy of the License at
  7. #
  8. # http://www.apache.org/licenses/LICENSE-2.0
  9. #
  10. # Unless required by applicable law or agreed to in writing, software
  11. # distributed under the License is distributed on an "AS IS" BASIS,
  12. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. # See the License for the specific language governing permissions and
  14. # limitations under the License.
  15. import inspect
  16. import sys
  17. import traceback
  18. from typing import Any, Dict, Optional
  19. from twisted.conch import manhole_ssh
  20. from twisted.conch.insults import insults
  21. from twisted.conch.manhole import ColoredManhole, ManholeInterpreter
  22. from twisted.conch.ssh.keys import Key
  23. from twisted.cred import checkers, portal
  24. from twisted.internet import defer
  25. from twisted.internet.protocol import ServerFactory
  26. from synapse.config.server import ManholeConfig
  27. PUBLIC_KEY = (
  28. "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHhGATaW4KhE23+7nrH4jFx3yLq9OjaEs5"
  29. "XALqeK+7385NlLja3DE/DO9mGhnd9+bAy39EKT3sTV6+WXQ4yD0TvEEyUEMtjWkSEm6U32+C"
  30. "DaS3TW/vPBUMeJQwq+Ydcif1UlnpXrDDTamD0AU9VaEvHq+3HAkipqn0TGpKON6aqk4vauDx"
  31. "oXSsV5TXBVrxP/y7HpMOpU4GUWsaaacBTKKNnUaQB4UflvydaPJUuwdaCUJGTMjbhWrjVfK+"
  32. "jslseSPxU6XvrkZMyCr4znxvuDxjMk1RGIdO7v+rbBMLEgqtSMNqJbYeVCnj2CFgc3fcTcld"
  33. "X2uOJDrJb/WRlHulthCh"
  34. )
  35. PRIVATE_KEY = """-----BEGIN RSA PRIVATE KEY-----
  36. MIIEpQIBAAKCAQEAx4RgE2luCoRNt/u56x+Ixcd8i6vTo2hLOVwC6nivu9/OTZS4
  37. 2twxPwzvZhoZ3ffmwMt/RCk97E1evll0OMg9E7xBMlBDLY1pEhJulN9vgg2kt01v
  38. 7zwVDHiUMKvmHXIn9VJZ6V6ww02pg9AFPVWhLx6vtxwJIqap9ExqSjjemqpOL2rg
  39. 8aF0rFeU1wVa8T/8ux6TDqVOBlFrGmmnAUyijZ1GkAeFH5b8nWjyVLsHWglCRkzI
  40. 24Vq41Xyvo7JbHkj8VOl765GTMgq+M58b7g8YzJNURiHTu7/q2wTCxIKrUjDaiW2
  41. HlQp49ghYHN33E3JXV9rjiQ6yW/1kZR7pbYQoQIDAQABAoIBAQC8KJ0q8Wzzwh5B
  42. esa1dQHZ8+4DEsL/Amae66VcVwD0X3cCN1W2IZ7X5W0Ij2kBqr8V51RYhcR+S+Ek
  43. BtzSiBUBvbKGrqcMGKaUgomDIMzai99hd0gvCCyZnEW1OQhFkNkaRNXCfqiZJ27M
  44. fqvSUiU2eOwh9fCvmxoA6Of8o3FbzcJ+1GMcobWRllDtLmj6lgVbDzuA+0jC5daB
  45. 9Tj1pBzu3wn3ufxiS+gBnJ+7NcXH3E73lqCcPa2ufbZ1haxfiGCnRIhFXuQDgxFX
  46. vKdEfDgtvas6r1ahGbc+b/q8E8fZT7cABuIU4yfOORK+MhpyWbvoyyzuVGKj3PKt
  47. KSPJu5CZAoGBAOkoJfAVyYteqKcmGTanGqQnAY43CaYf6GdSPX/jg+JmKZg0zqMC
  48. jWZUtPb93i+jnOInbrnuHOiHAxI8wmhEPed28H2lC/LU8PzlqFkZXKFZ4vLOhhRB
  49. /HeHCFIDosPFlohWi3b+GAjD7sXgnIuGmnXWe2ea/TS3yersifDEoKKjAoGBANsQ
  50. gJX2cJv1c3jhdgcs8vAt5zIOKcCLTOr/QPmVf/kxjNgndswcKHwsxE/voTO9q+TF
  51. v/6yCSTxAdjuKz1oIYWgi/dZo82bBKWxNRpgrGviU3/zwxiHlyIXUhzQu78q3VS/
  52. 7S1XVbc7qMV++XkYKHPVD+nVG/gGzFxumX7MLXfrAoGBAJit9cn2OnjNj9uFE1W6
  53. r7N254ndeLAUjPe73xH0RtTm2a4WRopwjW/JYIetTuYbWgyujc+robqTTuuOZjAp
  54. H/CG7o0Ym251CypQqaFO/l2aowclPp/dZhpPjp9GSjuxFBZLtiBB3DNBOwbRQzIK
  55. /vLTdRQvZkgzYkI4i0vjNt3JAoGBANP8HSKBLymMlShlrSx2b8TB9tc2Y2riohVJ
  56. 2ttqs0M2kt/dGJWdrgOz4mikL+983Olt/0P9juHDoxEEMK2kpcPEv40lnmBpYU7h
  57. s8yJvnBLvJe2EJYdJ8AipyAhUX1FgpbvfxmASP8eaUxsegeXvBWTGWojAoS6N2o+
  58. 0KSl+l3vAoGAFqm0gO9f/Q1Se60YQd4l2PZeMnJFv0slpgHHUwegmd6wJhOD7zJ1
  59. CkZcXwiv7Nog7AI9qKJEUXLjoqL+vJskBzSOqU3tcd670YQMi1aXSXJqYE202K7o
  60. EddTrx3TNpr1D5m/f+6mnXWrc8u9y1+GNx9yz889xMjIBTBI9KqaaOs=
  61. -----END RSA PRIVATE KEY-----"""
  62. def manhole(settings: ManholeConfig, globals: Dict[str, Any]) -> ServerFactory:
  63. """Starts a ssh listener with password authentication using
  64. the given username and password. Clients connecting to the ssh
  65. listener will find themselves in a colored python shell with
  66. the supplied globals.
  67. Args:
  68. username: The username ssh clients should auth with.
  69. password: The password ssh clients should auth with.
  70. globals: The variables to expose in the shell.
  71. Returns:
  72. A factory to pass to ``listenTCP``
  73. """
  74. username = settings.username
  75. password = settings.password.encode("ascii")
  76. priv_key = settings.priv_key
  77. if priv_key is None:
  78. priv_key = Key.fromString(PRIVATE_KEY)
  79. pub_key = settings.pub_key
  80. if pub_key is None:
  81. pub_key = Key.fromString(PUBLIC_KEY)
  82. checker = checkers.InMemoryUsernamePasswordDatabaseDontUse(**{username: password})
  83. rlm = manhole_ssh.TerminalRealm()
  84. # mypy ignored here because:
  85. # - can't deduce types of lambdas
  86. # - variable is Type[ServerProtocol], expr is Callable[[], ServerProtocol]
  87. rlm.chainedProtocolFactory = lambda: insults.ServerProtocol( # type: ignore[misc,assignment]
  88. SynapseManhole, dict(globals, __name__="__console__")
  89. )
  90. # type-ignore: This is an error in Twisted's annotations. See
  91. # https://github.com/twisted/twisted/issues/11812 and /11813 .
  92. factory = manhole_ssh.ConchFactory(portal.Portal(rlm, [checker])) # type: ignore[arg-type]
  93. # conch has the wrong type on these dicts (says bytes to bytes,
  94. # should be bytes to Keys judging by how it's used).
  95. factory.privateKeys[b"ssh-rsa"] = priv_key # type: ignore[assignment]
  96. factory.publicKeys[b"ssh-rsa"] = pub_key # type: ignore[assignment]
  97. # ConchFactory is a Factory, not a ServerFactory, but they are identical.
  98. return factory # type: ignore[return-value]
  99. class SynapseManhole(ColoredManhole):
  100. """Overrides connectionMade to create our own ManholeInterpreter"""
  101. def connectionMade(self) -> None:
  102. super().connectionMade()
  103. # replace the manhole interpreter with our own impl
  104. self.interpreter = SynapseManholeInterpreter(self, self.namespace)
  105. # this would also be a good place to add more keyHandlers.
  106. class SynapseManholeInterpreter(ManholeInterpreter):
  107. def showsyntaxerror(self, filename: Optional[str] = None) -> None:
  108. """Display the syntax error that just occurred.
  109. Overrides the base implementation, ignoring sys.excepthook. We always want
  110. any syntax errors to be sent to the terminal, rather than sentry.
  111. """
  112. type, value, tb = sys.exc_info()
  113. assert value is not None
  114. sys.last_type = type
  115. sys.last_value = value
  116. sys.last_traceback = tb
  117. if filename and type is SyntaxError:
  118. # Work hard to stuff the correct filename in the exception
  119. try:
  120. msg, (dummy_filename, lineno, offset, line) = value.args
  121. except ValueError:
  122. # Not the format we expect; leave it alone
  123. pass
  124. else:
  125. # Stuff in the right filename
  126. value = SyntaxError(msg, (filename, lineno, offset, line))
  127. sys.last_value = value
  128. lines = traceback.format_exception_only(type, value)
  129. self.write("".join(lines))
  130. def showtraceback(self) -> None:
  131. """Display the exception that just occurred.
  132. Overrides the base implementation, ignoring sys.excepthook. We always want
  133. any syntax errors to be sent to the terminal, rather than sentry.
  134. """
  135. sys.last_type, sys.last_value, last_tb = ei = sys.exc_info()
  136. sys.last_traceback = last_tb
  137. assert last_tb is not None
  138. try:
  139. # We remove the first stack item because it is our own code.
  140. lines = traceback.format_exception(ei[0], ei[1], last_tb.tb_next)
  141. self.write("".join(lines))
  142. finally:
  143. # On the line below, last_tb and ei appear to be dead.
  144. # It's unclear whether there is a reason behind this line.
  145. # It conceivably could be because an exception raised in this block
  146. # will keep the local frame (containing these local variables) around.
  147. # This was adapted taken from CPython's Lib/code.py; see here:
  148. # https://github.com/python/cpython/blob/4dc4300c686f543d504ab6fa9fe600eaf11bb695/Lib/code.py#L131-L150
  149. last_tb = ei = None # type: ignore
  150. def displayhook(self, obj: Any) -> None:
  151. """
  152. We override the displayhook so that we automatically convert coroutines
  153. into Deferreds. (Our superclass' displayhook will take care of the rest,
  154. by displaying the Deferred if it's ready, or registering a callback
  155. if it's not).
  156. """
  157. if inspect.iscoroutine(obj):
  158. super().displayhook(defer.ensureDeferred(obj))
  159. else:
  160. super().displayhook(obj)