Clean up, avoid some server info leak, prevent crash when procfs is unavailable

This commit is contained in:
Florian Mounier
2014-03-03 18:28:14 +01:00
parent 8b66e95006
commit 7d34dc6ba1
6 changed files with 91 additions and 72 deletions

View File

@@ -22,15 +22,14 @@ import tornado.ioloop
import tornado.httpserver
import ssl
tornado.options.define("secret", default='secret', help="Secret")
tornado.options.define("debug", default=False, help="Debug mode")
tornado.options.define("host", default='127.0.0.1', help="Server host")
tornado.options.define("port", default=57575, type=int, help="Server port")
tornado.options.define("shell", help="Shell to execute at login")
tornado.options.define("secure", default=False, \
help="Choose whether or not to use SSL")
tornado.options.define("reallysecure", default=False, \
help="Require certificate authentication.")
tornado.options.define("secure", default=False,
help="Choose whether or not to use SSL")
tornado.options.define("reallysecure", default=False,
help="Require certificate authentication.")
tornado.options.parse_command_line()
@@ -55,15 +54,16 @@ elif tornado.options.options.secure:
ssl_opts = None
if tornado.options.options.secure:
ssl_opts = dict(certfile="butterfly.crt", keyfile="butterfly.key", \
cert_reqs=reqs, ca_certs="butterflyca.crt")
ssl_opts = dict(certfile="butterfly.crt", keyfile="butterfly.key",
cert_reqs=reqs, ca_certs="butterflyca.crt")
http_server = tornado.httpserver.HTTPServer(application, ssl_options=ssl_opts)
http_server.listen(
tornado.options.options.port, address=tornado.options.options.host)
url = "http%s://%s:%d/*" % ( "s" if tornado.options.options.secure else "",
tornado.options.options.host, tornado.options.options.port)
url = "http%s://%s:%d/*" % ("s" if tornado.options.options.secure else "",
tornado.options.options.host,
tornado.options.options.port)
# This is for debugging purpose
try:

View File

@@ -14,7 +14,7 @@
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
__version__ = '1.2.6'
__version__ = '1.2.7'
import os
@@ -43,18 +43,11 @@ class Route(tornado.web.RequestHandler):
def log(self):
return log
if hasattr(tornado.options.options, 'debug'):
opts = dict(
debug=tornado.options.options.debug,
cookie_secret=tornado.options.options.secret)
else:
opts = {}
application = tornado.web.Application(
static_path=os.path.join(os.path.dirname(__file__), "static"),
template_path=os.path.join(os.path.dirname(__file__), "templates"),
**opts
debug=tornado.options.options.debug
)

View File

