mirror of
https://github.com/paradoxxxzero/butterfly.git
synced 2026-05-26 07:08:08 +00:00
Implement client/server certificate generation + enable ssl required by default on non localhost hosts
This commit is contained in:
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
*.crt
|
||||
*.key
|
||||
*.p12
|
||||
@@ -20,7 +20,15 @@
|
||||
import tornado.options
|
||||
import tornado.ioloop
|
||||
import tornado.httpserver
|
||||
import uuid
|
||||
import ssl
|
||||
import os
|
||||
import sys
|
||||
|
||||
try:
|
||||
input = raw_input
|
||||
except NameError:
|
||||
pass
|
||||
|
||||
tornado.options.define("debug", default=False, help="Debug mode")
|
||||
tornado.options.define("more", default=False,
|
||||
@@ -28,13 +36,17 @@ tornado.options.define("more", default=False,
|
||||
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("unsecure", default=False,
|
||||
help="Don't use ssl not recommended")
|
||||
|
||||
tornado.options.define("generate_certs", default=False,
|
||||
help="Generate butterfly certificates")
|
||||
tornado.options.define("generate_user_pkcs", default='',
|
||||
help="Generate user pfx for client authentication")
|
||||
|
||||
tornado.options.parse_command_line()
|
||||
|
||||
|
||||
import logging
|
||||
for logger in ('tornado.access', 'tornado.application',
|
||||
'tornado.general', 'butterfly'):
|
||||
@@ -48,28 +60,117 @@ for logger in ('tornado.access', 'tornado.application',
|
||||
log = logging.getLogger('butterfly')
|
||||
log.info('Starting server')
|
||||
ioloop = tornado.ioloop.IOLoop.instance()
|
||||
ca, ca_key = 'butterfly_ca.crt', 'butterfly_ca.key'
|
||||
cert, cert_key = 'butterfly.crt', 'butterfly.key'
|
||||
|
||||
|
||||
from butterfly import application
|
||||
|
||||
if tornado.options.options.reallysecure:
|
||||
tornado.options.options.secure = True
|
||||
reqs = ssl.CERT_REQUIRED
|
||||
elif tornado.options.options.secure:
|
||||
reqs = ssl.CERT_OPTIONAL
|
||||
if tornado.options.options.generate_certs:
|
||||
from OpenSSL import crypto
|
||||
ca_pk = crypto.PKey()
|
||||
ca_pk.generate_key(crypto.TYPE_RSA, 2048)
|
||||
ca_cert = crypto.X509()
|
||||
ca_cert.get_subject().CN = 'butterfly ca'
|
||||
ca_cert.set_serial_number(100)
|
||||
ca_cert.gmtime_adj_notBefore(0) # From now
|
||||
ca_cert.gmtime_adj_notAfter(315360000) # to 10y
|
||||
ca_cert.set_issuer(ca_cert.get_subject()) # Self signed
|
||||
ca_cert.set_pubkey(ca_pk)
|
||||
ca_cert.sign(ca_pk, 'sha1')
|
||||
|
||||
ssl_opts = None
|
||||
if tornado.options.options.secure:
|
||||
ssl_opts = dict(certfile="butterfly.crt", keyfile="butterfly.key",
|
||||
cert_reqs=reqs, ca_certs="butterflyca.crt")
|
||||
with open(ca, "wb") as cf:
|
||||
cf.write(
|
||||
crypto.dump_certificate(crypto.FILETYPE_PEM, ca_cert))
|
||||
with open(ca_key, "wb") as cf:
|
||||
cf.write(
|
||||
crypto.dump_privatekey(crypto.FILETYPE_PEM, ca_pk))
|
||||
|
||||
server_pk = crypto.PKey()
|
||||
server_pk.generate_key(crypto.TYPE_RSA, 2048)
|
||||
server_cert = crypto.X509()
|
||||
server_cert.get_subject().CN = tornado.options.options.host
|
||||
server_cert.set_serial_number(200)
|
||||
server_cert.gmtime_adj_notBefore(0) # From now
|
||||
server_cert.gmtime_adj_notAfter(315360000) # to 10y
|
||||
server_cert.set_issuer(ca_cert.get_subject()) # Signed by ca
|
||||
server_cert.set_pubkey(server_pk)
|
||||
server_cert.sign(ca_pk, 'sha1')
|
||||
|
||||
with open(cert, "wb") as cf:
|
||||
cf.write(crypto.dump_certificate(crypto.FILETYPE_PEM, server_cert))
|
||||
|
||||
with open(cert_key, "wb") as cf:
|
||||
cf.write(crypto.dump_privatekey(crypto.FILETYPE_PEM, server_pk))
|
||||
print('Done')
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if tornado.options.options.generate_user_pkcs:
|
||||
from OpenSSL import crypto
|
||||
if not all(map(os.path.exists,
|
||||
[cert, cert_key, ca, ca_key])):
|
||||
print('Please generate certificates using --generate_certs before')
|
||||
sys.exit(1)
|
||||
|
||||
user = tornado.options.options.generate_user_pkcs
|
||||
with open(ca, 'rb') as cf:
|
||||
ca_cert = crypto.load_certificate(crypto.FILETYPE_PEM, cf.read())
|
||||
with open(ca_key, 'rb') as cf:
|
||||
ca_pk = crypto.load_privatekey(crypto.FILETYPE_PEM, cf.read())
|
||||
|
||||
client_pk = crypto.PKey()
|
||||
client_pk.generate_key(crypto.TYPE_RSA, 2048)
|
||||
|
||||
client_cert = crypto.X509()
|
||||
client_cert.get_subject().CN = user
|
||||
client_cert.set_serial_number(uuid.uuid4().int)
|
||||
client_cert.gmtime_adj_notBefore(0) # From now
|
||||
client_cert.gmtime_adj_notAfter(315360000) # to 10y
|
||||
client_cert.set_issuer(ca_cert.get_subject()) # Signed by ca
|
||||
client_cert.set_pubkey(client_pk)
|
||||
client_cert.sign(client_pk, 'sha1')
|
||||
client_cert.sign(ca_pk, 'sha1')
|
||||
|
||||
pfx = crypto.PKCS12()
|
||||
pfx.set_certificate(client_cert)
|
||||
pfx.set_privatekey(client_pk)
|
||||
pfx.set_ca_certificates([ca_cert])
|
||||
pfx.set_friendlyname(('%s cert for butterfly' % user).encode('utf-8'))
|
||||
|
||||
with open('%s.p12' % user, "wb") as cf:
|
||||
cf.write(pfx.export(b''))
|
||||
print('%s.p12 written.' % user)
|
||||
sys.exit(0)
|
||||
|
||||
|
||||
if (tornado.options.options.unsecure or
|
||||
tornado.options.options.host == '127.0.0.1'):
|
||||
ssl_opts = None
|
||||
else:
|
||||
if not all(map(os.path.exists,
|
||||
[cert, cert_key, ca, ca_key])):
|
||||
print("Unable to find butterfly certificate. "
|
||||
"Can't run butterfly without certificate. "
|
||||
"Either generate them or run as --unsecure "
|
||||
"(NOT RECOMMENDED)")
|
||||
sys.exit(1)
|
||||
|
||||
ssl_opts = {
|
||||
'certfile': cert,
|
||||
'keyfile': cert_key,
|
||||
'ca_certs': ca,
|
||||
'cert_reqs': ssl.CERT_REQUIRED
|
||||
}
|
||||
|
||||
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 not tornado.options.options.unsecure else "",
|
||||
tornado.options.options.host,
|
||||
tornado.options.options.port)
|
||||
|
||||
# This is for debugging purpose
|
||||
try:
|
||||
|
||||
@@ -122,7 +122,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
env["COLORTERM"] = "butterfly"
|
||||
env["HOME"] = self.callee.dir
|
||||
env["LOCATION"] = "http%s://%s:%d/" % (
|
||||
"s" if tornado.options.options.secure else "",
|
||||
"s" if not tornado.options.options.unsecure 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"))
|
||||
@@ -186,7 +186,7 @@ class TermWebSocket(Route, tornado.websocket.WebSocketHandler):
|
||||
|
||||
def open(self, user, path):
|
||||
if self.request.headers['Origin'] != 'http%s://%s' % (
|
||||
"s" if tornado.options.options.secure else "",
|
||||
"s" if not tornado.options.options.unsecure else "",
|
||||
self.request.headers['Host']):
|
||||
self.log.warning(
|
||||
'Unauthorized connection attempt: from : %s to: %s' % (
|
||||
@@ -201,7 +201,7 @@ 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:
|
||||
if not tornado.options.options.unsecure:
|
||||
cert = self.request.get_ssl_certificate()
|
||||
if cert is not None:
|
||||
for field in cert['subject']:
|
||||
|
||||
@@ -33,6 +33,7 @@ if location.protocol == 'https:'
|
||||
ws_url = 'wss://'
|
||||
else
|
||||
ws_url = 'ws://'
|
||||
|
||||
ws_url += document.location.host + '/ws' + location.pathname
|
||||
ws = new WebSocket ws_url
|
||||
|
||||
|
||||
Reference in New Issue
Block a user