]>
git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/py/src/transport/TSSLSocket.py
2 # Licensed to the Apache Software Foundation (ASF) under one
3 # or more contributor license agreements. See the NOTICE file
4 # distributed with this work for additional information
5 # regarding copyright ownership. The ASF licenses this file
6 # to you under the Apache License, Version 2.0 (the
7 # "License"); you may not use this file except in compliance
8 # with the License. You may obtain a copy of the License at
10 # http://www.apache.org/licenses/LICENSE-2.0
12 # Unless required by applicable law or agreed to in writing,
13 # software distributed under the License is distributed on an
14 # "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 # KIND, either express or implied. See the License for the
16 # specific language governing permissions and limitations
27 from .sslcompat
import _match_hostname
, _match_has_ipaddress
28 from thrift
.transport
import TSocket
29 from thrift
.transport
.TTransport
import TTransportException
31 logger
= logging
.getLogger(__name__
)
32 warnings
.filterwarnings(
33 'default', category
=DeprecationWarning, module
=__name__
)
36 class TSSLBase(object):
37 # SSLContext is not available for Python < 2.7.9
38 _has_ssl_context
= sys
.hexversion
>= 0x020709F0
40 # ciphers argument is not available for Python < 2.7.0
41 _has_ciphers
= sys
.hexversion
>= 0x020700F0
43 # For python >= 2.7.9, use latest TLS that both client and server
45 # SSL 2.0 and 3.0 are disabled via ssl.OP_NO_SSLv2 and ssl.OP_NO_SSLv3.
46 # For python < 2.7.9, use TLS 1.0 since TLSv1_X nor OP_NO_SSLvX is
48 _default_protocol
= ssl
.PROTOCOL_SSLv23
if _has_ssl_context
else \
51 def _init_context(self
, ssl_version
):
52 if self
._has
_ssl
_context
:
53 self
._context
= ssl
.SSLContext(ssl_version
)
54 if self
._context
.protocol
== ssl
.PROTOCOL_SSLv23
:
55 self
._context
.options |
= ssl
.OP_NO_SSLv2
56 self
._context
.options |
= ssl
.OP_NO_SSLv3
59 self
._ssl
_version
= ssl_version
62 def _should_verify(self
):
63 if self
._has
_ssl
_context
:
64 return self
._context
.verify_mode
!= ssl
.CERT_NONE
66 return self
.cert_reqs
!= ssl
.CERT_NONE
69 def ssl_version(self
):
70 if self
._has
_ssl
_context
:
71 return self
.ssl_context
.protocol
73 return self
._ssl
_version
76 def ssl_context(self
):
79 SSL_VERSION
= _default_protocol
82 For backwards compatibility, it can be modified.
83 Use __init__ keyword argument "ssl_version" instead.
86 def _deprecated_arg(self
, args
, kwargs
, pos
, key
):
91 '%dth positional argument is deprecated.'
92 'please use keyword argument instead.'
93 % real_pos
, DeprecationWarning, stacklevel
=3)
97 'Duplicate argument: %dth argument and %s keyword argument.'
99 kwargs
[key
] = args
[pos
]
101 def _unix_socket_arg(self
, host
, port
, args
, kwargs
):
103 if host
is None and port
is None and len(args
) == 1 and key
not in kwargs
:
104 kwargs
[key
] = args
[0]
108 def __getattr__(self
, key
):
109 if key
== 'SSL_VERSION':
111 'SSL_VERSION is deprecated.'
112 'please use ssl_version attribute instead.',
113 DeprecationWarning, stacklevel
=2)
114 return self
.ssl_version
116 def __init__(self
, server_side
, host
, ssl_opts
):
117 self
._server
_side
= server_side
118 if TSSLBase
.SSL_VERSION
!= self
._default
_protocol
:
120 'SSL_VERSION is deprecated.'
121 'please use ssl_version keyword argument instead.',
122 DeprecationWarning, stacklevel
=2)
123 self
._context
= ssl_opts
.pop('ssl_context', None)
124 self
._server
_hostname
= None
125 if not self
._server
_side
:
126 self
._server
_hostname
= ssl_opts
.pop('server_hostname', host
)
128 self
._custom
_context
= True
131 'Incompatible arguments: ssl_context and %s'
132 % ' '.join(ssl_opts
.keys()))
133 if not self
._has
_ssl
_context
:
135 'ssl_context is not available for this version of Python')
137 self
._custom
_context
= False
138 ssl_version
= ssl_opts
.pop('ssl_version', TSSLBase
.SSL_VERSION
)
139 self
._init
_context
(ssl_version
)
140 self
.cert_reqs
= ssl_opts
.pop('cert_reqs', ssl
.CERT_REQUIRED
)
141 self
.ca_certs
= ssl_opts
.pop('ca_certs', None)
142 self
.keyfile
= ssl_opts
.pop('keyfile', None)
143 self
.certfile
= ssl_opts
.pop('certfile', None)
144 self
.ciphers
= ssl_opts
.pop('ciphers', None)
148 'Unknown keyword arguments: ', ' '.join(ssl_opts
.keys()))
150 if self
._should
_verify
:
151 if not self
.ca_certs
:
153 'ca_certs is needed when cert_reqs is not ssl.CERT_NONE')
154 if not os
.access(self
.ca_certs
, os
.R_OK
):
155 raise IOError('Certificate Authority ca_certs file "%s" '
156 'is not readable, cannot validate SSL '
157 'certificates.' % (self
.ca_certs
))
161 return self
._certfile
164 def certfile(self
, certfile
):
165 if self
._server
_side
and not certfile
:
166 raise ValueError('certfile is needed for server-side')
167 if certfile
and not os
.access(certfile
, os
.R_OK
):
168 raise IOError('No such certfile found: %s' % (certfile
))
169 self
._certfile
= certfile
171 def _wrap_socket(self
, sock
):
172 if self
._has
_ssl
_context
:
173 if not self
._custom
_context
:
174 self
.ssl_context
.verify_mode
= self
.cert_reqs
176 self
.ssl_context
.load_cert_chain(self
.certfile
,
179 self
.ssl_context
.set_ciphers(self
.ciphers
)
181 self
.ssl_context
.load_verify_locations(self
.ca_certs
)
182 return self
.ssl_context
.wrap_socket(
183 sock
, server_side
=self
._server
_side
,
184 server_hostname
=self
._server
_hostname
)
187 'ssl_version': self
._ssl
_version
,
188 'server_side': self
._server
_side
,
189 'ca_certs': self
.ca_certs
,
190 'keyfile': self
.keyfile
,
191 'certfile': self
.certfile
,
192 'cert_reqs': self
.cert_reqs
,
195 if self
._has
_ciphers
:
196 ssl_opts
['ciphers'] = self
.ciphers
199 'ciphers is specified but ignored due to old Python version')
200 return ssl
.wrap_socket(sock
, **ssl_opts
)
203 class TSSLSocket(TSocket
.TSocket
, TSSLBase
):
205 SSL implementation of TSocket
207 This class creates outbound sockets wrapped using the
208 python standard ssl module for encrypted connections.
212 # def __init__(self, host='localhost', port=9090, unix_socket=None,
214 # Deprecated signature
215 # def __init__(self, host='localhost', port=9090, validate=True,
216 # ca_certs=None, keyfile=None, certfile=None,
217 # unix_socket=None, ciphers=None):
218 def __init__(self
, host
='localhost', port
=9090, *args
, **kwargs
):
219 """Positional arguments: ``host``, ``port``, ``unix_socket``
221 Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``,
222 ``ssl_version``, ``ca_certs``,
223 ``ciphers`` (Python 2.7.0 or later),
224 ``server_hostname`` (Python 2.7.9 or later)
225 Passed to ssl.wrap_socket. See ssl.wrap_socket documentation.
227 Alternative keyword arguments: (Python 2.7.9 or later)
228 ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket
229 ``server_hostname``: Passed to SSLContext.wrap_socket
231 Common keyword argument:
232 ``validate_callback`` (cert, hostname) -> None:
233 Called after SSL handshake. Can raise when hostname does not
235 ``socket_keepalive`` enable TCP keepalive, default off.
237 self
.is_valid
= False
242 raise TypeError('Too many positional argument')
243 if not self
._unix
_socket
_arg
(host
, port
, args
, kwargs
):
244 self
._deprecated
_arg
(args
, kwargs
, 0, 'validate')
245 self
._deprecated
_arg
(args
, kwargs
, 1, 'ca_certs')
246 self
._deprecated
_arg
(args
, kwargs
, 2, 'keyfile')
247 self
._deprecated
_arg
(args
, kwargs
, 3, 'certfile')
248 self
._deprecated
_arg
(args
, kwargs
, 4, 'unix_socket')
249 self
._deprecated
_arg
(args
, kwargs
, 5, 'ciphers')
251 validate
= kwargs
.pop('validate', None)
252 if validate
is not None:
253 cert_reqs_name
= 'CERT_REQUIRED' if validate
else 'CERT_NONE'
255 'validate is deprecated. please use cert_reqs=ssl.%s instead'
257 DeprecationWarning, stacklevel
=2)
258 if 'cert_reqs' in kwargs
:
259 raise TypeError('Cannot specify both validate and cert_reqs')
260 kwargs
['cert_reqs'] = ssl
.CERT_REQUIRED
if validate
else ssl
.CERT_NONE
262 unix_socket
= kwargs
.pop('unix_socket', None)
263 socket_keepalive
= kwargs
.pop('socket_keepalive', False)
264 self
._validate
_callback
= kwargs
.pop('validate_callback', _match_hostname
)
265 TSSLBase
.__init
__(self
, False, host
, kwargs
)
266 TSocket
.TSocket
.__init
__(self
, host
, port
, unix_socket
,
267 socket_keepalive
=socket_keepalive
)
271 self
.handle
.settimeout(0.001)
272 self
.handle
= self
.handle
.unwrap()
273 except (ssl
.SSLError
, socket
.error
, OSError):
274 # could not complete shutdown in a reasonable amount of time. bail.
276 TSocket
.TSocket
.close(self
)
280 warnings
.warn('validate is deprecated. please use cert_reqs instead',
281 DeprecationWarning, stacklevel
=2)
282 return self
.cert_reqs
!= ssl
.CERT_NONE
285 def validate(self
, value
):
286 warnings
.warn('validate is deprecated. please use cert_reqs instead',
287 DeprecationWarning, stacklevel
=2)
288 self
.cert_reqs
= ssl
.CERT_REQUIRED
if value
else ssl
.CERT_NONE
290 def _do_open(self
, family
, socktype
):
291 plain_sock
= socket
.socket(family
, socktype
)
293 return self
._wrap
_socket
(plain_sock
)
294 except Exception as ex
:
296 msg
= 'failed to initialize SSL'
297 logger
.exception(msg
)
298 raise TTransportException(type=TTransportException
.NOT_OPEN
, message
=msg
, inner
=ex
)
301 super(TSSLSocket
, self
).open()
302 if self
._should
_verify
:
303 self
.peercert
= self
.handle
.getpeercert()
305 self
._validate
_callback
(self
.peercert
, self
._server
_hostname
)
307 except TTransportException
:
309 except Exception as ex
:
310 raise TTransportException(message
=str(ex
), inner
=ex
)
313 class TSSLServerSocket(TSocket
.TServerSocket
, TSSLBase
):
314 """SSL implementation of TServerSocket
316 This uses the ssl module's wrap_socket() method to provide SSL
317 negotiated encryption.
321 # def __init__(self, host='localhost', port=9090, unix_socket=None, **ssl_args):
322 # Deprecated signature
323 # def __init__(self, host=None, port=9090, certfile='cert.pem', unix_socket=None, ciphers=None):
324 def __init__(self
, host
=None, port
=9090, *args
, **kwargs
):
325 """Positional arguments: ``host``, ``port``, ``unix_socket``
327 Keyword arguments: ``keyfile``, ``certfile``, ``cert_reqs``, ``ssl_version``,
328 ``ca_certs``, ``ciphers`` (Python 2.7.0 or later)
329 See ssl.wrap_socket documentation.
331 Alternative keyword arguments: (Python 2.7.9 or later)
332 ``ssl_context``: ssl.SSLContext to be used for SSLContext.wrap_socket
333 ``server_hostname``: Passed to SSLContext.wrap_socket
335 Common keyword argument:
336 ``validate_callback`` (cert, hostname) -> None:
337 Called after SSL handshake. Can raise when hostname does not
342 raise TypeError('Too many positional argument')
343 if not self
._unix
_socket
_arg
(host
, port
, args
, kwargs
):
344 self
._deprecated
_arg
(args
, kwargs
, 0, 'certfile')
345 self
._deprecated
_arg
(args
, kwargs
, 1, 'unix_socket')
346 self
._deprecated
_arg
(args
, kwargs
, 2, 'ciphers')
348 if 'ssl_context' not in kwargs
:
349 # Preserve existing behaviors for default values
350 if 'cert_reqs' not in kwargs
:
351 kwargs
['cert_reqs'] = ssl
.CERT_NONE
352 if'certfile' not in kwargs
:
353 kwargs
['certfile'] = 'cert.pem'
355 unix_socket
= kwargs
.pop('unix_socket', None)
356 self
._validate
_callback
= \
357 kwargs
.pop('validate_callback', _match_hostname
)
358 TSSLBase
.__init
__(self
, True, None, kwargs
)
359 TSocket
.TServerSocket
.__init
__(self
, host
, port
, unix_socket
)
360 if self
._should
_verify
and not _match_has_ipaddress
:
361 raise ValueError('Need ipaddress and backports.ssl_match_hostname '
362 'module to verify client certificate')
364 def setCertfile(self
, certfile
):
365 """Set or change the server certificate file used to wrap new
368 @param certfile: The filename of the server certificate,
369 i.e. '/etc/certs/server.pem'
372 Raises an IOError exception if the certfile is not present or unreadable.
375 'setCertfile is deprecated. please use certfile property instead.',
376 DeprecationWarning, stacklevel
=2)
377 self
.certfile
= certfile
380 plain_client
, addr
= self
.handle
.accept()
382 client
= self
._wrap
_socket
(plain_client
)
383 except (ssl
.SSLError
, socket
.error
, OSError):
384 logger
.exception('Error while accepting from %s', addr
)
385 # failed handshake/ssl wrap, close socket to client
388 # We can't raise the exception, because it kills most TServer derived
390 # Instead, return None, and let the TServer instance deal with it in
391 # other exception handling. (but TSimpleServer dies anyway)
394 if self
._should
_verify
:
395 client
.peercert
= client
.getpeercert()
397 self
._validate
_callback
(client
.peercert
, addr
[0])
398 client
.is_valid
= True
400 logger
.warn('Failed to validate client certificate address: %s',
401 addr
[0], exc_info
=True)
406 result
= TSocket
.TSocket()
407 result
.handle
= client