mirror of
https://github.com/openvswitch/ovs
synced 2025-09-02 07:15:17 +00:00
python: Add SSL support to the python ovs client library
SSL support is added to the ovs/stream.py. pyOpenSSL library is used to support SSL. If this library is not present, then the SSL stream is not registered with the Stream class. Signed-off-by: Numan Siddique <nusiddiq@redhat.com> Signed-off-by: Ben Pfaff <blp@ovn.org>
This commit is contained in:
committed by
Ben Pfaff
parent
603a316288
commit
d90ed7d65b
@@ -19,6 +19,11 @@ import select
|
|||||||
import socket
|
import socket
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
try:
|
||||||
|
from OpenSSL import SSL
|
||||||
|
except ImportError:
|
||||||
|
SSL = None
|
||||||
|
|
||||||
try:
|
try:
|
||||||
import eventlet.patcher
|
import eventlet.patcher
|
||||||
|
|
||||||
@@ -54,6 +59,9 @@ class _SelectSelect(object):
|
|||||||
def register(self, fd, events):
|
def register(self, fd, events):
|
||||||
if isinstance(fd, socket.socket):
|
if isinstance(fd, socket.socket):
|
||||||
fd = fd.fileno()
|
fd = fd.fileno()
|
||||||
|
if SSL and isinstance(fd, SSL.Connection):
|
||||||
|
fd = fd.fileno()
|
||||||
|
|
||||||
assert isinstance(fd, int)
|
assert isinstance(fd, int)
|
||||||
if events & POLLIN:
|
if events & POLLIN:
|
||||||
self.rlist.append(fd)
|
self.rlist.append(fd)
|
||||||
|
@@ -22,6 +22,11 @@ import ovs.poller
|
|||||||
import ovs.socket_util
|
import ovs.socket_util
|
||||||
import ovs.vlog
|
import ovs.vlog
|
||||||
|
|
||||||
|
try:
|
||||||
|
from OpenSSL import SSL
|
||||||
|
except ImportError:
|
||||||
|
SSL = None
|
||||||
|
|
||||||
vlog = ovs.vlog.Vlog("stream")
|
vlog = ovs.vlog.Vlog("stream")
|
||||||
|
|
||||||
|
|
||||||
@@ -39,7 +44,7 @@ def stream_or_pstream_needs_probes(name):
|
|||||||
|
|
||||||
|
|
||||||
class Stream(object):
|
class Stream(object):
|
||||||
"""Bidirectional byte stream. Currently only Unix domain sockets
|
"""Bidirectional byte stream. Unix domain sockets, tcp and ssl
|
||||||
are implemented."""
|
are implemented."""
|
||||||
|
|
||||||
# States.
|
# States.
|
||||||
@@ -54,6 +59,10 @@ class Stream(object):
|
|||||||
|
|
||||||
_SOCKET_METHODS = {}
|
_SOCKET_METHODS = {}
|
||||||
|
|
||||||
|
_SSL_private_key_file = None
|
||||||
|
_SSL_certificate_file = None
|
||||||
|
_SSL_ca_cert_file = None
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def register_method(method, cls):
|
def register_method(method, cls):
|
||||||
Stream._SOCKET_METHODS[method + ":"] = cls
|
Stream._SOCKET_METHODS[method + ":"] = cls
|
||||||
@@ -68,7 +77,7 @@ class Stream(object):
|
|||||||
@staticmethod
|
@staticmethod
|
||||||
def is_valid_name(name):
|
def is_valid_name(name):
|
||||||
"""Returns True if 'name' is a stream name in the form "TYPE:ARGS" and
|
"""Returns True if 'name' is a stream name in the form "TYPE:ARGS" and
|
||||||
TYPE is a supported stream type (currently only "unix:" and "tcp:"),
|
TYPE is a supported stream type ("unix:", "tcp:" and "ssl:"),
|
||||||
otherwise False."""
|
otherwise False."""
|
||||||
return bool(Stream._find_method(name))
|
return bool(Stream._find_method(name))
|
||||||
|
|
||||||
@@ -116,7 +125,7 @@ class Stream(object):
|
|||||||
return error, None
|
return error, None
|
||||||
else:
|
else:
|
||||||
status = ovs.socket_util.check_connection_completion(sock)
|
status = ovs.socket_util.check_connection_completion(sock)
|
||||||
return 0, Stream(sock, name, status)
|
return 0, cls(sock, name, status)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def _open(suffix, dscp):
|
def _open(suffix, dscp):
|
||||||
@@ -264,6 +273,18 @@ class Stream(object):
|
|||||||
# Don't delete the file: we might have forked.
|
# Don't delete the file: we might have forked.
|
||||||
self.socket.close()
|
self.socket.close()
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ssl_set_private_key_file(file_name):
|
||||||
|
Stream._SSL_private_key_file = file_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ssl_set_certificate_file(file_name):
|
||||||
|
Stream._SSL_certificate_file = file_name
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def ssl_set_ca_cert_file(file_name):
|
||||||
|
Stream._SSL_ca_cert_file = file_name
|
||||||
|
|
||||||
|
|
||||||
class PassiveStream(object):
|
class PassiveStream(object):
|
||||||
@staticmethod
|
@staticmethod
|
||||||
@@ -362,6 +383,7 @@ def usage(name):
|
|||||||
Active %s connection methods:
|
Active %s connection methods:
|
||||||
unix:FILE Unix domain socket named FILE
|
unix:FILE Unix domain socket named FILE
|
||||||
tcp:IP:PORT TCP socket to IP with port no of PORT
|
tcp:IP:PORT TCP socket to IP with port no of PORT
|
||||||
|
ssl:IP:PORT SSL socket to IP with port no of PORT
|
||||||
|
|
||||||
Passive %s connection methods:
|
Passive %s connection methods:
|
||||||
punix:FILE Listen on Unix domain socket FILE""" % (name, name)
|
punix:FILE Listen on Unix domain socket FILE""" % (name, name)
|
||||||
@@ -385,3 +407,66 @@ class TCPStream(Stream):
|
|||||||
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
sock.setsockopt(socket.IPPROTO_TCP, socket.TCP_NODELAY, 1)
|
||||||
return error, sock
|
return error, sock
|
||||||
Stream.register_method("tcp", TCPStream)
|
Stream.register_method("tcp", TCPStream)
|
||||||
|
|
||||||
|
|
||||||
|
class SSLStream(Stream):
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def verify_cb(conn, cert, errnum, depth, ok):
|
||||||
|
return ok
|
||||||
|
|
||||||
|
@staticmethod
|
||||||
|
def _open(suffix, dscp):
|
||||||
|
error, sock = TCPStream._open(suffix, dscp)
|
||||||
|
if error:
|
||||||
|
return error, None
|
||||||
|
|
||||||
|
# Create an SSL context
|
||||||
|
ctx = SSL.Context(SSL.SSLv23_METHOD)
|
||||||
|
ctx.set_verify(SSL.VERIFY_PEER, SSLStream.verify_cb)
|
||||||
|
ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
|
||||||
|
ctx.set_session_cache_mode(SSL.SESS_CACHE_OFF)
|
||||||
|
# If the client has not set the SSL configuration files
|
||||||
|
# exception would be raised.
|
||||||
|
ctx.use_privatekey_file(Stream._SSL_private_key_file)
|
||||||
|
ctx.use_certificate_file(Stream._SSL_certificate_file)
|
||||||
|
ctx.load_verify_locations(Stream._SSL_ca_cert_file)
|
||||||
|
|
||||||
|
ssl_sock = SSL.Connection(ctx, sock)
|
||||||
|
ssl_sock.set_connect_state()
|
||||||
|
return error, ssl_sock
|
||||||
|
|
||||||
|
def connect(self):
|
||||||
|
retval = super(SSLStream, self).connect()
|
||||||
|
|
||||||
|
if retval:
|
||||||
|
return retval
|
||||||
|
|
||||||
|
# TCP Connection is successful. Now do the SSL handshake
|
||||||
|
try:
|
||||||
|
self.socket.do_handshake()
|
||||||
|
except SSL.WantReadError:
|
||||||
|
return errno.EAGAIN
|
||||||
|
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def recv(self, n):
|
||||||
|
try:
|
||||||
|
return super(SSLStream, self).recv(n)
|
||||||
|
except SSL.WantReadError:
|
||||||
|
return (errno.EAGAIN, "")
|
||||||
|
|
||||||
|
def send(self, buf):
|
||||||
|
try:
|
||||||
|
if isinstance(buf, six.text_type):
|
||||||
|
# Convert to byte stream if the buffer is string type/unicode.
|
||||||
|
# pyopenssl version 0.14 expects the buffer to be byte string.
|
||||||
|
buf = buf.encode('utf-8')
|
||||||
|
return super(SSLStream, self).send(buf)
|
||||||
|
except SSL.WantWriteError:
|
||||||
|
return errno.EAGAIN
|
||||||
|
|
||||||
|
|
||||||
|
if SSL:
|
||||||
|
# Register SSL only if the OpenSSL module is available
|
||||||
|
Stream.register_method("ssl", SSLStream)
|
||||||
|
@@ -1198,10 +1198,36 @@ m4_define([OVSDB_CHECK_IDL_NOTIFY_PY],
|
|||||||
OVSDB_SERVER_SHUTDOWN
|
OVSDB_SERVER_SHUTDOWN
|
||||||
AT_CLEANUP])
|
AT_CLEANUP])
|
||||||
|
|
||||||
|
# This test uses the Python IDL implementation with ssl
|
||||||
|
m4_define([OVSDB_CHECK_IDL_NOTIFY_SSL_PY],
|
||||||
|
[AT_SETUP([$1 - SSL])
|
||||||
|
AT_SKIP_IF([test $HAVE_PYTHON = no])
|
||||||
|
$PYTHON -m OpenSSL.SSL
|
||||||
|
SSL_PRESENT=$?
|
||||||
|
AT_SKIP_IF([test $SSL_PRESENT != 0])
|
||||||
|
AT_KEYWORDS([ovsdb server idl Python notify - ssl socket])
|
||||||
|
AT_CHECK([ovsdb-tool create db $abs_srcdir/idltest.ovsschema],
|
||||||
|
[0], [stdout], [ignore])
|
||||||
|
PKIDIR=$abs_top_builddir/tests
|
||||||
|
AT_CHECK([ovsdb-server --log-file '-vPATTERN:console:ovsdb-server|%c|%m' \
|
||||||
|
--detach --no-chdir --pidfile="`pwd`"/pid \
|
||||||
|
--private-key=$PKIDIR/testpki-privkey2.pem \
|
||||||
|
--certificate=$PKIDIR/testpki-cert2.pem \
|
||||||
|
--ca-cert=$PKIDIR/testpki-cacert.pem \
|
||||||
|
--remote=pssl:0:127.0.0.1 --unixctl="`pwd`"/unixctl db], [0], [ignore], [ignore])
|
||||||
|
PARSE_LISTENING_PORT([ovsdb-server.log], [TCP_PORT])
|
||||||
|
AT_CHECK([$PYTHON $srcdir/test-ovsdb.py -t10 idl $srcdir/idltest.ovsschema \
|
||||||
|
ssl:127.0.0.1:$TCP_PORT $PKIDIR/testpki-privkey.pem \
|
||||||
|
$PKIDIR/testpki-cert.pem $PKIDIR/testpki-cacert.pem $2],
|
||||||
|
[0], [stdout], [ignore], [kill `cat pid`])
|
||||||
|
AT_CHECK([sort stdout | ${PERL} $srcdir/uuidfilt.pl]m4_if([$5],,, [[| $5]]),
|
||||||
|
[0], [$3], [], [kill `cat pid`])
|
||||||
|
OVSDB_SERVER_SHUTDOWN
|
||||||
|
AT_CLEANUP])
|
||||||
|
|
||||||
m4_define([OVSDB_CHECK_IDL_NOTIFY],
|
m4_define([OVSDB_CHECK_IDL_NOTIFY],
|
||||||
[OVSDB_CHECK_IDL_NOTIFY_PY($@)])
|
[OVSDB_CHECK_IDL_NOTIFY_PY($@)
|
||||||
|
OVSDB_CHECK_IDL_NOTIFY_SSL_PY($@)])
|
||||||
|
|
||||||
OVSDB_CHECK_IDL_NOTIFY([simple idl verify notify],
|
OVSDB_CHECK_IDL_NOTIFY([simple idl verify notify],
|
||||||
[['track-notify' \
|
[['track-notify' \
|
||||||
|
@@ -27,6 +27,7 @@ from ovs.db import data
|
|||||||
import ovs.db.types
|
import ovs.db.types
|
||||||
import ovs.ovsuuid
|
import ovs.ovsuuid
|
||||||
import ovs.poller
|
import ovs.poller
|
||||||
|
import ovs.stream
|
||||||
import ovs.util
|
import ovs.util
|
||||||
from ovs.fatal_signal import signal_alarm
|
from ovs.fatal_signal import signal_alarm
|
||||||
import six
|
import six
|
||||||
@@ -519,6 +520,12 @@ def do_idl(schema_file, remote, *commands):
|
|||||||
schema_helper = ovs.db.idl.SchemaHelper(schema_file)
|
schema_helper = ovs.db.idl.SchemaHelper(schema_file)
|
||||||
track_notify = False
|
track_notify = False
|
||||||
|
|
||||||
|
if remote.startswith("ssl:"):
|
||||||
|
ovs.stream.Stream.ssl_set_private_key_file(commands[0])
|
||||||
|
ovs.stream.Stream.ssl_set_certificate_file(commands[1])
|
||||||
|
ovs.stream.Stream.ssl_set_ca_cert_file(commands[2])
|
||||||
|
commands = commands[3:]
|
||||||
|
|
||||||
if commands and commands[0] == "track-notify":
|
if commands and commands[0] == "track-notify":
|
||||||
commands = commands[1:]
|
commands = commands[1:]
|
||||||
track_notify = True
|
track_notify = True
|
||||||
|
Reference in New Issue
Block a user