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
142 master = self.parent
143 namespace = {
144 'master': master,
145 'status': master.getStatus(),
146 'show': show,
147 }
148 return namespace
149
150 def makeProtocol():
151 namespace = makeNamespace()
152 p = insults.ServerProtocol(manhole.ColoredManhole, namespace)
153 return p
154
155 self.using_ssh = using_ssh
156 if using_ssh:
157 r = manhole_ssh.TerminalRealm()
158 r.chainedProtocolFactory = makeProtocol
159 p = portal.Portal(r, [self.checker])
160 f = manhole_ssh.ConchFactory(p)
161 else:
162 r = _TelnetRealm(makeNamespace)
163 p = portal.Portal(r, [self.checker])
164 f = protocol.ServerFactory()
165 f.protocol = makeTelnetProtocol(p)
166 s = strports.service(self.port, f)
167 s.setServiceParent(self)
168
169
171 service.MultiService.startService(self)
172 if self.using_ssh:
173 via = "via SSH"
174 else:
175 via = "via telnet"
176 log.msg("Manhole listening %s on port %s" % (via, self.port))
177
178
180 """This Manhole accepts unencrypted (telnet) connections, and requires a
181 username and password authorize access. You are encouraged to use the
182 encrypted ssh-based manhole classes instead."""
183
184 compare_attrs = ["port", "username", "password"]
185
186 - def __init__(self, port, username, password):
187 """
188 @type port: string or int
189 @param port: what port should the Manhole listen on? This is a
190 strports specification string, like 'tcp:12345' or
191 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
192 simple tcp port.
193
194 @param username:
195 @param password: username= and password= form a pair of strings to
196 use when authenticating the remote user.
197 """
198
199 self.username = username
200 self.password = password
201
202 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
203 c.addUser(username, password)
204
205 _BaseManhole.__init__(self, port, c, using_ssh=False)
206
208 """This Manhole accepts encrypted (ssh) connections, and requires a
209 username and password to authorize access.
210 """
211
212 compare_attrs = ["port", "username", "password"]
213
214 - def __init__(self, port, username, password):
215 """
216 @type port: string or int
217 @param port: what port should the Manhole listen on? This is a
218 strports specification string, like 'tcp:12345' or
219 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
220 simple tcp port.
221
222 @param username:
223 @param password: username= and password= form a pair of strings to
224 use when authenticating the remote user.
225 """
226
227 self.username = username
228 self.password = password
229
230 c = checkers.InMemoryUsernamePasswordDatabaseDontUse()
231 c.addUser(username, password)
232
233 _BaseManhole.__init__(self, port, c)
234
236 """This Manhole accepts ssh connections, and requires that the
237 prospective client have an ssh private key that matches one of the public
238 keys in our authorized_keys file. It is created with the name of a file
239 that contains the public keys that we will accept."""
240
241 compare_attrs = ["port", "keyfile"]
242
244 """
245 @type port: string or int
246 @param port: what port should the Manhole listen on? This is a
247 strports specification string, like 'tcp:12345' or
248 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
249 simple tcp port.
250
251 @param keyfile: the name of a file (relative to the buildmaster's
252 basedir) that contains SSH public keys of authorized
253 users, one per line. This is the exact same format
254 as used by sshd in ~/.ssh/authorized_keys .
255 """
256
257
258
259 self.keyfile = keyfile
260 c = AuthorizedKeysChecker(keyfile)
261 _BaseManhole.__init__(self, port, c)
262
264 """This Manhole accepts ssh connections, but uses an arbitrary
265 user-supplied 'checker' object to perform authentication."""
266
267 compare_attrs = ["port", "checker"]
268
270 """
271 @type port: string or int
272 @param port: what port should the Manhole listen on? This is a
273 strports specification string, like 'tcp:12345' or
274 'tcp:12345:interface=127.0.0.1'. Bare integers are treated as a
275 simple tcp port.
276
277 @param checker: an instance of a twisted.cred 'checker' which will
278 perform authentication
279 """
280
281 _BaseManhole.__init__(self, port, checker)
282
283
284
286 """Display the data attributes of an object in a readable format"""
287 print "data attributes of %r" % (x,)
288 names = dir(x)
289 maxlen = max([0] + [len(n) for n in names])
290 for k in names:
291 v = getattr(x,k)
292 t = type(v)
293 if t == types.MethodType: continue
294 if k[:2] == '__' and k[-2:] == '__': continue
295 if t is types.StringType or t is types.UnicodeType:
296 if len(v) > 80 - maxlen - 5:
297 v = `v[:80 - maxlen - 5]` + "..."
298 elif t in (types.IntType, types.NoneType):
299 v = str(v)
300 elif v in (types.ListType, types.TupleType, types.DictType):
301 v = "%s (%d elements)" % (v, len(v))
302 else:
303 v = str(t)
304 print "%*s : %s" % (maxlen, k, v)
305 return x
306