Add ssl support for netrpc and xmlrpc

This commit is contained in:
Cédric Krier
2008-03-04 14:42:51 +01:00
parent c66b3c9755
commit ff55b01182
5 changed files with 140 additions and 41 deletions
+12
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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
View File
@@ -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))