@@ -27,7 +27,7 @@ import tornado.process
import tornado.ioloop
import tornado.options
import sys
from butterfly import url, Route, utils
from butterfly import url, Route, utils, __version__
ioloop = tornado.ioloop.IOLoop.instance()
@@ -47,41 +47,39 @@ def u(s):
return s
def motd(socket, caller, callee):
def motd(socket):
return (
'''
B ` '
;,,, ` ' ,,,;
`Y888888bo. : : .od888888Y'
8888888888b. : : .d8888888888 AWelcome to RbutterflyB
8888888888b. : : .d8888888888
88888Y' `Y8b. ` ' .d8Y' `Y88888
j88888 R.db.B Yb. ' ' .dY R.db.B 88888k AServer running as G%rB
j88888 R.db.B Yb. ' ' .dY R.db.B 88888k
`888 RY88YB `b ( ) d' RY88YB 888'
888b R'"B ,', R"'B d888 AConnecting to:B
j888888bd8gf"' ':' `"?g8bd888888k AHost: G%sB
R'Y'B .8' d' 'b '8. R'Y'X AUser: G%rB
R!B .8' RdbB d'; ;`b RdbB '8. R!B
d88 R`'B 8 ; ; 8 R`'B 88b AFrom:B
d888b .g8 ',' 8g. d888b AHost: G%sB
:888888888Y' 'Y888888888: AUser: G%rB
'! 8888888' `8888888 !'
888b R'"B ,', R"'B d888
j888888bd8gf"' ':' `"?g8bd888888k
R'Y'B .8' d' 'b '8. R'Y'X
R!B .8' RdbB d'; ;`b RdbB '8. R!
d88 R`'B 8 ; ; 8 R`'B 88b Rbutterfly Zv %sB
d888b .g8 ',' 8g. d888b
:888888888Y' 'Y888888888: AConnecting to:B
'! 8888888' `8888888 !' G%sB
'8Y R`Y Y'B Y8'
R Y Y
! !X
R Y Y AFrom:R
! ! G%sX
'''
.replace('B', '\x1b[34;1m')
.replace('G', '\x1b[32;1m')
.replace('R', '\x1b[37;1m')
.replace('Z', '\x1b[33;1m')
.replace('A', '\x1b[37;0m')
.replace('X', '\x1b[0m')
.replace('\n', '\r\n')
% (
server,
'%s:%d' % (socket.remote_addr, socket.remote_port),
callee,
'%s:%d' % (socket.local_addr, socket.local_port),
caller or '?'))
% (__version__,
'%s:%d' % (socket.local_addr, socket.local_port),
'%s:%d' % (socket.remote_addr, socket.remote_port)))
@url(r'/(?:user/(.+))?/?(?:wd/(.+))?')
@@ -105,36 +103,35 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.communicate()
def shell(self):
while self.callee is None:
if self.callee is None:
user = input('login: ')
try:
self.callee = utils.User(name=user)
except:
print('User %s not found' % user)
self.callee = utils.User(name='nologin')
try:
os.chdir(self.path or self.callee.dir)
except:
pass
shell = tornado.options.options.shell or self.callee.shell
env = os.environ
env.update(self.socket.env)
env["TERM"] = "xterm-256color"
env["COLORTERM"] = "butterfly"
env["HOME"] = self.callee.dir
env["LOCATION"] = "http%s://%s:%d/" % \
("s" if tornado.options.options.secure else "", \
tornado.options.options.host, tornado.options.options.port)
env["LOCATION"] = "http%s://%s:%d/" % (
"s" if tornado.options.options.secure else "",
tornado.options.options.host, tornado.options.options.port)
env["PATH"] = '%s:%s' % (os.path.abspath(os.path.join(
os.path.dirname(__file__), '..', 'bin')), env.get("PATH"))
args = [shell]
if self.socket.local:
# All users are the same -> launch shell
if self.caller == self.callee and server == self.callee:
args = [tornado.options.options.shell or self.callee.shell]
args.append('-i')
os.execvpe(shell, args, env)
os.execvpe(args[0], args, env)
# This process has been replaced
return
@@ -151,12 +148,19 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
sys.exit(1)
os.setuid(daemon.uid)
args.append('-p')
if tornado.options.options.shell:
args.append('-s')
args.append(tornado.options.options.shell)
if os.path.exists('/usr/bin/su'):
args = ['/usr/bin/su']
else:
args = ['/bin/su']
# This would be better to run
if sys.platform == 'linux':
args.append('-p')
if tornado.options.options.shell:
args.append('-s')
args.append(tornado.options.options.shell)
args.append(self.callee.name)
os.execvpe('/bin/su', args, env)
os.execvpe(args[0], args, env)
def communicate(self):
self.log.debug('Adding handler')
@@ -181,8 +185,8 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.fd, self.shell_handler, ioloop.READ | ioloop.ERROR)
def open(self, user, path):
if self.request.headers['Origin'] != 'http%s://%s' % \
("s" if tornado.options.options.secure else "",
if self.request.headers['Origin'] != 'http%s://%s' % (
"s" if tornado.options.options.secure else "",
self.request.headers['Host']):
self.log.warning(
'Unauthorized connection attempt: from : %s to: %s' % (
@@ -190,7 +194,9 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.request.headers['Host']))
self.close()
return
self.socket = utils.Socket(self.ws_connection.stream.socket)
self.errors = 0
self.set_nodelay(True)
self.log.info('Websocket opened %r' % self.socket)
self.path = path
@@ -198,7 +204,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.caller = self.callee = None
if tornado.options.options.secure:
cert = self.request.get_ssl_certificate()
if cert != None:
if cert is not None:
for field in cert['subject']:
if field[0][0] == 'commonName':
self.user = self.callee = field[0][1]
@@ -208,12 +214,11 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
else:
# We don't know uid is on the other machine
pass
if self.user:
try:
self.callee = utils.User(name=self.user)
except LookupError:
print('User %s not found' % self.user)
self.callee = None
# If no user where given and we are local, keep the same user
@@ -221,7 +226,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
# ie: the one openning a terminal in borwser
if not self.callee and not self.user and self.socket.local:
self.callee = self.caller
self.write_message(motd(self.socket, self.caller, self.callee))
self.write_message(motd(self.socket))
self.pty()
def on_message(self, message):
@@ -240,6 +245,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
def shell_handler(self, fd, events):
if events & ioloop.READ:
self.errors = 0
try:
read = self.reader.read()
except IOError:
@@ -251,12 +257,13 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
self.write_message(read.decode('utf-8', 'replace'))
if events & ioloop.ERROR:
self.log.info('Closing due to ioloop fd handler error')
ioloop.remove_handler(self.fd)
# Terminated
self.on_close()
self.close()
self.errors += 1
if self.errors >= 10:
self.log.info(
'Too many errors, the shell must have been closed')
# Terminated
self.on_close()
self.close()
def on_close(self):
if getattr(self, 'pid', 0) == 0:
@@ -275,4 +282,10 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
os.waitpid(self.pid, 0)
except OSError:
self.log.warning('waitpid fail', exc_info=True)
try:
ioloop.remove_handler(self.fd)
except Exception:
self.log.warning('handler removal fail', exc_info=True)
self.log.info('Websocket closed')

View File

@@ -17,6 +17,7 @@
cols = rows = null
quit = false
open_ts = 0
$ = document.querySelectorAll.bind(document)
@@ -38,6 +39,7 @@ ws = new WebSocket ws_url
ws.addEventListener 'open', ->
console.log "WebSocket open", arguments
ws.send 'R' + term.cols + ',' + term.rows
open_ts = (new Date()).getTime()
ws.addEventListener 'error', ->
console.log "WebSocket error", arguments
@@ -49,9 +51,16 @@ ws.addEventListener 'message', (e) ->
ws.addEventListener 'close', ->
console.log "WebSocket closed", arguments
setTimeout ->
term.write 'Closed'
# Allow quick reload
term.skipNextKey = true
term.element.classList.add('skip')
, 1
quit = true
open('','_self').close()
# Don't autoclose if websocket didn't last 1 minute
if (new Date()).getTime() - open_ts > 60 * 1000
open('','_self').close()
term = new Terminal $('#wrapper')[0], send, ctl
addEventListener 'beforeunload', ->

File diff suppressed because one or more lines are too long

View File

@@ -75,7 +75,7 @@ def get_socket_line(port):
# We got the socket
return line.split()
except:
log.error('getting socket inet4 line fail', exc_info=True)
log.debug('getting socket inet4 line fail', exc_info=True)
try:
with open('/proc/net/tcp6') as k:
@@ -87,7 +87,7 @@ def get_socket_line(port):
# We got the socket
return line.split()
except:
log.error('getting socket inet6 line fail', exc_info=True)
log.debug('getting socket inet6 line fail', exc_info=True)
def get_env(inode):
@@ -122,7 +122,11 @@ class Socket(object):
pn = socket.getpeername()
self.remote_addr = pn[0]
self.remote_port = pn[1]
line = get_socket_line(self.remote_port)
try:
line = get_socket_line(self.remote_port)
except Exception:
line = None
if line:
self.uid = int(line[7])
self.inode = line[9]
@@ -131,10 +135,10 @@ class Socket(object):
self.inode = None
self.env = {}
if self.local:
if self.local and self.inode:
try:
self.env = get_env(self.inode)
except:
except Exception:
log.debug('Unable to get env', exc_info=True)
@property