You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

manhole_tap.py 5.2KB

5 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165
  1. # Copyright (c) Twisted Matrix Laboratories.
  2. # See LICENSE for details.
  3. """
  4. TAP plugin for creating telnet- and ssh-accessible manhole servers.
  5. @author: Jp Calderone
  6. """
  7. from zope.interface import implementer
  8. from twisted.internet import protocol
  9. from twisted.application import service, strports
  10. from twisted.cred import portal, checkers
  11. from twisted.python import usage, filepath
  12. from twisted.conch import manhole, manhole_ssh, telnet
  13. from twisted.conch.insults import insults
  14. from twisted.conch.ssh import keys
  15. class makeTelnetProtocol:
  16. def __init__(self, portal):
  17. self.portal = portal
  18. def __call__(self):
  19. auth = telnet.AuthenticatingTelnetProtocol
  20. args = (self.portal,)
  21. return telnet.TelnetTransport(auth, *args)
  22. class chainedProtocolFactory:
  23. def __init__(self, namespace):
  24. self.namespace = namespace
  25. def __call__(self):
  26. return insults.ServerProtocol(manhole.ColoredManhole, self.namespace)
  27. @implementer(portal.IRealm)
  28. class _StupidRealm:
  29. def __init__(self, proto, *a, **kw):
  30. self.protocolFactory = proto
  31. self.protocolArgs = a
  32. self.protocolKwArgs = kw
  33. def requestAvatar(self, avatarId, *interfaces):
  34. if telnet.ITelnetProtocol in interfaces:
  35. return (telnet.ITelnetProtocol,
  36. self.protocolFactory(*self.protocolArgs,
  37. **self.protocolKwArgs),
  38. lambda: None)
  39. raise NotImplementedError()
  40. class Options(usage.Options):
  41. optParameters = [
  42. ["telnetPort", "t", None,
  43. ("strports description of the address on which to listen for telnet "
  44. "connections")],
  45. ["sshPort", "s", None,
  46. ("strports description of the address on which to listen for ssh "
  47. "connections")],
  48. ["passwd", "p", "/etc/passwd",
  49. "name of a passwd(5)-format username/password file"],
  50. ["sshKeyDir", None, "<USER DATA DIR>",
  51. "Directory where the autogenerated SSH key is kept."],
  52. ["sshKeyName", None, "server.key",
  53. "Filename of the autogenerated SSH key."],
  54. ["sshKeySize", None, 4096,
  55. "Size of the automatically generated SSH key."],
  56. ]
  57. def __init__(self):
  58. usage.Options.__init__(self)
  59. self['namespace'] = None
  60. def postOptions(self):
  61. if self['telnetPort'] is None and self['sshPort'] is None:
  62. raise usage.UsageError(
  63. "At least one of --telnetPort and --sshPort must be specified")
  64. def makeService(options):
  65. """
  66. Create a manhole server service.
  67. @type options: L{dict}
  68. @param options: A mapping describing the configuration of
  69. the desired service. Recognized key/value pairs are::
  70. "telnetPort": strports description of the address on which
  71. to listen for telnet connections. If None,
  72. no telnet service will be started.
  73. "sshPort": strports description of the address on which to
  74. listen for ssh connections. If None, no ssh
  75. service will be started.
  76. "namespace": dictionary containing desired initial locals
  77. for manhole connections. If None, an empty
  78. dictionary will be used.
  79. "passwd": Name of a passwd(5)-format username/password file.
  80. "sshKeyDir": The folder that the SSH server key will be kept in.
  81. "sshKeyName": The filename of the key.
  82. "sshKeySize": The size of the key, in bits. Default is 4096.
  83. @rtype: L{twisted.application.service.IService}
  84. @return: A manhole service.
  85. """
  86. svc = service.MultiService()
  87. namespace = options['namespace']
  88. if namespace is None:
  89. namespace = {}
  90. checker = checkers.FilePasswordDB(options['passwd'])
  91. if options['telnetPort']:
  92. telnetRealm = _StupidRealm(telnet.TelnetBootstrapProtocol,
  93. insults.ServerProtocol,
  94. manhole.ColoredManhole,
  95. namespace)
  96. telnetPortal = portal.Portal(telnetRealm, [checker])
  97. telnetFactory = protocol.ServerFactory()
  98. telnetFactory.protocol = makeTelnetProtocol(telnetPortal)
  99. telnetService = strports.service(options['telnetPort'],
  100. telnetFactory)
  101. telnetService.setServiceParent(svc)
  102. if options['sshPort']:
  103. sshRealm = manhole_ssh.TerminalRealm()
  104. sshRealm.chainedProtocolFactory = chainedProtocolFactory(namespace)
  105. sshPortal = portal.Portal(sshRealm, [checker])
  106. sshFactory = manhole_ssh.ConchFactory(sshPortal)
  107. if options['sshKeyDir'] != "<USER DATA DIR>":
  108. keyDir = options['sshKeyDir']
  109. else:
  110. from twisted.python._appdirs import getDataDirectory
  111. keyDir = getDataDirectory()
  112. keyLocation = filepath.FilePath(keyDir).child(options['sshKeyName'])
  113. sshKey = keys._getPersistentRSAKey(keyLocation,
  114. int(options['sshKeySize']))
  115. sshFactory.publicKeys[b"ssh-rsa"] = sshKey
  116. sshFactory.privateKeys[b"ssh-rsa"] = sshKey
  117. sshService = strports.service(options['sshPort'], sshFactory)
  118. sshService.setServiceParent(svc)
  119. return svc