1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 import os
18 import types
19 import binascii
20 import base64
21 from twisted.python import log
22 from twisted.application import service, strports
23 from twisted.cred import checkers, portal
24 from twisted.conch import manhole, telnet, manhole_ssh, checkers as conchc
25 from twisted.conch.insults import insults
26 from twisted.internet import protocol
27
28 from buildbot.util import ComparableMixin
29 from zope.interface import implements
30
31
32
34
35
38
40 auth = telnet.AuthenticatingTelnetProtocol
41 return telnet.TelnetTransport(auth, self.portal)
42
44 implements(portal.IRealm)
45
47 self.namespace_maker = namespace_maker
48
50 if telnet.ITelnetProtocol in interfaces:
51 namespace = self.namespace_maker()
52 p = telnet.TelnetBootstrapProtocol(insults.ServerProtocol,
53 manhole.ColoredManhole,
54 namespace)
55 return (telnet.ITelnetProtocol, p, lambda: None)
56 raise NotImplementedError()
57
58
60
61
63 self.namespace = namespace
64
66 return insults.ServerProtocol(manhole.ColoredManhole, self.namespace)
67
69 """Accept connections using SSH keys from a given file.
70
71 SSHPublicKeyDatabase takes the username that the prospective client has
72 requested and attempts to get a ~/.ssh/authorized_keys file for that
73 username. This requires root access, so it isn't as useful as you'd
74 like.
75
76 Instead, this subclass looks for keys in a single file, given as an
77 argument. This file is typically kept in the buildmaster's basedir. The
78 file should have 'ssh-dss ....' lines in it, just like authorized_keys.
79 """
80
81 - def __init__(self, authorized_keys_file):
82 self.authorized_keys_file = os.path.expanduser(authorized_keys_file)
83
85 f = open(self.authorized_keys_file)
86 for l in f.readlines():
87 l2 = l.split()
88 if len(l2) < 2:
89 continue
90 try:
91 if base64.decodestring(l2[1]) == credentials.blob:
92 return 1
93 except binascii.Error:
94 continue
95 return 0
96
97
99 """This provides remote access to a python interpreter (a read/exec/print
100 loop) embedded in the buildmaster via an internal SSH server. This allows
101 detailed inspection of the buildmaster state. It is of most use to
102 buildbot developers. Connect to this by running an ssh client.
103 """
104
105 - def __init__(self, port, checker, using_ssh=True):
106 """
107 @type port: string or int
108 @param port: what port should the Manhole listen on? This is a
109 strports specification string, like 'tcp:12345' or
110 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
111 simple tcp port.
112
113 @type checker: an object providing the
114 L{twisted.cred.checkers.ICredentialsChecker} interface
115 @param checker: if provided, this checker is used to authenticate the
116 client instead of using the username/password scheme. You must either
117 provide a username/password or a Checker. Some useful values are::
118 import twisted.cred.checkers as credc
119 import twisted.conch.checkers as conchc
120 c = credc.AllowAnonymousAccess # completely open
121 c = credc.FilePasswordDB(passwd_filename) # file of name:passwd
122 c = conchc.UNIXPasswordDatabase # getpwnam() (probably /etc/passwd)
123
124 @type using_ssh: bool
125 @param using_ssh: If True, accept SSH connections. If False, accept
126 regular unencrypted telnet connections.
127 """
128
129
130
131
132
133
134 service.MultiService.__init__(self)
135 if type(port) is int:
136 port = "tcp:%d" % port
137 self.port = port
138 self.checker = checker
139
140 def makeNamespace():
141 master = self.master
142 namespace = {
143 'master': master,
144 'status': master.getStatus(),
145 'show': show,
146 }
147 return namespace
148
149 def makeProtocol():
150 namespace = makeNamespace()
151 p = insults.ServerProtocol(manhole.ColoredManhole, namespace)
152 return p
153
154 self.using_ssh = using_ssh
155 if using_ssh:
156 r = manhole_ssh.TerminalRealm()
157 r.chainedProtocolFactory = makeProtocol
158 p = portal.Portal(r, [self.checker])
159 f = manhole_ssh.ConchFactory(p)
160 else:
161 r = _TelnetRealm(makeNamespace)
162 p = portal.Portal(r, [self.checker])
163 f = protocol.ServerFactory()
164 f.protocol = makeTelnetProtocol(p)
165 s = strports.service(self.port, f)
166 s.setServiceParent(self)
167
168
170 service.MultiService.startService(self)
171 if self.using_ssh:
172 via = "via SSH"
173 else:
174 via = "via telnet"
175 log.msg("Manhole listening %s on port %s" % (via, self.port))
176
177
179 """This Manhole accepts unencrypted (telnet) connections, and requires a
180 username and password authorize access. You are encouraged to use the
181 encrypted ssh-based manhole classes instead."""
182
183 compare_attrs = ["port", "username", "password"]
184
185 - def __init__(self, port, username, password):
186 """
187 @type port: string or int
188 @param port: what port should the Manhole listen on? This is a
189 strports specification string, like 'tcp:12345' or
190 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
191 simple tcp port.
192
193 @param username:
194 @param password: username= and password= form a pair of strings to
195 use when authenticating the remote user.
196 """
197
198 self.username = username
199 self.password = password
200
201 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
202 c.addUser(username, password)
203
204 _BaseManhole.__init__(self, port, c, using_ssh=False)
205
207 """This Manhole accepts encrypted (ssh) connections, and requires a
208 username and password to authorize access.
209 """
210
211 compare_attrs = ["port", "username", "password"]
212
213 - def __init__(self, port, username, password):
214 """
215 @type port: string or int
216 @param port: what port should the Manhole listen on? This is a
217 strports specification string, like 'tcp:12345' or
218 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
219 simple tcp port.
220
221 @param username:
222 @param password: username= and password= form a pair of strings to
223 use when authenticating the remote user.
224 """
225
226 self.username = username
227 self.password = password
228
229 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
230 c.addUser(username, password)
231
232 _BaseManhole.__init__(self, port, c)
233
235 """This Manhole accepts ssh connections, and requires that the
236 prospective client have an ssh private key that matches one of the public
237 keys in our authorized_keys file. It is created with the name of a file
238 that contains the public keys that we will accept."""
239
240 compare_attrs = ["port", "keyfile"]
241
243 """
244 @type port: string or int
245 @param port: what port should the Manhole listen on? This is a
246 strports specification string, like 'tcp:12345' or
247 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
248 simple tcp port.
249
250 @param keyfile: the name of a file (relative to the buildmaster's
251 basedir) that contains SSH public keys of authorized
252 users, one per line. This is the exact same format
253 as used by sshd in ~/.ssh/authorized_keys .
254 """
255
256
257
258 self.keyfile = keyfile
259 c = AuthorizedKeysChecker(keyfile)
260 _BaseManhole.__init__(self, port, c)
261
263 """This Manhole accepts ssh connections, but uses an arbitrary
264 user-supplied 'checker' object to perform authentication."""
265
266 compare_attrs = ["port", "checker"]
267
269 """
270 @type port: string or int
271 @param port: what port should the Manhole listen on? This is a
272 strports specification string, like 'tcp:12345' or
273 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
274 simple tcp port.
275
276 @param checker: an instance of a twisted.cred 'checker' which will
277 perform authentication
278 """
279
280 _BaseManhole.__init__(self, port, checker)
281
282
283
285 """Display the data attributes of an object in a readable format"""
286 print "data attributes of %r" % (x,)
287 names = dir(x)
288 maxlen = max([0] + [len(n) for n in names])
289 for k in names:
290 v = getattr(x,k)
291 t = type(v)
292 if t == types.MethodType: continue
293 if k[:2] == '__' and k[-2:] == '__': continue
294 if t is types.StringType or t is types.UnicodeType:
295 if len(v) > 80 - maxlen - 5:
296 v = `v[:80 - maxlen - 5]` + "..."
297 elif t in (types.IntType, types.NoneType):
298 v = str(v)
299 elif v in (types.ListType, types.TupleType, types.DictType):
300 v = "%s (%d elements)" % (v, len(v))
301 else:
302 v = str(t)
303 print "%*s : %s" % (maxlen, k, v)
304 return x
305