mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-05-28 16:09:44 +00:00
Added SSL support to butterfly.
Added SSL certificate capability to butterfly. Butterfly now has the --secure option, which requires the following files to be present in the local folder: - butterfly.crt - butterfly.key - butterflyca.crt This option forces butterfly to use HTTPS and secure WebSockets. The connection still requires a username and password. There is also the --reallysecure option, which forces the user's browser to provide a client side certificate. The certificate is validated against butterflyca.crt, before allowing the connection. Afterward, the commonName in the certificate is used as the username for the connection. The connection still requires the user to provide a password. Also forced a default user "daemon" to be returned by the User class, as it prevents someone from finding valid users on the remote host.
This commit is contained in:
@@ -20,16 +20,20 @@
|
||||
import tornado.options
|
||||
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.parse_command_line()
|
||||
|
||||
|
||||
import logging
|
||||
for logger in ('tornado.access', 'tornado.application',
|
||||
'tornado.general', 'butterfly'):
|
||||
@@ -42,11 +46,23 @@ ioloop = tornado.ioloop.IOLoop.instance()
|
||||
|
||||
|
||||
from butterfly import application
|
||||
http_server = tornado.httpserver.HTTPServer(application)
|
||||
|
||||
if tornado.options.options.reallysecure:
|
||||
tornado.options.options.secure = True
|
||||
reqs = ssl.CERT_REQUIRED
|
||||
elif tornado.options.options.secure:
|
||||
reqs = ssl.CERT_OPTIONAL
|
||||
|
||||
ssl_opts = None
|
||||
if tornado.options.options.secure:
|
||||
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:%d/*" % (
|
||||
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
|
||||
|
||||
@@ -123,8 +123,9 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
env["TERM"] = "xterm-256color"
|
||||
env["COLORTERM"] = "butterfly"
|
||||
env["HOME"] = self.callee.dir
|
||||
env["LOCATION"] = "http://%s:%d/" % (
|
||||
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]
|
||||
@@ -180,7 +181,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' % (
|
||||
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' % (
|
||||
@@ -194,12 +196,19 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
self.path = path
|
||||
self.user = user.decode('utf-8') if user else None
|
||||
self.caller = self.callee = None
|
||||
if tornado.options.options.secure:
|
||||
cert = self.request.get_ssl_certificate()
|
||||
if cert != None:
|
||||
for field in cert['subject']:
|
||||
if field[0][0] == 'commonName':
|
||||
self.user = self.callee = field[0][1]
|
||||
|
||||
if self.socket.local:
|
||||
self.caller = utils.User(uid=self.socket.uid)
|
||||
else:
|
||||
# We don't know uid is on the other machine
|
||||
pass
|
||||
|
||||
|
||||
if self.user:
|
||||
try:
|
||||
self.callee = utils.User(name=self.user)
|
||||
|
||||
@@ -28,7 +28,11 @@ ctl = (type, args...) ->
|
||||
if type == 'Resize'
|
||||
ws.send 'R' + params
|
||||
|
||||
ws_url = 'ws://' + document.location.host + '/ws' + location.pathname
|
||||
if localtion.protocol == 'https:'
|
||||
ws_url = 'wss://'
|
||||
else
|
||||
ws_url = 'ws://'
|
||||
ws_url += document.location.host + '/ws' + location.pathname
|
||||
ws = new WebSocket ws_url
|
||||
|
||||
ws.addEventListener 'open', ->
|
||||
|
||||
@@ -2792,7 +2792,13 @@ ctl = function() {
|
||||
}
|
||||
};
|
||||
|
||||
ws_url = 'ws://' + document.location.host + '/ws' + location.pathname;
|
||||
if(location.protocol == 'https:'){
|
||||
ws_url = 'wss://'
|
||||
}
|
||||
else{
|
||||
ws_url = 'ws://'
|
||||
}
|
||||
ws_url += document.location.host + '/ws' + location.pathname;
|
||||
|
||||
ws = new WebSocket(ws_url);
|
||||
|
||||
|
||||
@@ -31,7 +31,10 @@ class User(object):
|
||||
if uid is not None:
|
||||
self.pw = pwd.getpwuid(uid)
|
||||
else:
|
||||
self.pw = pwd.getpwnam(name)
|
||||
try:
|
||||
self.pw = pwd.getpwnam(name)
|
||||
except:
|
||||
self.pw = pwd.getpwnam('daemon')
|
||||
if self.pw is None:
|
||||
raise LookupError('Unknown user')
|
||||
|
||||
|
||||
Reference in New Issue
Block a user