12221.16.16
by Andrew Bennetts
Reorder some imports and update copyright years as requested by Aaron's review. |
1 |
# Copyright 2009-2011 Canonical Ltd. This software is licensed under the
|
8687.15.17
by Karl Fogel
Add the copyright header block to the rest of the files under lib/lp/. |
2 |
# GNU Affero General Public License version 3 (see the file LICENSE).
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
3 |
|
10693.5.5
by Jonathan Lange
Move auth logic to services |
4 |
"""Twisted `service.Service` class for the Launchpad SSH server.
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
5 |
|
6 |
An `SSHService` object can be used to launch the SSH server.
|
|
3858.1.5
by jml at canonical
Move SFTP server in-process |
7 |
"""
|
8 |
||
9 |
__metaclass__ = type |
|
7483.1.18
by Jonathan Lange
Tweak __all__ defns |
10 |
__all__ = [ |
11 |
'SSHService', |
|
12 |
]
|
|
3858.1.5
by jml at canonical
Move SFTP server in-process |
13 |
|
14 |
||
10693.3.13
by Jonathan Lange
Oops, missed the most important invocation |
15 |
import logging |
3858.1.5
by jml at canonical
Move SFTP server in-process |
16 |
import os |
17 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
18 |
from twisted.application import ( |
19 |
service, |
|
20 |
strports, |
|
21 |
)
|
|
7483.1.17
by Jonathan Lange
Import classes directly. This makes the contract between modules clearer. |
22 |
from twisted.conch.ssh.factory import SSHFactory |
6789.11.2
by Michael Hudson
rewrite TestPublicKeyFromLaunchpadChecker to use a fake authentication |
23 |
from twisted.conch.ssh.keys import Key |
7033.2.6
by Michael Hudson
merge trunk, but... |
24 |
from twisted.conch.ssh.transport import SSHServerTransport |
7033.2.9
by Michael Hudson
lint |
25 |
from twisted.internet import defer |
7483.2.41
by Jonathan Lange
Use the zope.event system for handling the logging events. |
26 |
from zope.event import notify |
27 |
||
11403.1.4
by Henning Eggers
Reformatted imports using format-imports script r32. |
28 |
from lp.services.sshserver import ( |
29 |
accesslog, |
|
30 |
events, |
|
31 |
)
|
|
10693.5.5
by Jonathan Lange
Move auth logic to services |
32 |
from lp.services.sshserver.auth import SSHUserAuthServer |
10548.1.1
by Jonathan Lange
Move twistedsupport to lp.services |
33 |
from lp.services.twistedsupport import gatherResults |
10693.3.4
by Jonathan Lange
No point in parametrizing oops reporting |
34 |
from lp.services.twistedsupport.loggingsupport import set_up_oops_reporting |
7483.1.17
by Jonathan Lange
Import classes directly. This makes the contract between modules clearer. |
35 |
|
36 |
||
7033.2.6
by Michael Hudson
merge trunk, but... |
37 |
class KeepAliveSettingSSHServerTransport(SSHServerTransport): |
38 |
||
39 |
def connectionMade(self): |
|
7033.2.8
by Michael Hudson
this works |
40 |
SSHServerTransport.connectionMade(self) |
7033.2.6
by Michael Hudson
merge trunk, but... |
41 |
self.transport.setTcpKeepAlive(True) |
42 |
||
43 |
||
7483.1.17
by Jonathan Lange
Import classes directly. This makes the contract between modules clearer. |
44 |
class Factory(SSHFactory): |
10693.3.21
by Jonathan Lange
More references to codehosting that are not necessary |
45 |
"""SSH factory that uses Launchpad's custom authentication.
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
46 |
|
47 |
This class tells the SSH service to use our custom authentication service
|
|
7483.2.38
by Jonathan Lange
Merge in changes from dependent branch, resolving conflicts. |
48 |
and configures the host keys for the SSH server. It also logs connection
|
49 |
to and disconnection from the SSH server.
|
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
50 |
"""
|
51 |
||
10363.1.4
by Michael Hudson
remove another workaround for a fixed twisted bug |
52 |
protocol = KeepAliveSettingSSHServerTransport |
53 |
||
10693.2.6
by Jonathan Lange
Parametrize banner up from the factory |
54 |
def __init__(self, portal, private_key, public_key, banner=None): |
10693.2.4
by Jonathan Lange
Parametrize the keys in the factory |
55 |
"""Construct an SSH factory.
|
56 |
||
57 |
:param portal: The portal used to turn credentials into users.
|
|
10693.2.17
by Jonathan Lange
Docstring improvements as per code review |
58 |
:param private_key: The private key of the server, must be an RSA
|
59 |
key, given as a `twisted.conch.ssh.keys.Key` object.
|
|
60 |
:param public_key: The public key of the server, must be an RSA
|
|
61 |
key, given as a `twisted.conch.ssh.keys.Key` object.
|
|
10693.2.6
by Jonathan Lange
Parametrize banner up from the factory |
62 |
:param banner: The text to display when users successfully log in.
|
10693.2.4
by Jonathan Lange
Parametrize the keys in the factory |
63 |
"""
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
64 |
# Although 'portal' isn't part of the defined interface for
|
65 |
# `SSHFactory`, defining it here is how the `SSHUserAuthServer` gets
|
|
66 |
# at it. (Look for the beautiful line "self.portal =
|
|
67 |
# self.transport.factory.portal").
|
|
68 |
self.portal = portal |
|
10693.2.6
by Jonathan Lange
Parametrize banner up from the factory |
69 |
self.services['ssh-userauth'] = self._makeAuthServer |
10693.2.4
by Jonathan Lange
Parametrize the keys in the factory |
70 |
self._private_key = private_key |
71 |
self._public_key = public_key |
|
10693.2.6
by Jonathan Lange
Parametrize banner up from the factory |
72 |
self._banner = banner |
10693.2.5
by Jonathan Lange
Parametrize the banner in the UserAuth agent |
73 |
|
74 |
def _makeAuthServer(self, *args, **kwargs): |
|
10693.2.6
by Jonathan Lange
Parametrize banner up from the factory |
75 |
kwargs['banner'] = self._banner |
10693.2.5
by Jonathan Lange
Parametrize the banner in the UserAuth agent |
76 |
return SSHUserAuthServer(*args, **kwargs) |
7483.1.16
by Jonathan Lange
Move the Factory object to the service module. |
77 |
|
7483.2.34
by Jonathan Lange
Log user connection, and change the 'session id' so that it can be tracked |
78 |
def buildProtocol(self, address): |
7483.2.38
by Jonathan Lange
Merge in changes from dependent branch, resolving conflicts. |
79 |
"""Build an SSH protocol instance, logging the event.
|
80 |
||
81 |
The protocol object we return is slightly modified so that we can hook
|
|
82 |
into the 'connectionLost' event and log the disconnection.
|
|
83 |
"""
|
|
10363.1.4
by Michael Hudson
remove another workaround for a fixed twisted bug |
84 |
transport = SSHFactory.buildProtocol(self, address) |
7483.2.35
by Jonathan Lange
Log actual TCP disconnect. |
85 |
transport._realConnectionLost = transport.connectionLost |
86 |
transport.connectionLost = ( |
|
87 |
lambda reason: self.connectionLost(transport, reason)) |
|
10693.3.1
by Jonathan Lange
Split the events classes into their own module. |
88 |
notify(events.UserConnected(transport, address)) |
7483.2.34
by Jonathan Lange
Log user connection, and change the 'session id' so that it can be tracked |
89 |
return transport |
90 |
||
7483.2.35
by Jonathan Lange
Log actual TCP disconnect. |
91 |
def connectionLost(self, transport, reason): |
7483.2.38
by Jonathan Lange
Merge in changes from dependent branch, resolving conflicts. |
92 |
"""Call 'connectionLost' on 'transport', logging the event."""
|
7483.2.35
by Jonathan Lange
Log actual TCP disconnect. |
93 |
try: |
94 |
return transport._realConnectionLost(reason) |
|
95 |
finally: |
|
7483.2.50
by Jonathan Lange
Generate an event when authentication fails. |
96 |
# Conch's userauth module sets 'avatar' on the transport if the
|
97 |
# authentication succeeded. Thus, if it's not there,
|
|
98 |
# authentication failed. We can't generate this event from the
|
|
99 |
# authentication layer since:
|
|
100 |
#
|
|
101 |
# a) almost every SSH login has at least one failure to
|
|
102 |
# authenticate due to multiple keys on the client-side.
|
|
103 |
#
|
|
104 |
# b) the server doesn't normally generate a "go away" event.
|
|
105 |
# Rather, the client simply stops trying.
|
|
106 |
if getattr(transport, 'avatar', None) is None: |
|
10693.3.1
by Jonathan Lange
Split the events classes into their own module. |
107 |
notify(events.AuthenticationFailed(transport)) |
108 |
notify(events.UserDisconnected(transport)) |
|
7483.2.35
by Jonathan Lange
Log actual TCP disconnect. |
109 |
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
110 |
def getPublicKeys(self): |
111 |
"""Return the server's configured public key.
|
|
112 |
||
113 |
See `SSHFactory.getPublicKeys`.
|
|
114 |
"""
|
|
10693.2.4
by Jonathan Lange
Parametrize the keys in the factory |
115 |
return {'ssh-rsa': self._public_key} |
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
116 |
|
117 |
def getPrivateKeys(self): |
|
118 |
"""Return the server's configured private key.
|
|
119 |
||
120 |
See `SSHFactory.getPrivateKeys`.
|
|
121 |
"""
|
|
10693.2.4
by Jonathan Lange
Parametrize the keys in the factory |
122 |
return {'ssh-rsa': self._private_key} |
7483.2.32
by Jonathan Lange
Log server start and stop to access log. |
123 |
|
7483.1.16
by Jonathan Lange
Move the Factory object to the service module. |
124 |
|
4292.1.31
by Jonathan Lange
Rename SFTPService to SSHService |
125 |
class SSHService(service.Service): |
10693.3.21
by Jonathan Lange
More references to codehosting that are not necessary |
126 |
"""A Twisted service for the SSH server."""
|
3858.1.10
by jml at canonical
Turn the 'setup' code in daemons/ into Services. |
127 |
|
10693.2.11
by Jonathan Lange
Parametrize a bunch of other things |
128 |
def __init__(self, portal, private_key_path, public_key_path, |
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
129 |
oops_configuration, main_log, access_log, |
12221.16.1
by Andrew Bennetts
Initial hackery towards #702024. |
130 |
access_log_path, strport='tcp:22', factory_decorator=None, |
12221.16.7
by Andrew Bennetts
Remove obsolete idle_timeout param. |
131 |
banner=None): |
10693.2.9
by Jonathan Lange
Push the key path parametrization out to the SSH service |
132 |
"""Construct an SSH service.
|
133 |
||
10918.2.1
by Steve Kowalik
Merge in changes for plumbling for poppy-sftp |
134 |
:param portal: The `twisted.cred.portal.Portal` that turns
|
135 |
authentication requests into views on the system.
|
|
10693.2.9
by Jonathan Lange
Push the key path parametrization out to the SSH service |
136 |
:param private_key_path: The path to the SSH server's private key.
|
137 |
:param public_key_path: The path to the SSH server's public key.
|
|
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
138 |
:param oops_configuration: The section of the configuration file with
|
139 |
the OOPS config details for this server.
|
|
10918.2.1
by Steve Kowalik
Merge in changes for plumbling for poppy-sftp |
140 |
:param main_log: The name of the logger to log most of the server
|
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
141 |
stuff to.
|
10918.2.1
by Steve Kowalik
Merge in changes for plumbling for poppy-sftp |
142 |
:param access_log: The name of the logger object to log the server
|
143 |
access details to.
|
|
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
144 |
:param access_log_path: The path to the access log file.
|
10693.2.17
by Jonathan Lange
Docstring improvements as per code review |
145 |
:param strport: The port to run the server on, expressed in Twisted's
|
10693.2.11
by Jonathan Lange
Parametrize a bunch of other things |
146 |
"strports" mini-language. Defaults to 'tcp:22'.
|
12221.16.15
by Andrew Bennetts
Docstrings and cosmetic code changes requested by Aaron's review. |
147 |
:param factory_decorator: An optional callable that can decorate the
|
148 |
server factory (e.g. with a
|
|
149 |
`twisted.protocols.policies.TimeoutFactory`). It takes one
|
|
150 |
argument, a factory, and must return a factory.
|
|
10693.2.11
by Jonathan Lange
Parametrize a bunch of other things |
151 |
:param banner: An announcement printed to users when they connect.
|
152 |
By default, announce nothing.
|
|
10693.2.9
by Jonathan Lange
Push the key path parametrization out to the SSH service |
153 |
"""
|
12221.16.1
by Andrew Bennetts
Initial hackery towards #702024. |
154 |
ssh_factory = Factory( |
155 |
portal, |
|
156 |
private_key=Key.fromFile(private_key_path), |
|
157 |
public_key=Key.fromFile(public_key_path), |
|
158 |
banner=banner) |
|
159 |
if factory_decorator is not None: |
|
160 |
ssh_factory = factory_decorator(ssh_factory) |
|
10693.2.17
by Jonathan Lange
Docstring improvements as per code review |
161 |
self.service = strports.service(strport, ssh_factory) |
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
162 |
self._oops_configuration = oops_configuration |
163 |
self._main_log = main_log |
|
164 |
self._access_log = access_log |
|
165 |
self._access_log_path = access_log_path |
|
3858.1.10
by jml at canonical
Turn the 'setup' code in daemons/ into Services. |
166 |
|
167 |
def startService(self): |
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
168 |
"""Start the SSH service."""
|
10693.3.13
by Jonathan Lange
Oops, missed the most important invocation |
169 |
manager = accesslog.LoggingManager( |
10693.3.17
by Jonathan Lange
Push all of the config out to the service |
170 |
logging.getLogger(self._main_log), |
171 |
logging.getLogger(self._access_log_path), |
|
172 |
self._access_log_path) |
|
10693.3.10
by Jonathan Lange
Parametrize the loggers access path |
173 |
manager.setUp() |
10693.3.1
by Jonathan Lange
Split the events classes into their own module. |
174 |
notify(events.ServerStarting()) |
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
175 |
# By default, only the owner of files should be able to write to them.
|
176 |
# Perhaps in the future this line will be deleted and the umask
|
|
177 |
# managed by the startup script.
|
|
178 |
os.umask(0022) |
|
3858.1.10
by jml at canonical
Turn the 'setup' code in daemons/ into Services. |
179 |
service.Service.startService(self) |
180 |
self.service.startService() |
|
181 |
||
182 |
def stopService(self): |
|
7483.1.28
by Jonathan Lange
Significantly refactor the service module to make it documentable. |
183 |
"""Stop the SSH service."""
|
7483.2.62
by Jonathan Lange
A raft of review comments. |
184 |
deferred = gatherResults([ |
185 |
defer.maybeDeferred(service.Service.stopService, self), |
|
186 |
defer.maybeDeferred(self.service.stopService)]) |
|
14198.1.1
by Jeroen Vermeulen
Lint. |
187 |
|
7483.2.48
by Jonathan Lange
Handle return values of stopService correctly. |
188 |
def log_stopped(ignored): |
10693.3.1
by Jonathan Lange
Split the events classes into their own module. |
189 |
notify(events.ServerStopped()) |
7483.2.48
by Jonathan Lange
Handle return values of stopService correctly. |
190 |
return ignored |
14198.1.1
by Jeroen Vermeulen
Lint. |
191 |
|
7483.2.48
by Jonathan Lange
Handle return values of stopService correctly. |
192 |
return deferred.addBoth(log_stopped) |