Add ssl support for netrpc and xmlrpc
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
|
||||
== Howto activate SSL on trytond ==
|
||||
|
||||
- You need to have OpenSSL and pyOpenSSL installed on your system.
|
||||
|
||||
- Generate a self-signed certificate with this command:
|
||||
|
||||
openssl req -new -x509 -keyout server.pem -out server.pem -days 365 -nodes
|
||||
|
||||
- Start the trytond server with those options:
|
||||
|
||||
trytond.py -S --privatekey server.pem --certificate server.pem
|
||||
+13
-2
@@ -29,6 +29,8 @@ class ConfigManager(object):
|
||||
'pidfile': None,
|
||||
'logfile': None,
|
||||
'secure': False,
|
||||
'privatekey': '/etc/ssl/trytond/server.key',
|
||||
'certificate': '/etc/ssl/trytond/server.pem',
|
||||
'smtp_server': 'localhost',
|
||||
'smtp_user': False,
|
||||
'smtp_password': False,
|
||||
@@ -59,8 +61,6 @@ class ConfigManager(object):
|
||||
help="stop the server after it initializes")
|
||||
group.add_option("-n", "--interface", dest="interface",
|
||||
help="specify the TCP IP address")
|
||||
group.add_option("-S", "--secure", dest="secure", action="store_true",
|
||||
help="launch server over https instead of http")
|
||||
group.add_option("--no-netrpc", dest="netrpc", action="store_false",
|
||||
help="disable netrpc")
|
||||
group.add_option("-p", "--net-port", dest="netport",
|
||||
@@ -75,6 +75,15 @@ class ConfigManager(object):
|
||||
help="specify the TCP port for webdav")
|
||||
parser.add_option_group(group)
|
||||
|
||||
group = optparse.OptionGroup(parser, "SSL options")
|
||||
group.add_option("-S", "--secure", dest="secure", action="store_true",
|
||||
help="launch server over SSL")
|
||||
group.add_option("--privatekey", dest="privatekey",
|
||||
help="specify the file for the private key")
|
||||
group.add_option("--certificate", dest="certificate",
|
||||
help="specify the file for the certificate")
|
||||
parser.add_option_group(group)
|
||||
|
||||
group = optparse.OptionGroup(parser, "Modules related options")
|
||||
group.add_option("-i", "--init", dest="init",
|
||||
help="init a module (use \"all\" for all modules)")
|
||||
@@ -149,6 +158,8 @@ class ConfigManager(object):
|
||||
'logfile',
|
||||
'pidfile',
|
||||
'secure',
|
||||
'privatekey',
|
||||
'certificate',
|
||||
'smtp_server',
|
||||
'smtp_user',
|
||||
'smtp_password',
|
||||
|
||||
+59
-28
@@ -250,6 +250,14 @@ class SimpleXMLRPCRequestHandler(GenericXMLRPCRequestHandler,
|
||||
)
|
||||
|
||||
|
||||
class SecureXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
|
||||
|
||||
def setup(self):
|
||||
self.connection = self.request
|
||||
self.rfile = socket.fileobject(self.request, "rb", self.rbufsize)
|
||||
self.wfile = socket._fileobject(self.request, "wb", self.wbufsize)
|
||||
|
||||
|
||||
class SimpleThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
|
||||
SimpleXMLRPCServer.SimpleXMLRPCServer):
|
||||
|
||||
@@ -263,6 +271,25 @@ class SimpleThreadedXMLRPCServer6(SimpleThreadedXMLRPCServer):
|
||||
address_family = socket.AF_INET6
|
||||
|
||||
|
||||
class SecureThreadedXMLRPCServer(SimpleThreadedXMLRPCServer):
|
||||
|
||||
def __init__(self, server_address, HandlerClass, logRequests=1):
|
||||
from OpenSSL import SSL
|
||||
SimpleThreadedXMLRPCServer.__init__(self, server_address, HandlerClass,
|
||||
logRequests)
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||
ctx.use_privatekey_file(CONFIG['privatekey'])
|
||||
ctx.use_certificate_file(CONFIG['certificate'])
|
||||
self.socket = SSL.Connection(ctx, socket.socket(self.address_family,
|
||||
self.socket_type))
|
||||
self.server_bind()
|
||||
self.server_activate()
|
||||
|
||||
|
||||
class SecureThreadedXMLRPCServer6(SecureThreadedXMLRPCServer):
|
||||
address_family = socket.AF_INET6
|
||||
|
||||
|
||||
class HttpDaemon(threading.Thread):
|
||||
|
||||
def __init__(self, interface, port, secure=False):
|
||||
@@ -270,27 +297,19 @@ class HttpDaemon(threading.Thread):
|
||||
self.secure = secure
|
||||
self.running = False
|
||||
if secure:
|
||||
# from ssl import SecureXMLRPCServer
|
||||
# class SecureXMLRPCRequestHandler(GenericXMLRPCRequestHandler,
|
||||
# SecureXMLRPCServer.SecureXMLRPCRequestHandler):
|
||||
# SecureXMLRPCServer.SecureXMLRPCRequestHandler.rpc_paths = (
|
||||
# '/xmlrpc/db',
|
||||
# '/xmlrpc/common',
|
||||
# '/xmlrpc/object',
|
||||
# '/xmlrpc/report',
|
||||
# '/xmlrpc/wizard',
|
||||
# )
|
||||
# class SecureThreadedXMLRPCServer(SocketServer.ThreadingMixIn,
|
||||
# SecureXMLRPCServer.SecureXMLRPCServer):
|
||||
#
|
||||
# def server_bind(self):
|
||||
# self.socket.setsockopt(socket.SOL_SOCKET,
|
||||
# socket.SO_REUSEADDR, 1)
|
||||
# SecureXMLRPCServer.SecureXMLRPCServer.server_bind(self)
|
||||
#
|
||||
# self.server = SecureThreadedXMLRPCServer((interface, port),
|
||||
# SecureXMLRPCRequestHandler,0)
|
||||
raise
|
||||
server_class = SecureThreadedXMLRPCServer
|
||||
if socket.has_ipv6:
|
||||
try:
|
||||
socket.getaddrinfo(interface or None, port, socket.AF_INET6)
|
||||
server_class = SecureThreadedXMLRPCServer6
|
||||
if not interface:
|
||||
interface = '::'
|
||||
except:
|
||||
pass
|
||||
if not interface:
|
||||
interface = '0.0.0.0'
|
||||
self.server = server_class((interface, port),
|
||||
SecureXMLRPCRequestHandler, 0)
|
||||
else:
|
||||
server_class = SimpleThreadedXMLRPCServer
|
||||
if socket.has_ipv6:
|
||||
@@ -342,11 +361,12 @@ class HttpDaemon(threading.Thread):
|
||||
|
||||
|
||||
class TinySocketClientThread(threading.Thread):
|
||||
def __init__(self, sock, threads):
|
||||
def __init__(self, sock, threads, secure):
|
||||
threading.Thread.__init__(self)
|
||||
self.sock = sock
|
||||
self.threads = threads
|
||||
self.running = False
|
||||
self.secure = secure
|
||||
|
||||
def run(self):
|
||||
self.running = True
|
||||
@@ -384,8 +404,7 @@ class TinySocketClientThread(threading.Thread):
|
||||
tback = sys.exc_info()[2]
|
||||
pdb.post_mortem(tback)
|
||||
pysocket.send(str(exp), exception=True, traceback=tb_s)
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
self.sock.close()
|
||||
pysocket.disconnect()
|
||||
self.threads.remove(self)
|
||||
return True
|
||||
|
||||
@@ -409,6 +428,12 @@ class TinySocketServerThread(threading.Thread):
|
||||
interface = '0.0.0.0'
|
||||
self.socket = socket.socket(familly, socket.SOCK_STREAM)
|
||||
self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
|
||||
if secure:
|
||||
from OpenSSL import SSL
|
||||
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||
ctx.use_privatekey_file(CONFIG['privatekey'])
|
||||
ctx.use_certificate_file(CONFIG['certificate'])
|
||||
self.socket = SSL.Connection(ctx, self.socket)
|
||||
self.socket.bind((interface, port))
|
||||
self.socket.listen(5)
|
||||
self.threads = []
|
||||
@@ -420,7 +445,7 @@ class TinySocketServerThread(threading.Thread):
|
||||
self.running = True
|
||||
while self.running:
|
||||
(clientsocket, address) = self.socket.accept()
|
||||
c_thread = TinySocketClientThread(clientsocket, self.threads)
|
||||
c_thread = TinySocketClientThread(clientsocket, self.threads, self.secure)
|
||||
self.threads.append(c_thread)
|
||||
c_thread.start()
|
||||
self.socket.close()
|
||||
@@ -433,10 +458,16 @@ class TinySocketServerThread(threading.Thread):
|
||||
for thread in self.threads:
|
||||
thread.stop()
|
||||
try:
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
if self.secure:
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
self.socket.sock_shutdown(socket.SHUT_RDWR)
|
||||
else:
|
||||
self.socket.sock_shutdown(2)
|
||||
else:
|
||||
self.socket.shutdown(2)
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
else:
|
||||
self.socket.shutdown(2)
|
||||
self.socket.close()
|
||||
except:
|
||||
return False
|
||||
|
||||
+55
-10
@@ -28,6 +28,8 @@ class PySocket:
|
||||
self.sock = sock
|
||||
self.host = None
|
||||
self.port = None
|
||||
self.ssl = False
|
||||
self.ssl_sock = None
|
||||
|
||||
def connect(self, host, port=False):
|
||||
if not port:
|
||||
@@ -47,15 +49,38 @@ class PySocket:
|
||||
self.sock.settimeout(120)
|
||||
self.sock.connect((host, int(port)))
|
||||
DNS_CACHE[host], port = self.sock.getpeername()[:2]
|
||||
try:
|
||||
familly = socket.AF_INET
|
||||
if socket.has_ipv6:
|
||||
try:
|
||||
socket.getaddrinfo(host, int(port), socket.AF_INET6)
|
||||
familly = socket.AF_INET6
|
||||
except:
|
||||
pass
|
||||
sock = socket.socket(familly, socket.SOCK_STREAM)
|
||||
sock.settimeout(120)
|
||||
sock.connect((host, int(port)))
|
||||
ssl_sock = socket.ssl(sock)
|
||||
self.ssl = True
|
||||
except:
|
||||
pass
|
||||
if self.ssl:
|
||||
self.ssl_sock = socket.ssl(self.sock)
|
||||
self.host = host
|
||||
self.port = port
|
||||
|
||||
def disconnect(self):
|
||||
try:
|
||||
self.sock.shutdown(socket.SHUT_RDWR)
|
||||
except:
|
||||
pass
|
||||
try:
|
||||
if self.secure:
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
self.socket.sock_shutdown(socket.SHUT_RDWR)
|
||||
else:
|
||||
self.socket.sock_shutdown(2)
|
||||
else:
|
||||
if hasattr(socket, 'SHUT_RDWR'):
|
||||
self.socket.shutdown(socket.SHUT_RDWR)
|
||||
else:
|
||||
self.socket.shutdown(2)
|
||||
self.sock.close()
|
||||
except:
|
||||
pass
|
||||
@@ -75,15 +100,26 @@ class PySocket:
|
||||
familly, socket.SOCK_STREAM)
|
||||
self.sock.settimeout(120)
|
||||
self.sock.connect((self.host, int(self.port)))
|
||||
if self.ssl:
|
||||
self.ssl_sock = socket.ssl(self.sock)
|
||||
|
||||
def send(self, msg, exception=False, traceback=None):
|
||||
msg = cPickle.dumps([msg, traceback])
|
||||
size = len(msg)
|
||||
self.sock.send('%8d' % size)
|
||||
self.sock.send(exception and "1" or "0")
|
||||
if self.ssl:
|
||||
self.ssl_sock.write('%8d' % size)
|
||||
else:
|
||||
self.sock.send('%8d' % size)
|
||||
if self.ssl:
|
||||
self.ssl_sock.write(exception and "1" or "0")
|
||||
else:
|
||||
self.sock.send(exception and "1" or "0")
|
||||
totalsent = 0
|
||||
while totalsent < size:
|
||||
sent = self.sock.send(msg[totalsent:])
|
||||
if self.ssl:
|
||||
sent = self.ssl_sock.write(msg[totalsent:])
|
||||
else:
|
||||
sent = self.sock.send(msg[totalsent:])
|
||||
if sent == 0:
|
||||
raise RuntimeError, "socket connection broken"
|
||||
totalsent = totalsent + sent
|
||||
@@ -91,19 +127,28 @@ class PySocket:
|
||||
def receive(self):
|
||||
buf = ''
|
||||
while len(buf) < 8:
|
||||
chunk = self.sock.recv(8 - len(buf))
|
||||
if self.ssl:
|
||||
chunk = self.ssl_sock.read(8 - len(buf))
|
||||
else:
|
||||
chunk = self.sock.recv(8 - len(buf))
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
buf += chunk
|
||||
size = int(buf)
|
||||
buf = self.sock.recv(1)
|
||||
if self.ssl:
|
||||
buf = self.ssl_sock.read(1)
|
||||
else:
|
||||
buf = self.sock.recv(1)
|
||||
if buf != "0":
|
||||
exception = buf
|
||||
else:
|
||||
exception = False
|
||||
msg = ''
|
||||
while len(msg) < size:
|
||||
chunk = self.sock.recv(size-len(msg))
|
||||
if self.ssl:
|
||||
chunk = self.ssl_sock.read(size-len(msg))
|
||||
else:
|
||||
chunk = self.sock.recv(size-len(msg))
|
||||
if chunk == '':
|
||||
raise RuntimeError, "socket connection broken"
|
||||
msg = msg + chunk
|
||||
|
||||
+1
-1
@@ -157,7 +157,7 @@ class TrytonServer(object):
|
||||
sys.exit(1)
|
||||
|
||||
tinysocket = netsvc.TinySocketServerThread(interface, port,
|
||||
False)
|
||||
secure)
|
||||
self.logger.notify_channel("web-services", netsvc.LOG_INFO,
|
||||
"starting netrpc service, port " + str(port))
|
||||
|
||||
|
||||
Reference in New Issue
Block a user