| 1 | # -*- test-case-name: twisted.conch.test.test_tap -*- |
|---|
| 2 | # Copyright (c) Twisted Matrix Laboratories. |
|---|
| 3 | # See LICENSE for details. |
|---|
| 4 | |
|---|
| 5 | """ |
|---|
| 6 | Support module for making SSH servers with twistd. |
|---|
| 7 | """ |
|---|
| 8 | |
|---|
| 9 | from twisted.conch import unix |
|---|
| 10 | from twisted.conch import checkers as conch_checkers |
|---|
| 11 | from twisted.conch.openssh_compat import factory |
|---|
| 12 | from twisted.cred import portal, checkers, strcred |
|---|
| 13 | from twisted.python import usage |
|---|
| 14 | from twisted.application import strports |
|---|
| 15 | try: |
|---|
| 16 | from twisted.cred import pamauth |
|---|
| 17 | except ImportError: |
|---|
| 18 | pamauth = None |
|---|
| 19 | |
|---|
| 20 | |
|---|
| 21 | |
|---|
| 22 | class Options(usage.Options, strcred.AuthOptionMixin): |
|---|
| 23 | synopsis = "[-i <interface>] [-p <port>] [-d <dir>] " |
|---|
| 24 | longdesc = ("Makes a Conch SSH server. If no authentication methods are " |
|---|
| 25 | "specified, the default authentication methods are UNIX passwords, " |
|---|
| 26 | "SSH public keys, and PAM if it is available. If --auth options are " |
|---|
| 27 | "passed, only the measures specified will be used.") |
|---|
| 28 | optParameters = [ |
|---|
| 29 | ["interface", "i", "", "local interface to which we listen"], |
|---|
| 30 | ["port", "p", "tcp:22", "Port on which to listen"], |
|---|
| 31 | ["data", "d", "/etc", "directory to look for host keys in"], |
|---|
| 32 | ["moduli", "", None, "directory to look for moduli in " |
|---|
| 33 | "(if different from --data)"] |
|---|
| 34 | ] |
|---|
| 35 | compData = usage.Completions( |
|---|
| 36 | optActions={"data": usage.CompleteDirs(descr="data directory"), |
|---|
| 37 | "moduli": usage.CompleteDirs(descr="moduli directory"), |
|---|
| 38 | "interface": usage.CompleteNetInterfaces()} |
|---|
| 39 | ) |
|---|
| 40 | |
|---|
| 41 | |
|---|
| 42 | def __init__(self, *a, **kw): |
|---|
| 43 | usage.Options.__init__(self, *a, **kw) |
|---|
| 44 | |
|---|
| 45 | # call the default addCheckers (for backwards compatibility) that will |
|---|
| 46 | # be used if no --auth option is provided - note that conch's |
|---|
| 47 | # UNIXPasswordDatabase is used, instead of twisted.plugins.cred_unix's |
|---|
| 48 | # checker |
|---|
| 49 | super(Options, self).addChecker(conch_checkers.UNIXPasswordDatabase()) |
|---|
| 50 | super(Options, self).addChecker(conch_checkers.SSHPublicKeyDatabase()) |
|---|
| 51 | if pamauth is not None: |
|---|
| 52 | super(Options, self).addChecker( |
|---|
| 53 | checkers.PluggableAuthenticationModulesChecker()) |
|---|
| 54 | |
|---|
| 55 | |
|---|
| 56 | def addChecker(self, checker): |
|---|
| 57 | """ |
|---|
| 58 | If addChecker is called, clear out the default checkers first |
|---|
| 59 | """ |
|---|
| 60 | self['credCheckers'] = [] |
|---|
| 61 | self['credInterfaces'] = {} |
|---|
| 62 | super(Options, self).addChecker(checker) |
|---|
| 63 | |
|---|
| 64 | |
|---|
| 65 | |
|---|
| 66 | def makeService(config): |
|---|
| 67 | """ |
|---|
| 68 | Construct a service for operating a SSH server. |
|---|
| 69 | |
|---|
| 70 | @param config: An L{Options} instance specifying server options, including |
|---|
| 71 | where server keys are stored and what authentication methods to use. |
|---|
| 72 | |
|---|
| 73 | @return: An L{IService} provider which contains the requested SSH server. |
|---|
| 74 | """ |
|---|
| 75 | |
|---|
| 76 | t = factory.OpenSSHFactory() |
|---|
| 77 | |
|---|
| 78 | r = unix.UnixSSHRealm() |
|---|
| 79 | t.portal = portal.Portal(r, config.get('credCheckers', [])) |
|---|
| 80 | t.dataRoot = config['data'] |
|---|
| 81 | t.moduliRoot = config['moduli'] or config['data'] |
|---|
| 82 | |
|---|
| 83 | port = config['port'] |
|---|
| 84 | if config['interface']: |
|---|
| 85 | # Add warning here |
|---|
| 86 | port += ':interface=' + config['interface'] |
|---|
| 87 | return strports.service(port, t) |
|---|